Dubbo协议打开服务器
书接上回Dubbo Provider发布服务Provider会通过RegistryProtocol#export注册服务,通过DubboProtocol发布服务吗,DubboProtocol发布服务时会打开服务器,DubboProtocol中有一个serviceMap,存储ip:port和ExchangeServer的映射关系,刚开始创建的时候会检查serviceMap,如果key对应的ExchangeServer不存在会调用createServer创建Server,代码如下:
private void openServer(URL url) {
// ip端口
String key = url.getAddress();
// 客户端发布用于服务器的服务
boolean isServer = url.getParameter(IS_SERVER_KEY, true);
if (isServer) {
// 从serviceMap查找Server
ProtocolServer server = serverMap.get(key);
// serverMap中没有Server,这里使用两次查找
if (server == null) {
synchronized (this) {
server = serverMap.get(key);
if (server == null) {
// 创建Server并放到serviceMap中
serverMap.put(key, createServer(url));
}
}
} else {
// server supports reset, use together with override
server.reset(url);
}
}
}
private ProtocolServer createServer(URL url) {
// url
url = URLBuilder.from(url)
// send readonly event when server closes, it's enabled by default
.addParameterIfAbsent(CHANNEL_READONLYEVENT_SENT_KEY, Boolean.TRUE.toString())
// enable heartbeat by default
.addParameterIfAbsent(HEARTBEAT_KEY, String.valueOf(DEFAULT_HEARTBEAT))
.addParameter(CODEC_KEY, DubboCodec.NAME)
.build();
// 默认服务器采用netty
String str = url.getParameter(SERVER_KEY, DEFAULT_REMOTING_SERVER);
if (str != null && str.length() > 0 && !ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(str)) {
throw new RpcException("Unsupported server type: " + str + ", url: " + url);
}
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(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 new DubboProtocolServer(server);
}
打开服务器流程
底层通信分为两层,信息交换层和传输层,传输层使用netty等实现。
|- Exchanger 信息交换层 header
|- Transporters 传输层 netty
|- NettyServer
|- HeaderExchangeServer
- 创建服务器时,
dubbo:protocol标签中设置服务器实现类型,比如:dubbo协议的mina,netty等,http协议的jetty,servlet等,dubbo协议默认的服务器是netty。 Exchangers.bind会根据url启动服务器,首先根据url通过SPI机制获取Exchanger,默认的Exchanger是header,对应的是HeaderExchanger,Exchanger是信息交换层。- 获取到
Exchanger调用其bind方法,会使用Transporters的bind。代码如下
/**
* DubboProtocol bind方法首先根据url通过SPI机制获取`Exchanger`,默认的`Exchanger`是`header`
*
*
*/
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);
}
/**
* DefaultMessenger
*
*
*/
public class HeaderExchanger implements Exchanger {
public static final String NAME = "header";
@Override
public ExchangeClient connect(URL url, ExchangeHandler handler) throws RemotingException {
return new HeaderExchangeClient(Transporters.connect(url, new DecodeHandler(new HeaderExchangeHandler(handler))), true);
}
@Override
public ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {
return new HeaderExchangeServer(Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler))));
}
}
Transporters的bind方法会getTransporter()获取Transporter的实现类,这里也是基于SPI机制,默认是NettyTransporter,使用的是netty3版本,也可以在配置中指定使用netty4,配置如下:
<dubbo:protocol name="dubbo" port="20881" transporter="netty4"/>
Transporters类bind方法
public static RemotingServer 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 {
handler = new ChannelHandlerDispatcher(handlers);
}
return getTransporter().bind(url, handler);
}
public static Transporter getTransporter() {
// SPI机制获取Transporter,默认是 netty,可以配置是netty4
return ExtensionLoader.getExtensionLoader(Transporter.class).getAdaptiveExtension();
}
- 以
netty4为例看一下NettyTransporter的bind,bind方法创建NettyServer对象。
public class NettyTransporter implements Transporter {
public static final String NAME = "netty";
public NettyTransporter() {
}
public RemotingServer bind(URL url, ChannelHandler handler) throws RemotingException {
return new NettyServer(url, handler);
}
public Client connect(URL url, ChannelHandler handler) throws RemotingException {
return new NettyClient(url, handler);
}
}
NettyServer构造函数中设置线程池名称,包装ChannelHandler链。最后NettyServer会被包装成HeaderExchangeServer
public NettyServer(URL url, ChannelHandler handler) throws RemotingException {
// you can customize name and type of client thread pool by THREAD_NAME_KEY and THREADPOOL_KEY in CommonConstants.
// the handler will be wrapped: MultiMessageHandler->HeartbeatHandler->handler
super(ExecutorUtil.setThreadName(url, SERVER_THREAD_POOL_NAME), ChannelHandlers.wrap(handler, url));
}
包装ChannelHandler链,Dispatcher是线程池配置,默认是AllDispatcher,AllDispatcher内部返回AllChannelHandler
public static ChannelHandler wrap(ChannelHandler handler, URL url) {
return ChannelHandlers.getInstance().wrapInternal(handler, url);
}
protected ChannelHandler wrapInternal(ChannelHandler handler, URL url) {
return new MultiMessageHandler(new HeartbeatHandler(ExtensionLoader.getExtensionLoader(Dispatcher.class)
.getAdaptiveExtension().dispatch(handler, url)));
}
包装后的ChannelHandler处理链如下:
|-MultiMessageHandler
|-HeartbeatHandler
|-AllChannelHandler
|- DecodeHandler
|- HeaderExchangeHandler
|- DubboProtocol中的requestHandler
NettyServer的doOpen()初始化并启动netty服务器,NettyServer构造函数会通过其父类AbstractServer的构造函数调用doOpen(),代码如下:
- NettyServer构造函数
public NettyServer(URL url, ChannelHandler handler) throws RemotingException {
// you can customize name and type of client thread pool by THREAD_NAME_KEY and THREADPOOL_KEY in CommonConstants.
// the handler will be wrapped: MultiMessageHandler->HeartbeatHandler->handler
super(ExecutorUtil.setThreadName(url, SERVER_THREAD_POOL_NAME), ChannelHandlers.wrap(handler, url));
}
- AbstractServer构造函数
public AbstractServer(URL url, ChannelHandler handler) throws RemotingException {
super(url, handler);
// 服务的ip:端口
localAddress = getUrl().toInetSocketAddress();
String bindIp = getUrl().getParameter(Constants.BIND_IP_KEY, getUrl().getHost());
int bindPort = getUrl().getParameter(Constants.BIND_PORT_KEY, getUrl().getPort());
if (url.getParameter(ANYHOST_KEY, false) || NetUtils.isInvalidLocalHost(bindIp)) {
bindIp = ANYHOST_VALUE;
}
// 绑定到的ip:端口, 这里是 0.0.0.0:端口
bindAddress = new InetSocketAddress(bindIp, bindPort);
this.accepts = url.getParameter(ACCEPTS_KEY, DEFAULT_ACCEPTS);
this.idleTimeout = url.getParameter(IDLE_TIMEOUT_KEY, DEFAULT_IDLE_TIMEOUT);
try {
// 调用NettyServer的doOpen方法启动netty服务器
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 = executorRepository.createExecutorIfAbsent(url);
}
- NettyServer#doOpen();启动netty服务器
@Override
protected void doOpen() throws Throwable {
// 创建netty bootstrap
bootstrap = new ServerBootstrap();
// boss线程
bossGroup = NettyEventLoopFactory.eventLoopGroup(1, "NettyServerBoss");
// worker 线程
workerGroup = NettyEventLoopFactory.eventLoopGroup(
getUrl().getPositiveParameter(IO_THREADS_KEY, Constants.DEFAULT_IO_THREADS),
"NettyServerWorker");
// 处理器
final NettyServerHandler nettyServerHandler = new NettyServerHandler(getUrl(), this);
channels = nettyServerHandler.getChannels();
bootstrap.group(bossGroup, workerGroup)
.channel(NettyEventLoopFactory.serverSocketChannelClass())
.option(ChannelOption.SO_REUSEADDR, Boolean.TRUE)
.childOption(ChannelOption.TCP_NODELAY, Boolean.TRUE)
.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
// FIXME: should we use getTimeout()?
int idleTimeout = UrlUtils.getIdleTimeout(getUrl());
NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec(), getUrl(), NettyServer.this);
if (getUrl().getParameter(SSL_ENABLED_KEY, false)) {
ch.pipeline().addLast("negotiation",
SslHandlerInitializer.sslServerHandler(getUrl(), nettyServerHandler));
}
ch.pipeline()
.addLast("decoder", adapter.getDecoder())
.addLast("encoder", adapter.getEncoder())
.addLast("server-idle-handler", new IdleStateHandler(0, 0, idleTimeout, MILLISECONDS))
.addLast("handler", nettyServerHandler);
}
});
// 绑定服务器ip:端口
ChannelFuture channelFuture = bootstrap.bind(getBindAddress());
channelFuture.syncUninterruptibly();
channel = channelFuture.channel();
}
建立连接流程
建立连接是Dubbo消费者连接到Dubbo生产者的过程,建立连接会在用户向注册中心注册信息和订阅接口之后,建立连接也是在Transporters中完成的,通过HeaderExchanger的connect建立连接,该方法包装并返回HeaderExchangeClient,Transporters的connect方法会建立连接,并且通过装饰器模式装饰处理器ChannelHandler
public class HeaderExchanger implements Exchanger {
public static final String NAME = "header";
@Override
public ExchangeClient connect(URL url, ExchangeHandler handler) throws RemotingException {
return new HeaderExchangeClient(Transporters.connect(url, new DecodeHandler(new HeaderExchangeHandler(handler))), true);
}
...
}
Transporters.connect建立连接,这里使用Netty作为例子分析
public class NettyTransporter implements Transporter {
public static final String NAME = "netty3";
...
@Override
public Client connect(URL url, ChannelHandler handler) throws RemotingException {
return new NettyClient(url, handler);
}
}
- 最后会通过
NettyClient的doConnect()建立连接
@Override
protected void doConnect() throws Throwable {
long start = System.currentTimeMillis();
// 建立连接
ChannelFuture future = bootstrap.connect(getConnectAddress());
try {
boolean ret = future.awaitUninterruptibly(getConnectTimeout(), MILLISECONDS);
if (ret && future.isSuccess()) {
Channel newChannel = future.channel();
try {
// Close old channel
// copy reference
Channel oldChannel = NettyClient.this.channel;
if (oldChannel != null) {
try {
if (logger.isInfoEnabled()) {
logger.info("Close old netty channel " + oldChannel + " on create new netty channel " + newChannel);
}
oldChannel.close();
} finally {
NettyChannel.removeChannelIfDisconnected(oldChannel);
}
}
} finally {
if (NettyClient.this.isClosed()) {
try {
if (logger.isInfoEnabled()) {
logger.info("Close new netty channel " + newChannel + ", because the client closed.");
}
newChannel.close();
} finally {
NettyClient.this.channel = null;
NettyChannel.removeChannelIfDisconnected(newChannel);
}
} else {
NettyClient.this.channel = newChannel;
}
}
} else if (future.cause() != null) {
throw new RemotingException(this, "client(url: " + getUrl() + ") failed to connect to server "
+ getRemoteAddress() + ", error message is:" + future.cause().getMessage(), future.cause());
} else {
throw new RemotingException(this, "client(url: " + getUrl() + ") failed to connect to server "
+ getRemoteAddress() + " client-side timeout "
+ getConnectTimeout() + "ms (elapsed: " + (System.currentTimeMillis() - start) + "ms) from netty client "
+ NetUtils.getLocalHost() + " using dubbo version " + Version.getVersion());
}
} finally {
// just add new valid channel to NettyChannel's cache
if (!isConnected()) {
//future.cancel(true);
}
}
}
总结
本篇主要研究了一下Dubbo底层服务器创建,打开,建立连接的过程,dubbo服务器底层分为信息交换和数据传输两层,数据交换层能力来自于Transporter接口,该接口支持SPI机制,Dubbo提供了基于netty,netty4,Mina等实现,该接口提供服务的绑定和连接建立,分别是bind和connect方法,核心代码参考NettyServer#doOpen方法和NettyClient#doConnect方法。