(个人理解,如果有误,望请指正,谢谢,配合源码阅读效果更好哟)
上篇我们讲解到即将执行export,那么我们接下来继续来解析剩下的代码:
String scope = url.getParameter(Constants.SCOPE_KEY);
// don't export when none is configured
if (!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();
}
@Override
public 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) {
@Override
protected 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 invoke
boolean 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连接中断,执行渠道重连。到此就完成了心跳检查的功能。