之前已经分析的dubbo的服务的发现和注册,这里先看一下dubbo协议是如何实现的,之前已经知道了,调用DubboProtocol类的export来暴露服务的,协议实现比较复杂,这里只关系主体实现即排除一些特性功能的处理代码
本章主要处理服务端对应的暴露流程,继续回到···com.alibaba.dubbo.config.ServiceConfig···的doExportUrlsFor1Protocol方法(487行附近)
Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));
Exporter<?> exporter = protocol.export(invoker);
exporters.add(exporter);
proxyFactory 是一个全局变量(自适应扩展点),之前已经反复复习不多说明了
proxyFactory.getInvoker实现如下,该方法内部会调用一个封装类com.alibaba.dubbo.common.bytecode.Wrapper,该类封装了实例的方法调用。proxyFactory.getInvoker 会返回一个
public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
// TODO Wrapper类不能正确处理带$的类名
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);
}
};
}
这里com.alibaba.dubbo.common.bytecode.Wrapper
类不做过多说明,其会生成有一个名为invokeMethod封装方法,invokeMethod 方法根据传入实例和方法名,调用实例对应的方法,invokeMethod方法签名如下:
/**
*
* @param o 接口类实例
* @param n 方法名
* @param p 参数类型
* @param v 参数值
* @return
* @throws java.lang.reflect.InvocationTargetException
*/
public Object invokeMethod(Object o, String n, Class[] p, Object[] v) throws java.lang.reflect.InvocationTargetException
所以到这里可以发现,对于服务的提供者来说通过com.alibaba.dubbo.rpc.Invoker的 protected Object doInvoke(T proxy, String methodName,Class
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
/*url此刻内容如下。
*dubbo://ip:port/服务接口?anyhost=true&application=dsp-ad&default.timeout=10000&dubbo=2.8.4&generic=false&
*interface=服务接口&logger=log4j&methods=接口方法名&organization=company&owner=weiythi&pid=5948&revision=1.6.1&serialization=kryo&side=provider×tamp=1511849953486
*/
URL url = invoker.getUrl();
// export service.
// serviceKey为 服务接口类名:端口号
String key = serviceKey(url);
//DubboExporter 实现接口 com.alibaba.dubbo.rpc.Exporter ,会在抽象实现com.alibaba.dubbo.rpc.protocol.AbstractExporter 的基础上维护exporterMap ,key
DubboExporter<T> exporter = new DubboExporter<T>(invoker, key, exporterMap);
exporterMap.put(key, exporter);
//省略......export an stub service for dispaching event......//
//开启服务
openServer(url);
// modified by lishen 序列化优化相关,暂不去了解
optimizeSerialization(url);
return exporter;
}
这里先找到最关心的点“openServer(url);”,其是dubbo协议服务暴露的入口,其实现如下:
private void openServer(URL url) {
// find server.
// 这里是ip:port
String key = url.getAddress();
//client 也可以暴露一个只有server可以调用的服务。
boolean isServer = url.getParameter(Constants.IS_SERVER_KEY,true);
if (isServer) {//true
//按照ip:port为key 找到信息交换层的服务端
ExchangeServer server = serverMap.get(key);
if (server == null) {//如果木有创建过server端,则创建并将服务端缓存
serverMap.put(key, createServer(url));
} else {
//server支持reset,配合override功能使用
server.reset(url); //这个暂时木有看懂,等回过头来看
}
}
}
注意这里serverMap虽说不是一个static域,但基于扩展点机制的实现,貌似都是单例的,所以这里一个服务端进程可以为多个provider提供服务,那么createServer(url)是如何开启服务的呢?
createServer(url)的方法实现如下, 注意该方法会通过url总线的server参数来决定使用dubbo的协议实现,dubbox这里默认nio框架是netty
private ExchangeServer createServer(URL url) {
//默认开启server关闭时发送readonly事件
url = url.addParameterIfAbsent(Constants.CHANNEL_READONLYEVENT_SENT_KEY, Boolean.TRUE.toString());
//默认开启heartbeat
url = url.addParameterIfAbsent(Constants.HEARTBEAT_KEY, String.valueOf(Constants.DEFAULT_HEARTBEAT));
//获得协议的server实现,默认netty
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, Version.isCompatibleVersion() ? COMPATIBLE_CODEC_NAME : DubboCodec.NAME);
ExchangeServer server;
try {
//注意这里的requestHandler 是一个com.alibaba.dubbo.remoting.exchange.ExchangeHandler 的接口实现。
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;
}
在方法中能注意到一个闪闪亮的语句,server = Exchangers.bind(url, requestHandler); 这里传入了一个参数requestHandler,requestHandler在此类是一个com.alibaba.dubbo.remoting.exchange.ExchangeHandler的接口实现,看了下源码这里又引入了一系列暂时不知道用处的类,所以此处暂时忽略接口实现,既然其是一个handler所以接到consumer的调用之后必然会执行的,先放在一边。我们接数据的时候一起看。
com.alibaba.dubbo.remoting.exchange.Exchangers 类主要有两个功能,即服务器的绑定和客户端的连接功能
Exchangers.bind(url, requestHandler); 内部又涉及一系列的扩展点,不贴代码出来了。简单介绍一下, 这里肯定实现的功能就是服务器的绑定了
1.bind方法会根据URL总线的exchanger参数获得一个com.alibaba.dubbo.remoting.exchange.Exchanger 扩展点实例,本例实例为(com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchanger),嗯 目前已知的扩展点实现也就这一个。
2.Exchanger?在一个框架中我们通常把负责数据交换和网络通信的组件叫做Exchanger ,接下来调用 ExchangeServer bind(URL url, ExchangeHandler handler) 方法,注意这里又是一个自适应扩展点。
接下来进入到Exchanger仅有的一个实现com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchanger
public ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {
return new HeaderExchangeServer(Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler))));
}
可见在该方法中,创建了一个com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeServer
对象,当然根据方法定义该类肯定实现接口com.alibaba.dubbo.remoting.exchange.ExchangeServer
这里先暂时不关心该接口行为,因为创建对象前,先对handler进行了两层封装,然后使用com.alibaba.dubbo.remoting.Transporters
的bind方法,进行绑定。
对handler的封装可能会绝对handler调用前或调用后的行为,设置决定了handler的调用方式,那么先去确定com.alibaba.dubbo.remoting.transport.DecodeHandler
的运作情况。
额,点开看了一下貌似也挺复杂,因为这个类一顿继承啊~,找一下自己关心的两个东东,分别是,我家的HeaderExchangeHandler在哪里?有木有能让我接受到消息调用到的方法。
然后发现这两个重点是这样的
public DecodeHandler(ChannelHandler handler) {
super(handler); //父类构造器
}
public void received(Channel channel, Object message) throws RemotingException {
if (message instanceof Decodeable) {
decode(message);
}
if (message instanceof Request) {
decode(((Request)message).getData());
}
if (message instanceof Response) {
decode( ((Response)message).getResult());
}
//调用received方法,既然我们的类已经被封装了两层,那么这里应该调用的是HeaderExchangeHandler
handler.received(channel, message);
}
private void decode(Object message) { //这个方法没有任何handler相关处理,暂不去关心。
if (message != null && message instanceof Decodeable) {
try {
((Decodeable)message).decode();
if (log.isDebugEnabled()) {
log.debug(new StringBuilder(32).append("Decode decodeable message ")
.append(message.getClass().getName()).toString());
}
} catch (Throwable e) {
if (log.isWarnEnabled()) {
log.warn(
new StringBuilder(32)
.append("Call Decodeable.decode failed: ")
.append(e.getMessage()).toString(),
e);
}
} // ~ end of catch
} // ~ end of if
} // ~ end of method decode
那么再去看看 HeaderExchangeHandler 是个啥
还有既然会调用received方法,主要去看看received的实现。点开一看略复杂、、、、好多东东,看到这里还不能理解,所以又去敲Transporters.bind的门,因为已经先行忽略了handler这个,这里只关心服务是怎么被open的
代码里又耍流氓了,又是自适应扩展点,但是我已不怕,打开看配置文件直接锁定实现com.alibaba.dubbo.remoting.transport.netty.NettyTransporter
然后去看bind方法(等等索性直接看了)
/*
* Copyright 1999-2011 Alibaba Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.dubbo.remoting.transport.netty;
import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.remoting.ChannelHandler;
import com.alibaba.dubbo.remoting.Client;
import com.alibaba.dubbo.remoting.RemotingException;
import com.alibaba.dubbo.remoting.Server;
import com.alibaba.dubbo.remoting.Transporter;
/**
* @author ding.lid
*/
public class NettyTransporter implements Transporter {
public static final String NAME = "netty";
public Server bind(URL url, ChannelHandler listener) throws RemotingException {
return new NettyServer(url, listener);
}
public Client connect(URL url, ChannelHandler listener) throws RemotingException {
return new NettyClient(url, listener);
}
}
这个很明显么,直接new了一个com.alibaba.dubbo.remoting.transport.netty.NettyServer
出来,那么问题简单了,直接去看构造函数
public NettyServer(URL url, ChannelHandler handler) throws RemotingException{
super(url, ChannelHandlers.wrap(handler, ExecutorUtil.setThreadName(url, SERVER_THREAD_POOL_NAME)));
}
调用了父类的构造器
public AbstractServer(URL url, ChannelHandler handler) throws RemotingException {
// 哭 handler又甩给上层了
super(url, handler);
localAddress = getUrl().toInetSocketAddress();
String host = url.getParameter(Constants.ANYHOST_KEY, false)
|| NetUtils.isInvalidLocalHost(getUrl().getHost())
? NetUtils.ANYHOST : getUrl().getHost();
bindAddress = new InetSocketAddress(host, getUrl().getPort());
this.accepts = url.getParameter(Constants.ACCEPTS_KEY, Constants.DEFAULT_ACCEPTS);
this.idleTimeout = url.getParameter(Constants.IDLE_TIMEOUT_KEY, Constants.DEFAULT_IDLE_TIMEOUT);
try {
//好吧,偷偷的调用了doOpen 害我好找
doOpen();
if (logger.isInfoEnabled()) {
logger.info("Start " + getClass().getSimpleName() + " bind " + getBindAddress() + ", export " + getLocalAddress());
}
} catch (Throwable t) {
throw new RemotingException(url.toInetSocketAddress(), null, "Failed to bind " + getClass().getSimpleName()
+ " on " + getLocalAddress() + ", cause: " + t.getMessage(), t);
}
executor = (ExecutorService) ExtensionLoader.getExtensionLoader(DataStore.class)
.getDefaultExtension().get(Constants.EXECUTOR_SERVICE_COMPONENT_KEY, Integer.toString(url.getPort()));
}
主要关注两个事,就是我们的handler哪去了,后面可能还要用呢,还有服务在哪开的?
首先看那第一行,调用父类的构造器,一直跟着找,发现handler在com.alibaba.dubbo.remoting.transport.AbstractPeer
中维护了,然后AbstractPeer又是一个ChannelHandler ,好吧这个关系很混乱。先记着这个关键点,上一次看见类似风格是在RegistryDictory中
既然调用了doOpen,这个就不用想了,方法的实现就在NettyServer里啊,但是netty之前没玩过,所以只能看个热闹
@Override
protected void doOpen() throws Throwable {
NettyHelper.setNettyLoggerFactory();
ExecutorService boss = Executors.newCachedThreadPool(new NamedThreadFactory("NettyServerBoss", true));
ExecutorService worker = Executors.newCachedThreadPool(new NamedThreadFactory("NettyServerWorker", true));
ChannelFactory channelFactory = new NioServerSocketChannelFactory(boss, worker, getUrl().getPositiveParameter(Constants.IO_THREADS_KEY, Constants.DEFAULT_IO_THREADS));
bootstrap = new ServerBootstrap(channelFactory);
// 这里的NettyHandler 封装了一个this? 这个this是什么?
final NettyHandler nettyHandler = new NettyHandler(getUrl(), this);
channels = nettyHandler.getChannels();
// https://issues.jboss.org/browse/NETTY-365
// https://issues.jboss.org/browse/NETTY-379
// final Timer timer = new HashedWheelTimer(new NamedThreadFactory("NettyIdleTimer", true));
bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
public ChannelPipeline getPipeline() {
NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec() ,getUrl(), NettyServer.this);
ChannelPipeline pipeline = Channels.pipeline();
/*int idleTimeout = getIdleTimeout();
if (idleTimeout > 10000) {
pipeline.addLast("timer", new IdleStateHandler(timer, idleTimeout / 1000, 0, 0));
}*/
pipeline.addLast("decoder", adapter.getDecoder());
pipeline.addLast("encoder", adapter.getEncoder());
pipeline.addLast("handler", nettyHandler);
return pipeline;
}
});
// bind
channel = bootstrap.bind(getBindAddress());
}
因为35太难了,我学不会,而且server开启之后只要能一直提供服务就OK,我只能关注另一个点就是接到消息后怎么处理的。所以这里跟消息有关的就一个nettyHandler ,其初始化是通过这样的语句完成的,这里传入了一个this,我们要知道这个this是什么。
final NettyHandler nettyHandler = new NettyHandler(getUrl(), this);
@Sharable
public class NettyHandler extends SimpleChannelHandler {
private final Map<String, Channel> channels = new ConcurrentHashMap<String, Channel>(); // <ip:port, channel>
private final URL url;
private final ChannelHandler handler;
//.......//
public NettyHandler(URL url, ChannelHandler handler){
if (url == null) {
throw new IllegalArgumentException("url == null");
}
if (handler == null) {
throw new IllegalArgumentException("handler == null");
}
this.url = url;
this.handler = handler;
}
}
好吧,很巧妙的方式,因为NettyServer他是一个ChannelHandler啊,是一个ChannelHandler啊…啊。
迷只混乱,只能拿出他家家谱,然后默默流眼泪
这个家谱里貌似没有我们熟悉的朋友。
看看他的长辈有没有人认识我们上文忽略的com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeServer
,因为是这么个关系new HeaderExchangeServer(Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler)))); 所以去看看他的构造器
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();
}
参数正好是NettyServer所实现的接口com.alibaba.dubbo.remoting.Server
,到这里我们就返回一个ExchangeServer了,不过好像目前为止并没有有什么实质性的动作,回到createServer
好吧,到这一个服务就创建完了。总结一下开启服务这一部的调用关系(引用大佬的)
openServer netty server打开侦听服务,并缓存服务。
dubbo -> export() -> openServer() -> createServer() -> Exchangers.bind()(HeaderExchanger) -> NettyTransporter -> NettyServer.open()(编码解码采用exchange)
netty ChannelFactory(boss worker) ChannelPipeline(责任链模式) ChannelHandler(处理器) — 反应器模式