写在前面
在dubbo之NIO服务器-抽象API 一文中我们分析了dubbo提供的NIO服务器相关的API抽象,本文一起来看下transport网络传输层的相关代码,实现的功能是抽象mina和netty为统一接口,然后用户基于SPI机制就可以自由选择使用哪种NIO框架了。
1:AbstractPeer
类签名为public abstract class AbstractPeer implements Endpoint, ChannelHandler{}
,其中Endpoint接口参考1.1:Endpoint
。AbstractPeer主要源码如下:
// com.alibaba.dubbo.remoting.transport.AbstractPeer
public abstract class AbstractPeer implements Endpoint, ChannelHandler {
// 通道处理器
private final ChannelHandler handler;
// 服务提供者URL地址
private volatile URL url;
// 正在关闭 startClose()
private volatile boolean closing;
// 已经关闭 close()
private volatile boolean closed;
public AbstractPeer(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;
}
// 发送消息
@Override
public void sent(Channel ch, Object msg) throws RemotingException {
if (closed) {
return;
}
// 2022年3月17日15:38:38
handler.sent(ch, msg);
}
}
2022年3月17日15:38:38
处是直接使用ChannelHandler来发送消息,是一种装饰设计模式 的使用。
1.1:Endpoint
// com.alibaba.dubbo.remoting.Endpoint
public interface Endpoint {
// 获取服务提供者的URL地址
URL getUrl();
// 获取ChannelHandler
ChannelHandler getChannelHandler();
// 获取本地地址
InetSocketAddress getLocalAddress();
// 发送消息
void send(Object message) throws RemotingException;
// 发送消息
void send(Object message, boolean sent) throws RemotingException;
// 关闭连接通道
void close();
// 带有超时时长的关闭,优雅关闭方式
void close(int timeout);
void startClose();
// 判断通道是否已经关闭
boolean isClosed();
}
其有一个抽象实现类AbstractEndpoint
,具体参考1.2:AbstractEndpoint
。
1.2:AbstractEndpoint
源码如下:
// com.alibaba.dubbo.remoting.transport.AbstractEndpoint
public abstract class AbstractEndpoint extends AbstractPeer implements Resetable {
private static final Logger logger = LoggerFactory.getLogger(AbstractEndpoint.class);
// 编解码器
private Codec2 codec;
private int timeout;
// 连接超时时间
private int connectTimeout;
// 构造函数
public AbstractEndpoint(URL url, ChannelHandler handler) {
super(url, handler);
this.codec = getChannelCodec(url);
this.timeout = url.getPositiveParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);
this.connectTimeout = url.getPositiveParameter(Constants.CONNECT_TIMEOUT_KEY, Constants.DEFAULT_CONNECT_TIMEOUT);
}
// 获取编解码器,基于URL参数配合SPI机制获取对应的Codec实现类
protected static Codec2 getChannelCodec(URL url) {
// 通过key codec从URL上获取参数,默认是telnet
String codecName = url.getParameter(Constants.CODEC_KEY, "telnet");
if (ExtensionLoader.getExtensionLoader(Codec2.class).hasExtension(codecName)) {
return ExtensionLoader.getExtensionLoader(Codec2.class).getExtension(codecName);
} else {
return new CodecAdapter(ExtensionLoader.getExtensionLoader(Codec.class)
.getExtension(codecName));
}
}
// 重置相关属性
@Override
public void reset(URL url) {
if (isClosed()) {
throw new IllegalStateException("Failed to reset parameters "
+ url + ", cause: Channel closed. channel: " + getLocalAddress());
}
try {
if (url.hasParameter(Constants.TIMEOUT_KEY)) {
int t = url.getParameter(Constants.TIMEOUT_KEY, 0);
if (t > 0) {
this.timeout = t;
}
}
} catch (Throwable t) {
logger.error(t.getMessage(), t);
}
try {
if (url.hasParameter(Constants.CONNECT_TIMEOUT_KEY)) {
int t = url.getParameter(Constants.CONNECT_TIMEOUT_KEY, 0);
if (t > 0) {
this.connectTimeout = t;
}
}
} catch (Throwable t) {
logger.error(t.getMessage(), t);
}
try {
if (url.hasParameter(Constants.CODEC_KEY)) {
this.codec = getChannelCodec(url);
}
} catch (Throwable t) {
logger.error(t.getMessage(), t);
}
}
@Deprecated
public void reset(com.alibaba.dubbo.common.Parameters parameters) {
reset(getUrl().addParameters(parameters.getParameters()));
}
}
2:Client
接口源码如下:
// com.alibaba.dubbo.remoting.Client
public interface Client extends Endpoint, Channel, Resetable {
// 重新连接
void reconnect() throws RemotingException;
@Deprecated
void reset(com.alibaba.dubbo.common.Parameters parameters);
}
其抽象实现类为AbstractClient,具体参考2.1:AbstractClient
。
2.1:AbstractClient
主要抽象方法如下:
class FakeCls {
protected abstract void doOpen() throws Throwable;
protected abstract void doClose() throws Throwable;
protected abstract void doConnect() throws Throwable;
protected abstract void doDisConnect() throws Throwable;
protected abstract Channel getChannel();
}
构造函数如下:
// com.alibaba.dubbo.remoting.transport.AbstractClient
class FakeCls {
// 定时重连的线程池
private static final ScheduledThreadPoolExecutor reconnectExecutorService = new ScheduledThreadPoolExecutor(2, new NamedThreadFactory("DubboClientReconnectTimer", true));
public AbstractClient(URL url, ChannelHandler handler) throws RemotingException {
super(url, handler);
// 从URL获取重连配置
send_reconnect = url.getParameter(Constants.SEND_RECONNECT_KEY, false);
shutdown_timeout = url.getParameter(Constants.SHUTDOWN_TIMEOUT_KEY, Constants.DEFAULT_SHUTDOWN_TIMEOUT);
// The default reconnection interval is 2s, 1800 means warning interval is 1 hour.
reconnect_warning_period = url.getParameter("reconnect.waring.period", 1800);
// 初始化客户端(如netty客户端),失败则调用close方法关闭连接
try {
doOpen();
} catch (Throwable t) {
close();
throw new RemotingException(url.toInetSocketAddress(), null,
"Failed to start " + getClass().getSimpleName() + " " + NetUtils.getLocalAddress()
+ " connect to the server " + getRemoteAddress() + ", cause: " + t.getMessage(), t);
}
// 连接到服务提供者端,如果是失败则调用close方法关闭,比如会输出如下日志:
// [2022-03-1716:35:03][INFO ][jpm-AbstractClient-connect(282)]- [DUBBO] Successed connect to server /192.168.64.1:20828 from NettyClient 192.168.64.1 using dubbo version 2.6.6, channel is NettyChannel [channel=[id: 0x2b3832d9, L:/192.168.64.1:17885 - R:/192.168.64.1:20828]], dubbo version: 2.6.6, current host: 192.168.64.1
try {
// 2022年3月17日17:09:06
connect();
if (logger.isInfoEnabled()) {
logger.info("Start " + getClass().getSimpleName() + " " + NetUtils.getLocalAddress() + " connect to the server " + getRemoteAddress());
}
} catch (RemotingException t) {
// 如果开始检查的话,则关闭,并抛出异常,否则仅仅打印警告日志
if (url.getParameter(Constants.CHECK_KEY, true)) {
close();
throw t;
} else {
logger.warn("Failed to start " + getClass().getSimpleName() + " " + NetUtils.getLocalAddress()
+ " connect to the server " + getRemoteAddress() + " (check == false, ignore and retry later!), cause: " + t.getMessage(), t);
}
} catch (Throwable t) {
close();
throw new RemotingException(url.toInetSocketAddress(), null,
"Failed to start " + getClass().getSimpleName() + " " + NetUtils.getLocalAddress()
+ " connect to the server " + getRemoteAddress() + ", cause: " + t.getMessage(), t);
}
// 获得线程池
executor = (ExecutorService) ExtensionLoader.getExtensionLoader(DataStore.class)
.getDefaultExtension().get(Constants.CONSUMER_SIDE, Integer.toString(url.getPort()));
ExtensionLoader.getExtensionLoader(DataStore.class)
.getDefaultExtension().remove(Constants.CONSUMER_SIDE, Integer.toString(url.getPort()));
}
}
2022年3月17日17:09:06
处是连接到服务提供者,具体参考2.2:connect
。
2.1.1:发送消息
源码如下:
class FakeCls {
// com.alibaba.dubbo.remoting.transport.AbstractClient.send
public void send(Object message, boolean sent) throws RemotingException {
// 未连接时,连接
if (send_reconnect && !isConnected()) {
connect();
}
// 获取通道
Channel channel = getChannel();
// 通道关闭的情况
if (channel == null || !channel.isConnected()) {
throw new RemotingException(this, "message can not send, because channel is closed . url:" + getUrl());
}
// 发送消息,当使用的是netty时,这里是NettyChannel
channel.send(message, sent);
}
}
2.1.2:包装通道处理器
class FakeCls {
// com.alibaba.dubbo.remoting.transport.AbstractClient.wrapChannelHandler
protected static ChannelHandler wrapChannelHandler(URL url, ChannelHandler handler) {
// 2022年3月17日19:53:30
url = ExecutorUtil.setThreadName(url, CLIENT_THREAD_POOL_NAME);
// 追加String THREADPOOL_KEY = "threadpool"; 即线程池键,默认DEFAULT_CLIENT_THREADPOOL = "cached";
// dubbo://192.168.64.1:20828/...?...&threadpool=cached&...
url = url.addParameterIfAbsent(Constants.THREADPOOL_KEY, Constants.DEFAULT_CLIENT_THREADPOOL);
return ChannelHandlers.wrap(handler, url);
}
}
2022年3月17日19:53:30
处是追加线程名称到URL上,如dubbo://192.168.64.1:20828/dongshi.daddy.service.cluster.ClusterService?...&threadname=DubboClientHandler-192.168.64.1:20828&...
,源码如下:
class FakeCls {
// com.alibaba.dubbo.common.utils.ExecutorUtil.setThreadName
public static URL setThreadName(URL url, String defaultName) {
// 先通过THREAD_NAME_KEY = "threadname";作为key获取,否则取默认值CLIENT_THREAD_POOL_NAME = "DubboClientHandler"
String name = url.getParameter(Constants.THREAD_NAME_KEY, defaultName);
// 拼上地址
name = name + "-" + url.getAddress();
// 追加key THREAD_NAME_KEY = "threadname"到URL上
url = url.addParameter(Constants.THREAD_NAME_KEY, name);
return url;
}
}
具体的NIO框架都提供了具体的实现子类,具体参考2.1.3:具体客户端子类
。
2.1.3:具体客户端子类
类图如下:
红框中就是具体的实现子类,注意其中的NettyClient有2个,是分别针对netty3,netty4的实现,如下:
com.alibaba.dubbo.remoting.transport.netty.NettyClient
com.alibaba.dubbo.remoting.transport.netty4.NettyClient
2.2:connect
源码如下:
class FakeCls {
// com.alibaba.dubbo.remoting.transport.AbstractClient.connect
protected void connect() throws RemotingException {
// private final Lock connectLock = new ReentrantLock(); 并发控制
connectLock.lock();
try {
// 已经连接到服务提供者
if (isConnected()) {
return;
}
// 2022年3月17日17:18:54
initConnectStatusCheckCommand();
// 执行连接
doConnect();
// 没连接成功,则抛出RemotingException
if (!isConnected()) {
throw new RemotingException(this, "Failed connect to server " + getRemoteAddress() + " from " + getClass().getSimpleName() + " "
+ NetUtils.getLocalHost() + " using dubbo version " + Version.getVersion()
+ ", cause: Connect wait timeout: " + getConnectTimeout() + "ms.");
// 打印成功连接日志
} else {
if (logger.isInfoEnabled()) {
logger.info("Successed connect to server " + getRemoteAddress() + " from " + getClass().getSimpleName() + " "
+ NetUtils.getLocalHost() + " using dubbo version " + Version.getVersion()
+ ", channel is " + this.getChannel());
}
}
// 设置重连次数为0
reconnect_count.set(0);
// 设置没有打印过重连日志
reconnect_error_log_flag.set(false);
} catch (RemotingException e) {
throw e;
} catch (Throwable e) {
throw new RemotingException(this, "Failed connect to server " + getRemoteAddress() + " from " + getClass().getSimpleName() + " "
+ NetUtils.getLocalHost() + " using dubbo version " + Version.getVersion()
+ ", cause: " + e.getMessage(), e);
} finally {
// 释放锁
connectLock.unlock();
}
}
}
2022年3月17日17:18:54
初始化重连线程,具体参考2.3:initConnectStatusCheckCommand
。
2.3:initConnectStatusCheckCommand
源码如下:
class FakeCls {
// com.alibaba.dubbo.remoting.transport.AbstractClient.initConnectStatusCheckCommand
private synchronized void initConnectStatusCheckCommand() {
// 2022年3月17日17:26:46
int reconnect = getReconnectParam(getUrl());
if (reconnect > 0 && (reconnectExecutorFuture == null || reconnectExecutorFuture.isCancelled())) {
Runnable connectStatusCheckCommand = new Runnable() {
@Override
public void run() {
try {
// 没有连接,则调用方法connect重连
if (!isConnected()) {
connect();
} else {
// 记录上次尝试重连的时间
lastConnectedTime = System.currentTimeMillis();
}
} catch (Throwable t) {
String errorMsg = "client reconnect to " + getUrl().getAddress() + " find error . url: " + getUrl();
// 超过了一定时间还没有通过重连连接上,则打印错误日志,默认是DEFAULT_SHUTDOWN_TIMEOUT = 1000 * 60 * 15;即15分钟
if (System.currentTimeMillis() - lastConnectedTime > shutdown_timeout) {
// 还没有打印过重连超时错误日志
if (!reconnect_error_log_flag.get()) {
// 设置已经打印过超时重连错误日志
reconnect_error_log_flag.set(true);
// 打印超时重连错误日志
logger.error(errorMsg, t);
return;
}
}
// 达到了下一次打印超时重连错误日志此处周期则打印错误日志,reconnect_warning_period是定义多少个重连周期,如每5秒重连一次
// ,当该值为10时,代表重连十次才会打印错误日志
if (reconnect_count.getAndIncrement() % reconnect_warning_period == 0) {
logger.warn(errorMsg, t);
}
}
}
};
// 按照reconnect的时间间隔开始定时执行重连操作
reconnectExecutorFuture = reconnectExecutorService.scheduleWithFixedDelay(connectStatusCheckCommand, reconnect, reconnect, TimeUnit.MILLISECONDS);
}
}
}
2022年3月17日17:26:46
处是获取重连频率配置,具体参考2.3.1:getReconnectParam
。
2.3.1:getReconnectParam
源码如下:
class FakeCls {
// com.alibaba.dubbo.remoting.transport.AbstractClient.getReconnectParam
private static int getReconnectParam(URL url) {
int reconnect;
// 从URL获取RECONNECT_KEY = "reconnect";配置的值
String param = url.getParameter(Constants.RECONNECT_KEY);
// 如果是没有配置,或者是配置的值是true,则使用默认的重连时间间隔int DEFAULT_RECONNECT_PERIOD = 2000;即2000毫秒
if (param == null || param.length() == 0 || "true".equalsIgnoreCase(param)) {
reconnect = Constants.DEFAULT_RECONNECT_PERIOD;
// 如果是配置的值false,则代表不重连,0->false
} else if ("false".equalsIgnoreCase(param)) {
reconnect = 0;
} else {
// 转为整数,只允许是正整数
try {
reconnect = Integer.parseInt(param);
} catch (Exception e) {
throw new IllegalArgumentException("reconnect param must be nonnegative integer or false/true. input is:" + param);
}
// 判断配置为负数的情况,如配置<dubbo:reference ... reconnect="-30"/>
if (reconnect < 0) {
throw new IllegalArgumentException("reconnect param must be nonnegative integer or false/true. input is:" + param);
}
}
return reconnect;
}
}
3:Server
定义服务端的相关API,顶层接口是com.alibaba.dubbo.remoting.Server
,源码如下:
public interface Server extends Endpoint, Resetable {
boolean isBound();
// 获取连接的通道们,因为可能被多个客户端连接,所以这里是个集合
Collection<Channel> getChannels();
// 获取对应远程客户端的通道
Channel getChannel(InetSocketAddress remoteAddress);
// 重置相关参数,该方法已废弃
@Deprecated
void reset(com.alibaba.dubbo.common.Parameters parameters);
}
其抽象实现类是com.alibaba.dubbo.remoting.transport.AbstractServer
,具体参考3.1:AbstractServer
。
3.1:AbstractServer
供子类实现的抽象方法:
protected abstract void doOpen() throws Throwable;
protected abstract void doClose() throws Throwable;
构造方法如下:
class FakeCls {
// com.alibaba.dubbo.remoting.transport.AbstractServer.AbstractServer
// 线程池名称
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);
// dubbo://192.168.64.1:20828/dongshi.daddy.service.cluster.ClusterService?...&weight=30 结果-> /192.168.64.1:20828
localAddress = getUrl().toInetSocketAddress();
// 服务提供者绑定的IP地址
// dubbo://192.168.64.1:20828/dongshi.daddy.service.cluster.ClusterService?...&weight=30 结果-> 192.168.64.1
String bindIp = getUrl().getParameter(Constants.BIND_IP_KEY, getUrl().getHost());
// 服务提供者绑定的端口号
// dubbo://192.168.64.1:20828/dongshi.daddy.service.cluster.ClusterService?...&weight=30 结果-> 20828
int bindPort = getUrl().getParameter(Constants.BIND_PORT_KEY, getUrl().getPort());
// 是否绑定到任意主机,即0.0.0.0,这样所有可以通过任何可访问到本机的IP来访问
if (url.getParameter(Constants.ANYHOST_KEY, false) || NetUtils.isInvalidLocalHost(bindIp)) {
// String ANYHOST = "0.0.0.0";
bindIp = NetUtils.ANYHOST;
}
// 当为任意主机时:/0.0.0.0:20828
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()));
}
}
3.1.1:被客户端连接的方法
源码如下:
class FakeCls {
// com.alibaba.dubbo.remoting.transport.AbstractServer.connected
// 如果是Netty,这里的实现类就是NettyChannel
public void connected(Channel ch) throws RemotingException {
// 如果是服务端正在关闭或者是已经关闭,则打印警告日志,并关闭客户端连接
if (this.isClosing() || this.isClosed()) {
logger.warn("Close new channel " + ch + ", cause: server is closing or has been closed. For example, receive a new connect request while in shutdown process.");
ch.close();
return;
}
// 获取当前建立的连接数,如果是已经超过了最大允许连接数,则打印拒绝连接的error日志(为什么要用error,用warn应该更合适吧!),并关闭连接
Collection<Channel> channels = getChannels();
if (accepts > 0 && channels.size() > accepts) {
logger.error("Close channel " + ch + ", cause: The server " + ch.getLocalAddress() + " connections greater than max config " + accepts);
ch.close();
return;
}
// 调用父类公共逻辑完成连接
super.connected(ch);
}
}
3.1.2:发送消息
源码如下:
class FakeCls {
// com.alibaba.dubbo.remoting.transport.AbstractServer.send
public void send(Object message, boolean sent) throws RemotingException {
// 获取所有的客户端通道
Collection<Channel> channels = getChannels();
// 群发消息
for (Channel channel : channels) {
if (channel.isConnected()) {
channel.send(message, sent);
}
}
}
}
具体的NIO框架都提供了具体的实现子类,具体参考3.1.4:Server子类
。
3.1.3:Server子类
类图如下:
注意到其中有2个NettyServer,这分别是netty3,netty4对应的实现类,如下:
com.alibaba.dubbo.remoting.transport.netty.NettyServer
com.alibaba.dubbo.remoting.transport.netty4.NettyServer
4:Channel
通道的抽象,对应的接口是com.alibaba.dubbo.remoting.Channel
,源码如下:
// com.alibaba.dubbo.remoting.Channel
public interface Channel extends Endpoint {
// 获取远端地址
InetSocketAddress getRemoteAddress();
// 判断是否已经连接
boolean isConnected();
// 判断是否拥有某属性
boolean hasAttribute(String key);
// 获取属性
Object getAttribute(String key);
// 设置属性
void setAttribute(String key, Object value);
// 删除某属性
void removeAttribute(String key);
}
其对应的抽象类是com.alibaba.dubbo.remoting.transport.AbstractChannel
,具体参考4.1:AbstractChannel
。
4.1:AbstractChannel
源码如下:
// com.alibaba.dubbo.remoting.transport.AbstractChannel
public abstract class AbstractChannel extends AbstractPeer implements Channel {
public AbstractChannel(URL url, ChannelHandler handler) {
super(url, handler);
}
// 发送消息,仅提供通道状态的判断,具体的发送交由子类实现
@Override
public void send(Object message, boolean sent) throws RemotingException {
if (isClosed()) {
throw new RemotingException(this, "Failed to send message "
+ (message == null ? "" : message.getClass().getName()) + ":" + message
+ ", cause: Channel closed. channel: " + getLocalAddress() + " -> " + getRemoteAddress());
}
}
}
子类如下图:
5:ChannelHandler
通道状态和消息的监听器类,接口源码如下:
@SPI
public interface ChannelHandler {
// 当连接时
void connected(Channel channel) throws RemotingException;
// 当断开连接时
void disconnected(Channel channel) throws RemotingException;
// 当发送消息时
void sent(Channel channel, Object message) throws RemotingException;
// 当收到消息时
void received(Channel channel, Object message) throws RemotingException;
// 当捕获异常时
void caught(Channel channel, Throwable exception) throws RemotingException;
}
因为ChanelHandler是接口,所以非抽象子类都需要重写所有方法,但并非需要重写所有方法,因此定义了适配器类ChannelHandlerAdapter
,只是给出了空实现,源码如下:
public class ChannelHandlerAdapter implements ChannelHandler {
@Override
public void connected(Channel channel) throws RemotingException {
}
@Override
public void disconnected(Channel channel) throws RemotingException {
}
@Override
public void sent(Channel channel, Object message) throws RemotingException {
}
@Override
public void received(Channel channel, Object message) throws RemotingException {
}
@Override
public void caught(Channel channel, Throwable exception) throws RemotingException {
}
}
我们重点看下一个子类ChannelHandlerDispatcher,具体参考5.1:ChannelHandlerDispatcher
。
5.1:ChannelHandlerDispatcher
维护一组ChannelHandler,作为这一组ChannelHandler的调度器,以send方法为例,源码如下:
class FakeCls {
// com.alibaba.dubbo.remoting.transport.ChannelHandlerDispatcher.sent
@Override
public void sent(Channel channel, Object message) {
for (ChannelHandler listener : channelHandlers) {
try {
listener.sent(channel, message);
} catch (Throwable t) {
logger.error(t.getMessage(), t);
}
}
}
}
5.2:ChannelHandlerDelegate
ChannelHandler的装饰类,典型的装饰设计模式 落地应用,是比较重要的API,源码如下:
// com.alibaba.dubbo.remoting.transport.ChannelHandlerDelegate
public interface ChannelHandlerDelegate extends ChannelHandler {
ChannelHandler getHandler();
}
可以看到仅仅定义了一个getHandler
方法用来获取被装饰的ChannelHandler。
5.2.1:AbstractChannelHandlerDelegate
ChannelHandlerDelegate的抽象类,比较简答,只是直接调用被装饰类的方法,以sent方法为例,源码如下:
public abstract class AbstractChannelHandlerDelegate implements ChannelHandlerDelegate {
protected ChannelHandler handler;
protected AbstractChannelHandlerDelegate(ChannelHandler handler) {
Assert.notNull(handler, "handler == null");
this.handler = handler;
}
@Override
public void sent(Channel channel, Object message) throws RemotingException {
handler.sent(channel, message);
}
}
5.2.2:DecodeHandler
解码处理器,处理器接收到的消息,我们从复写的接收消息方法received开始看,源码如下:
class FakeCls {
// com.alibaba.dubbo.remoting.transport.DecodeHandler.received
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());
}
handler.received(channel, message);
}
}
以上的decode方法源码如下:
class FakeCls {
// com.alibaba.dubbo.remoting.transport.DecodeHandler.decode
private void decode(Object message) {
// 2022年3月21日18:15:24
if (message != null && message instanceof Decodeable) {
try {
((Decodeable) message).decode();
if (log.isDebugEnabled()) {
log.debug("Decode decodeable message " + message.getClass().getName());
}
} catch (Throwable e) {
if (log.isWarnEnabled()) {
log.warn("Call Decodeable.decode failed: " + e.getMessage(), e);
}
} // ~ end of catch
} // ~ end of if
} // ~ end of method decode
}
2022年3月21日18:15:24
处如果是实现了com.alibaba.dubbo.remoting.Decodeable
接口则调用其decode方法完成解码,目前的子类有2个如下图:
5.2.3:MultiMessageHandler
多消息处理器,处理一次接收多个消息的情况,源码如下:
// com.alibaba.dubbo.remoting.transport.MultiMessageHandler
public class MultiMessageHandler extends AbstractChannelHandlerDelegate {
public MultiMessageHandler(ChannelHandler handler) {
super(handler);
}
@SuppressWarnings("unchecked")
@Override
public void received(Channel channel, Object message) throws RemotingException {
// 如果是com.alibaba.dubbo.remoting.exchange.support.MultiMessage类型消息,则迭代依次处理,否则直接处理
if (message instanceof MultiMessage) {
MultiMessage list = (MultiMessage) message;
for (Object obj : list) {
handler.received(channel, obj);
}
} else {
handler.received(channel, message);
}
}
}
6:Dispatcher
接口源码如下:
@SPI(AllDispatcher.NAME)
public interface Dispatcher {
// 分发消息到线程池,返回的ChannelHandler内部就带有了分发逻辑,因此调用返回的ChannelHandler进行处理,
// 也相当于完成了消息分发的功能
@Adaptive({Constants.DISPATCHER_KEY, "dispather", "channel.handler"})
ChannelHandler dispatch(ChannelHandler handler, URL url);
}
该模块负责将消息的处理分发到线程池中,交给ChannleHandler处理,如下可能的代码就是本部分要分享的内容:
class FakeCls {
class FakeFn() {
executor.execute(new Runnable() {
handler.received(channel, message)
});
}
}
6.1:Dispatcher实现类
类图如下:
下面我们来分别看下每个实现类。
6.1.1:AllDispatcher
源码如下:
// com.alibaba.dubbo.remoting.transport.dispatcher.all.AllDispatcher
// 默认的Dispatcher
public class AllDispatcher implements Dispatcher {
public static final String NAME = "all";
@Override
public ChannelHandler dispatch(ChannelHandler handler, URL url) {
// 2022年3月22日18:45:43
return new AllChannelHandler(handler, url);
}
}
2022年3月22日18:45:43
处AllChanelHandler也是ChannelHandlerDelegate
的子类,因此其也是来装饰 类,如下图可以证明:
关于AllChanelHandler
具体参考6.1.2:AllChanelHandler
。
6.1.2:AllChanelHandler
AllChannelHandler继承自WrappedChannelHandler,关于WrappedChannelHandler,具体参考6.1.3:WrappedChannelHandler
。
我们以connected方法为例来看下,源码如下:
class FakeCls {
// com.alibaba.dubbo.remoting.transport.dispatcher.all.AllChannelHandler.connected
@Override
public void connected(Channel channel) throws RemotingException {
ExecutorService cexecutor = getExecutorService();
try {
// 2022年3月23日17:05:54
cexecutor.execute(new ChannelEventRunnable(channel, handler, ChannelState.CONNECTED));
} catch (Throwable t) {
throw new ExecutionException("connect event", channel, getClass() + " error when process connected event .", t);
}
}
}
2022年3月23日17:05:54
处创建ChannelEventRunnable对象该对象是一个java.lang.Runnable子类,封装了不同的通道状态的不同操作,具体参考6.2:ChannelEventRunnable
。其他的
6.1.3:WrappedChannelHandler
源码如下:
public class WrappedChannelHandler implements ChannelHandlerDelegate {
protected static final Logger logger = LoggerFactory.getLogger(WrappedChannelHandler.class);
protected static final ExecutorService SHARED_EXECUTOR = Executors.newCachedThreadPool(new NamedThreadFactory("DubboSharedHandler", true));
// JUC线程池API
protected final ExecutorService executor;
// 被包装的ChannelHandler
protected final ChannelHandler handler;
protected final URL url;
public WrappedChannelHandler(ChannelHandler handler, URL url) {
this.handler = handler;
this.url = url;
executor = (ExecutorService) ExtensionLoader.getExtensionLoader(ThreadPool.class).getAdaptiveExtension().getExecutor(url);
String componentKey = Constants.EXECUTOR_SERVICE_COMPONENT_KEY;
if (Constants.CONSUMER_SIDE.equalsIgnoreCase(url.getParameter(Constants.SIDE_KEY))) {
componentKey = Constants.CONSUMER_SIDE;
}
DataStore dataStore = ExtensionLoader.getExtensionLoader(DataStore.class).getDefaultExtension();
dataStore.put(componentKey, Integer.toString(url.getPort()), executor);
}
// 关闭线程池
public void close() {
try {
if (executor != null) {
executor.shutdown();
}
} catch (Throwable t) {
logger.warn("fail to destroy thread pool of server: " + t.getMessage(), t);
}
}
@Override
public void connected(Channel channel) throws RemotingException {
handler.connected(channel);
}
@Override
public void disconnected(Channel channel) throws RemotingException {
handler.disconnected(channel);
}
@Override
public void sent(Channel channel, Object message) throws RemotingException {
handler.sent(channel, message);
}
@Override
public void received(Channel channel, Object message) throws RemotingException {
handler.received(channel, message);
}
@Override
public void caught(Channel channel, Throwable exception) throws RemotingException {
handler.caught(channel, exception);
}
public ExecutorService getExecutor() {
return executor;
}
@Override
public ChannelHandler getHandler() {
if (handler instanceof ChannelHandlerDelegate) {
return ((ChannelHandlerDelegate) handler).getHandler();
} else {
return handler;
}
}
public URL getUrl() {
return url;
}
public ExecutorService getExecutorService() {
ExecutorService cexecutor = executor;
if (cexecutor == null || cexecutor.isShutdown()) {
cexecutor = SHARED_EXECUTOR;
}
return cexecutor;
}
}
主要是线程池相关内容protected final ExecutorService executor
,其他基本都是直接调用被包装的ChanelHandler对应方法。
6.2:ChannelEventRunnable
核心源码如下:
// com.alibaba.dubbo.remoting.transport.dispatcher.ChannelEventRunnable
class FakeCls {
@Override
public void run() {
// 以下不同的通道状态调用ChannelHandler的不同方法进行处理
if (state == ChannelState.RECEIVED) {
try {
handler.received(channel, message);
} catch (Exception e) {
logger.warn("ChannelEventRunnable handle " + state + " operation error, channel is " + channel
+ ", message is " + message, e);
}
} else {
switch (state) {
// 连接
case CONNECTED:
try {
handler.connected(channel);
} catch (Exception e) {
logger.warn("ChannelEventRunnable handle " + state + " operation error, channel is " + channel, e);
}
break;
// 断开连接
case DISCONNECTED:
try {
handler.disconnected(channel);
} catch (Exception e) {
logger.warn("ChannelEventRunnable handle " + state + " operation error, channel is " + channel, e);
}
break;
// 发送消息
case SENT:
try {
handler.sent(channel, message);
} catch (Exception e) {
logger.warn("ChannelEventRunnable handle " + state + " operation error, channel is " + channel
+ ", message is " + message, e);
}
// 异常捕获
case CAUGHT:
try {
handler.caught(channel, exception);
} catch (Exception e) {
logger.warn("ChannelEventRunnable handle " + state + " operation error, channel is " + channel
+ ", message is: " + message + ", exception is " + exception, e);
}
break;
// 当前未知状态
default:
logger.warn("unknown state: " + state + ", message is " + message);
}
}
}
}
7:Codec
有一个废弃的接口,com.alibaba.dubbo.remoting.Codec,目前使用的是com.alibaba.dubbo.remoting.Codec2,本部分分析的也是Codec2。
负责消息编解码的API,源码如下:
// com.alibaba.dubbo.remoting.Codec2
@SPI
public interface Codec2 {
@Adaptive({Constants.CODEC_KEY})
void encode(Channel channel, ChannelBuffer buffer, Object message) throws IOException;
@Adaptive({Constants.CODEC_KEY})
Object decode(Channel channel, ChannelBuffer buffer) throws IOException;
enum DecodeResult {
NEED_MORE_INPUT, SKIP_SOME_INPUT
}
}
可以看到是自适应 的。先来看下其抽象实现类AbstractCodec
。
7.1:AbstractCodec
源码如下:
// com.alibaba.dubbo.remoting.transport.AbstractCodec
public abstract class AbstractCodec implements Codec2 {
private static final Logger logger = LoggerFactory.getLogger(AbstractCodec.class);
// 2022年3月23日18:11:02
protected static void checkPayload(Channel channel, long size) throws IOException {
int payload = Constants.DEFAULT_PAYLOAD;
if (channel != null && channel.getUrl() != null) {
payload = channel.getUrl().getParameter(Constants.PAYLOAD_KEY, Constants.DEFAULT_PAYLOAD);
}
if (payload > 0 && size > payload) {
ExceedPayloadLimitException e = new ExceedPayloadLimitException("Data length too large: " + size + ", max payload: " + payload + ", channel: " + channel);
logger.error(e);
throw e;
}
}
// 2022年3月23日18:24:35
protected Serialization getSerialization(Channel channel) {
return CodecSupport.getSerialization(channel.getUrl());
}
// 判断是否为客户端
protected boolean isClientSide(Channel channel) {
String side = (String) channel.getAttribute(Constants.SIDE_KEY);
if ("client".equals(side)) {
return true;
} else if ("server".equals(side)) {
return false;
} else {
InetSocketAddress address = channel.getRemoteAddress();
URL url = channel.getUrl();
boolean client = url.getPort() == address.getPort()
&& NetUtils.filterLocalHost(url.getIp()).equals(
NetUtils.filterLocalHost(address.getAddress()
.getHostAddress()));
channel.setAttribute(Constants.SIDE_KEY, client ? "client"
: "server");
return client;
}
}
// 判断是否为服务端
protected boolean isServerSide(Channel channel) {
return !isClientSide(channel);
}
}
2022年3月23日18:11:02
处是校验实际的数据长度是否超过允许的数据长度,比如配置<dubbo:provider payload="2"/>
,当调用为System.err.println(clusterService.sayHi("helloooooo"));
时就会超过最大长度,如下输出:
2022年3月23日18:24:35
处是获取序列化类,具体参考7.2:CodecSupport
。不同类型的消息对应了不同的子类,如下子类:
通过集成来增强了编解码的功能(此时没有使用包装,当然也是一个选择的过程)
。我们以TransportCodec为例来一起看下,具体参考7.3:TransportCodec
。
7.2:CodecSupport
编解码工具类,提供了序列化的功能,源码如下:
// com.alibaba.dubbo.remoting.transport.CodecSupport
public class CodecSupport {
private static final Logger logger = LoggerFactory.getLogger(CodecSupport.class);
private static Map<Byte, Serialization> ID_SERIALIZATION_MAP = new HashMap<Byte, Serialization>();
private static Map<Byte, String> ID_SERIALIZATIONNAME_MAP = new HashMap<Byte, String>();
static {
Set<String> supportedExtensions = ExtensionLoader.getExtensionLoader(Serialization.class).getSupportedExtensions();
for (String name : supportedExtensions) {
Serialization serialization = ExtensionLoader.getExtensionLoader(Serialization.class).getExtension(name);
byte idByte = serialization.getContentTypeId();
if (ID_SERIALIZATION_MAP.containsKey(idByte)) {
logger.error("Serialization extension " + serialization.getClass().getName()
+ " has duplicate id to Serialization extension "
+ ID_SERIALIZATION_MAP.get(idByte).getClass().getName()
+ ", ignore this Serialization extension");
continue;
}
ID_SERIALIZATION_MAP.put(idByte, serialization);
ID_SERIALIZATIONNAME_MAP.put(idByte, name);
}
}
}
7.3:TransportCodec
传输编解码器,直接使用Serialization进行序列化/反序列化。
7.3.1:编码消息
源码如下:
// com.alibaba.dubbo.remoting.transport.codec.TransportCodec.encode
@Override
public void encode(Channel channel, ChannelBuffer buffer, Object message) throws IOException {
// 包装
OutputStream output = new ChannelBufferOutputStream(buffer);
// 获取序列化器并序列化
ObjectOutput objectOutput = getSerialization(channel).serialize(channel.getUrl(), output);
// 编码
encodeData(channel, objectOutput, message);
// 刷缓存
objectOutput.flushBuffer();
if (objectOutput instanceof Cleanable) {
((Cleanable) objectOutput).cleanup();
}
}
7.3.2:解码消息
类似于编码,源码如下:
// com.alibaba.dubbo.remoting.transport.codec.TransportCodec.decode
@Override
public Object decode(Channel channel, ChannelBuffer buffer) throws IOException {
InputStream input = new ChannelBufferInputStream(buffer);
ObjectInput objectInput = getSerialization(channel).deserialize(channel.getUrl(), input);
Object object = decodeData(channel, objectInput);
if (objectInput instanceof Cleanable) {
((Cleanable) objectInput).cleanup();
}
return object;
}
7.4:CodecAdapter
实现了com.alibaba.dubbo.remoting.Codec2
接口,适配com.alibaba.dubbo.remoting.Codec
,