Пример #1
0
        /// <summary>
        /// HttpProxy 配置,主要配置那些微服务可以提供外部api服务
        /// </summary>
        /// <param name="proxyName">proxyName</param>
        /// <param name="updateAction">updateAction</param>
        /// <returns>HttpProxy</returns>
        public static Configuration GetHttpProxy(string proxyName, Action <Configuration> updateAction)
        {
            switch (configCenter.ConfigType)
            {
            case ConfigType.Local:
            case ConfigType.HttpFile:
                return(config);    // 直接返回本地配置

            case ConfigType.Zookeeper:
                Action <Configuration> callBack = (Configuration cfgValue) =>
                {
                    GetHttpProxy(proxyName, updateAction); // 调用一次方法,挂载回调
                    updateAction(cfgValue);                // 重连之后要执行回调,做变更
                };
                callBackList[proxyName] = callBack;        // 断线重连之后,要把当前方法封装起来,作为回调

                var router = new ConfigWatcher();
                router.OnChange += (string path) =>
                {
                    if (updateAction != null && !string.IsNullOrEmpty(path))
                    {
                        var proxyStr = GrantZookeeperManager.GetNodeData(path, router);
                        if (string.IsNullOrEmpty(proxyStr))
                        {
                            return;
                        }
                        var httpProxy = Newtonsoft.Json.JsonConvert.DeserializeObject <Configuration>(proxyStr);
                        config.HttpProxy = httpProxy.HttpProxy;
                        updateAction(config);
                    }
                };
                string p   = GrantZookeeperManager.getConfigPath(proxyName);
                var    cfg = GrantZookeeperManager.GetNodeData(p + "/" + GrantHttpProxy.HttpProxy, router);
                if (string.IsNullOrEmpty(cfg))
                {
                    logger.LogWarning("获取代理层配置为空,这个代理层将无法提供代理服务 ServerSetting.GetHttpProxy is Null");
                }
                else
                {
                    var proxy = Newtonsoft.Json.JsonConvert.DeserializeObject <Configuration>(cfg);
                    config.HttpProxy = proxy.HttpProxy;
                }
                return(config);
            }

            return(null);
        }
Пример #2
0
        /// <summary>
        /// 注册zookeeper使用
        /// </summary>
        /// <param name="serverName"></param>
        /// <param name="ip"></param>
        /// <param name="port"></param>
        /// <param name="enable"></param>
        /// <param name="timeout"></param>
        public static void RegisterRouter(string serverName, string ip, int port, bool enable, int timeout)
        {
            if (configCenter.ConfigType != ConfigType.Zookeeper)
            {
                return; // 本地配置不走zookeeper;
            }
            string path       = GrantZookeeperManager.SetRouter(serverName, ip, port, enable, timeout);
            var    cfgWatcher = new ConfigWatcher();

            cfgWatcher.OnChange += (string p) =>
            {
                var changeData = GrantZookeeperManager.GetNodeData(path, cfgWatcher);
                if (!string.IsNullOrEmpty(changeData))
                {
                    try
                    {
                        var clientItem = Newtonsoft.Json.JsonConvert.DeserializeObject <ClientItem>(changeData);
                        logger.LogWarning($"我的配置被修改为:{changeData}");
                        if (clientItem != null && !clientItem.Enable)
                        {
                            // 我被下线了,更新本地配置"Enable": false,下一次重启需要带上这个状态
                            logger.LogInformation($"我被管理员下线了,哼。。。。serverName={serverName},ip={ip},port = {port}");
                        }
                        else
                        {
                            logger.LogInformation($"我被管理员上线了,哈哈哈哈。。。。serverName={serverName},ip={ip},port = {port}");
                        }
                        // 在这里修改本地配置快照
                        if (clientItem == null || string.IsNullOrEmpty(clientItem.Ip) || clientItem.Port < 1)
                        {
                            return;
                        }
                        config.GrantConfig.RpcService.Ip      = clientItem.Ip;
                        config.GrantConfig.RpcService.Port    = clientItem.Port;
                        config.GrantConfig.RpcService.Pool    = clientItem.Pool;
                        config.GrantConfig.RpcService.Enable  = clientItem.Enable;
                        config.GrantConfig.RpcService.TimeOut = clientItem.TimeOut;
                        //copyConfig(); 暂时注掉,这里还没思考好
                    }
                    catch (Exception e)
                    {
                        logger.LogCritical(e, $"RegisterRouter.cfgWatcher.OnChange.Error,serverName={serverName},ip={ip},port = {port}");
                    }
                }
            };
            GrantZookeeperManager.GetNodeData(path, cfgWatcher); // 监控自己router节点的内容,有可能被置为下线;
        }
