一步步解析Dubbo之ServiceBean(二)

(个人理解,如果有误,望请指正,谢谢,配合源码阅读效果更好哟)

上篇我们讲解到即将执行export,那么我们接下来继续来解析剩下的代码:

  String scope = url.getParameter(Constants.SCOPE_KEY);// don't export when none is configuredif (!Constants.SCOPE_NONE.toString().equalsIgnoreCase(scope)) {// export to local if the config is not remote (export to remote only when config is remote)if (!Constants.SCOPE_REMOTE.toString().equalsIgnoreCase(scope)) {exportLocal(url);}// export to remote if the config is not local (export to local only when config is local)if (!Constants.SCOPE_LOCAL.toString().equalsIgnoreCase(scope)) {if (logger.isInfoEnabled()) {

​                    logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);}if (registryURLs != null && registryURLs.size() > 0) {for (URL registryURL : registryURLs) {

​                        url = url.addParameterIfAbsent("dynamic", registryURL.getParameter("dynamic"));

​                        URL monitorUrl = loadMonitor(registryURL);if (monitorUrl != null) {

​                            url = url.addParameterAndEncoded(Constants.MONITOR_KEY, monitorUrl.toFullString());}if (logger.isInfoEnabled()) {

​                            logger.info("Register dubbo service " + interfaceClass.getName() + " url " + url + " to registry " + registryURL);}

​                        Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));

​                        DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);



​                        Exporter<?> exporter = protocol.export(wrapperInvoker);

​                        exporters.add(exporter);}} else {

​                    Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url);

​                    DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);



​                    Exporter<?> exporter = protocol.export(wrapperInvoker);

​                    exporters.add(exporter);}}}

前面已经把该处理的都处理好了,万事俱备只欠暴露了!首先获取服务暴露的范围,如果是本地暴露,执行exportLocal(url)。我们先来看下服务的本地暴露吧,

 private void exportLocal(URL url) {if (!Constants.LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) {

​            URL local = URL.valueOf(url.toFullString()).setProtocol(Constants.LOCAL_PROTOCOL).setHost(LOCALHOST).setPort(0);

​            Exporter<?> exporter = protocol.export(

​                    proxyFactory.getInvoker(ref, (Class) interfaceClass, local));

​            exporters.add(exporter);

​            logger.info("Export dubbo service " + interfaceClass.getName() + " to local registry");}}

判断协议是不是injvm,是的话,获取本地的URL,通过protocol来进行服务暴露。这个protocol是用哪个进行执行呢?看下protocol的初始化代码:private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();通过这里,我们可以根据以前的@SPI机制,可以知道这里生成的只是一个Protocol的代理类,Protocol$Adaptive,具体用哪个Protocol具体要到执行具体方法的时候,根据URL里的Protocol来,调用getExtension(name)来决定。那这里的Protocol是injvm,调用getExtension(injvm),应该获取到InjvmProtocol吗?不全是,因为,我们知道Protocol还有包装类(用来做一些拦截处理,或监听处理),所以这里获取到的protocol实例应该是

new ProtocoleFilterWrapper(new ProtocolListenerWrapper(new InjvmProtocoel())); 所以当调用export的过程应是先调用ProtocolFilterWrapper.export() —> ProtocolListnerWrapper.export() -->InjvmProtocoel.export();接下来看下ProtocolFilterWrapper里的export()方法:

public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {return protocol.export(invoker);}return protocol.export(buildInvokerChain(invoker, Constants.SERVICE_FILTER_KEY, Constants.PROVIDER));}

如果是注册协议,直接调用下层protocol。如果不是,调用buildInvokerChain构建invoker链:

  private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {

​        Invoker<T> last = invoker;

​        List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);if (filters.size() > 0) {for (int i = filters.size() - 1; i >= 0; i--) {final Filter filter = filters.get(i);final Invoker<T> next = last;

​                last = new Invoker<T>() {public Class<T> getInterface() {return invoker.getInterface();}public URL getUrl() {return invoker.getUrl();}public boolean isAvailable() {return invoker.isAvailable();}public Result invoke(Invocation invocation) throws RpcException {return filter.invoke(next, invocation);}public void destroy() {

​                        invoker.destroy();}@Overridepublic String toString() {return invoker.toString();}};}}return last;}

