Dubbo的网络传输层支持Netty、Mina、Grizzly类型的NIO框架;那么Dubbo是怎么屏蔽三种NIO框架底层细节实现,做到统一处理Channel的。下面我们拿Netty与Mina实现网络传输为例,来简单总结下
NettyServer.java
@Override
protected void doOpen() throws Throwable {
//启动器
bootstrap = new ServerBootstrap();
//Reactor线程模型之创建Selector用于不断监听Socket连接、客户端读写操作
bossGroup = new NioEventLoopGroup(1, new DefaultThreadFactory("NettyServerBoss", true));
//用一个线程池即workers,负责处理Selector派发的读写操作。
workerGroup = new NioEventLoopGroup(getUrl().getPositiveParameter(Constants.IO_THREADS_KEY, Constants.DEFAULT_IO_THREADS),
new DefaultThreadFactory("NettyServerWorker", true));
//定义NettyServerHandler,处理客户端channel连接到服务端的相关事件,不明白自行学习Netty
final NettyServerHandler nettyServerHandler = new NettyServerHandler(getUrl(), this);
channels = nettyServerHandler.getChannels();
//配置启动器
bootstrap.group(bossGroup, workerGroup)
//指定服务端的channel类型
.channel(NioServerSocketChannel.class)
.childOption(ChannelOption.TCP_NODELAY, Boolean.TRUE)
.childOption(ChannelOption.SO_REUSEADDR, Boolean.TRUE)
.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
.childHandler(new ChannelInitializer<NioSocketChannel>() {
@Override
protected void initChannel(NioSocketChannel ch) throws Exception {
//Netty编解码适配器
NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec(), getUrl(), NettyServer.this);
//添加编码、解码handler还有与业务有关的handler
ch.pipeline()//.addLast("logging",new LoggingHandler(LogLevel.INFO))//for debug
.addLast("decoder", adapter.getDecoder())
.addLast("encoder", adapter.getEncoder())
.addLast("handler", nettyServerHandler);
}
});
//
ChannelFuture channelFuture = bootstrap.bind(getBindAddress());
channelFuture.syncUninterruptibly();
channel = channelFuture.channel();
}
如果我们了解Netty怎么构构建一个简单的网络程序,上面代码不难理解
MinaServer.java
@Override
protected void doOpen() throws Throwable {
// set thread pool.
acceptor = new SocketAcceptor(getUrl().getPositiveParameter(Constants.IO_THREADS_KEY, Constants.DEFAULT_IO_THREADS),
Executors.newCachedThreadPool(new NamedThreadFactory("MinaServerWorker",
true)));
// config
SocketAcceptorConfig cfg = (SocketAcceptorConfig) acceptor.getDefaultConfig();
cfg.setThreadModel(ThreadModel.MANUAL);
// set codec.
acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(new MinaCodecAdapter(getCodec(), getUrl(), this)));
acceptor.bind(getBindAddress(), new MinaHandler(getUrl(), this));
}
Netty和Mina的服务端实现
1.开启服务方式不太一样
2.都需要ChannelHandler,需要继承org.jboss.netty.channel.SimpleChannelHandler(或者其他方式),来处理org.jboss.netty.channel.Channel的连接读写事件 对于mina来说:需要继承org.apache.mina.common.IoHandlerAdapter(或者其他方式),来处理org.apache.mina.common.IoSession的连接读写事件
3.Channel不同,Netty用的是io.netty.channel.Channel进行数据的传输,Mina用的是org.apache.mina.common.IoSession
4.编解码器;Netty需要继承MessageToByteEncoder、ByteToMessageDecoder;Mina需要继承IoFilterAdapter类;
5.编解码器的不同必然带来不同字节缓冲区实现不同,Netty最终会将编码数据写入到io.netty.buffer.ByteBuf,Mina最终会将编码的数据通过ProtocolEncoderOutput写入到org.apache.mina.common.ByteBuffer中,
Dubbo是怎么实现统一的
- Channel
public interface Channel extends Endpoint {
/**
* get remote address.
*
* @return remote address.
*/
InetSocketAddress getRemoteAddress();
/**
* is connected.
*
* @return connected
*/
boolean isConnected();
/**
* has attribute.
*
* @param key key.
* @return has or has not.
*/
boolean hasAttribute(String key);
/**
* get attribute.
*
* @param key key.
* @return value.
*/
Object getAttribute(String key);
/**
* set attribute.
*
* @param key key.
* @param value value.
*/
void setAttribute(String key, Object value);
/**
* remove attribute.
*
* @param key key.
*/
void removeAttribute(String key);
}
1.针对netty,上述Channel的实现为NettyChannel,内部含有一个netty自己的org.jboss.netty.channel.Channel channel对象,即该com.alibaba.dubbo.remoting.Channel接口的功能实现全部委托为底层的org.jboss.netty.channel.Channel channel对象来实现
2. 针对mina,上述Channel实现为MinaChannel,内部包含一个mina自己的org.apache.mina.common.IoSession session对象,即该com.alibaba.dubbo.remoting.Channel接口的功能实现全部委托为底层的org.apache.mina.common.IoSession session对象来实现
- ChannelHandler
ublic interface ChannelHandler {
/**
* on channel connected.
*
* @param channel channel.
*/
void connected(Channel channel) throws RemotingException;
/**
* on channel disconnected.
*
* @param channel channel.
*/
void disconnected(Channel channel) throws RemotingException;
/**
* on message sent.
*
* @param channel channel.
* @param message message.
*/
void sent(Channel channel, Object message) throws RemotingException;
/**
* on message received.
*
* @param channel channel.
* @param message message.
*/
void received(Channel channel, Object message) throws RemotingException;
/**
* on exception caught.
*
* @param channel channel.
* @param exception exception.
*/
void caught(Channel channel, Throwable exception) throws RemotingException;
}
1.针对Netty,ChannelHandler的服务端的NettyServerHandler通过继承ChannelDupexHandler,接收连接读写事件并委托给自身的com.alibaba.dubbo.remoting.ChannelHandler,客户端则是NettyClientHandler
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
NettyChannel channel = NettyChannel.getOrAddChannel(ctx.channel(), url, handler);
try {
handler.received(channel, msg);
} finally {
NettyChannel.removeChannelIfDisconnected(ctx.channel());
}
}
2.针对Mina,ChannelHandler的服务端实现MinaHandler通过继承自IoHandlerAdapter,接收连接读写事件并委托给自己的com.alibaba.dubbo.remoting.ChannelHandler,
@Override
public void sessionOpened(IoSession session) throws Exception {
MinaChannel channel = MinaChannel.getOrAddChannel(session, url, handler);
try {
handler.connected(channel);
} finally {
MinaChannel.removeChannelIfDisconnected(session);
}
}
- ChannelBuffer:该接口的设计参考io.netty.buffer.ByteBuf设计,注释和方法基本相同
- AbstractChannelBuffer:readerIndex和writerIndex用于维护读取和写入索引,markedReaderIndex和markedWriterIndex,当对缓冲区进行读写操作时,可能需要对之前的操作进行回滚。
/**
* 读索引
*/
private int readerIndex;
/**
* 写索引
*/
private int writerIndex;
/**
* 标记读索引
*/
private int markedReaderIndex;
/**
* 标记写索引
*/
private int markedWriterIndex;
1.针对Netty,字节缓冲区采用NettyBackedChannelBuffer实现,该类内部持有一个io.netty.buffer.ByteBuf对象,用于实现对字节读取操作,Dubbo的编解码最终也是对NettyBackedChannelBuffer中的ByteBuf对象的操作
2.针对Mina,字节缓冲区采用DynamicChannelBuffer实现,该类内部维护了ChannelBufferFactory和ChannelBuffer类型的对象,ChannelBuffer的创建通过ChannelBufferFactory实现。
/**
* 通道缓存工厂
*/
private final ChannelBufferFactory factory;
/**
* 通道缓存区
*/
private ChannelBuffer buffer;
对于编解码统一使用ChannelBuffer对象进行字节的处理接口。