源码分析Dubbo服务调用-服务提供者如何处理请求命令与再谈Invoker

final NettyHandler nettyHandler = new NettyHandler(getUrl(), this);

channels = nettyHandler.getChannels();

// https://issues.jboss.org/browse/NETTY-365

// https://issues.jboss.org/browse/NETTY-379

// final Timer timer = new HashedWheelTimer(new NamedThreadFactory(“NettyIdleTimer”, true));

bootstrap.setPipelineFactory(new ChannelPipelineFactory() {

@Override

public ChannelPipeline getPipeline() {

NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec(), getUrl(), NettyServer.this);

ChannelPipeline pipeline = Channels.pipeline();

pipeline.addLast(“decoder”, adapter.getDecoder()); // @start

pipeline.addLast(“encoder”, adapter.getEncoder());

pipeline.addLast(“handler”, nettyHandler); // @end

return pipeline;

}

});

可以看出,传入Netty框架的事件处理Handler主要是3个:1、解码器;2、编码器;3、业务类NettyHandler。也就是说当服务端(Server)的读事件就绪后,进行网络读写后,会将二进制流传入解码器(Decoder),解码出一个一个的RPC请求,然后针对每一个RPC请求,交给NettyHandler相关事件处理方法去处理,在这里传入NettyHandler的ChannelHandler为NettyServer,以网络读命令为例,最终将调用NettyServer的父类AbstractPeer的received方法:

@Override

public void received(Channel ch, Object msg) throws RemotingException {

if (closed) {

return;

}

handler.received(ch, msg);

}

那AbstractPeer中的ChannelHandler又是“何许人也”,是通过调用NettyServer(URL url, ChannelHandler handler)中传入的,结合上图中NettyServer的构建流程,可以追溯其流程如下:

  1. DubboProtocol#createServer

server = Exchangers.bind(url, requestHandler); // @1, requestHandler,为最原始的ChannelHandler,接下来整个过程都是对该handler的包装。

  1. HeaderExchanger#bind

return new HeaderExchangeServer(Transporters.bind(url, new

DecodeHandler(new HeaderExchangeHandler(handler))));其包装顺序为 DecodeHandler 》HeaderExchangeHandler 》(DubboProtocol#requestHandler)

  1. NettyTransporter#bind

  2. NettyServer构造函数

super(url, ChannelHandlers.wrap(handler, ExecutorUtil.setThreadName(url, SERVER_THREAD_POOL_NAME)));这里主要包装的是事件的派发Handler,例如AllChannelHandler、ExecutionChannelHandler【Dispatch】业务Handler最终的包装顺序为:事件派发模型handler[AllChannelHandler] 》DecodeHandler 》HeaderExchangeHandler 》 DubboProtocol#requestHandler(最终的业务Handler)。结合网络Netty的处理Handler,服务端事件Handler的处理为:DubboCodec2(解码器) 》 事件派发模型handler[AllChannelHandler] 》DecodeHandler 》 HeaderExchangeHandler》 DubboProtocol#requestHandler(最终的业务Handler)。

上述Handler都在前面的章节中详细介绍了,接下来重点分析服务调用流程,自然需要从DubboProtocol$requestHandler入手。

DubboProtocol$requestHandler#recive

@Override

public void received(Channel channel, Object message) throws RemotingException {

if (message instanceof Invocation) {

reply((ExchangeChannel) channel, message);

} else {

super.received(channel, message);

}

}

如果是服务调用,就进入到reply方法中,否则调用父类进行请求响应。

DubboProtocol$requestHandler#reply

@Override

public Object reply(ExchangeChannel channel, Object message) throws RemotingException {

if (message instanceof Invocation) {

Invocation inv = (Invocation) message; // @1

Invoker<?> invoker = getInvoker(channel, inv); // @2

// need to consider backward-compatibility if it’s a callback

if (Boolean.TRUE.toString().equals(inv.getAttachments().get(IS_CALLBACK_SERVICE_INVOKE))) {

String methodsStr = invoker.getUrl().getParameters().get(“methods”);

boolean hasMethod = false;

if (methodsStr == null || methodsStr.indexOf(“,”) == -1) {

hasMethod = inv.getMethodName().equals(methodsStr);

} else {

String[] methods = methodsStr.split(“,”);

for (String method : methods) {

if (inv.getMethodName().equals(method)) {

hasMethod = true;

break;

}

}

}

if (!hasMethod) {

logger.warn(new IllegalStateException("The methodName " + inv.getMethodName()

  • " not found in callback service interface ,invoke will be ignored."

  • " please update the api interface. url is:"

  • invoker.getUrl()) + " ,invocation is :" + inv);

return null;

}

}

RpcContext.getContext().setRemoteAddress(channel.getRemoteAddress());

return invoker.invoke(inv); // @3

}

throw new RemotingException(channel, "Unsupported request: "

  • (message == null ? null : (message.getClass().getName() + ": " + message))

  • ", channel: consumer: " + channel.getRemoteAddress() + " --> provider: " + channel.getLocalAddress());

}

代码@1:获取服务调用信息,例如调用服务类名(interface)、服务方法名、参数类型,参数值。

代码@2:获取调用者Invoker。

代码@3:调用Invoker,执行具体的方法调用。

上述过程非常简单,但其关键点在于Invoker,那这个Invoker到底是什么呢?

回顾一下我们在讲解:Dubbo服务提供者启动流程中已提到Invoker,在本篇中我们再次对该文进行补充说明。

2、再谈Dubbo服务提供者暴露服务(Invoker)

2.1 服务提供者视角看Invoker

我们应该记得,服务提供者在暴露服务时(export)会创建Invoker,其代码片段如下:

ServiceConfig#doExportUrlsFor1Protocol

if (registryURLs != null && !registryURLs.isEmpty()) {

for (URL registryURL : registryURLs) {

url = url.addParameterIfAbsent(Constants.DYNAMIC_KEY, registryURL.getParameter(Constants.DYNAMIC_KEY));

URL monitorUrl = loadMonitor(registryURL);

if (monitorUrl != null) {

url = url.addParameterAndEncoded(Constants.MONITOR_KEY, monitorUrl.toFullString());

}

if (logger.isInfoEnabled()) {

logger.info("Register dubbo service " + interfaceClass.getName() + " url " + url + " to registry " + registryURL);

}

Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString())); // @1

DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this); // @2