通过上面代码可知,首先通过ExtensionLoarder获取当前系统配置有@Activate的Filter类,就是获取所有的过滤器,查看com.alibaba.dubbo.rpc.Filter文件下有哪些过滤器

echo=com.alibaba.dubbo.rpc.filter.EchoFilter

generic=com.alibaba.dubbo.rpc.filter.GenericFilter

genericimpl=com.alibaba.dubbo.rpc.filter.GenericImplFilter

token=com.alibaba.dubbo.rpc.filter.TokenFilter

accesslog=com.alibaba.dubbo.rpc.filter.AccessLogFilter

activelimit=com.alibaba.dubbo.rpc.filter.ActiveLimitFilter

classloader=com.alibaba.dubbo.rpc.filter.ClassLoaderFilter

context=com.alibaba.dubbo.rpc.filter.ContextFilter

consumercontext=com.alibaba.dubbo.rpc.filter.ConsumerContextFilter

exception=com.alibaba.dubbo.rpc.filter.ExceptionFilter

executelimit=com.alibaba.dubbo.rpc.filter.ExecuteLimitFilter

deprecated=com.alibaba.dubbo.rpc.filter.DeprecatedFilter

compatible=com.alibaba.dubbo.rpc.filter.CompatibleFilter

timeout=com.alibaba.dubbo.rpc.filter.TimeoutFilter

monitor=com.alibaba.dubbo.monitor.support.MonitorFilter

validation=com.alibaba.dubbo.validation.filter.ValidationFilter

cache=com.alibaba.dubbo.cache.filter.CacheFilter

trace=com.alibaba.dubbo.rpc.protocol.dubbo.filter.TraceFilter

future=com.alibaba.dubbo.rpc.protocol.dubbo.filter.FutureFilter

有这么多,但是我只获取@Activate注解的,且group为provider的,有:EchoFilter,AccessLogFilter,ClassLoaderFilter,ContextFilter,ExceptionFilter,ExecuteLimitFilter,GenericFilter,TimeoutFilter,TokenFilter,TpsLimitFilter。那这些过滤器的顺序是如何的呢?这里我们可以在getActivateExtension方法里看到:Collections.sort(exts, ActivateComparator.COMPARATOR); ,从这里可以看出来,是用比较器ActivateComparator来进行排序的,从中,我们可以看到,是通过@Activate里的before和after值来判断的,如果双方都没有,则在通过比较order来比较,如果都相同,直接返回-1.

通过上面,我们已经获取到了一个有序的过滤器,接下来回到代码中,我们看到,针对每个过滤器都会生成一个新的Invoker类,其实会产生如下这种结构:

Invoker (last) Invoker(next) Invoker(next1) … Invoker(header)

next next1 next2 … null

所以如果调用last的invoke,那一直对调到header为止,这样就实现了filter的链。

ProtocolFilterWrapper只是包装了过滤器链,而此时ProtocolFilterWrapper的Protocol是ProtocolListenerWrapper,执行export,代码将来到ProtocolListener中:

  public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {return protocol.export(invoker);}return new ListenerExporterWrapper<T>(protocol.export(invoker),

​                Collections.unmodifiableList(ExtensionLoader.getExtensionLoader(ExporterListener.class).getActivateExtension(invoker.getUrl(), Constants.EXPORTER_LISTENER_KEY)));}

同样的如果是注册协议直接往下执行。创建一个ListenerExportWrapper返回,其中获取扩展类的ExportListener.class,用com.alibaba.dubbo.rpc.ExporterListener搜索系统,发现居然没有任何扩展,那只能再去看下ListenerExportWrapper中的代码:

 public ListenerExporterWrapper(Exporter<T> exporter, List<ExporterListener> listeners) {if (exporter == null) {throw new IllegalArgumentException("exporter == null");}this.exporter = exporter;this.listeners = listeners;if (listeners != null && listeners.size() > 0) {

​            RuntimeException exception = null;for (ExporterListener listener : listeners) {if (listener != null) {try {

​                        listener.exported(this);} catch (RuntimeException t) {

​                        logger.error(t.getMessage(), t);

​                        exception = t;}}}if (exception != null) {throw exception;}}}

从这里可以看到,listener会一层层执行exported,但是没有listener所以跳过。

