网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
ExchangeHandlerAdapter
当请求到来的时候, 首先是调用received(Channel channel, Object message),然后会调用reply(ExchangeChannel channel, Object message)处理ing求,message就是请求的数据, channel则表示与客户端的长连接;
工作过程: 最终就是通过反射技术调用Method.invoke执行服务;
- 类型转换, 将Object转换为Invocation;
- 调用getInvoker获取服务提供者的执行器;(这个Invoker会被多层包装)
- 设置远程地址remoteAddress给RpcContext
- 调用执行器的invoke方法, 返回结果;
- 返回一个CompletionFuture实例;
private ExchangeHandler requestHandler = new ExchangeHandlerAdapter() {
@Override
public CompletableFuture<Object> reply(ExchangeChannel channel, Object message) throws RemotingException {
Invocation inv = (Invocation) message;
Invoker<?> invoker = getInvoker(channel, inv);
//省略部分代码
RpcContext.getContext().setRemoteAddress(channel.getRemoteAddress());
Result result = invoker.invoke(inv);
return result.completionFuture().thenApply(Function.identity());
}
@Override
public void received(Channel channel, Object message) throws RemotingException {
if (message instanceof Invocation) {
// 这是服务端接收到Invocation时的处理逻辑
reply((ExchangeChannel) channel, message);
} else {
super.received(channel, message);
}
}
}
Exchangers.bind(url, requestHandler)
过程:
- 调用getExchanger(url),通过SPI机制获取Exchanger的扩展实现类, 默认的实现类是HeaderExhanger
- 调用bind方法启动netty;
public static ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {
// codec表示协议编码方式
url = url.addParameterIfAbsent(Constants.CODEC_KEY, "exchange");
// 通过url得到HeaderExchanger, 利用HeaderExchanger进行bind,将得到一个HeaderExchangeServer
return getExchanger(url).bind(url, handler);
}
HeaderExchanger#bind(Url, hander)
工作:
- 首先会传进来的ExchangeHandlerAdapter实例handler进行包装, 包装为HeaderExchangerHandler;
- 再对HeaderExchangerHandler包装为DecodeHandler的实例;
- 调用Transporters#bind方法创建一个NettyServer,
- 将NettyServer实例包装为HeaderExchangerServer; 返回;
@Override
public ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {
// 下面会去启动Netty
// 对handler包装了两层,表示当处理一个请求时,每层Handler负责不同的处理逻辑
// 为什么在connect和bind时都是DecodeHandler,解码,解的是把InputStream解析成RpcInvocation对象
return new HeaderExchangeServer(Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler))));
}
Transporters.bind(url, handlers)
工作流程:
- 如果bind了多个handler,那么当有一个连接过来时,会循环每个handler去处理连接
- 调用getTransport()方法通过SPI机制获取一个Transporter实例, 默认情况下是NettyTransporter实例;
- 调用NettyTransport#bind方法,创建一个nettyServer;
public static Server bind(URL url, ChannelHandler... handlers) throws RemotingException {
//....
//
ChannelHandler handler;
if (handlers.length == 1) {
handler = handlers[0];
} else {
handler = new ChannelHandlerDispatcher(handlers);
}
// 调用NettyTransporter去绑定,Transporter表示网络传输层
return getTransporter().bind(url, handler);
}
NettyTransporter#bind(url, listener)
创建Nettyserver实例;
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);
}
}
NettyServer(URL url, ChannelHandler handler)
- 调用ChannerHandlers.wrap方法包装DecoderHandler实例;
- 调用父类构造方法;
public class NettyServer extends AbstractServer implements Server {
private Map<String, Channel> channels;
private ServerBootstrap bootstrap;
private io.netty.channel.Channel channel;
private EventLoopGroup bossGroup;
private EventLoopGroup workerGroup;
public NettyServer(URL url, ChannelHandler handler) throws RemotingException {
super(url, ChannelHandlers.wrap(handler, ExecutorUtil.setThreadName(url, SERVER_THREAD_POOL_NAME)));
}
ChannelHandlers.wrap(ChannelHandler handler, URL url)
- 调用ChannelHandlers#getInstance()方法获取单例ChannelHandlers, 使用的是饿汉式;
- 调用wrapInternal(handler, url) 包装DecoderHandler实例handler:
public static ChannelHandler wrap(ChannelHandler handler, URL url) {
return ChannelHandlers.getInstance().wrapInternal(handler, url);
}
ChannelHandlers#wrapInternal(handler, url)
工作流程:
- 通过Spi机制调用将DecoderHandler实例包装为AllChannelHandler实例;
- 再将AllChannelHandler包装为MultiMessageHandler实例;
public class ChannelHandlers {
// 单例模式
private static ChannelHandlers INSTANCE = new ChannelHandlers();
protected ChannelHandlers() {
}
public static ChannelHandler wrap(ChannelHandler handler, URL url) {
return ChannelHandlers.getInstance().wrapInternal(handler, url);
}
protected static ChannelHandlers getInstance() {
return INSTANCE;
}
static void setTestingChannelHandlers(ChannelHandlers instance) {
INSTANCE = instance;
}
protected ChannelHandler wrapInternal(ChannelHandler handler, URL url) {
// 先通过ExtensionLoader.getExtensionLoader(Dispatcher.class).getAdaptiveExtension().dispatch(handler, url)
// 得到一个AllChannelHandler(handler, url)
// 然后把AllChannelHandler包装成HeartbeatHandler,HeartbeatHandler包装成MultiMessageHandler
// 所以当Netty接收到一个数据时,会经历MultiMessageHandler--->HeartbeatHandler---->AllChannelHandler
// 而AllChannelHandler会调用handler
return new MultiMessageHandler(new HeartbeatHandler(ExtensionLoader.getExtensionLoader(Dispatcher.class)
.getAdaptiveExtension().dispatch(handler, url)));
}
}
这里Dispacter涉及到服务端的线程模型, 标记
AbstractServer(URL url, ChannelHandler handler)
创建NettyServer实例会将调用父类构造方法, 父类式AbstractServer, 抽象类;
工作流程:
- 调用父类的构造方法, 将handler赋值给父类AbstractPeer的handler属性;
- 获取本地地址localAddress;
- 获取服务绑定的IP;
- 获取服务绑定的端口号;
- 创建InetSocketAddress实例, 用来创建Socke连接的构造参数;
- 调用doOpen()启动netty;
public AbstractServer(URL url, ChannelHandler handler) throws RemotingException {
super(url, handler);
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;
}
bindAddress = new InetSocketAddress(bindIp, bindPort);
this.accepts = url.getParameter(ACCEPTS_KEY, DEFAULT_ACCEPTS);
this.idleTimeout = url.getParameter(IDLE_TIMEOUT_KEY, DEFAULT_IDLE_TIMEOUT);
try {
doOpen();
if (logger.isInfoEnabled()) {
logger.info("Start " + getClass().getSimpleName() + " bind " + getBindAddress() + ", export " + getLocalAddress());
}
} catch (Throwable t) {
//....
}
//...
}
AbstractEndpoint(url, handler)
调用父类构造方法, 设置编码方式, 设置服务提供者的超时时间, 设置创建连接的超时时间。
public AbstractEndpoint(URL url, ChannelHandler handler) {
super(url, handler);
this.codec = getChannelCodec(url);
this.timeout = url.getPositiveParameter(TIMEOUT_KEY, DEFAULT_TIMEOUT);
this.connectTimeout = url.getPositiveParameter(Constants.CONNECT_TIMEOUT_KEY, Constants.DEFAULT_CONNECT_TIMEOUT);
}
AbstractPeer(URL url, ChannelHandler handler)
将handler保存起来;
public abstract class AbstractPeer implements Endpoint, ChannelHandler {
private final ChannelHandler handler;
private volatile URL url;
private volatile boolean closing;
private volatile boolean closed;
public AbstractPeer(URL url, ChannelHandler handler) {
this.url = url;
this.handler = handler;
}
NettyServer#doOpen()
上面过程中,调用new NettyServer, 会将MultiMessageHandler赋值给NettyServer的Handler属性;
因此,NettyServer另一种身份是一个MultiMessageHandler:
工作流程:
- 创建ServerBootstrap服务端;
- 创建工作线程组,IO事件线程组;
- 创建NettyServerHandler, 将this传进去, 即传了一个MultiMessageHandler,包装为一个NettyServerHandler实例;
- 服务端参数的设置;
@Override
protected void doOpen() throws Throwable {
bootstrap = new ServerBootstrap();
bossGroup = new NioEventLoopGroup(1, new DefaultThreadFactory("NettyServerBoss", true));
workerGroup = new NioEventLoopGroup(getUrl().getPositiveParameter(IO_THREADS_KEY, Constants.DEFAULT_IO_THREADS),
new DefaultThreadFactory("NettyServerWorker", true));
final NettyServerHandler nettyServerHandler = new NettyServerHandler(getUrl(), this);
channels = nettyServerHandler.getChannels();
bootstrap.group(bossGroup, workerGroup)
.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 {
// FIXME: should we use getTimeout()?
int idleTimeout = UrlUtils.getIdleTimeout(getUrl());
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("server-idle-handler", new IdleStateHandler(0, 0, idleTimeout, MILLISECONDS))
.addLast("handler", nettyServerHandler);
}
});
// bind
ChannelFuture channelFuture = bootstrap.bind(getBindAddress());
channelFuture.syncUninterruptibly();
channel = channelFuture.channel();
}
服务端Netty的hander包装过程总结
- ExchangeHandlerAdapter的requestHandler实例包装为HeaderExchangerhandler
- 将HandlerExchangerHandler实例包装为Decodehandler;
- 将DecodeHandler实例包装为AllChannelHandler;
- 将AllChannelHandler实例包装为MultiMessageHandler实例;
- 将MultiMessageHandler实例包装为NettyServerHandler;
- 最终将NettyServerHandler绑定到PipeLine种的handler处理器;
服务端线程模型
涉及的过程是 : 将DecodeHandler包装为AllChannelhandler的过程;
protected ChannelHandler wrapInternal(ChannelHandler handler, URL url) {
return new MultiMessageHandler(new HeartbeatHandler(ExtensionLoader.getExtensionLoader(Dispatcher.class)
.getAdaptiveExtension().dispatch(handler, url)));
}
通过SPI机制获取Dispatcher的扩展实现类,
@SPI(AllDispatcher.NAME)
public interface Dispatcher {
/\*\*
\* dispatch the message to threadpool.
\*
\* @param handler
\* @param url
\* @return channel handler
\*/
@Adaptive({Constants.DISPATCHER_KEY, "dispather", "channel.handler"})
// The last two parameters are reserved for compatibility with the old configuration
ChannelHandler dispatch(ChannelHandler handler, URL url);
}
默认使用AllDispatcher#dispatch方法;
AllDispatcher #dispatch(handler, url)
创建AllChannelHandler实例, 包装了一个handler;
public class AllDispatcher implements Dispatcher {
public static final String NAME = "all";
@Override
public ChannelHandler dispatch(ChannelHandler handler, URL url) {
return new AllChannelHandler(handler, url);
}
}
AllChannelHandler
当netty接收到数据后,会调用received方法
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
{
return new AllChannelHandler(handler, url);
}
}
##### AllChannelHandler
当netty接收到数据后,会调用received方法
[外链图片转存中...(img-Iu6HAGuO-1715744560750)]
[外链图片转存中...(img-XX8iKggC-1715744560751)]
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化的资料的朋友,可以添加戳这里获取](https://bbs.csdn.net/topics/618668825)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**