Exporter<?> exporter = protocol.export(wrapperInvoker); // @3

exporters.add(exporter);

}

} else {

Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url);

DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);

Exporter<?> exporter = protocol.export(wrapperInvoker);

exporters.add(exporter);

}

代码@1:根据<dubbo:service ref = “interfaceImpl” interface = “” /> 首先获取ref的代理对象,真正的服务实现类proxy,然后通过proxyFactory【JavassistProxyFactory、JdkProxyFactory】创建最原始的Invoker,即AbstractProxyInvoker,使用的是匿名实现类,即提供反射方式进行方法的调用。

这里写图片描述

从abstract Object doInvoker(T proxy, String methodName, Class< ? >[] parameterTypes, Object[] arguments) 可以最终是通过对象发射方式进行方法调用。

代码@2:首先使用DelegateProviderMetaDataInvoker对AbstractProxyInvoker进行包装,主要是将ServerConfig对象与Invoker一起保存。

代码@3:根据具体协议对服务端Invoker进行导出(继续包装)。

registry://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.0.0&export=dubbo%3A%2F%2F192.168.56.1%3A20880%2Fcom.alibaba.dubbo.demo.DemoService%3Fanyhost%3Dtrue%26application%3Ddemo-provider%26bind.ip%3D192.168.56.1%26bind.port%3D20880%26dubbo%3D2.0.0%26generic%3Dfalse%26interface%3Dcom.alibaba.dubbo.demo.DemoService%26methods%3DsayHello%26pid%3D14360%26qos.port%3D22222%26side%3Dprovider%26stub%3Dcom.alibaba.dubbo.demo.provider.DemoServiceStub%26timestamp%3D1533944510702&pid=14360&qos.port=22222&registry=zookeeper&timestamp=1533944510687

协议前缀:registry,故根据SPI机制,具体的协议为RegistryProtocol。

registry=zookeeper :代表注册中心使用zookeeper,在连接注册中心时根据该值进行策略选择。export= dubbo://… : 根据export,在服务端按照协议启动对应的服务端程序,该协议注意指定请求包的二进制协议,例如协议头和协议体。

按照registry协议,应该会直接调用RegistryProtocol#export,但我们忽略了Dubbo的另一机制,该部分也是在服务提供者启动流程中被遗漏。Dubbo为了对服务调用进行包装,采用了过滤器Filter 链模式,在AbstractProxyInvoker调用之前,先执行一系列的过滤器Filter,Dubbo协议默认的协议层面的过滤器代理实现为:com.alibaba.dubbo.rpc.protocol.ProtocolListenerWrapper,SPI定义文件见:

dubbo-rpc-api/src/main/resources/METAINF/dubbo/internal/com.alibaba.dubbo.rpc.Pr

otocol:

filter=com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper

listener=com.alibaba.dubbo.rpc.protocol.ProtocolListenerWrapper

mock=com.alibaba.dubbo.rpc.support.MockProtocol

ProtocolFilterWrapper#export

public Exporter export(Invoker invoker) throws RpcException {

if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) { // @1

return protocol.export(invoker);

}

return protocol.export(buildInvokerChain(invoker, Constants.SERVICE_FILTER_KEY, Constants.PROVIDER)); // @2

}

代码@1:如果协议为registry,则直接调用RegistryProtocol#expoert完成协议导出,协议为registry其含义是通过注册中心暴露,最终会根据expoert,调用具体的协议进行服务暴露,最终会再次进入该方法。

代码@2:如果为具体协议,例如dubbo等,则通过buildInvokerChain构建Invoker链。

ProtocolFilterWrapper#buildInvokerChain

private static Invoker buildInvokerChain(final Invoker invoker, String key, String group) {

Invoker last = invoker;

List filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group); // @1

if (!filters.isEmpty()) {

for (int i = filters.size() - 1; i >= 0; i–) {

final Filter filter = filters.get(i);

final Invoker next = last;

last = new Invoker() { // @2

@Override

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

更多:Java进阶核心知识集

包含:JVM,JAVA集合,网络,JAVA多线程并发,JAVA基础,Spring原理,微服务,Zookeeper,Kafka,RabbitMQ,Hbase,MongoDB,Cassandra,设计模式,负载均衡,数据库,一致性哈希,JAVA算法,数据结构,加密算法,分布式缓存等等

image

高效学习视频

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
54671a72faed303032d36.jpg" alt=“img” style=“zoom: 33%;” />

更多:Java进阶核心知识集

包含:JVM,JAVA集合,网络,JAVA多线程并发,JAVA基础,Spring原理,微服务,Zookeeper,Kafka,RabbitMQ,Hbase,MongoDB,Cassandra,设计模式,负载均衡,数据库,一致性哈希,JAVA算法,数据结构,加密算法,分布式缓存等等

[外链图片转存中…(img-eAT85cne-1713750805274)]

高效学习视频

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 23
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值