接下就到了真实的Protocol执行了,这里我们用的是InjvmProtocol来暴露服务,来看下export方法:

   public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {return new InjvmExporter<T>(invoker, invoker.getUrl().getServiceKey(), exporterMap);}


返回的InjvmExporter,这里只是简单的把invoker和exporterMap进行关联,不做任何其他处理,相对而言比较简单。到此,本地暴露就结束了。

那剩下来了解下远程暴露的过程:

 // export to remote if the config is not local (export to local only when config is local)if (!Constants.SCOPE_LOCAL.toString().equalsIgnoreCase(scope)) {if (logger.isInfoEnabled()) {

​                    logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);}if (registryURLs != null && registryURLs.size() > 0) {for (URL registryURL : registryURLs) {

​                        url = url.addParameterIfAbsent("dynamic", registryURL.getParameter("dynamic"));

​                        URL monitorUrl = loadMonitor(registryURL);if (monitorUrl != null) {

​                            url = url.addParameterAndEncoded(Constants.MONITOR_KEY, monitorUrl.toFullString());}if (logger.isInfoEnabled()) {

​                            logger.info("Register dubbo service " + interfaceClass.getName() + " url " + url + " to registry " + registryURL);}

​                        Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));

​                        DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);



​                        Exporter<?> exporter = protocol.export(wrapperInvoker);

​                        exporters.add(exporter);}} else {

​                    Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url);

​                    DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);



​                    Exporter<?> exporter = protocol.export(wrapperInvoker);

​                    exporters.add(exporter);}}

首先判断注册URL,如果为空,则无法远程暴露,不为空的情况,然后遍历registry进行服务暴露:

针对每个registryURL,先执行loadMonitor(registryURL),载入监听器,然后来看下这个监听器:

 protected URL loadMonitor(URL registryURL) {if (monitor == null) {

​            String monitorAddress = ConfigUtils.getProperty("dubbo.monitor.address");

​            String monitorProtocol = ConfigUtils.getProperty("dubbo.monitor.protocol");if ((monitorAddress == null || monitorAddress.length() == 0) && (monitorProtocol == null || monitorProtocol.length() == 0)) {return null;}



​            monitor = new MonitorConfig();if (monitorAddress != null && monitorAddress.length() > 0) {

​                monitor.setAddress(monitorAddress);}if (monitorProtocol != null && monitorProtocol.length() > 0) {

​                monitor.setProtocol(monitorProtocol);}}appendProperties(monitor);

​        Map<String, String> map = new HashMap<String, String>();

​        map.put(Constants.INTERFACE_KEY, MonitorService.class.getName());

​        map.put("dubbo", Version.getVersion());

​        map.put(Constants.TIMESTAMP_KEY, String.valueOf(System.currentTimeMillis()));if (ConfigUtils.getPid() > 0) {

​            map.put(Constants.PID_KEY, String.valueOf(ConfigUtils.getPid()));}//set ip

​        String hostToRegistry = ConfigUtils.getSystemProperty(Constants.DUBBO_IP_TO_REGISTRY);if (hostToRegistry == null || hostToRegistry.length() == 0) {

​            hostToRegistry = NetUtils.getLocalHost();} else if (isInvalidLocalHost(hostToRegistry)) {throw new IllegalArgumentException("Specified invalid registry ip from property:" + Constants.DUBBO_IP_TO_REGISTRY + ", value:" + hostToRegistry);}

​        map.put(Constants.REGISTER_IP_KEY, hostToRegistry);appendParameters(map, monitor);appendParameters(map, application);

​        String address = monitor.getAddress();

​        String sysaddress = System.getProperty("dubbo.monitor.address");if (sysaddress != null && sysaddress.length() > 0) {

​            address = sysaddress;}if (ConfigUtils.isNotEmpty(address)) {if (!map.containsKey(Constants.PROTOCOL_KEY)) {if (ExtensionLoader.getExtensionLoader(MonitorFactory.class).hasExtension("logstat")) {

​                    map.put(Constants.PROTOCOL_KEY, "logstat");} else {

​                    map.put(Constants.PROTOCOL_KEY, "dubbo");}}return UrlUtils.parseURL(address, map);} else if (Constants.REGISTRY_PROTOCOL.equals(monitor.getProtocol()) && registryURL != null) {return registryURL.setProtocol("dubbo").addParameter(Constants.PROTOCOL_KEY, "registry").addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map));}return null;}

