dubbo在项目中怎么用(dubbo怎么部署)

http://www.itjxue.com  2023-01-28 00:03  来源:未知  点击次数: 

dubbo在项目有什么作用

Dubbo是、阿里巴巴公司开源的一个高性能优秀的服务框架,使得应用可通过高性能的 RPC 实现服务的输出和输入功能,可以和Spring框架无缝集成。

(1) 连通性:

注册中心负责服务地址的注册与查找,相当于目录服务,服务提供者和消费者只在启动时与注册中心交互,注册中心不转发请求,压力较小

监控中心负责统计各服务调用次数,调用时间等,统计先在内存汇总后每分钟一次发送到监控中心服务器,并以报表展示

服务提供者向注册中心注册其提供的服务,并汇报调用时间到监控中心,此时间不包含网络开销

服务消费者向注册中心获取服务提供者地址列表,并根据负载算法直接调用提供者,同时汇报调用时间到监控中心,此时间包含网络开销

注册中心,服务提供者,服务消费者三者之间均为长连接,监控中心除外

注册中心通过长连接感知服务提供者的存在,服务提供者宕机,注册中心将立即推送事件通知消费者

注册中心和监控中心全部宕机,不影响已运行的提供者和消费者,消费者在本地缓存了提供者列表

注册中心和监控中心都是可选的,服务消费者可以直连服务提供者

(2) 健壮性:

监控中心宕掉不影响使用,只是丢失部分采样数据

数据库宕掉后,注册中心仍能通过缓存提供服务列表查询,但不能注册新服务

注册中心对等集群,任意一台宕掉后,将自动切换到另一台

注册中心全部宕掉后,服务提供者和服务消费者仍能通过本地缓存通讯

服务提供者无状态,任意一台宕掉后,不影响使用

服务提供者全部宕掉后,服务消费者应用将无法使用,并无限次重连等待服务提供者恢复

(3) 伸缩性:

注册中心为对等集群,可动态增加机器部署实例,所有客户端将自动发现新的注册中心

服务提供者无状态,可动态增加机器部署实例,注册中心将推送新的服务提供者信息给消费者

dubbo泛化调用使用及原理解析

通常我们想调用别人的dubbo服务时,我们需要在项目中引入对应的jar包。而泛化调用的作用是,我们无需依赖相关jar包,也能调用到该服务。

这个特性一般使用在网关类项目中,在业务开发中基本不会使用。

假设我现在要调用下面的接口服务

在xml文件做以下配置

然后注入使用

在两种调用方式中,我们都需要使用被调用接口的字符串参数生成GenericService,通过GenericService的$invoke间接调用目标接口的接口。

$invoke的三个参数分别为,方法名,方法参数类型数组,方法参数数组。

可以看到泛化调用的一个复杂性在于$invoke的第三个参数的组装,下面介绍几种复杂入参的调用方式

首先丰富提供者接口

与入参相似,虽然$invoke的返回定义为Object,实际上针对不同类型有不同的返回。

泛化调用和直接调用在消费者者端,在 使用上的区别 是,我们调用服务时使用的接口为GenericService,方法为$invoker。在 底层的区别 是,消费者端发出的rpc报文发生了变化。

在使用上,不管哪种配置方式,我们都需要配置generic=true

设置generic=true后,RefereceConfig的interfaceClass会被强制设置为GenericService

这也使得我们的RefereanceBean返回的是GenericService类型的代理。

生成的代理是GenericService的代理只是我们使用方式上的变化,更为核心的是,底层发送的rpc报文发生了什么变化。

Dubbo的rpc报文分为header和body两部分。我们这边只需要关注body部分。构造逻辑如下

那么我们通过直接调用与泛化调用ByeService的bye方法在报文上有啥区别呢?

我一开始以为报文中的path是GenericeService,其实并没有,path就是我们调用的目标方法。

path来源???todo

而报文中的方法名,方法参数类型以及具体参数,还是按照GenericeService的$invoke方法入参传递的。

这么个二合一的报文,发送到提供者那边,它估计也会很懵逼,我应该怎么执行?

所以针对泛化调用报文还会把generic=true放在attchment中传递过去

具体逻辑在GenericImplFilter中。

GenericImplFilter中有很多其他逻辑,比如泛化调用使用的序列化协议,正常接口走泛化调用的模式,我们只需要设置attachment的那部分。

知道消费者端报文发生了什么变化,那么接下来就去看提供者端如何处理这个改造后的报文。

总结一下ReferenceConfig中interfaceClass和interfaceName的区别?(这道面试题好像不错)

interfaceClass用于指定生成代理的接口

interfaceName用于指定发送rpc报文中的path(告诉服务端我要调用那个服务)

消费者泛化调用的rpc报文传递到提供者还不能直接使用,虽然path是对的,但是实际的方法名,参数类型,参数要从rpc报文的参数中提取出来。

GenericFilter就是用来做这件事情。

在提供者这边,针对泛化调用的逻辑全部封装到了GenericFilter,解耦的非常好。

注意第4个条件,一开始很疑惑,后来发现rpc报文中的path是目标接口的,这边invoker.getInterface()返回的肯定就是实际接口了

这边有个疑问,为什么这边还要再次反序列化一次,netty不是有decoder么??

嗯,你别忘了,针对一个POJO你传过来是一个Map,从Map转换为POJO需要这边进一步处理。

这边需要注意一下!!针对接口的泛化调用,抛出的异常都会经过GenericException包装一下。

从功能上来看,泛化调用提供了在没有接口依赖情况下进行的解决方案,丰富框架的使用场景。

从设计上来看,泛化调用的功能还是通过扩展的方式实现的,侵入性不强,值得学习借鉴。

dubbo是如何启动的

