此文已由作者赵计刚授权网易云社区发布。
欢迎访问网易云社区,了解更多网易技术产品运营经验。
二、consumer端心跳机制
//创建ExchangeClient,对第一次服务发现providers路径下的相关url建立长连接 -->getClients(URL url) -->getSharedClient(URL url) -->ExchangeClient exchangeClient = initClient(url) -->Exchangers.connect(url, requestHandler) -->HeaderExchanger.connect(URL url, ExchangeHandler handler) -->new DecodeHandler(new HeaderExchangeHandler(handler))) -->Transporters.connect(URL url, ChannelHandler... handlers) -->NettyTransporter.connect(URL url, ChannelHandler listener) -->new NettyClient(url, listener) -->new MultiMessageHandler(HeartbeatHandler(AllChannelHandler(handler))) -->getChannelCodec(url)//获取Codec2,这里是DubboCountCodec实例 -->doOpen()//开启netty客户端 -->doConnect()//连接服务端,建立长连接 -->new HeaderExchangeClient(Client client, boolean needHeartbeat)//上述的NettyClient实例,needHeartbeat:true -->startHeatbeatTimer()//启动心跳计数器
客户端在initClient(url)中设置了heartbeat参数(默认为60s,用户自己设置的方式见“一”中所讲),如下:
1 /** 2 * Create new connection 3 */ 4 private ExchangeClient initClient(URL url) { 5 ... 6 // enable heartbeat by default 7 url = url.addParameterIfAbsent(Constants.HEARTBEAT_KEY, String.valueOf(Constants.DEFAULT_HEARTBEAT)); 8 9 ... 10 11 ExchangeClient client; 12 try { 13 // connection should be lazy 14 if (url.getParameter(Constants.LAZY_CONNECT_KEY, false)) { 15 client = new LazyConnectExchangeClient(url, requestHandler);16 } else { 17 client = Exchangers.connect(url, requestHandler); 18 } 19 } catch (RemotingException e) { 20 throw new RpcException("Fail to create remoting client for service(" + url + "): " + e.getMessage(), e); 21 } 22 return client; 23 }
与provider类似,来看一下最后开启心跳检测的地方。
1 public class HeaderExchangeClient implements ExchangeClient { 2 private static final ScheduledThreadPoolExecutor scheduled = new ScheduledThreadPoolExecutor(2, new NamedThreadFactory("dubbo-remoting-client-heartbeat", true)); 3 private final Client client; 4 private final ExchangeChannel channel; 5 // heartbeat timer 6 private ScheduledFuture<?> heartbeatTimer; 7 // heartbeat(ms), default value is 0 , won't execute a heartbeat. 8 private int heartbeat; 9 private int heartbeatTimeout; 10 11 public HeaderExchangeClient(Client client, boolean needHeartbeat) { 12 if (client == null) { 13 throw new IllegalArgumentException("client == null"); 14 } 15 this.client = client; 16 this.channel = new HeaderExchangeChannel(client); 17 String dubbo = client.getUrl().getParameter(Constants.DUBBO_VERSION_KEY); 18 this.heartbeat = client.getUrl().getParameter(Constants.HEARTBEAT_KEY, dubbo != null && dubbo.startsWith("1.0.") ? Constants.DEFAULT_HEARTBEAT : 0); 19 this.heartbeatTimeout = client.getUrl().getParameter(Constants.HEARTBEAT_TIMEOUT_KEY, heartbeat * 3); 20 if (heartbeatTimeout < heartbeat * 2) { 21 throw new IllegalStateException("heartbeatTimeout < heartbeatInterval * 2"); 22 } 23 if (needHeartbeat) { 24 startHeatbeatTimer(); 25 } 26 } 27 28 private void startHeatbeatTimer() { 29 stopHeartbeatTimer(); 30 if (heartbeat > 0) { 31 heartbeatTimer = scheduled.scheduleWithFixedDelay( 32 new HeartBeatTask(new HeartBeatTask.ChannelProvider() { 33 public Collection<Channel> getChannels() { 34 return Collections.<Channel>singletonList(HeaderExchangeClient.this); 35 } 36 }, heartbeat, heartbeatTimeout), 37 heartbeat, heartbeat, TimeUnit.MILLISECONDS); 38 } 39 } 40 41 private void stopHeartbeatTimer() { 42 if (heartbeatTimer != null && !heartbeatTimer.isCancelled()) { 43 try { 44 heartbeatTimer.cancel(true); 45 scheduled.purge(); 46 } catch (Throwable e) { 47 if (logger.isWarnEnabled()) { 48 logger.warn(e.getMessage(), e); 49 } 50 } 51 } 52 heartbeatTimer = null; 53 } 54 }
主要看一下startHeartbeatTimer()方法,与provider相同,只是provider是获取NettyServer的所有的NettyChannel,而consumer只是获取当前的对象。
consumer的handler处理链与provider完全相同。
最后来看一下consumer的重连机制:AbstractClient#reconnect
1 public void reconnect() throws RemotingException { 2 disconnect(); 3 connect(); 4 } 5 6 public void disconnect() { 7 connectLock.lock(); 8 try { 9 destroyConnectStatusCheckCommand(); 10 try { 11 Channel channel = getChannel(); 12 if (channel != null) { 13 channel.close(); 14 } 15 } catch (Throwable e) { 16 logger.warn(e.getMessage(), e); 17 } 18 try { 19 doDisConnect(); 20 } catch (Throwable e) { 21 logger.warn(e.getMessage(), e); 22 } 23 } finally { 24 connectLock.unlock(); 25 } 26 } 27 28 protected void connect() throws RemotingException { 29 connectLock.lock(); 30 try { 31 if (isConnected()) { 32 return; 33 } 34 initConnectStatusCheckCommand(); 35 doConnect(); 36 if (!isConnected()) { 37 throw new RemotingException(this, "Failed connect to server " + getRemoteAddress() + " from " + getClass().getSimpleName() + " " 38 + NetUtils.getLocalHost() + " using dubbo version " + Version.getVersion() 39 + ", cause: Connect wait timeout: " + getTimeout() + "ms."); 40 } else { 41 if (logger.isInfoEnabled()) { 42 logger.info("Successed connect to server " + getRemoteAddress() + " from " + getClass().getSimpleName() + " " 43 + NetUtils.getLocalHost() + " using dubbo version " + Version.getVersion() 44 + ", channel is " + this.getChannel()); 45 } 46 } 47 reconnect_count.set(0); 48 reconnect_error_log_flag.set(false); 49 } catch (RemotingException e) { 50 throw e; 51 } catch (Throwable e) { 52 throw new RemotingException(this, "Failed connect to server " + getRemoteAddress() + " from " + getClass().getSimpleName() + " " 53 + NetUtils.getLocalHost() + " using dubbo version " + Version.getVersion() 54 + ", cause: " + e.getMessage(), e); 55 } finally { 56 connectLock.unlock(); 57 } 58 }
代码比较简单,先断连,再连接。
更多网易技术、产品、运营经验分享请点击。
相关文章:
【推荐】 ApiDoc 一键生成注释