这里的处理方式,和前面进行加载也是差不多,都是一层层的获取值,然后封装成一个MonitorConfig中,代码其实阅读的难度也不是很大,所以就不进行详细描述了,还是继续看后面的代码吧:

  Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));

​                        DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);

接下来执行invoker类,这里又用到了ExtensionLoarder来获取proxyFactory,就暂定URL.getProtocol为Dubbo为例来进行讲解,我们来看下有哪些proxyFactory,

stub=com.alibaba.dubbo.rpc.proxy.wrapper.StubProxyFactoryWrapper

jdk=com.alibaba.dubbo.rpc.proxy.jdk.JdkProxyFactory

javassist=com.alibaba.dubbo.rpc.proxy.javassist.JavassistProxyFactory

这里应该会生成一个proxyFactory的动态代理类,具体用那个要根据参数来动态生成name,然后用ExtensionLoard.getExtension(name)来获取。那这个name是什么呢?getNameCode = String.format(“url.getParameter(”%s", “%s”)", value[i], defaultExtName),生成的代理类中的获取getNameCode应该是这一句,value[i]是Adaptive注解值,这里应该是proxy,defualExtName应该是javassist,所以name = url.getParameter(“proxy”,“javassist"),而url中没有proxy值,所以使用默认值javassist,故name=javassist,最后这里获取到的proxyFactory应该是com.alibaba.dubbo.rpc.proxy.javassist.JavassistProxyFactory。既然已经找到了代理类,那接下来,我们来看下太多getInvoker方法:

  public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {// TODO Wrapper cannot handle this scenario correctly: the classname contains '$'final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);return new AbstractProxyInvoker<T>(proxy, type, url) {@Overrideprotected Object doInvoke(T proxy, String methodName,

​                                      Class<?>[] parameterTypes,

​                                      Object[] arguments) throws Throwable {return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);}};}

这里会去获取一个Wrapper包装类,前面我们已经解析过他的makeWrapper方法了,就不在做详细分析了,这里获取到以后匿名类的方式,看下AbstractProxyInvoker这个抽象类:

    public Result invoke(Invocation invocation) throws RpcException {try {return new RpcResult(doInvoke(proxy, invocation.getMethodName(), invocation.getParameterTypes(), invocation.getArguments()));} catch (InvocationTargetException e) {return new RpcResult(e.getTargetException());} catch (Throwable e) {throw new RpcException("Failed to invoke remote proxy method " + invocation.getMethodName() + " to " + getUrl() + ", cause: " + e.getMessage(), e);}}protected abstract Object doInvoke(T proxy, String methodName, Class<?>[] parameterTypes, Object[] arguments) throws Throwable;


从这里可以看到,当执行invoke方法的时候,实际调用的是doInvoke,而doInvoke会去再调用wrapper的invokeMethod方法,而wrapper是一个包装类,实际还是会调原来的方法。那到这里,我们已经获取了一个Invoker。接下,执行Exporter<?> exporter = protocol.export(wrapperInvoker);这里的和本地暴露时是一样的,也会被包装,那这里就直接从DubboProtocol来解析,我们来看下DubboProtocol里的export方法:

  public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {

​        URL url = invoker.getUrl();// export service.

​        String key = serviceKey(url);

​        DubboExporter<T> exporter = new DubboExporter<T>(invoker, key, exporterMap);

​        exporterMap.put(key, exporter);//export an stub service for dispatching event

​        Boolean isStubSupportEvent = url.getParameter(Constants.STUB_EVENT_KEY, Constants.DEFAULT_STUB_EVENT);

​        Boolean isCallbackservice = url.getParameter(Constants.IS_CALLBACK_SERVICE, false);if (isStubSupportEvent && !isCallbackservice) {

​            String stubServiceMethods = url.getParameter(Constants.STUB_EVENT_METHODS_KEY);if (stubServiceMethods == null || stubServiceMethods.length() == 0) {if (logger.isWarnEnabled()) {

​                    logger.warn(new IllegalStateException("consumer [" + url.getParameter(Constants.INTERFACE_KEY) +"], has set stubproxy support event ,but no stub methods founded."));}} else {

​                stubServiceMethodsMap.put(url.getServiceKey(), stubServiceMethods);}}openServer(url);return exporter;}


