作者:京东物流 王北永 姚再毅 李振
1 背景
目前,ducc实现了实时近乎所有配置动态生效的场景,但是配置是否实时生效,不能直观展示每个机器上jvm内对象对应的参数是否已变更为准确的值,大部分时候需要查看日志确认是否生效。
2 技术依赖
1)Jsf:京东RPC框架,用作机器之间的通讯工具
2)redis/redisson:redis,用作配置信息的存储
3)ZK/Curator: Zookeeper,用作配置信息的存储 和redis二选一
3)clover:定时任务集群,用作任务延迟或周期性执行
3 实现原理
1)接入方:
各个接入系统通过接入管理模块获取token,并指定所在系统发布的的服务器ip,用作后续的ip鉴权。当系统启动时,自动在各个系统生成接口提供方,并注册到JSF注册中心。别名需各个系统唯一不重复。鉴权为统一服务端做IP鉴权。
2)统一配置服务端:
提供按不同接入方、不同系统、不同环境的配置界面。业务人员可设定自动生效时间或者立即生效时间。如果是立刻生效,则通过JSF广播或者指定机器生效配置。如果是定时生效,则新增定时器并指定生效规则,达到时间后触发广播通知。
整个接入方和统一配置服务端的架构如下图
4 实现步骤
1)重写JSF类ConsumerConfig类方法refer,将其中的轮训调用客户端改为广播调用客户端BroadCastClient。
this.client= new BroadCastClient(this); this.proxyInvoker = new ClientProxyInvoker(this); ProtocolFactory.check(Constants.ProtocolType.valueOf(this.getProtocol()), Constants.CodecType.valueOf(this.getSerialization())); this.proxyIns = (T) ProxyFactory.buildProxy(this.getProxy(), this.getProxyClass(), this.proxyInvoker);
2)广播调用客户端方法分别获取当前注册中心有心跳的服务提供者和已失去连接的机器列表。对统一配置来讲,要么同时失败,要么同时成功,判断如果存在不正常的服务提供方,则不同步。只有全部提供方存在才可以开始广播配置信息
ConcurrentHashMap<Provider, ClientTransport> concurrentHashMap = this.connectionHolder.getAliveConnections(); ConcurrentHashMap<Provider, ClientTransportConfig> deadConcurrentHashMap = this.connectionHolder.getDeadConnections(); if(deadConcurrentHashMap!=null && deadConcurrentHashMap.size()>0){ log.warn("当前别名{}存在不正常服务提供方数量{},请关注!",msg.getAlias(),deadConcurrentHashMap.size()); throw new RpcException(String.format("当前别名%s存在不正常服务提供方数量%s,请关注!",msg.getAlias(),deadConcurrentHashMap.size())); } if(concurrentHashMap.isEmpty()){ log.info("当前别名{}不存在正常服务提供方",msg.getAlias()); throw new RpcException(String.format("当前别名%s不存在正常服务提供方",msg.getAlias())); } Iterator aliveConnections = concurrentHashMap.entrySet().iterator(); log.info("当前别名{}存在正常服务提供方数量{}",msg.getAlias(),concurrentHashMap.size()); while (aliveConnections.hasNext()) { Entry<Provider, ClientTransport> entry = (Entry) aliveConnections.next(); Provider provider = (Provider) entry.getKey(); log.info("当前连接ip={}、port={}、datacenterCode={}",provider.getIp(),provider.getPort(),provider.getDatacenterCode()); ClientTransport connection = (ClientTransport) entry.getValue(); if (connection != null && connection.isOpen()) { try { result = super.sendMsg0(new Connection(provider, connection), msg); } catch (RpcException rpc) { exception = rpc; log.warn(rpc.getMessage(), rpc); } catch (Throwable e) { exception = new RpcException(e.getMessage(), e); log.warn(e.getMessage(), e); } } }
3)服务配置端,当业务人员配置及时生效或者任务达到时,则根据配置,生成服务调用方,通过统一刷新接口将配置同步刷新到对应的接入系统中,如下图为操作界面,当增删改查时,会将属性增量同步。