SpringCloud Alibaba系列—4Dubbo简介与应用(上)
作者 | 宇木木兮
来源 |今日头条
学习目标
- Dubbo是什么以及它的作用
- Dubbo与springcloud的区别
- Dubbo与Feign的区别
- Dubbo的使用
- Dubbo的高级应用
- Dubbo的集群容错机制有哪几种,默认的是什么
Dubbo的负载均衡算法有哪几种,默认是什么
第1章 Dubbo简介
官网
https://dubbo.apache.org/zh/
1.1 出现的背景
当企业开始大规模的服务化以后,远程通信带来的弊端就越来越明显了。比如说
- 服务链路变长了,如何实现对服务链路的跟踪和监控呢?
- 服务的大规模集群使得服务之间需要依赖第三方注册中心来解决服务的发现和服务的感知问题
- 服务通信之间的异常,需要有一种保护机制防止一个节点故障引发大规模的系统故障,所以要有容错机制
- 服务大规模集群会是的客户端需要引入负载均衡机制实现请求分发
而这些对于服务治理的要求,传统的RPC技术在这样的场景中显得有点力不从心,因此很多企业开始研发自己的RPC框架,比如阿里的HSF、Dubbo;京东的JSF框架、当当的dubbox、新浪的motan、蚂蚁金服的sofa等等
有技术输出能力的公司,都会研发适合自己场景的rpc框架,要么是从0到1开发,要么是基于现有的思想结合公司业务特色进行改造。而没有技术输出能力的公司,遇到服务治理的需求时,会优先选择那些比较成熟的开源框架。而Dubbo就是其中一个
1.2 Dubbo的功能
- 面向接口代理的高性能RPC调用
- 智能容错和负载均衡
- 服务自动注册和发现
- 高度可扩展能力
- 运行期流量调度
- 可视化的服务治理与运维。
1.3 Dubbo3介绍
如开篇所述,Dubbo 提供了构建云原生微服务业务的一站式解决方案,可以使用 Dubbo 快速定义并发布微服务组件,同时基于 Dubbo 开箱即用的丰富特性及超强的扩展能力,构建运维整个微服务体系所需的各项服务治理能力,如 Tracing、Transaction 等,Dubbo 提供的基础能力包括:
- 服务发现
- 流式通信
- 负载均衡
- 流量治理
- …..
Dubbo 计划提供丰富的多语言客户端实现,其中 Java、Golang 版本是当前稳定性、活跃度最好的版本,其他多语言客户端[]正在持续建设中。
自开源以来,Dubbo 就被一众大规模互联网、IT公司选型,经过多年企业实践积累了大量经验。Dubbo3 是站在巨人肩膀上的下一代产品,它汲取了上一代的优点并针对已知问题做了大量优化,因此,Dubbo 在解决业务落地与规模化实践方面有着无可比拟的优势:
1.开箱即用
- 易用性高,如 Java 版本的面向接口代理特性能实现本地透明调用
- 功能丰富,基于原生库或轻量扩展即可实现绝大多数的微服务治理能力
2.超大规模微服务集群实践 - 高性能的跨进程通信协议
- 地址发现、流量治理层面,轻松支持百万规模集群实例
3.企业级微服务治理能力 - 服务测试
- 服务Mock
Dubbo3 是在云原生背景下诞生的,使用 Dubbo 构建的微服务遵循云原生思想,能更好地复用底层云原生基础设施、贴合云原生微服务架构。这体现在: - 服务支持部署在容器、Kubernetes平台,服务生命周期可实现与平台调度周期对齐;
- 支持经典 Service Mesh 微服务架构,引入了 Proxyless Mesh 架构,进一步简化 Mesh 的落地与迁移成本,提供更灵活的选择;
- 作为桥接层,支持与 SpringCloud、gRPC 等异构微服务体系的互调互通
1.4 Dubbo架构
1.4.1 服务发现
服务发现,即消费端自动发现服务地址列表的能力,是微服务框架需要具备的关键能力,借助于自动化的服务发现,微服务之间可以在无需感知对端部署位置与 IP 地址的情况下实现通信。
实现服务发现的方式有很多种,Dubbo 提供的是一种 Client-Based 的服务发现机制,通常还需要部署额外的第三方注册中心组件来协调服务发现过程,如常用的 Nacos、Consul、Zookeeper 等,Dubbo 自身也提供了对多种注册中心组件的对接,用户可以灵活选择。
Dubbo 基于消费端的自动服务发现能力,其基本工作原理如下图:1.4.2 部署架构
了解 Dubbo 的三大中心化组件,它们各自的职责、工作方式。
作为一个微服务框架,Dubbo sdk 跟随着微服务组件被部署在分布式集群各个位置,为了在分布式环境下实现各个微服务组件间的协作, Dubbo 定义了一些中心化组件,这包括:
1.注册中心。协调 Consumer 与 Provider 之间的地址注册与发现
2.配置中心。
- 存储 Dubbo 启动阶段的全局配置,保证配置的跨环境共享与全局一致性
- 负责服务治理规则(路由规则、动态配置等)的存储与推送。
3.元数据中心。 - 接收 Provider 上报的服务接口元数据,为 Admin 等控制台提供运维能力(如服务测试、接口文档等)
- 作为服务发现机制的补充,提供额外的接口/方法级别配置信息的同步能力,相当于注册中心的额外扩展
上图完整地描述了 Dubbo 微服务组件与各个中心的交互过程。
以上三个中心并不是运行 Dubbo 的必要条件,用户完全可以根据自身业务情况决定只启用其中一个或多个,以达到简化部署的目的。通常情况下,所有用户都会以独立的注册中心 开始 Dubbo 服务开发,而配置中心、元数据中心则会在微服务演进的过程中逐步的按需被引入进来。
当然在 Dubbo + Mesh 的场景下,随着 Dubbo 服务注册能力的弱化,注册中心也不再是必选项,其职责开始被控制面取代。 请参见 Dubbo Mesh 方案的描述,ThinSDK 与 Proxyless Mesh。
注册中心配置中心
类比 Spring Cloud Config同元数据中心1.5 Dubbo协议
1.6 对比SpringCloud
Dubbo 的功能只是 Spring Cloud 体系的一部分。Dubbo 是 SOA 时代的产物,它的关注点主要在于服务调用,流量分发、流量监控和熔断。而 Spring Cloud 诞生于微服务架构时代,考虑的是微服务治理的方方面面,另外由于依托Spring、Spring Boot 的优势之上,两个框架在开始目标就不一致,Dubbo 定位服务治理、Spring Cloud 是一个生态。 仅关注于服务治理的这个层面,Dubbo 优于 Spring Cloud:
- Dubbo 支持更多的协议,如:rmi、hessian、http、webservice、thrift、memcached、redis 等。
- Dubbo 使用 RPC 协议效率更高,Dubbo由于是二进制的传输,占用带宽会更少 Spring Cloud 是http协议传输,带宽会比较多,同时使用http协议一般会使用JSON报文,消耗会更大,在极端压力测试下,Dubbo 的效率会高于 Spring Cloud 效率一倍多,但RESTful比RPC更加灵活,不存在代码级别的强依赖
- Dubbo 有更强大的后台管理,Dubbo 提供的后台管理 Dubbo Admin 功能强大,提供路由规则、动态配置、访问控制、权重调节、均衡负载等诸多强大的功能。
可以限制某个 IP 流量的访问权限,设置不同服务器分发不同的流量权重,并且支持多种算法,利用这些功能可以在线上做服务治理、灰度发布、故障转移、流量分发等,Spring Cloud 到现在还不支持灰度发布、流量权重等功能。 - 最大的区别就是通信方式不同,Dubbo底层是使用Netty这样的NIO框架,是基于TCP协议传输,配合hession序列化进行RPC调用。而SpringCloud是基于Http协议+rest接口调用远程过程的通信,相对来说,Http请求会有更大的报文,占的带宽也会更多。但是REST相比RPC更为灵活,服务提供方和调用方的依赖只依靠一纸契约,不存在代码级别的强依赖,这在强调快速演化的微服务环境下,显得更为合适,至于注重通信速度还是方便灵活性,具体情况具体考虑。
1.7 对比Feign
Feign是Spring Cloud提供的一个声明式的伪Http客户端,它使得调用远程服务就像调用本地服务一样简单,只需要创建一个接口并添加一个注解即可。
Nacos注册中心很好的兼容了Feign,Feign默认集成了Ribbon,所以在Nacos下使用Fegin默认就实现了负载均衡的效果。
1.Feign与Dubbo的对比
- Feign与Dubbo功能上有很多类似的地方,因为都是专注于远程调用这个动作。比如注册中心解耦、负载均衡、失败重试熔断、链路监控等。
- Dubbo除了注册中心需要进行整合,其它功能都自己实现了,而Feign大部分功能都是依赖全家桶的组件来实现的。Dubbo小而专一,专注于远程调用。而Spring全家桶而言,远程调用只是一个重要的功能而已。
2.协议支持方面: - Feign更加优雅简单。Feign是通过REST API实现的远程调用,基于Http传输协议,服务提供者需要对外暴露Http接口供消费者调用,服务粒度是http接口级的。通过短连接的方式进行通信,不适合高并发的访问。Feign追求的是简洁,少侵入(因为就服务端而言,在SpringCloud环境下,不需要做任何额外的操作,而Dubbo的服务端需要配置开放的Dubbo接口)。
- Dubbo方式更灵活。Dubbo是通过RPC调用实现的远程调用,支持多传输协议(Dubbo、Rmi、http、redis等等),可以根据业务场景选择最佳的方式,非常灵活。默认的Dubbo协议:利用Netty,TCP传输,单一、异步、长连接,适合数据量小、高并发和服务提供者远远少于消费者的场景。Dubbo通过TCP长连接的方式进行通信,服务粒度是方法级的。
- 从协议层选择看,Dubbo是配置化的,更加灵活。Dubbo协议更适合小数据高并发场景。
3.通信性能方面: - 从通信的性能上来分析,SpringCloud的通信采用Openfeign(feign)组件。Feign基于Http传输协议,底层实现是rest。从OSI 7层模型上来看rest属于应用层。在高并发场景下性能不够理想,成为性能瓶颈(虽然他是基于Ribbon以及带有熔断机制可以防止雪崩),需要改造。具体需要改造的内容需要时再研究。
- Dubbo框架的通信协议采用RPC协议,属于传输层协议,性能上自然比rest高。提升了交互的性能,保持了长连接,高性能。Dubbo性能更好,比如支持异步调用、Netty性能更好。Dubbo主要是配置而无需改造。
使用SpringCloud整合Dubbo,正所谓是强强联合。
4.负载均衡方面:
- Feign默认使用Ribbon作为负载均衡的组件。Dubbo和Ribbon(Feign默认集成Ribbon)都支持负载均衡策略,但是Dubbo支持的更灵活。
Dubbo和Ribbon对比:
Ribbon的负载均衡策略:随机、规则轮询、空闲策略、响应时间策略。
Dubbo的负载均衡策略:Dubbo支持4种算法,随机、权重轮询、最少活跃调用数、一致性Hash策略。而且算法里面引入权重的概念。
Dubbo可以使用路由策略,然后再进行负载均衡。
Dubbo配置的形式不仅支持代码配置,还支持Dubbo控制台灵活动态配置。
Dubbo负载均衡的算法可以精准到某个服务接口的某个方法,而Ribbon的算法是Client级别的。Ribbon需要进行全局配置,个性化配置比较麻烦。
5.容错机制方面:
- Feign默认使用Hystix作为服务熔断的组件。Hystix提供了服务降级,服务熔断,依赖隔离,监控(Hystrix Dashboard)等功能。Feign是利用熔断机制来实现容错的,与Dubbo处理的方式不一样。
- Dubbo支持多种容错策略,FailOver、FailFast、Failsafe、FailBack、Aviailable、Broadcast、Forking策略等,以及Mock。也引入了retry次数,timeout等配置参数。Dubbo自带了失败重试的功能。
6.总结 - Dubbo支持更多功能、更灵活、支持高并发的RPC框架。
- SpringCloud全家桶里面(Feign、Ribbon、Hystrix),特点是非常方便。Ribbon、Hystrix、Feign在服务治理中,配合Spring Cloud做微服务,使用上有很多优势,社区也比较活跃,看将来更新发展。
- 业务发展影响着架构的选型,当服务数量不是很大时,使用普通的分布式RPC架构即可,当服务数量增长到一定数据,需要进行服务治理时,就需要考虑使用流式计算架构。Dubbo可以方便的做更精细化的流量调度,服务结构治理的方案成熟,适合生产上使用,虽然Dubbo是尘封后重新开启,但这并不影响其技术价值。
如果项目对性能要求不是很严格,可以选择使用Feign,它使用起来更方便。 - 如果需要提高性能,避开基于Http方式的性能瓶颈,可以使用Dubbo。
Dubbo Spring Cloud的出现,使得Dubbo既能够完全整合到Spring Cloud的技术栈中,享受Spring Cloud生态中的技术支持和标准化输出,又能够弥补Spring Cloud中服务治理这方面的短板。
第2章 Dubbo使用
官方案例:
https://github.com/apache/dubbo/tree/3.0/dubbo-demo
2.1 Dubbo初使用
在maven中查看匹配版本
https://mvnrepository.com/artifact/org.apache.dubbo/dubbozookeeper集群搭建教程见zookeeper集群部署讲义
- 构建maven-empty项目dubbo-eclipse2019-demo目录
- 构建三个maven-archetype-quickstart子项目
- dubbo-api
- dubbo-providerrovider
- dubbo-consumer
2.1.1 dubbo-api
1.配置pom
<!-- 增加下面的依赖,后面高级使用会用到参数验证 -->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.0.0.GA</version>
</dependency>
2.dubbo-api提供服务的公共契约,里面提供了对外的服务。
public interface FirstService {
//获取返回值
String getFristStr(String str);
}
2.1.2 dubbo-provider
1.配置pom
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<source.level>1.8</source.level>
<target.level>1.8</target.level>
<!-- 3.0.2.1版本跟spring5.2.8.release匹配 -->
<dubbo.version>3.0.2.1</dubbo.version>
<!-- spring用这个版本因为有源码注释 -->
<spring.version>5.2.8.RELEASE</spring.version>
<junit.version>4.13</junit.version>
<maven-compiler-plugin.version>3.7.0</maven-compiler-plugin.version>
<skip_maven_deploy>true</skip_maven_deploy>
<dubbo.compiler.version>0.0.2</dubbo.compiler.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-framework-bom</artifactId>
<version>${spring.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-bom</artifactId>
<version>${dubbo.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-dependencies-zookeeper</artifactId>
<version>${dubbo.version}</version>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- 引入dubbo和zookeeper依赖包 -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-dependencies-zookeeper</artifactId>
<type>pom</type>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-metadata-report-zookeeper</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
</dependency>
<dependency>
<groupId>com.example</groupId>
<artifactId>dubbo-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>8.5.23</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jasper</artifactId>
<version>8.5.16</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.0.0.GA</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>4.2.0.Final</version>
</dependency>
</dependencies>
2.在dubbo-provider服务中提供ContactService的实现
@DubboService //@DubboService注解就是服务暴露的注解,想要暴露什么服务就在类上加上该注解
public class FirstServiceImpl implements FirstService {
@Override
public String getContact(String name) {
System.out.println("===服务端接收到获取联系方式的请求==="+name);
return name+"的联系方式是:111";
}
}
3.增加配置类,且加入@EnableDubbo注解即可完成对@DubboService的扫描,如下:
@Configuration
//作用 扫描 @DubboService注解 @DubboReference
@EnableDubbo(scanBasePackages = "com.example")
@PropertySource("classpath:/dubbo-provider.properties")
public class ProviderConfiguration {
}
4.在resource目录下配置dubbo-provider.properties
dubbo.application.name=dubbo_provider
dubbo.registry.address=zookeeper://${zookeeper.address:127.0.0.1}:2181
dubbo.protocol.name=dubbo
dubbo.protocol.port=20880
dubbo.config-center.address=zookeeper://${zookeeper.address:127.0.0.1}:2181
5.在resource目录下增加日志配置log4j.properties(消费端也要增加)
#优先级从高到低依次为:OFF FATAL ERROR WARN INFO DEBUG TRACE ALL
log4j.rootLogger=info, stdout
###output to the console###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=[%d{dd/MM/yy hh:mm:ss:sss z}] %t %5p %c{2}: %m%n
6.因为注册中心用的是zookeeper,所以还得在本地启动zookeeper服务(如何启动参照zookeeper文章)
7.然后启动服务端服务,将接口注册到zookeeper上去
public class AnnotationProvider {
public static void main(String[] args) throws InterruptedException {
new AnnotationConfigApplicationContext(ProviderConfiguration.class);
System.out.println("---dubbo服务端启动---");
new CountDownLatch(1).await();
}
}
2.1.3 dubbo-consumer
1.配置pom
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<source.level>1.8</source.level>
<target.level>1.8</target.level>
<dubbo.version>3.0.2.1</dubbo.version>
<spring.version>5.2.8.RELEASE</spring.version>
<junit.version>4.12</junit.version>
<maven-compiler-plugin.version>3.7.0</maven-compiler-plugin.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-framework-bom</artifactId>
<version>${spring.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-bom</artifactId>
<version>${dubbo.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-dependencies-zookeeper</artifactId>
<version>${dubbo.version}</version>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-dependencies-zookeeper</artifactId>
<type>pom</type>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
</dependency>
<dependency>
<groupId>com.example</groupId>
<artifactId>dubbo-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>8.5.23</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jasper</artifactId>
<version>8.5.16</version>
</dependency>
<!-- 日志相关依赖 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.10</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.0.0.GA</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>4.2.0.Final</version>
</dependency>
</dependencies>
2.增加配置类
@Configuration
//@EnableDubbo(scanBasePackages = "com.example")就会扫描@DubboReference注解的属性并且进行属性的依赖注入。
@EnableDubbo(scanBasePackages = "com.example")
@PropertySource("classpath:/dubbo-consumer.properties")
public class ConsumerConfiguration {
}
3.在resource目录下配置dubbo-consumer.properties
dubbo.application.name=dubbo_consumer
dubbo.registry.address=zookeeper://${zookeeper.address:127.0.0.1}:2181
dubbo.protocol.port=29015
dubbo.config-center.address=zookeeper://${zookeeper.address:127.0.0.1}:2181
4.启动消费
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = ConsumerConfiguration.class)
public class TestFirstService {
@DubboReference
private ContactService contactService;
@Test
public void test(){
System.out.println(contactService.getContact("eclipse2019"));
}
}
2.1.4 总结
简单总结一下上面的整个过程,其实不难发现,Dubbo这个中间件为我们提供了服务远程通信的解决方案。通过dubbo这个框架,可以开发者快速高效的构建微服务架构下的远程通信实现。
不知道大家是否发现,我们在使用dubbo发布服务,或者消费服务的时候,全程都是采用spring的配置来完成的,这样的好处是我们在学习或者使用dubbo时,如果你用过spring这个框架,那么对于它的学习难度会大大的降低。而且我们也可以看到,dubbo是完全集成Spring 的,因此后续我们去分析dubbo的源码时,还是会有一些和spring有关的内容。
2.2 集成注册中心
Dubbo并不仅仅只是一个RPC框架,他还是一个服务治理框架,它提供了对服务的统一管理、以及服务的路由等功能。
在上面的案例中,我们只是掩饰了Dubbo作为RPC通信的点对点服务,但是就像咱们前面在学习spring cloud的内容一样,服务多了以后,如何管理和维护,以及动态发现呢?
而且,从Dubbo的架构图中可以看到,Dubbo天然就支持服务注册与发现,官方最早推荐的服务注册中心是zookeeper,当然,目前dubbo能够支持的注册中心已经非常多了,比如consul、etcd、nacos、sofa、zookeeper、eureka、redis等等,很显然,Dubbo已经在往一个独立微服务解决方案的生态在发展。
2.2.1 集成zookeeper
集成Zookeeper作为服务注册中心
- 添加zookeeper的jar包依赖
<dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-dependencies-zookeeper</artifactId> <version>${dubbo.version}</version> <type>pom</type> </dependency>
添加注册中心配置,创建dubbo-providerrovider.properties文件用于取代xml中的配置属性,然后用@PropertySource加载该properties配置文件即可 。 dubbo-providerrovider.properties文件内容如下:
dubbo.application.name=dubbo_provider
dubbo.registry.address=zookeeper://${zookeeper.address:127.0.0.1}:2181
dubbo.protocol.name=dubbo
dubbo.protocol.port=20880
#dubbo.provider.token=true
2.2.2 配置管理
1.配置组件 Dubbo框架的配置项比较繁多,为了更好地管理各种配置,将其按照用途划分为不同的组件,最终所有配置项都会汇聚到URL中,传递给后续处理模块。 常用配置组件如下:
- application: Dubbo应用配置
- registry: 注册中心
- protocol: 服务提供者RPC协议
- config-center: 配置中心
- metadata-report: 元数据中心
- service: 服务提供者配置
- reference: 远程服务引用配置
- provider: service的默认配置或分组配置
- consumer: reference的默认配置或分组配置
- module: 模块配置
- monitor: 监控配置
- metrics: 指标配置
- ssl: SSL/TLS配置
2.配置来源 从Dubbo支持的配置来源说起,默认有6种配置来源: - JVM System Properties,JVM -D 参数
- System environment,JVM进程的环境变量
- Externalized Configuration,外部化配置,从配置中心读取
- Application Configuration,应用的属性配置,从Spring应用的Environment中提取"dubbo"打头的属性集
- API / XML /注解等编程接口采集的配置可以被理解成配置来源的一种,是直接面向用户编程的配置采集方式
- 从classpath读取配置文件 dubbo.properties
3.覆盖关系 下图展示了配置覆盖关系的优先级,从上到下优先级依次降低:
2.3 API的使用
https://dubbo.apache.org/zh/docs/references/configuration/api/
以API 配置的方式来配置你的 Dubbo 应用
通过API编码方式组装配置,启动Dubbo,发布及订阅服务。此方式可以支持动态创建
ReferenceConfig/ServiceConfig,结合泛化调用可以满足API Gateway或测试平台的需要。
- api
public interface ApiService {
String getApiServiceStr(String str);
}
- 服务提供者
通过ServiceConfig暴露服务接口,发布服务接口到注册中心。
public class ApiServiceImpl implements ApiService {
@Override
public String getApiServiceStr(String str) {
return "api方式调用返回:"+str;
}
public static void main(String[] args) throws IOException {
ApiServiceImpl apiService = new ApiServiceImpl();
//1、应用信息
ApplicationConfig applicationConfig = new ApplicationConfig();
applicationConfig.setName("dubbo-provider-api");
//2、注册信息
RegistryConfig registryConfig = new RegistryConfig();
registryConfig.setAddress("zookeeper://127.0.0.1:2181");
//3、协议信息
ProtocolConfig protocolConfig = new ProtocolConfig();
protocolConfig.setName("dubbo");
protocolConfig.setPort(20881);
protocolConfig.setThreads(200);
//服务发布
ServiceConfig<ApiService> serviceConfig = new ServiceConfig<>();
serviceConfig.setApplication(applicationConfig);
serviceConfig.setRegistry(registryConfig);// 多个注册中心可以用setRegistries()
serviceConfig.setProtocol(protocolConfig);// 多个协议可以用setProtocols()
serviceConfig.setInterface(ApiService.class);
serviceConfig.setRef(apiService);
// serviceConfig.setVersion("1.0.0");
serviceConfig.setTimeout(1000000);
//暴露及注册服务
serviceConfig.export();
//挂起等待(防止进程退出)
System.in.read();
}
}
- 服务消费者 通过ReferenceConfig引用远程服务,从注册中心订阅服务接口。
public class ApiTest { @Test public void api(){ // 1、应用信息 ApplicationConfig applicationConfig = new ApplicationConfig(); applicationConfig.setName("dubbo_consumer-api"); //2、注册信息 RegistryConfig registry = new RegistryConfig(); registry.setAddress("zookeeper://127.0.0.1:2181"); //引用API ReferenceConfig<ApiService> referenceConfig = new ReferenceConfig<>(); referenceConfig.setApplication(applicationConfig); referenceConfig.setRegistry(registry); referenceConfig.setInterface(ApiService.class); //服务引用 。这个引用过程非常重,如果想用api方式去引用服务,这个对象需要缓存 ApiService apiService = referenceConfig.get(); System.out.println(apiService.getApiServiceStr("eclipse2019")); } }
- bootstrap 服务发布
public class BootstrapApi { public static void main(String[] args) { new EmbeddedZooKeeper(2181, false).start(); ConfigCenterConfig configCenter = new ConfigCenterConfig(); configCenter.setAddress("zookeeper://127.0.0.1:2181"); // 服务提供者协议配置 ProtocolConfig protocol = new ProtocolConfig(); protocol.setName("dubbo"); protocol.setPort(12345); protocol.setThreads(200); // 注意:ServiceConfig为重对象,内部封装了与注册中心的连接,以及开启服务端口 // 服务提供者暴露服务配置 ServiceConfig<UserService> demoServiceConfig = new ServiceConfig<>(); demoServiceConfig.setInterface(UserService.class); demoServiceConfig.setRef(new UserServiceImpl()); demoServiceConfig.setVersion("1.0.0"); // 第二个服务配置 ServiceConfig<MockService> fooServiceConfig = new ServiceConfig<>(); fooServiceConfig.setInterface(MockService.class); fooServiceConfig.setRef(new MockServiceImpl()); fooServiceConfig.setVersion("1.0.0"); // 通过DubboBootstrap简化配置组装,控制启动过程 DubboBootstrap.getInstance() .application("demo-provider") // 应用配置 .registry(new RegistryConfig("zookeeper://127.0.0.1:2181")) // 注册中心配置 .protocol(protocol) // 全局默认协议配置 .service(demoServiceConfig) // 添加ServiceConfig .service(fooServiceConfig) .start() // 启动Dubbo .await(); // 挂起等待(防止进程退出) } }
- bootstrap 服务发现
public class BootstrapApi { public static void main(String[] args) { // 引用远程服务 ReferenceConfig<UserService> demoServiceReference = new ReferenceConfig<UserService>(); demoServiceReference.setInterface(UserService.class); demoServiceReference.setVersion("1.0.0"); ReferenceConfig<MockService> fooServiceReference = new ReferenceConfig<MockService>(); fooServiceReference.setInterface(MockService.class); fooServiceReference.setVersion("1.0.0"); // 通过DubboBootstrap简化配置组装,控制启动过程 DubboBootstrap bootstrap = DubboBootstrap.getInstance(); bootstrap.application("demo-consumer") // 应用配置 .registry(new RegistryConfig("zookeeper://127.0.0.1:2181")) // 注册中心配置 .reference(demoServiceReference) // 添加ReferenceConfig .reference(fooServiceReference) .start(); // 启动Dubbo // 和本地bean一样使用demoService // 通过Interface获取远程服务接口代理,不需要依赖ReferenceConfig对象 UserService demoService = DubboBootstrap.getInstance().getCache().get(UserService.class); System.out.println(demoService.queryUser("jack")); MockService fooService = DubboBootstrap.getInstance().getCache().get(MockService.class); System.out.println(fooService.queryArea("1")); } }