Пример #3
0
        private static void initlizeData(string appName)
        {
            // 检查标准配置,第一次可能zk是空
            // 检查标准配置节点,帮助初始化
            var standConfig = getStandConfig();

            GrantZookeeperManager.CheckConfig(appName, standConfig);


            // 拉取当前AppName的配置,需要注册watcher
            var dataWatcher = new ConfigWatcher();

            dataWatcher.OnChange += (string path) =>
            {
                string configData = GrantZookeeperManager.GetNodeData(path, dataWatcher);
                if (string.IsNullOrEmpty(configData))
                {
                    return;
                }
                UpdateZookeeper(path, configData);
            };

            List <string>
            childrens = GrantZookeeperManager.GetConfigChildren(appName,
                                                                null); // 配置是整个获取节点,分别获取配置和分别增加watcher

            if (childrens != null && childrens.Count > 0)
            {
                string root = GrantZookeeperManager.getConfigPath(appName);
                if (string.IsNullOrEmpty(root))
                {
                    return;
                }
                foreach (var item in childrens)
                {
                    // 需要根据节点路径来判断是哪个节点变化了
                    string path       = root + "/" + item;
                    string configData = GrantZookeeperManager.GetNodeData(path, dataWatcher);
                    UpdateZookeeper(path, configData);
                }
            }
        }
Пример #4
0
        private static RpcClients getRouters(string appName, Watcher serviceRouterWatcher)
        {
            List <string> nodeList = GrantZookeeperManager.GetRouterChildren(appName, serviceRouterWatcher); // 路由是整个获取节点整个跟节点监控,因为子节点是虚拟的

            if (nodeList == null || nodeList.Count < 1)
            {
                string msg = $"你代码里面调用了 {appName} ,但是从zookeeper中取不到这个服务的路由信息,GetAppClient.appName={appName}.GetChildrenNode==null";
                logger.LogWarning(msg);
                return(null);
            }

            RpcClients rpcClients = new RpcClients();

            rpcClients.Clients = new List <Client>();
            Client client = new Client()
            {
                RouterType = RouterType.Random, ServerName = appName
            };

            client.Items = new List <ClientItem>();
            rpcClients.Clients.Add(client);
            string p = GrantZookeeperManager.getRouterPath(appName);

            if (string.IsNullOrEmpty(p))
            {
                return(null);
            }
            foreach (var item in nodeList)
            {
                string nodeData = GrantZookeeperManager.GetNodeData(p + "/" + item, serviceRouterWatcher);
                if (string.IsNullOrEmpty(nodeData))
                {
                    continue;
                }
                ClientItem clientItem = Newtonsoft.Json.JsonConvert.DeserializeObject <ClientItem>(nodeData);
                client.Items.Add(clientItem);
            }
            config.RpcClients = rpcClients;
            return(rpcClients);
        }
