前書き
前回の記事では、NettyServerを起動したと述べています。Nettyプログラムを作成したすべての友人は、
ChannelHandlerインターフェイスを実装してビジネスロジックを処理し、ChannelHandlerをChannelPipelineに追加すると、リクエストがChannelHandlerによって処理されることを知っています。 ChannelPipelineは、典型的な責任連鎖モデルです。
しかし、前のセクションでNettyServerを起動したとき、ChannelPipelineに追加されたChannelHandlerはNettyServerHandlerのみであることがわかりました(io.netty.channel.ChannelHandlerインターフェイスを実装しています)。実際、このNettyServerHandlerは何もせず、リクエストをDubboに転送するだけです。 ChannelHandler(org.apache.dubbo.remoting.ChannelHandlerを実装します)。
ChannelHandlerインターフェースはNettyとDubboの両方で定義されていることに注意してください。NettyのChannelHandlerはChainof Responsibilityモードで実行され、DubboのChannelHandlerはデコレータモードで実行されます。Dubboは、主に特定の通信レイヤーフレームワークとの結合を回避するために、ChannelHandlerインターフェイスを再定義するだけで済みます。結局のところ、ネットワーク通信フレームワークはNettyだけではありません。
したがって、実際のリクエストの実行は次のChannelHandlerを経由します。そのうち、NettyServerHandlerのみがNettyフレームワークのChannelHandlerインターフェースを実装し、残りはDubboのChannelHandlerインターフェースを実装します。
これらのChannelHandlerの役割について話させてください
ChannelHandler | 効果 |
---|---|
NettyServerHandler | 接続、切断、読み取り、書き込み、例外などのNettyサーバーイベントを処理します |
MultiMessageHandler | マルチメッセージバッチ処理 |
HeartbeatHandler | ハートビート処理 |
AllChannelHandler | すべてのNettyリクエストを処理のためにビジネススレッドプールに入れます |
DecodeHandler | メッセージをデコードします |
HeaderExchangeHandler | パッケージ処理要求/応答、およびtelnet要求 |
ExchangeHandlerAdapter | サービスメソッドを見つけて呼び出します |
リクエストを受け取る
NettyServerは、Dubboサービスのエクスポート中に開始されます。つまり、NettyServer#doOpenメソッドが実行されます。
protected void doOpen() throws Throwable {
bootstrap = new ServerBootstrap();
bossGroup = new NioEventLoopGroup(1, new DefaultThreadFactory("NettyServerBoss", true));
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();
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()) // 解码器handler
.addLast("encoder", adapter.getEncoder()) // 编码器handler
// 心跳检查handler
.addLast("server-idle-handler", new IdleStateHandler(0, 0, idleTimeout, MILLISECONDS))
.addLast("handler", nettyServerHandler);
}
});
// bind
ChannelFuture channelFuture = bootstrap.bind(getBindAddress());
channelFuture.syncUninterruptibly();
channel = channelFuture.channel();
}
NettyServerHandlerは、ビジネスロジックを処理するハンドラーであることがわかります。メッセージを受信すると、NettyServerHandler#channelReadメソッドがアクティブになります。
// NettyServerHandler.java
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());
}
}
次に、受信したメソッド
MultiMessageHandlerと次のハンドラーのHeartbeatHandlerを順番に実行します。これらはメインプロセスとはほとんど関係がないため、慎重に分析せずに直接AllChannelHandlerに移動します。
次に、AllChannelHandlerに対して、要求は実行のためにビジネススレッドプールに配置されます(Dubbo Spiを介してさまざまな異なる実装を構成できます。後の記事で、スレッドモデルとスレッドプール戦略を詳細に分析します)
// AllChannelHandler.java
public void received(Channel channel, Object message) throws RemotingException {
ExecutorService cexecutor = getExecutorService();
try {
// 将请求和响应消息派发到线程池中处理
cexecutor.execute(new ChannelEventRunnable(channel, handler, ChannelState.RECEIVED, message));
} catch (Throwable t) {
}
}
次に、DecodeHandlerに対して、メッセージがデコードされます。サービスプロバイダーとサービスコンシューマーはデフォルトでAllChannelHandlerを使用するため、メッセージタイプはRequestまたはResponseの場合があります。
// DecodeHandler
public void received(Channel channel, Object message) throws RemotingException {
if (message instanceof Decodeable) {
// 对 Decodeable 接口实现类对象进行解码
decode(message);
}
if (message instanceof Request) {
// 对 Request 的 data 字段进行解码
decode(((Request) message).getData());
}
if (message instanceof Response) {
// 对 Response 的 result 字段进行解码
decode(((Response) message).getResult());
}
// 解码完毕后的下一站为 HeaderExchangeHandler
handler.received(channel, message);
}
次の停止はHeaderExchangeHandlerです。
これには、主に要求/応答をカプセル化して処理するための多くのコードが含まれています。要求応答は、このハンドラーに実装されます。
要求が応答を必要としない場合、ExchangeHandlerAdapter#received(DubboProtocolの匿名内部クラス)メソッド
が呼び出されます。要求が応答を必要とする場合、ExchangeHandlerAdapter#replyが呼び出されます。
最終的にターミナルExchangeHandlerAdapterに到達しました
// 在DubboProtocol.java中
// ExchangeHandlerAdapter.java
@Override
public CompletableFuture<Object> reply(ExchangeChannel channel, Object message) throws RemotingException {
if (!(message instanceof Invocation)) {
throw new RemotingException(channel, "Unsupported request: "
+ (message == null ? null : (message.getClass().getName() + ": " + message))
+ ", channel: consumer: " + channel.getRemoteAddress() + " --> provider: " + channel.getLocalAddress());
}
Invocation inv = (Invocation) message;
// 获取 Invoker 实例
// 服务导出的时候在 exporterMap 中保存了 serviceKey -> Exporter 的映射关系
// 这里根据inv得到serviceKey得到 Exporter,再得到 Invoker
Invoker<?> invoker = getInvoker(channel, inv);
// need to consider backward-compatibility if it's a callback
// 省略回调相关的代码
RpcContext rpcContext = RpcContext.getContext();
// 用ThreadLocal来保存上下文信息
rpcContext.setRemoteAddress(channel.getRemoteAddress());
// 通过 Invoker 调用具体的服务
// 这里是 AbstractProxyInvoker
Result result = invoker.invoke(inv);
// 异步执行
if (result instanceof AsyncRpcResult) {
// thenApply相当于Stream中的map,对元素进行转换
return ((AsyncRpcResult) result).getResultFuture().thenApply(r -> (Object) r);
} else {
// 同步执行,直接设置结果返回
return CompletableFuture.completedFuture(result);
}
}
これは主に、Invocationオブジェクト(要求されたメソッド名、パラメータータイプ、およびパラメーターをカプセル化)に基づいて、対応するInvokerを検索し、Invoker#invokeメソッドを呼び出します。
サービスがエクスポートされると、このマッピング関係は次のマップに保存されます。
public abstract class AbstractProtocol implements Protocol {
protected final Map<String, Exporter<?>> exporterMap = new ConcurrentHashMap<String, Exporter<?>>();
}
サービスからエクスポートすると、このInvokerが最も独創的なAbstractProxyInvokerであり、典型的なデコレータパターンであるさまざまな装飾で装飾されていることがわかります。
次のメソッドを呼び出す場合(結果を取得するには、ローカルメソッドを呼び出します)
// DubboProtocol
Result result = invoker.invoke(inv);
ProtocolFilterWrapper $ 1は匿名の内部クラスであり、匿名の内部クラスがFilterインターフェイスの実装を呼び出すたびに、最終的にAbstractProxyInvoker#doInvokerメソッドを呼び出します。サービスがエクスポートされると、このInvokerはJavassistProxyFactoryによって作成されると言われています。 。インターセプターについては後で詳しく説明します
public class JavassistProxyFactory extends AbstractProxyFactory {
/**
* 针对provider端,将服务对象包装成一个Invoker对象
*/
@Override
public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
// TODO Wrapper cannot handle this scenario correctly: the classname contains '$'
final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);
// 重写类AbstractProxyInvoker类的doInvoke方法
return new AbstractProxyInvoker<T>(proxy, type, url) {
@Override
protected Object doInvoke(T proxy, String methodName,
Class<?>[] parameterTypes,
Object[] arguments) throws Throwable {
// 这个方法里面调用执行本地方法
return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);
}
};
}
}
ラッパーは、対応するサービス実装メソッドを見つけて、メソッド名、パラメータータイプ、およびパラメーターに従って実行できます。
このラッパーは、実際にはServiceインターフェースの実装クラスのカプセル化であり、リフレクションを通じてServiceインターフェースの実装クラスを呼び出すことを回避し、パフォーマンスを向上させます。
AbstractProxyInvoker#doInvokeメソッドが実行されると、要求されたメソッドが呼び出され、結果が返されます
最後に、要約すると、このリクエストの処理は次のとおりです。
- NettyServerHandler#channelRead
- NettyServer(AbstractPeer#received)
- MultiMessageHandler#received
- HeartbeatHandler#received
- AllChannelHandler#received(ここでは、デフォルトはAllChannelHandlerであり、スレッドモデルとスレッドプール戦略はSPIを介して決定できます)
- DecodeHandler#received
- HeaderExchangeHandler#received
- ExchangeHandlerAdapter#reply
HeaderExchangeHandlerが戻り値を受け取ると、channel.send(res)メソッドを呼び出します。
したがって、結果が返されると、NettyServerHandler#writeメソッドが最初に呼び出されます
応答を返す
通話リンク全体は以下の通りです、私はそれを追いかけません、あなたがそれを追いかけた後にあなたはそれを理解することができます
- NettyServerHandler#write
- NettyServer(AbstractPeer#sent)
- MultiMessageHandler#sent
- HeartbeatHandler#sent
- AllChannelHandler#sent
- DecodeHandler#sent
- HeaderExchangeHandler#sent
- ExchangeHandlerAdapter#sent(DubboProtocolの匿名の内部クラス。これは空の実装です)
フォローへようこそ
リファレンスブログ
クライアントタイムアウトまたはサーバータイムアウト
[1] https://juejin.im/post/6844903857416323079