首先从invoker中获取URL,调用serviceKey方法处理出ip:port,然后创建DubboExporter放到map中,剩下的就是对stub和callback服务的一些判断处理,逻辑比较简单。然后执行openServer(url),这里就开始暴露服务了:

 private void openServer(URL url) {// find server.

​        String key = url.getAddress();//client can export a service which's only for server to invokeboolean isServer = url.getParameter(Constants.IS_SERVER_KEY, true);if (isServer) {

​            ExchangeServer server = serverMap.get(key);if (server == null) {

​                serverMap.put(key, createServer(url));} else {// server supports reset, use together with override

​                server.reset(url);}}}


从url中获取address,然后到serverMap中获取ExchangeServer,没有ExchageServer的话,就会调用createServer(url)来新建一个。我们来看下:

private ExchangeServer createServer(URL url) {// send readonly event when server closes, it's enabled by default

​        url = url.addParameterIfAbsent(Constants.CHANNEL_READONLYEVENT_SENT_KEY, Boolean.TRUE.toString());// enable heartbeat by default

​        url = url.addParameterIfAbsent(Constants.HEARTBEAT_KEY, String.valueOf(Constants.DEFAULT_HEARTBEAT));

​        String str = url.getParameter(Constants.SERVER_KEY, Constants.DEFAULT_REMOTING_SERVER);if (str != null && str.length() > 0 && !ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(str))throw new RpcException("Unsupported server type: " + str + ", url: " + url);



​        url = url.addParameter(Constants.CODEC_KEY, DubboCodec.NAME);

​        ExchangeServer server;try {

​            server = Exchangers.bind(url, requestHandler);} catch (RemotingException e) {throw new RpcException("Fail to start server(url: " + url + ") " + e.getMessage(), e);}

​        str = url.getParameter(Constants.CLIENT_KEY);if (str != null && str.length() > 0) {

​            Set<String> supportedTypes = ExtensionLoader.getExtensionLoader(Transporter.class).getSupportedExtensions();if (!supportedTypes.contains(str)) {throw new RpcException("Unsupported client type: " + str);}}return server;}

这里主要的代码是Exchages.bind(url,requestHandler);来看下这里的代码:

public static ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {if (url == null) {throw new IllegalArgumentException("url == null");}if (handler == null) {throw new IllegalArgumentException("handler == null");}

​        url = url.addParameterIfAbsent(Constants.CODEC_KEY, "exchange");return getExchanger(url).bind(url, handler);}



   public static Exchanger getExchanger(URL url) {

​        String type = url.getParameter(Constants.EXCHANGER_KEY, Constants.DEFAULT_EXCHANGER);return getExchanger(type);}public static Exchanger getExchanger(String type) {return ExtensionLoader.getExtensionLoader(Exchanger.class).getExtension(type);}

如果URL里有指定Exchange,既exchanger有值的话,用这个为type获取扩展类里的对应Exchange,没有就用默认的header,这里来看下Exchange有哪些扩展类:

header=com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchanger

只有一个,那这里获取到type值就是header(一般没有特殊去设置exchange值)。从而可知是通过这个Exchange类实现服务的暴露的。接下来看下HeaderExchanger这个类:

public class HeaderExchanger implements Exchanger {public static final String NAME = "header";public ExchangeClient connect(URL url, ExchangeHandler handler) throws RemotingException {return new HeaderExchangeClient(Transporters.connect(url, new DecodeHandler(new HeaderExchangeHandler(handler))), true);}public ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {return new HeaderExchangeServer(Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler))));}



}

在HeaderExchange中只有两个方法connect(后面reference的时候会用到,这里就先不进行讲解),还有bind方法,这个是我们暴露服务的时候用到的。

在bind方法里,实际返回一个HeaderExchangeServer,来看下HeaderExchangServer的结构:

 public HeaderExchangeServer(Server server) {if (server == null) {throw new IllegalArgumentException("server == null");}this.server = server;this.heartbeat = server.getUrl().getParameter(Constants.HEARTBEAT_KEY, 0);this.heartbeatTimeout = server.getUrl().getParameter(Constants.HEARTBEAT_TIMEOUT_KEY, heartbeat * 3);if (heartbeatTimeout < heartbeat * 2) {throw new IllegalStateException("heartbeatTimeout < heartbeatInterval * 2");}startHeatbeatTimer();}