Пример #5
0
        /// <summary>
        /// RpcClinet 客户端初始化信息,路由
        /// </summary>
        /// <param name="appName">appName</param>
        /// <returns>XElement</returns>
        public static Configuration GetAppClient(string appName, Action <Configuration> updateAction)
        {
            switch (configCenter.ConfigType)
            {
            case ConfigType.Local:
            case ConfigType.HttpFile:
                return(config);    // 直接返回本地配置

            case ConfigType.Zookeeper:
                // 去远程拉取,因为初始化的时候,是初始化的当前服务的配置,
                // 引用客户端的配置需要分别拉取
                // 因为zk没有提供拉节点数据和子节点的接口,只能先拉到Node然后在依次拉子节点的数据
                var serviceRouterWatcher = new ServiceRouterWatcher();
                serviceRouterWatcher.OnChange += (string path) =>
                {
                    if (updateAction != null && !string.IsNullOrEmpty(path))
                    {
                        config.RpcClients = getRouters(appName, serviceRouterWatcher);
                        updateAction(config);
                    }
                };

                Action <Configuration> callBack = (Configuration cfgValue) =>
                {
                    GetAppClient(appName, updateAction); // 调用一次方法,挂载回调
                    updateAction(config);                // 重连之后要执行回调,做变更
                };

                callBackList[appName] = callBack;     // 断线重连之后,要把当前方法封装起来,作为回调

                config.RpcClients = getRouters(appName, serviceRouterWatcher);

                GrantZookeeperManager.SetRelation(appName, _appName);     // 设置调用关系

                return(config);

            default:
                return(null);
            }
        }
Пример #6
0
        /// <summary>
        /// 注册服务
        /// </summary>
        /// <param name="server">服务配置</param>
        public void RpcServerRegister(GrantServerConfig server)
        {
            try
            {
                distributer = new GrantRpcDistributer(server);  // 这里会初始化serverSetting和zk连接以及其他配置

                // 注册zk登记自己(包括serverName,ip,port,pool)
                server.ServerName = distributer.ShortName; // 兼容集中配置
                if (server.PortList != null && server.PortList.ContainsKey(server.ServerName))
                {
                    server.Port = server.PortList[server.ServerName];
                    ServerSetting.UpdateRpcPort(server.Port);
                }

                bool IsExit = false;
                // 要先初始化系统相关组件之后才能注册Rpc端口,要不请·求上来了,还没有初始化其他的,会有问题
                // 去zk注册自己
                if (ServerSetting.ConfigCenter.ConfigType == ConfigType.Zookeeper)
                {
                    Task.Run(() =>
                    {
                        Random r = new Random();
                        // rpc注册socket是阻塞的,只能提前通过线程注册, 等待1s 再注册路由,这个过程中,如果rpc端口不成功,将会撤销资源退出,这个注册也就失败了,防止先注册路由,然后再rpc注册异常,导致路由瞬间抖动,造成瞬间无效广播;
                        Thread.Sleep(r.Next(1500, 2500));  // 随机等待在1s--2s之间吧,防止集群一瞬间同时重启,容易形成广播风暴,形成雪崩;
                        if (IsExit)
                        {
                            return;
                        }
                        ServerSetting.RegisterRouter(server.ServerName, server.Ip, server.Port,
                                                     ServerSetting.GetRpcServer().Enable, server.TimeOut);
                        logger.LogInformation($"\r\n服务名:{server.ServerName},开始监听Ip是:{server.Ip},端口是:{server.Port}\r\n*******************▄︻┻┳══━一zookeeper路由注册成功,系统启动成功!▄︻┻┳══━一*******************");
                    });
                }
                else
                {
                    Task.Run(() =>
                    {
                        Thread.Sleep(1500);
                        if (IsExit)
                        {
                            return;
                        }
                        logger.LogInformation($"\r\n服务名:{server.ServerName},开始监听Ip是:{server.Ip},端口是:{server.Port}\r\n*******************▄︻┻┳══━一启用本地配置,系统启动成功!▄︻┻┳══━一*******************");
                    });
                }
                ServerRegister(server); // 底层通讯和业务进行绑定
                // 只有socket才会阻塞
                if (server.ServerType == ServerType.Thrift || server.ServerType == ServerType.Grpc)
                {
                    IsExit = true; // 通知所有注册方,停止注册,系统要撤销所有资源了,防止其他异常,优雅一点点,不能太暴力
                    logger.LogWarning($"\r\n rpc端口监听异常退出:{server.ServerName}{server.Port}{server.AssemblyPath}" + "  \r\n time=" +
                                      DateTime.Now.ToString("yy-MM-dd HH:mm:ss:ffff"));

                    if (ServerSetting.ConfigCenter.ConfigType == ConfigType.Zookeeper)
                    {
                        GrantZookeeperManager.ClearRouter(server.ServerName, server.Ip);
                    }

                    // rpc监听是hold住当前线程的,因为底层吞掉异常了,跑到这里来就说明异常了,要彻底释放
                    // 让他等一下,把日志写完
                    System.Threading.Thread.Sleep(1000);

                    // 如果启动失败,一定要彻底清理退出,因为在线程中,只能这样,要不只是线程退出,主程序还运行
                    Environment.Exit(0);
                }
            }
            catch (Exception ex)
            {
                logger.LogCritical(ex, "\r\n Error 服务异常退出  " + DateTime.Now.ToString("yy-MM-dd HH:mm:ss:ffff"));

                // 让他等一下,把日志写完
                System.Threading.Thread.Sleep(1000);

                // 如果启动失败,一定要彻底清理退出,因为在线程中,只能这样,要不只是线程退出,主程序还运行
                Environment.Exit(0);
            }
        }
