
默认情况下,每个dubbo服务都会被以InjvmProtocol注册到本地
可以通过设置scope来调整
根据url的protocol的injvm参数来获取InjvmProtocol协议对象,来暴露服务
在ReferenceConfig的createProxy会调用InjvmProtocolgetInjvmProtocol()的isInjvmRefer方法,是否是jvm引用
直接调用exporter的Invoker来执行
参考:
>
在 Dubbo——路由机制(上) ,介绍了 Router 接口的基本功能以及 RouterChain 加载多个 Router 的实现,之后介绍了 ConditionRouter 这个类对条件路由规则的处理逻辑以及 ScriptRouter 这个类对脚本路由规则的处理逻辑。本文继续介绍剩余的三个 Router 接口实现类。
FileRouterFactory 是 ScriptRouterFactory 的装饰器,其扩展名为 file,FileRouterFactory 在 ScriptRouterFactory 基础上增加了读取文件的能力。可以将 ScriptRouter 使用的路由规则保存到文件中,然后在 URL 中指定文件路径,FileRouterFactory 从中解析到该脚本文件的路径并进行读取,调用 ScriptRouterFactory 去创建相应的 ScriptRouter 对象。
下面来看 FileRouterFactory 对 getRouter() 方法的具体实现,其中完成了 file 协议的 URL 到 script 协议 URL 的转换,如下是一个转换示例,首先会将 file:// 协议转换成 script:// 协议,然后会添加 type 参数和 rule 参数,其中 type 参数值根据文件后缀名确定,该示例为 js,rule 参数值为文件内容。
可以再结合接下来这个示例分析 getRouter() 方法的具体实现:
TagRouterFactory 作为 RouterFactory 接口的扩展实现,其扩展名为 tag。但是需要注意的是,TagRouterFactory 与之前介绍的 ConditionRouterFactory、ScriptRouterFactory 的不同之处在于,它是通过继承 CacheableRouterFactory 这个抽象类,间接实现了 RouterFactory 接口。
CacheableRouterFactory 抽象类中维护了一个 ConcurrentMap 集合(routerMap 字段)用来缓存 Router,其中的 Key 是 ServiceKey。在 CacheableRouterFactory 的 getRouter() 方法中,会优先根据 URL 的 ServiceKey 查询 routerMap 集合,查询失败之后会调用 createRouter() 抽象方法来创建相应的 Router 对象。在 TagRouterFactorycreateRouter() 方法中,创建的自然就是 TagRouter 对象了。
通过 TagRouter,可以将某一个或多个 Provider 划分到同一分组,约束流量只在指定分组中流转,这样就可以轻松达到流量隔离的目的,从而支持灰度发布等场景。
目前,Dubbo 提供了动态和静态两种方式给 Provider 打标签,其中动态方式就是通过服务治理平台动态下发标签,静态方式就是在 XML 等静态配置中打标签。Consumer 端可以在 RpcContext 的 attachment 中添加 requesttag 附加属性,注意保存在 attachment 中的值将会在一次完整的远程调用中持续传递,我们只需要在起始调用时进行设置,就可以达到标签的持续传递。
了解了 Tag 的基本概念和功能之后,再简单介绍一个 Tag 的使用示例。
在实际的开发测试中,一个完整的请求会涉及非常多的 Provider,分属不同团队进行维护,这些团队每天都会处理不同的需求,并在其负责的 Provider 服务中进行修改,如果所有团队都使用一套测试环境,那么测试环境就会变得很不稳定。如下图所示,4 个 Provider 分属不同的团队管理,Provider 2 和 Provider 4 在测试环境测试,部署了有 Bug 的版本,这样就会导致整个测试环境无法正常处理请求,在这样一个不稳定的测试环境中排查 Bug 是非常困难的,因为可能排查到最后,发现是别人的 Bug。
为了解决上述问题,我们可以针对每个需求分别独立出一套测试环境,但是这个方案会占用大量机器,前期的搭建成本以及后续的维护成本也都非常高。
下面是一个通过 Tag 方式实现环境隔离的架构图,其中,需求 1 对 Provider 2 的请求会全部落到有需求 1 标签的 Provider 上,其他 Provider 使用稳定测试环境中的 Provider;需求 2 对 Provider 4 的请求会全部落到有需求 2 标签的 Provider 4 上,其他 Provider 使用稳定测试环境中的 Provider。
在一些特殊场景中,会有 Tag 降级的场景,比如找不到对应 Tag 的 Provider,会按照一定的规则进行降级。如果在 Provider 集群中不存在与请求 Tag 对应的 Provider 节点,则默认将降级请求 Tag 为空的 Provider;如果希望在找不到匹配 Tag 的 Provider 节点时抛出异常的话,我们需设置 requesttagforce = true。
如果请求中的 requesttag 未设置,只会匹配 Tag 为空的 Provider,也就是说即使集群中存在可用的服务,若 Tag 不匹配也就无法调用。一句话总结,携带 Tag 的请求可以降级访问到无 Tag 的 Provider,但不携带 Tag 的请求永远无法访问到带有 Tag 的 Provider。
下面再来看 TagRouter 的具体实现。在 TagRouter 中持有一个 TagRouterRule 对象的引用,在 TagRouterRule 中维护了一个 Tag 集合,而在每个 Tag 对象中又都维护了一个 Tag 的名称,以及 Tag 绑定的网络地址集合,如下图所示:
另外,在 TagRouterRule 中还维护了 addressToTagnames、tagnameToAddresses 两个集合(都是 Map<String, List<String>> 类型),分别记录了 Tag 名称到各个 address 的映射以及 address 到 Tag 名称的映射。在 TagRouterRule 的 init() 方法中,会根据 tags 集合初始化这两个集合。
了解了 TagRouterRule 的基本构造之后,我们继续来看 TagRouter 构造 TagRouterRule 的过程。TagRouter 除了实现了 Router 接口之外,还实现了 ConfigurationListener 接口,如下图所示:
ConfigurationListener 用于监听配置的变化,其中就包括 TagRouterRule 配置的变更。当我们通过动态更新 TagRouterRule 配置的时候,就会触发 ConfigurationListener 接口的 process() 方法,TagRouter 对 process() 方法的实现如下:
我们可以看到,如果是删除配置的 *** 作,则直接将 tagRouterRule 设置为 null,如果是修改或新增配置,则通过 TagRuleParser 解析传入的配置,得到对应的 TagRouterRule 对象。TagRuleParser 可以解析 yaml 格式的 TagRouterRule 配置,下面是一个配置示例:
经过 TagRuleParser 解析得到的 TagRouterRule 结构,如下所示:
除了上图展示的几个集合字段,TagRouterRule 还从 AbstractRouterRule 抽象类继承了一些控制字段,后面介绍的 ConditionRouterRule 也继承了 AbstractRouterRule。
AbstractRouterRule 中核心字段的具体含义大致可总结为如下:
我们可以看到,AbstractRouterRule 中的核心字段与前面的示例配置是一一对应的。
我们知道,Router 最终目的是要过滤符合条件的 Invoker 对象,下面我们一起来看 TagRouter 是如何使用 TagRouterRule 路由逻辑进行 Invoker 过滤的,大致步骤如下:
上述流程的具体实现是在 TagRouterroute() 方法中,如下所示:
除了之前介绍的 TagRouterFactory 继承了 CacheableRouterFactory 之外,ServiceRouterFactory 也继承 CachabelRouterFactory,具有了缓存的能力,具体继承关系如下图所示:
ServiceRouterFactory 创建的 Router 实现是 ServiceRouter,与 ServiceRouter 类似的是 AppRouter,两者都继承了 ListenableRouter 抽象类(虽然 ListenableRouter 是个抽象类,但是没有抽象方法留给子类实现),继承关系如下图所示:
ListenableRouter 在 ConditionRouter 基础上添加了动态配置的能力,ListenableRouter 的 process() 方法与 TagRouter 中的 process() 方法类似,对于 ConfigChangedEventDELETE 事件,直接清空 ListenableRouter 中维护的 ConditionRouterRule 和 ConditionRouter 集合的引用;对于 ADDED、UPDATED 事件,则通过 ConditionRuleParser 解析事件内容,得到相应的 ConditionRouterRule 对象和 ConditionRouter 集合。这里的 ConditionRuleParser 同样是以 yaml 文件的格式解析 ConditionRouterRule 的相关配置。ConditionRouterRule 中维护了一个 conditions 集合(List<String> 类型),记录了多个 Condition 路由规则,对应生成多个 ConditionRouter 对象。
整个解析 ConditionRouterRule 的过程,与前文介绍的解析 TagRouterRule 的流程类似。
在 ListenableRouter 的 route() 方法中,会遍历全部 ConditionRouter 过滤出符合全部路由条件的 Invoker 集合,具体实现如下:
ServiceRouter 和 AppRouter 都是简单地继承了 ListenableRouter 抽象类,且没有覆盖 ListenableRouter 的任何方法,两者只有以下两点区别。
本文我们是紧接 Dubbo——路由机制(上) 的内容,继续介绍了剩余 Router 接口实现的内容。
我们介绍了基于文件的 FileRouter 实现,其底层会依赖之前介绍的 ScriptRouter;接下来又讲解了基于 Tag 的测试环境隔离方案,以及如何基于 TagRouter 实现该方案,同时深入分析了 TagRouter 的核心实现;最后我们还介绍了 ListenableRouter 抽象类以及 ServerRouter 和 AppRouter 两个实现,它们是在条件路由的基础上添加了动态变更路由规则的能力,同时区分了服务级别和服务实例级别的配置。
如果我们手动写一个简单的RPC调用,一般需要把服务调用的信息传递给服务端,包括每次服务调用的一些共用信息包括服务调用接口、方法名、方法参数类型和方法参数值等,在传递方法参数值时需要先序列化对象并经过网络传输到服务端,在服务端接受后再按照客户端序列化的顺序再做一次反序列化,然后拼装成请求对象进行服务反射调用,最终将调用结果传给客户端。Dubbo的实现也基本是相同的原理,下图是Dubbo一次完整RPC调用中经过的步骤:
首先在客户端启动时,会从注册中心拉取和订阅对应的服务列表,Cluster会把拉取的服务列表聚合成一个Invoker,每次RPC调用前会通过Directory#list获取providers地址(已经生成好的Invoker地址),获取这些服务列表给后续路由和负载均衡使用。对应上图①中将多个服务提供者做聚合。在框架内部实现Directory接口的是RegistryDirectory类,它和接口名是一对一的关系(每一个接口都有一个RegistryDirectory实例),主要负责拉取和订阅服务提供者、动态配置和路由项。
在Dubbo发起服务调用时,所有路由和负载均衡都是在客户端实现的。客户端服务调用首先会触发路由 *** 作,然后将路由结果得到的服务列表作为负载均衡参数,经过负载均衡后会选出一台机器进行RPC调用,这3个步骤一次对应图中②③④。客户端经过路由和负载均衡后,会将请求交给底层IO线程池(如Netty)进行处理,IO线程池主要处理读写、序列化和反序列化等逻辑,因此这里一定不能阻塞 *** 作,Dubbo也提供参数控制(decodeinio)参数,在处理反序列化对象时会在业务线程池中处理。在⑤中包含两种类似的线程池,一种是IO线程池(Netty),另一种是Dubbo业务线程池(承载业务方法调用)。
目前Dubbo将服务调用和Telnet调用做了端口复用,子啊编解码层面也做了适配。在Telnet调用时,会新建立一个TCP连接,传递接口、方法和json格式的参数进行服务调用,在编解码层面简单读取流中的字符串(因为不是Dubbo标准头报文),最终交给Telnet对应的Handler去解析方法调用。如果不是Telnet调用,则服务提供方会根据传递过来的接口、分组和版本信息查找Invoker对应的实例进行反射调用。在⑦中进行了端口复用,Telnet和正常RPC调用不一样的地方是序列化和反序列化使用的不是Hessian方式,而是直接使用fastjson进行处理。
讲解完主要调用原理,接下来开始探讨细节,比如Dubbo协议、编解码实现和线程模型等,本篇重点主要放在⑤⑥⑦。
Dubbo协议参考了现有的TCP/IP协议,每一次RPC调用包括协议头和协议体两部分。16字节长的报文头部主要包含魔数(0xdabb),以及当前请求报文是否是Request、Response、心跳和事件的信息,请求时也会携带当前报文体内序列化协议编号。除此之外,报文头还携带了请求状态,以及请求唯一标识和报文体长度。
在消息体中,客户端严格按照序列化顺序写入消息,服务端也会遵循相同的顺序读取消息,客户端发起的请求消息体一次依次保存下列内容:Dubbo版本号、服务接口名、服务接口版本、方法名、参数类型、方法参数值和请求额外参数(attachment)。
服务端返回的响应消息体主要包含回值状态标记和返回值,其中回值状态标记包含6中:
我们知道在网络通信中(TCP)需要解决网络粘包/解包的问题,常用的方法比如用回车、换行、固定长度和特殊分隔符进行处理,而Dubbo是使用特殊符号0xdabb魔法数来分割处理粘包问题。
在实际场景中,客户端会使用多线程并发调用服务,Dubbo如何做到正确响应调用线程呢?关键在于协议头的全局请求id标识,先看原理图:
当客户端多个线程并发请求时,框架内部会调用DefaultFuture对象的get方法进行等待。在请求发起时,框架内部会创建Request对象,这时候会被分配一个唯一id,DefaultFuture可以从Request中获取id,并将关联关系存储到静态HashMap中,就是上图中的Futures集合。当客户端收到响应时,会根据Response对象中的id,从Futures集合中查找对应DefaultFuture对象,最终会唤醒对应的线程并通知结果。客户端也会启动一个定时扫描线程去探测超时没有返回的请求。
先了解一下编解码器的类关系图:
如上,AbstractCodec主要提供基础能力,比如校验报文长度和查找具体编解码器等。TransportCodec主要抽象编解码实现,自动帮我们去调用序列化、反序列化实现和自动cleanup流。我们通过Dubbo编解码继承结构可以清晰看到,DubboCodec继承自ExchageCodec,它又再次继承了TelnetCodec实现。我们前面说过Telnet实现复用了Dubbo协议端口,其实就是在这层编解码做了通用处理。因为流中可能包含多个RPC请求,Dubbo框架尝试一次性读取更多完整报文编解码生成对象,也就是图中的DubboCountCodec,它的实现思想比较简单,依次调用DubboCodec去解码,如果能解码成完整报文,则加入消息列表,然后触发下一个Handler方法调用。
编码器的作用是将Java对象转成字节流,主要分两部分,构造报文头部,和对消息体进行序列化处理。所有编辑码层实现都应该继承自ExchangeCodec,当Dubbo协议编码请求对象时,会调用ExchangeCodec#encode方法。我们来看下这个方法是如何对请求对象进行编码的:
如上,是Dubbo将请求对象转成字节流的过程,其中encodeRequestData方法是对RpcInvocation调用对象的编码,主要是对接口、方法、方法参数类型、方法参数等进行编码,在DubboCodec#encodeRequestData中对此方法进行了重写:
如上,响应编码与请求编码的逻辑基本大同小异,在编码出现异常时,会将异常响应返回给客户端,防止客户端只能一直等到超时。为了防止报错对象无法在客户端反序列化,在服务端会将异常信息转成字符串处理。对于响应体的编码,在DubboCodec#encodeResponseData方法中实现:
注意不管什么样的响应,都会先写入1个字节的标识符,具体的值和含义前面已经讲过。
解码相对更复杂一些,分为2部分,第一部分是解码报文的头部,第二部分是解码报文体内容并将其转换成RpcInvocation对象。我们先看服务端接受到请求后的解码过程,具体解码实现在ExchangeCodec#decode方法:
可以看出,解码过程中需要解决粘包和半包问题。接下来我们看一下DubboCodec对消息题解码的实现:
如上,如果默认配置在IO线程解码,直接调用decode方法;否则不做解码,延迟到业务线程池中解码。这里没有提到的是心跳和事件的解码,其实很简单,心跳报文是没有消息体的,事件又消息体,在使用Hessian2协议的情况下默认会传递字符R,当优雅停机时会通过发送readonly事件来通知客户端当前服务端不可用。
接下来,我们分析一下如何把消息体转换成RpcInvocation对象,具体在DecodeableRpcInvocation#decode方法中:
解码请求时,严格按照客户端写数据的顺序处理。
解码响应和解码请求类似,调用的同样是DubboCodec#decodeBody,就是上面省略的部分,这里就不赘述了,重点看下响应体的解码,即DecodeableRpcResult#decode方法:
如果读者熟悉Netty,就很容易理解Dubbo内部使用的ChannelHandler组件的原理,Dubbo内部使用了大量的Handler组成类似链表,依次处理具体逻辑,包括编解码、心跳时间戳和方法调用Handler等。因为Nettty每次创建Handler都会经过ChannelPipeline,大量的事件经过很多Pipeline会有较多开销,因此Dubbo会将多个Handler聚合成一个Handler。(个人表示,这简直bullshit)
Dubbo的Channelhandler有5中状态:
Dubbo针对每个特性都会实现对应的ChannelHandler,在讲解Handler的指责之前,我们Dubbo有哪些常用的Handler:
Dubbo提供了大量的Handler去承载特性和扩展,这些Handler最终会和底层通信框架做关联,比如Netty等。一次完整的RPC调用贯穿了一系列的Handler,如果直接挂载到底层通信框架(Netty),因为整个链路比较长,则需要大量链式查找和事件,不仅低效,而且浪费资源。
下图展示了同时具有入站和出站ChannelHandler的布局,如果一个入站事件被触发,比如连接或数据读取,那么它会从ChannelPipeline头部一直传播到ChannelPipeline的尾端。出站的IO事件将从ChannelPipeline最右边开始,然后向左传播。当然ChannelPipeline传播时,会检测入站的是否实现了ChannelInboundHandler,出站会检测是否实现了ChannelOutboundHandler,如果没有实现,则自动跳过。Dubbo框架中实现这两个接口类主要是NettyServerHandler和NettyClientHandler。Dubbo通过装饰者模式包装Handler,从而不需要将每个Handler都追加到Pipeline中。因此NettyServer和NettyClient中最多有3个Handler,分别是编码、解码和NettyHandler。
讲完Handler的流转机制后,我们再来探讨RPC调用Provider方处理Handler的逻辑,在DubboProtocol中通过内部类继承自ExchangeHandlerAdapter,完成服务提供方Invoker实例的查找并进行服务的真实调用。
如上是触发业务方法调用的关键,在服务暴露时服务端已经按照特定规则(端口、接口名、接口版本和接口分组)把实例Invoker存储到HashMap中,客户端调用过来时必须携带相同信息构造的key,找到对应Exporter(里面持有Invoker)然后调用。
我们先跟踪getInvoker的实现,会发现服务端唯一标识的服务由4部分组成:端口、接口名、接口版本和接口分组。
如上,Dispatcher是线程池的派发器。这里需要注意的是,Dispatcher真实的职责是创建有线程派发能力的ChannelHandler,比如AllChannelHandler、MessageOnlyChannelHandler和ExecutionChannelHanlder,其本身并不具备线程派发能力。
Dispatcher属于Dubbo中的扩展点,这个扩展点用来动态产生Handler,以满足不同的场景,目前Dubbo支持一下6种策略调用:
具体需要按照使用场景不同启用不同的策略,建议使用默认策略,如果在TCP连接中需要做安全或校验,则可以使用ConnectionOrderedDispatcher策略。如果引入新的线程池,则不可避免的导致额外的线程切换,用户可在Dubbo配置中指定dispatcher属性让具体策略生效。
在Dubbo内部,所有方法调用都被抽象成Request/Response,每次调用都会创建一个Request,如果是方法调用则返回一个Response对象。HeaderExceptionExchangeHandler就是用了处理这种场景,主要负责4中事情:
(1) 更新发送和读取请求时间戳。
(2) 判断请求格式或编解码是否有错,并响应客户端失败的具体原因。
(3) 处理Request请求和Response正常响应。
(4) 支持Telnet调用。
我们先来看一下HeaderExchangeHandler#received实现:
Dubbo是Alibaba开源的分布式服务框架,它按照分层的方式来架构,使用这种方式可以使各层解耦。
Dubbo在调用远程的服务的时候再本地有一个接口,就想调用本地方法一样去调用,底层实现好参数传输和远程服务运行结果传回之后的返回。
Dubbo的特点:
(1)它主要使用高效的网络框架和序列化框架,让分布式服务之间调用效率更高。
(2)采用注册中心管理众多的服务接口地址,当你想调用服务的时候只需要跟注册中心询问即可,不像使用WebService一样每个服务都得记录好接口调用方式。
(3)监控中心时实现服务方和调用方之间运行状态的监控,还能控制服务的优先级、权限、权重、上下线等,让整个庞大的分布式服务系统的维护和治理比较方便。
(4)高可用,如果有服务挂了,注册中心就会从服务列表去掉该节点,客户端会像注册中心请求另一台可用的服务节点重新调用。同时注册中心也能实现高可用(ZooKeeper)。
(5)负载均衡,采用软负载均衡算法实现对多个相同服务的节点的请求负载均衡。
Dubbo需要四大基本组件:Rigistry,Monitor,Provider,Consumer。
1、监控中心的配置文件-dubboproperties文件
(1)容器,监控中心是在jetty和spring环境下运行,依赖于注册中心,日志系统是log4j
dubbocontainer = log4j,spring,registry,jetty
(2)监控服务的名称,监控系统对整个Dubbo服务系统来说也是一个服务
dubboapplicationname = simple-monitor
(3)服务的所有者,这是Dubbbo的服务的功能,可以指定服务的负责人
dubboapplicationowner = coselding
(4)注册中心的地址,配置后监控中心就能通过注册中心获取当前可用的服务列表及其状态,在页面向你汇报Dubbo中的服务运行情况。
dubboregistraddress = multicast://{ip}:{port} //广播
dubboregistraddress = zookeeper://{ip}:{port} //zookeper
dubboregistraddress = redis://{ip}:{port} //redis
dubboregistraddress = dubbo://{ip}:{port} //dubbo
(5)dubbo协议端口号
dubboprotocolport = 7070
(6)jetty工作端口号
dubbojettyport = 8082
(7)工作目录,用于存放监控中心的数据
dubbojettydirectory = ${userhome}/monitor
(8)监控中心报表存放目录
dubbochartsdirectory=${dubbojettydirectory}/charts
(9)监控中心数据资料目录
dubbostatisticsdirectory=${userhome}/monitor/statistics
(10)监控中心日志文件路径
dubbolog4jfile=logs/dubbo-monitor-simplelog
(11)监控中心日志记录级别
dubbolog4jlevel=WARN
2、Dubbo提供负载均衡方式
(1)Random,随机,按权重配置随机概率,调用量越大分布越均匀,默认方式。
(2)RounRobin,轮询,按权重设置轮询比例,如果存在比较慢的机器容易在这台机器上请求阻塞较多。
(3)LeastActive,最少活跃调用数,不支持权重,只能根据自动识别的活跃数分配,不能灵活调配。
(4)ConsistenHash,一致性hash,对相同参数的请求路由到一个服务提供者上,如果有类似灰度发布需求可采用。
3、Dubbo过滤器
Dubbo初始化过程加载ClassPath下的META-INF/dubbo/internal/,META-INF/dubbo/,META-INF/services/三个路径下的comalibabadubborpcFilter文件。文件内容:
Name = FullClassName,这些类必须实现Filter接口。
自定义Filter类:
配置文件在配置过滤器,consumerxml中:
Dubbo对过滤器的加载过程:
先加载三个路径下的comalibabadubborpcFilter文件里面的键值对,key为过滤器名称,value为过滤器的类的全限定名(这个类必须实现Dubbo中的Filter接口)。
自定义的类中@Active注解是过滤器设定的全局基本属性。
Spring在加载consumerxml文件时,通过 <dubbo:consumer filter="xxx" id = "xxx" retrries = "0">这个配置指定消费者端要加载的过滤器,通过filter属性指定过滤器名称。
@Activate注解-自动激活,group属性是表示匹配了对应的角色才被加载,value表示表明过滤条件,不写则表示所有条件都会被加载,写了则只有dubbo URL中包含该参数名且参数值不为空才被加载,这个参数会以dubbo协议的一个参数K-V对传到Provider。
4、Dubbo的Provider配置
5、Dubbo的Consumer配置
1、Dubbo是什么?
Dubbo是阿里巴巴开源的基于Java的高性能RPC分布式框架。
2、为什么使用Dubbo?
很多公司都在使用,经过很多线上的考验,内部使用了Netty,Zookeeper,保证了高性能可用性。
使用Dubbo可以将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,可以提高业务复用灵活性扩展,使前端应用能快速的响应对边的市场需求。分布式架构可以承受更大规模的并发流量。
Dubbo的服务治理图:
3、Dubbo和Spring Cloud的区别
两个没有关联,但是非要说区别,有如下几点:
(1)通信方式不同,Dubbo使用RPC通信,Spring Cloud使用>
1、问题描述
如图,本地在内网的ip为17227438,但是服务注册到了zk上后,ip变成了本地的ip:192168682
2、原因解析
发布服务到远程zk,dubbo是调用ServiceConfigdoExportUrlsFor1Protocol方法,
在doExportUrlsFor1Protocol方法中,通过以下两步获取到host和port两个值。
服务注册到zk上所用的ip,正是这个host,这个findConfigedHosts方法,入参就包括protocolConfig。
因此我们猜想,修改protocolConfig中的host配置,是不是就可以改变服务注册到zk上所显示的ip?
3、解决方法
在dubbo的xml配置文件中,配置protocol属性如下:
<dubbo:protocol name="dubbo" id="dubbo" host="17227438"/>
将host改为本地的ip后,重新尝试将服务注册到zk,发现zk上的ip已经变为我们所配置的host,问题解决~
以上就是关于Dubbo的基本使用全部的内容,包括:Dubbo的基本使用、微服务跨语言调用(摘选)、dubbo-本地服务暴露与调用等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)