这个是headerExchangeServer的有参构造方法,至于参数Server我们一会再进行解析,先来看下这个构造方做了哪些事。先将本地的server赋值,然后从Url中获取heartbeat的值,这个是心跳检查时间,然后设置心跳检查超时时间。最后执行startHeatbeatTimer();执行心跳检查,来看下代码:

 private void startHeatbeatTimer() {stopHeartbeatTimer();if (heartbeat > 0) {

​            heatbeatTimer = scheduled.scheduleWithFixedDelay(new HeartBeatTask(new HeartBeatTask.ChannelProvider() {public Collection<Channel> getChannels() {return Collections.unmodifiableCollection(

​                                    HeaderExchangeServer.this.getChannels());}}, heartbeat, heartbeatTimeout),

​                    heartbeat, heartbeat, TimeUnit.MILLISECONDS);}}

这里先执行将已有的心跳检测停止调,然后如果heartbeat时间大于0,则开始重新创建心跳,调用的是一个定时线程池 private final

ScheduledExecutorService scheduled = Executors.newScheduledThreadPool(1,new NamedThreadFactory("dubbo-remoting-server-heartbeat",true));

里面只有一个线程,定时执行。然后创建心跳任务,new HeartBeatTask().ChannelProvider(),这是一个匿名类,返回HeaderExchangeServer的Channel,我们来看下获取Channel:

 public Collection<Channel> getChannels() {return (Collection) getExchangeChannels();}public Collection<ExchangeChannel> getExchangeChannels() {

​        Collection<ExchangeChannel> exchangeChannels = new ArrayList<ExchangeChannel>();

​        Collection<Channel> channels = server.getChannels();if (channels != null && channels.size() > 0) {for (Channel channel : channels) {

​                exchangeChannels.add(HeaderExchangeChannel.getOrAddChannel(channel));}}return exchangeChannels;}

从上面的代码可以看到,channels是从server中获取的,然后再转成HeaderExchangeChannel。我们暂时先不进行查看Server,稍后再去解析那一部分。channels已经获取到了,我们来看下HeartBeatTask是怎么执行的:

public void run() {try {long now = System.currentTimeMillis();for (Channel channel : channelProvider.getChannels()) {if (channel.isClosed()) {continue;}try {

​                    Long lastRead = (Long) channel.getAttribute(

​                            HeaderExchangeHandler.KEY_READ_TIMESTAMP);

​                    Long lastWrite = (Long) channel.getAttribute(

​                            HeaderExchangeHandler.KEY_WRITE_TIMESTAMP);if ((lastRead != null && now - lastRead > heartbeat)|| (lastWrite != null && now - lastWrite > heartbeat)) {

​                        Request req = new Request();

​                        req.setVersion("2.0.0");

​                        req.setTwoWay(true);

​                        req.setEvent(Request.HEARTBEAT_EVENT);

​                        channel.send(req);if (logger.isDebugEnabled()) {

​                            logger.debug("Send heartbeat to remote channel " + channel.getRemoteAddress()

​                                    \+ ", cause: The channel has no data-transmission exceeds a heartbeat period: " + heartbeat + "ms");}}if (lastRead != null && now - lastRead > heartbeatTimeout) {

​                        logger.warn("Close channel " + channel

​                                \+ ", because heartbeat read idle time out: " + heartbeatTimeout + "ms");if (channel instanceof Client) {try {((Client) channel).reconnect();} catch (Exception e) {//do nothing}} else {

​                            channel.close();}}} catch (Throwable t) {

​                    logger.warn("Exception when heartbeat to remote channel " + channel.getRemoteAddress(), t);}}} catch (Throwable t) {

​            logger.warn("Unhandled exception when heartbeat, cause: " + t.getMessage(), t);}}

从这里,我们可以看到,先获取channel里的最后的读或写的时间戳,然后与当前时间进行比较,如果时间差大于心跳时间,那就可以发起心跳检查了,然后封装一个心跳检查的请求,通过channel的send把消息发送出去。当最后读取时间和当前时间的时间差比心跳超时时间大,而且当前channel是client,则默认当前channel连接中断,执行渠道重连。到此就完成了心跳检查的功能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值