注:本文基于dubbo2.6.x
1. protocol.export(invoker)
接着《深度解析dubbo服务远程暴露(一)》这篇的讲,我们讲到exporter = new ExporterChangeableWrapper<T>((Exporter<T>) protocol.export(invokerDelegete), originInvoker);/// dubbo protocol
,我们来看一下RegistryProtocol类的private Protocol protocol 成员,这个成员的值是由dubbo spi 扩展技术在实例完成后setter注入进来的,实际上这里protocol注入的是ExtensionLoader.getExtensionLoader(Protocol).getAdaptiveExtension();是自适应的实现类。我们可以看下自适应的实现类的export方法:
public com.alibaba.dubbo.rpc.Exporter export(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.RpcException {
if (arg0 == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null");
if (arg0.getUrl() == null)
throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");
com.alibaba.dubbo.common.URL url = arg0.getUrl();
String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
if (extName == null)
throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
return extension.export(arg0);
}
然后invokerDelegete 这个委托类getUrl得到的是protocol=dubbo的url,所以
Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(Protocol.class).getExtension("dubbo");
最后获取的extension是经过dubbo spi setter注入与wrapper包装后的。
Listener-----> Filter----->Qos-----> Dubbo
这里的包装其实是没有顺序的,因为在包装的时候是遍历的一个ConcurrentHashSet集合,可以看下包装这块代码
所以我们每次启动可能会看到不一样的包装顺序。
我们先来看下ProtocolListenerWrapper这个类
2.ProtocolListenerWrapper
@Override
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {// registry 是否是注册中心
return protocol.export(invoker);
}
return new ListenerExporterWrapper<T>(protocol.export(invoker),// dubbo export dubbo --->暴露服务 生成exporter
Collections.unmodifiableList(ExtensionLoader.getExtensionLoader(ExporterListener.class)
.getActivateExtension(invoker.getUrl(), Constants.EXPORTER_LISTENER_KEY)));// exporter listener
}
这里不是REGISTRY_PROTOCOL,然后走了下面使用ListenerExporterWrapper 将服务暴露返回的exporter与自动激活的listener们绑在了一起。我们可以看下这个ListenerExporterWrapper类,
public class ListenerExporterWrapper<T> implements Exporter<T> {
private static final Logger logger = LoggerFactory.getLogger(ListenerExporterWrapper.class);
private final Exporter<T> exporter;
private final List<ExporterListener> listeners;
public ListenerExporterWrapper(Exporter<T> exporter, List<ExporterListener> listeners) {
if (exporter == null) {// 判断null
throw new IllegalArgumentException("exporter == null");
}
this.exporter = exporter;
this.listeners = listeners;
if (listeners != null && !listeners.isEmpty()) {
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,告诉他们当前服务暴露完了。这些listener们我们后面单独拿出来解析。
3.ProtocolFilterWrapper
接下来我们看下ProtocolFilterWrapper类的export方法
@Override
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {/// registry protocol 就到下一个wapper 包装对象中就可以了
return protocol.export(invoker);
}
return protocol.export(buildInvokerChain(invoker, Constants.SERVICE_FILTER_KEY, Constants.PROVIDER));
}
这里不是registry protocol 然后会走下面,我们看下buildInvokerChain 方法,这里buildInvokerChain的三个参数分别是
invoker, service.filter,provider
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.isEmpty()) {
for (int i = filters.size() - 1; i >= 0; i--) {
final Filter filter = filters.get(i);
final Invoker<T> next = last;
last = new Invoker<T>() {
...
//exception->moniter->timeout->trace->context->generic->classloader->echo
@Override
public Result invoke(Invocation invocation) throws RpcException {
return filter.invoke(next, invocation);
}
...
};
}
}
return last;
}
这个方法实际上是根据dubbo spi 扩展技术自动激活的特性获取到对应的filter们,然后一层一层的包装这个invoker,生成一个过滤调用链,最后到真实的invoker上面。这个Filter我们后期会单独拿出来解析,这里只需要知道它一层层包装了invoker就行。
4.QosProtocolWrapper
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
//判断是registry
if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
// 启动Qos服务器?
startQosServer(invoker.getUrl());
return protocol.export(invoker);
}
return protocol.export(invoker);
}
这里同理不是registry protocol ,直接到了下一层
5. DubboProtocol
@Override
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
URL url = invoker.getUrl();
// export service. key = com.alibaba.dubbo.demo.DemoService:20880
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);
optimizeSerialization(url);
return exporter;
}
这里首先是根据url生成一个服务的key,创建一个DubboExporter 把invoker,key与缓存exporter的map 绑在一起。将 创建的这个exporter缓存到exporterMap里面。我们可以exporterMap的定义:
protected final Map<String, Exporter<?>> exporterMap = new ConcurrentHashMap<String, Exporter<?>>();
其中key就是生成的那个serviceKey,然后value是创建的那个exporter。
接着就是调用openServer(url),来打开服务器,我们看下源码:
// 打开server
private void openServer(URL url) {
// find server. 获得地址 ip:port 192.168.1.104:20880
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) {// 没有找到server 就要创建server
serverMap.put(key, createServer(url));
} else {
// server supports reset, use together with override server 支持重置,与覆盖一起使用
server.reset(url); //
}
}
}
首先是获取地址,这个地址形式是ip:port,获取isserver参数,默认是true,然后从serverMap这个缓存中查找对应的server,如果之前没有创建过,就调用createServer(url) 来创建server,之后把创建的server缓存在serverMap中,我们先看下serverMap这个成员变量
private final Map<String, ExchangeServer> serverMap = new ConcurrentHashMap<String, ExchangeServer>(); // <host:port,Exchanger>
其中key是服务器地址,也就是上面url.getAddress();获得的,value就是对应的ExchangeServer。
我们再来看看是怎么创建server的,看下createServer(url)这个方法的源码:
// 创建server
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 60 * 1000 设置心跳
url = url.addParameterIfAbsent(Constants.HEARTBEAT_KEY, String.valueOf(Constants.DEFAULT_HEARTBEAT));
//获取配置的服务器类型, 缺省就是使用netty 默认服务器是netty
String str = url.getParameter(Constants.SERVER_KEY, Constants.DEFAULT_REMOTING_SERVER); // 获取的使用的server 缺省使用netty
// 不存在Transports 话 , 就抛出 不支持的server type
if (str != null && str.length() > 0 && !ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(str))
throw new RpcException("Unsupported server type: " + str + ", url: " + url);
// 设置 Codec 的类型为dubbo
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);// client
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;
}
这个方法前面部分就是设置一些参数,channel.readonly.sent =true,就是服务器关闭的时候指发送只读属性,heartbeat=60*1000 设置默认的心跳时间,获取server,缺省的情况下使用netty,设置Codec 的类型为dubbo。
然后Exchangers.bind(url, requestHandler),其实这个Exchangers 是个门面类,封装了bind与connect两个方法的调用
6.Exchanger
我们可以看看Exchanger的方法们
我们接着,看下当前调用的bind方法:
// bind
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"); // 如果没有codec 的话 就设置为exchange
// exchanger.bind()
return getExchanger(url).bind(url, handler);
}
前面的都是参数校验,我们看下getExchanger(url) 方法:
public static Exchanger getExchanger(URL url) {
// 获取exchanger 缺省header
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中获取exchanger ,缺省是header,然后使用dubbo spi 获取到HeaderExchanger,我们看下HeaderExchanger源码:
7.HeaderExchanger
public class HeaderExchanger implements Exchanger {
public static final String NAME = "header";
@Override
public ExchangeClient connect(URL url, ExchangeHandler handler) throws RemotingException {
// 创建一个通信client
//DecodeHandler => HeaderExchangeHandler => ExchangeHandler( handler ) 。
return new HeaderExchangeClient(Transporters.connect(url, new DecodeHandler(new HeaderExchangeHandler(handler))), true);
}
@Override
public ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {
// 创建一个通信server DecodeHandler << HeaderExchangeHandler << handler
return new HeaderExchangeServer(Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler))));
}
}
这里有三个点,一是将这handler又包装了两层,二是使用Transporters 这个门面类进行bind,再就是创建HeaderExchangeServer 将server进行增强,其实HeaderExchangeServer 这个是专门发送心跳的。
我们先看下Transporters这个类
8.Transporters
这个Transporters也是门面类,对外统一了bind 与connect。我们只看下与这次有关的部分
public static Server bind(URL url, ChannelHandler... handlers) throws RemotingException {
//验证
if (url == null) {
throw new IllegalArgumentException("url == null");
}
if (handlers == null || handlers.length == 0) {
throw new IllegalArgumentException("handlers == null");
}
ChannelHandler handler;
if (handlers.length == 1) {
handler = handlers[0];
} else { // 多个channal 对 Channel分发 ChannelHandlerDispatcher 循环
handler = new ChannelHandlerDispatcher(handlers);
}
// 真正服务器 进行bind
return getTransporter().bind(url, handler);
}
public static Transporter getTransporter() { // 获取transporter
return ExtensionLoader.getExtensionLoader(Transporter.class).getAdaptiveExtension();
}
首先是参数校验,接着判断handler的个数,如果一个话还好说,多个话就要使用ChannelHandlerDispatcher类来包装了,其实里面就是对多个handler循环调用,接着调用getTransporter获取Transporter扩展点的自适应类。
我们可以稍微看下Transporter 这个扩展点的代码,
@SPI("netty") // 默认是netty3的
public interface Transporter {
/**
* Bind a server.
*
* @param url server url
* @param handler
* @return server
* @throws RemotingException
* @see com.alibaba.dubbo.remoting.Transporters#bind(URL, ChannelHandler...)
*/
@Adaptive({Constants.SERVER_KEY, Constants.TRANSPORTER_KEY})
Server bind(URL url, ChannelHandler handler) throws RemotingException;
/**
* Connect to a server.
*
* @param url server url
* @param handler
* @return client
* @throws RemotingException
* @see com.alibaba.dubbo.remoting.Transporters#connect(URL, ChannelHandler...)
*/
@Adaptive({Constants.CLIENT_KEY, Constants.TRANSPORTER_KEY})
Client connect(URL url, ChannelHandler handler) throws RemotingException;
}
可以看到bind自适应实现类是根据server 与transporter 这两个参数决定的,server这个参数咱们上面的DubboProtocol类里面createServer 方法中就设置好了,缺省使用netty,我们通过看dubbo spi 配置文件
netty对应的也就是NettyTransporter这个实现类。
9.NettyTransporter
/**
* 实现 Transporter 接口,基于 Netty4 的网络传输实现类
*/
public class NettyTransporter implements Transporter {
/**
* 扩展名
*/
public static final String NAME = "netty";
@Override
public Server bind(URL url, ChannelHandler listener) throws RemotingException {
return new NettyServer(url, listener);
}
@Override
public Client connect(URL url, ChannelHandler listener) throws RemotingException {
return new NettyClient(url, listener);
}
}
我们可以看到new了一个NettyServer对象。我们来看下代码
10.NettyServer
在看NettyServer前先看下它的继承结构
看下NettyServer的构造方法:
public NettyServer(URL url, ChannelHandler handler) throws RemotingException {
super(url, ChannelHandlers.wrap(handler, ExecutorUtil.setThreadName(url, SERVER_THREAD_POOL_NAME)));
}
调用的父类的构造
先看下AbstractServer代码
public abstract class AbstractServer extends AbstractEndpoint implements Server {
protected static final String SERVER_THREAD_POOL_NAME = "DubboServerHandler";
private static final Logger logger = LoggerFactory.getLogger(AbstractServer.class);
// 线程池
ExecutorService executor;
// 服务地址
private InetSocketAddress localAddress;
//绑定地址
private InetSocketAddress bindAddress;
// 服务器最大可接受连接数
private int accepts;
// 空闲超时时间
private int idleTimeout = 600;
public AbstractServer(URL url, ChannelHandler handler) throws RemotingException {
super(url, handler);
localAddress = getUrl().toInetSocketAddress();
String bindIp = getUrl().getParameter(Constants.BIND_IP_KEY, getUrl().getHost());//ip
int bindPort = getUrl().getParameter(Constants.BIND_PORT_KEY, getUrl().getPort()); //port
if (url.getParameter(Constants.ANYHOST_KEY, false) || NetUtils.isInvalidLocalHost(bindIp)) {
bindIp = NetUtils.ANYHOST;
}
bindAddress = new InetSocketAddress(bindIp, bindPort);
// 服务器最大可接受连接数
this.accepts = url.getParameter(Constants.ACCEPTS_KEY, Constants.DEFAULT_ACCEPTS);
this.idleTimeout = url.getParameter(Constants.IDLE_TIMEOUT_KEY, Constants.DEFAULT_IDLE_TIMEOUT);
try {
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);
}
//fixme replace this with better method
// 获取线程池
DataStore dataStore = ExtensionLoader.getExtensionLoader(DataStore.class).getDefaultExtension();
executor = (ExecutorService) dataStore.get(Constants.EXECUTOR_SERVICE_COMPONENT_KEY, Integer.toString(url.getPort()));
}
protected abstract void doOpen() throws Throwable;
}
我们可以看到AbstractServer的构造前面都是些参数的获取,之后调用doOpen()方法,具体是由子类实现的我们在看看NettyServer的doOpen方法
@Override
protected void doOpen() throws Throwable {
bootstrap = new ServerBootstrap();
bossGroup = new NioEventLoopGroup(1, new DefaultThreadFactory("NettyServerBoss", true));
//iothreads 默认是cpu核心数+1 与 32 进行比较,取小的那个 也就是最大不超过32
workerGroup = new NioEventLoopGroup(getUrl().getPositiveParameter(Constants.IO_THREADS_KEY, Constants.DEFAULT_IO_THREADS),
new DefaultThreadFactory("NettyServerWorker", true));
final NettyServerHandler nettyServerHandler = new NettyServerHandler(getUrl(), this);
channels = nettyServerHandler.getChannels();
/**
* ChannelOption.SO_REUSEADDR 这个参数表示允许重复使用本地地址和端口,
*
* 比如,某个服务器进程占用了TCP的80端口进行监听,此时再次监听该端口就会返回错误,使用该参数就可以解决问题,该参数允许共用该端口,这个在服务器程序中比较常使用,
*
* 比如某个进程非正常退出,该程序占用的端口可能要被占用一段时间才能允许其他进程使用,而且程序死掉以后,内核一需要一定的时间才能够释放此端口,不设置SO_REUSEADDR
*
* 就无法正常使用该端口。
*/
// 设置线程组
bootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)// 设置channel类型 nio
.childOption(ChannelOption.TCP_NODELAY, Boolean.TRUE)//禁止使用nagle算法
.childOption(ChannelOption.SO_REUSEADDR, Boolean.TRUE)
.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
.childHandler(new ChannelInitializer<NioSocketChannel>() {
@Override
protected void initChannel(NioSocketChannel ch) throws Exception {
NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec(), getUrl(), NettyServer.this);
ch.pipeline()//.addLast("logging",new LoggingHandler(LogLevel.INFO))//for debug
.addLast("decoder", adapter.getDecoder())
.addLast("encoder", adapter.getEncoder())
.addLast("handler", nettyServerHandler);
}
});
// bind
ChannelFuture channelFuture = bootstrap.bind(getBindAddress());
channelFuture.syncUninterruptibly();
channel = channelFuture.channel();
}
我们可以看到就是启动netty服务器,然后boss线程是一个,然后work线程是是cpu数+1然后与32做比较,取最小的那个,也就是最大不超过32个线程。
到这里我们的服务就算启动完成了。
我们再回过头来看看HeaderExchangeServer这个维护心跳的
11. HeaderExchangeServer
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");
}
// 启动心跳
startHeartbeatTimer();
}
这里有heartbeat 与heartbeatTimeout 这两个参数,我们可以看到heartbeat 缺省是0 ,但是我们在DubboProtocol的createServer创建server方法设置了值为60 * 1000,也就是60s,然后heartbeatTimeout就是3*60s
接着我们看下启动心跳timer的方法startHeartbeatTimer;
//启动心跳timer
private void startHeartbeatTimer() {
stopHeartbeatTimer();
if (heartbeat > 0) {
heartbeatTimer = scheduled.scheduleWithFixedDelay(
new HeartBeatTask(new HeartBeatTask.ChannelProvider() {
@Override
public Collection<Channel> getChannels() {
return Collections.unmodifiableCollection(
HeaderExchangeServer.this.getChannels());
}
}, heartbeat, heartbeatTimeout),
heartbeat, heartbeat, TimeUnit.MILLISECONDS);
}
}
好了,到这里我们远程服务暴露中启动部分就算结束了,这里我们并没有说太多的细节,我只需要把服务暴露服务器启动这个过程缕顺就可以了,具体的细节方面放到后面分析。