已知,在项目启动过程中,我们会将dubbo的配置文件写到spring的配置文件里,如下xml文件:

dubbo:application name="anyname_provider" /

!-- 使用zookeeper注册中心暴露服务地址 --

dubbo:registry address="zookeeper://127.0.0.1:2181" /

!-- 用dubbo协议在20880端口暴露服务 --

dubbo:protocol name="dubbo" port="20880" /

!-- 声明需要暴露的服务接口 --

dubbo:service interface="com.shxz130.provider.Provider"

ref="demoService" /

从官方文档中,我们能看到如下:

启动过程.png

也就是说spring启动过程中,随着Spring在初始化过程中,碰到dubbo命名的标签,如(dubbo:service,dubbo:registry)等标签,会由DubboNamespaceHandler类处理,具体原理见链接Spring自定义标签

DubboBeanDefinitionParser代码如下:

public class DubboNamespaceHandler extends NamespaceHandlerSupport { ? ?static {

Version.checkDuplicate(DubboNamespaceHandler.class);

} ? ?public void init() {

registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));

registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true));

registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true));

registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true));

registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true));

registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true));

registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true));

registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));

registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));

registerBeanDefinitionParser("annotation", new AnnotationBeanDefinitionParser());

}

}

遇到不同的标签,会由不同的Parser处理,这里重点看服务发布,这行代码:

registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));

也就是说,当Spring容器处理完dubbo:service标签后,会在Spring容器中生成一个ServiceBean ,服务的发布也会在ServiceBean中完成。不妨看一下ServiceBean的定义:

public class ServiceBeanT extends ServiceConfigT implements InitializingBean, DisposableBean, ApplicationContextAware, ApplicationListenerContextRefreshedEvent, BeanNameAware {

}

该Bean实现了很多接口,关于InitializingBean,DisposableBean,ApplicationContextAware,BeanNameAware,这些接口的使用介绍如下链接:

InitializingBeanDisposableBean

BeanNameAware ApplicationContextAware

而在Spring初始化完成Bean的组装,会调用InitializingBean的afterPropertiesSet方法,在Spring容器加载完成,会接收到事件ContextRefreshedEvent,调用ApplicationListener的onApplicationEvent方法。

在afterPropertiesSet中,和onApplicationEvent中,会调用export(),在export()中,会暴露dubbo服务,具体区别在于是否配置了delay属性,是否延迟暴露,如果delay不为null,或者不为-1时,会在afterPropertiesSet中调用export()暴露dubbo服务,如果为null,或者为-1时,会在Spring容器初始化完成,接收到ContextRefreshedEvent事件,调用onApplicationEvent,暴露dubbo服务。

部分ServiceBean的代码如下:

public class ServiceBeanT extends ServiceConfigT implements InitializingBean, DisposableBean, ApplicationContextAware, ApplicationListenerContextRefreshedEvent, BeanNameAware { //Spring容器初始化完成,调用

public void onApplicationEvent(ContextRefreshedEvent event) { ? ? ? ?if (isDelay() !isExported() !isUnexported()) { ? ? ? ? ? ?if (logger.isInfoEnabled()) {

? ? ? ? ? ? ? ?logger.info("The service ready on spring started. service: " + getInterface());

? ? ? ? ? ?} ? ? ? ? ? ?//暴露服务

? ? ? ? ? ?export();

? ? ? ?}

? ?} ? ?//判断是否延迟发布

?private boolean isDelay() {

? ? ? ?Integer delay = getDelay();

? ? ? ?ProviderConfig provider = getProvider(); ? ? ? ?if (delay == null provider != null) {

? ? ? ? ? ?delay = provider.getDelay();

? ? ? ?} ? ? ? ?return supportedApplicationListener (delay == null || delay == -1);

? ?} //当bean初始化完成调用

public void afterPropertiesSet() throws Exception { ? ? ? ?//......此处省略10000行代码

? ? ? ?if (!isDelay()) { ? ? ? ? ? ?//暴露服务

? ? ? ? ? ?export();

? ? ? ?}

? ?}

}

在export(),暴露服务过程中,如果发现有delay属性,则延迟delay时间,暴露服务,如果没有,则直接暴露服务。

public synchronized void export() { ? ? ? ?//忽略若干行代码

? ? ? ?if (delay != null delay 0) { ? ? ? ? ? ?//当delay不为null,且大于0时,延迟delay时间,暴露服务

? ? ? ? ? ?delayExportExecutor.schedule(new Runnable() { ? ? ? ? ? ? ? ?public void run() { ? ? ? ? ? ? ? ? ? ?//暴露服务

? ? ? ? ? ? ? ? ? ?doExport();

? ? ? ? ? ? ? ?}

? ? ? ? ? ?}, delay, TimeUnit.MILLISECONDS);

? ? ? ?} else { ? ? ? ? ? ?//直接暴露服务

? ? ? ? ? ?doExport();

? ? ? ?}

? ?}

而在doExport()中,验证参数,按照不同的Protocol,比如(dubbo,injvm)暴露服务,在不同的zookeeper集群节点上注册自己的服务。

protected synchronized void doExport() { ? ? ? ? //忽略10000行代码

? ? ? ?doExportUrls(); ? ? ? ?//忽略10000行代码

? ?} private void doExportUrls() {

? ? ? ?ListURL registryURLs = loadRegistries(true); ? ? ? ?for (ProtocolConfig protocolConfig : protocols) { ? ? ? ? ? ?//按照不同的Protocal暴露服务

? ? ? ? ? ?doExportUrlsFor1Protocol(protocolConfig, registryURLs);

? ? ? ?}

? ?}

作者:一滴水的坚持

链接:

来源:简书

简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

(责任编辑:IT教学网)

更多

推荐ASP.NET教程文章