Пример #7
0
        /// <summary>
        /// RpcServer 服务端
        /// 根据配置加载配置信息,凡是后端微服务不用自己初始化,
        /// 直接注册RpcServer就可以(RpcServer里面已经初始化了),如果也要注册RpcClient,RpcClient的注册要放在后面
        /// 在前端代理层,需要先初始化Initlize()
        /// </summary>
        /// <param name="appName">appName</param>
        /// <param name="pool">pool</param>
        public static void Initlize(string appName, int pool)
        {
            try
            {
                checkInitlize();

                ServerSetting._appName = appName;
                ServerSetting._pool    = pool;

                switch (configCenter.ConfigType)
                {
                default:
                case ConfigType.Local:
                case ConfigType.HttpFile:
                    UpdateLocal(config);     // 本地的配置,一次就加载完整了
                    break;

                case ConfigType.Zookeeper:
                    // 根据appName拉取信息,注册zk,这里需要注意热更新的问题
                    // 初始化连接,并注册对连接的监听
                    ZKConnectionWatcher connectionWatcher = new ZKConnectionWatcher();
                    connectionWatcher.OnChange += (string path) =>
                    {
                        GrantZookeeperManager.reConnection(new KeeperException.SessionExpiredException(),
                                                           () =>
                        {
                            try
                            {
                                // Initlize(appName, pool); // 更新了config
                                initlizeData(appName);
                            }
                            catch (Exception e)         // 重连的就不能抛异常了
                            {
                                logger.LogError(e, $"reConnection.Initlize.Error");
                            }

                            var list = callBackList.Values.ToArray();
                            for (int i = 0; i < list.Length; i++)
                            {
                                try
                                {
                                    list[i]?.Invoke(config);
                                    // callBackList[callBackList.Keys[i]]?.Invoke(config); // 依次执行回调链,保证更新及时
                                }
                                catch (Exception e)
                                {
                                    logger.LogError(e, $"reConnection.callBack.Error");
                                }
                            }


                            // 断线重连,注册自己
                            RegisterRouter(ServerSetting.AppName,
                                           ServerSetting.Config.GrantConfig.RpcService.Ip,
                                           ServerSetting.Config.GrantConfig.RpcService.Port,
                                           ServerSetting.Config.GrantConfig.RpcService.Enable,
                                           ServerSetting.Config.GrantConfig.RpcService.TimeOut);
                        });
                    };
                    GrantZookeeperManager.Initlize(
                        configCenter.Ip,
                        configCenter.SessionTimeout, connectionWatcher);

                    initlizeData(appName);

                    break;
                }
            }
            catch (Exception ex)
            {
                logger.LogCritical(ex, "ServerSetting.Initlize.Error");
                throw ex;
            }
        }