SpringCloud+Dubbo+Nacos整合实现RPC调用
Dubbo很多人都不陌生,这毕竟是一款从2012年就开始开源的Java RPC框架,中间由于各种各样的原因停止更新4年半的时间,中间只发过一个小版本修了一个小bug,甚至大家都以为这个项目已经死掉了,竟然又在2017年9月份恢复了更新。
网络上很多人都拿Dubbo和Spring Cloud做对比,可能在大家的心目中,这两个框架是可以画上等号的吧。后来在网络上有一个非常流行的表格,比较详细的对比了 Spring Cloud 和 Dubbo,表格如下:
Dubbo | SpringCloud | |
服务注册中心 | Zookeeper | Spring Cloud Netfix Eureka |
服务调用方式 | RPC | REST API |
服务监控 | Dubbo-monitor | Spring Boot Admin |
熔断器 | 不完善 | Spring Cloud Netflix Hystrix |
服务网关 | 无 | Spring Cloud Netflix Zuul |
分布式配置 | 无 | Spring Cloud Config |
服务跟踪 | 无 | Spring Cloud Sleuth |
数据流 | 无 | Spring Cloud Stream |
批量任务 | 无 | Spring Cloud Task |
信息总线 | 无 | Spring Cloud Bus |
以上列举了一些核心部件,当然这里需要申明一点,Dubbo对于上表中总结为“无”的组件不代表不能实现,而只是Dubbo框架自身不提供,需要另外整合以实现对应的功能,这样看起来确实Dubbo更像是Spring Cloud的一个子集。
Dubbo在国内拥有着巨大的用户群,大家希望在使用Dubbo的同时享受Spring Cloud的生态,出现各种各样的整合方案,但是因为服务中心的不同,各种整合方案并不是那么自然,直到Spring Cloud Alibaba这个项目出现,由官方提供了Nacos服务注册中心后,才将这个问题完美的解决。并且提供了Dubbo和Spring Cloud整合的方案,命名为:Dubbo Spring Cloud。
Dubbo Spring Cloud概述
Dubbo Spring Cloud构建在原生的Spring Cloud之上,其服务治理方面的能力可认为是Spring Cloud Plus,不仅完全覆盖Spring Cloud 原生特性,而且提供更为稳定和成熟的实现,特性比对如下表所示:
功能组件 | Spring Cloud | Dubbo Spring Cloud |
---|---|---|
分布式配置(Distributed configuration) | Git、Zookeeper、Consul、JDBC | Spring Cloud 分布式配置 + Dubbo 配置中心 |
服务注册与发现(Service registration and discovery) | Eureka、Zookeeper、Consul | Spring Cloud 原生注册中心 + Dubbo 原生注册中心 |
负载均衡(Load balancing) | Ribbon(随机、轮询等算法) | Dubbo 内建实现(随机、轮询等算法 + 权重等特性) |
服务熔断(Circuit Breakers) | Spring Cloud Hystrix | Spring Cloud Hystrix + Alibaba Sentinel 等 |
服务调用(Service-to-service calls) | Open Feign、RestTemplate | Spring Cloud 服务调用 + Dubbo @Reference |
链路跟踪(Tracing) | Spring Cloud Sleuth + Zipkin | Zipkin、opentracing 等 |
以上对比表格摘自Dubbo Spring Cloud官方文档。
而且Dubbo Spring Cloud基于Dubbo Spring Boot 2.7.1 和 Spring Cloud 2.x开发,无论开发人员是Dubbo用户还是Spring Cloud用户, 都能轻松地驾驭,并以接近“零”成本的代价使应用向上迁移。Dubbo Spring Cloud致力于简化云原生开发成本,以达成提高研发效能以及提升应用性能等目的。
Dubbo Spring Cloud 主要特性
(1)面向接口代理的高性能RPC调用:提供高性能的基于代理的远程调用能力,服务以接口为粒度,屏蔽了远程调用底层细节。
(2)智能负载均衡:内置多种负载均衡策略,智能感知下游节点健康状况,显著减少调用延迟,提高系统吞吐量。
(3)服务自动注册与发现:支持多种注册中心服务,服务实例上下线实时感知。
(4)高度可扩展能力:遵循微内核+插件的设计原则,所有核心能力如Protocol、Transport、Serialization被设计为扩展点,平等对待内置实现和第三方实现。
(5)运行期流量调度:内置条件、脚本等路由策略,通过配置不同的路由规则,轻松实现灰度发布,同机房优先等功能。
(6)可视化的服务治理与运维:提供丰富服务治理、运维工具:随时查询服务元数据、服务健康状态及调用统计,实时下发路由策略、调整配置参数。
Spring Cloud为什么需要RPC?
在Spring Cloud构建的微服务系统中,大多数的开发者使用都是官方提供的Feign组件来进行内部服务通信,这种声明式的HTTP客户端使用起来非常的简洁、方便、优雅,但是有一点,在使用Feign消费服务的时候,相比较Dubbo这种RPC框架而言,性能堪忧。
虽说在微服务架构中,会将按照业务划分的微服务独立部署,并且运行在各自的进程中。微服务之间的通信更加倾向于使用HTTP这种简单的通信机制,大多数情况都会使用REST API。这种通信方式非常的简洁高效,并且和开发平台、语言无关,但是通常情况下,HTTP并不会开启KeepAlive功能,即当前连接为短连接,短连接的缺点是每次请求都需要建立TCP连接,这使得其效率变的相当低下。
对外部提供REST API服务是一件非常好的事情,但是如果内部调用也是使用HTTP调用方式,就会显得显得性能低下,Spring Cloud默认使用的Feign组件进行内部服务调用就是使用的HTTP协议进行调用。这时,我们如果内部服务使用RPC调用,对外使用REST API,将会是一个非常不错的选择。恰巧,Dubbo Spring Cloud给了我们这种选择的实现方式。
SpringCloud+Nacos+Dubbo整合实战
下面将会以一个简单的入门案例,介绍一下在使用Nacos作为服务中心,使用Dubbo来实现服务提供方和服务消费方的案例。
(1)服务端接口契约定义
创建普通Maven项目dubbo-sample-api,定义服务端与消费端的接口契约。该项目仅是用于定义Dubbo服务端与消费端的接口契约。这里创建仅为更好的代码重用以及接口、模型规格控制管理。然后打包成jar包,安装到私有仓库,供客户端与Dubbo服务端接口实现的项目来依赖使用。
pom.xml不用引用任何包。
定义服务端接口类IHelloService:
package com.zxy.dubbo_sample_api; public interface IHelloService { String sayHello(String name); }
然后执行mvn install,将dubbo-sample-api添加到本地仓库,供其他项目依赖。
(2)服务端服务实现
创建一个SpringBoot项目dubbo-sample-provider。该项目实现上面定义好的接口,并该项目作为服务注册到服务注册中心,这里采用nacos,也可以采用ZooKeeper,而且dubbo还可以支持多注册中心。
工程依赖pom.xml如下:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.zxy</groupId> <artifactId>dubbo-sample-provider</artifactId> <version>1.0</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.7.RELEASE</version> <relativePath/> </parent> <properties> <java.version>1.8</java.version> <spring-cloud.version>Hoxton.SR10</spring-cloud.version> <spring-cloud-alibaba.version>2.2.5.RELEASE</spring-cloud-alibaba.version> </properties> <dependencyManagement> <dependencies> <!-- spring-cloud --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> <!-- spring-cloud-alibaba --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>${spring-cloud-alibaba.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <!-- spring boot --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!-- 热部署 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> </dependency> <!-- Nacos Discovery --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <!-- Dubbo --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-dubbo</artifactId> </dependency> <!-- 健康监控,必须包含spring-boot-starter-actuator包,不然启动会报错 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <!-- 需要实现的服务接口包 --> <dependency> <groupId>com.zxy</groupId> <artifactId>dubbo-sample-api</artifactId> <version>1.0</version> </dependency> </dependencies> </project>
注意:必须包含spring-boot-starter-actuator包,不然启动会报错。pom中引入定义服务契约接口的API模块的包。
实现Dubbo接口IHelloService,新建HelloServiceImpl类,代码如下:
package com.zxy.dubbo_sample_provider.service; import com.zxy.dubbo_sample_api.IHelloService; import org.apache.dubbo.config.annotation.DubboService; @DubboService public class HelloServiceImpl implements IHelloService { public String sayHello(String name){ return "Hello World:"+ name; } }
注意:这里的@DubboService注解是Dubbo的org.apache.dubbo.config.annotation.DubboService,千万不要引用错误。@DubboService注解仅声明该Java服务(本地)实现为Dubbo服务。旧版本的dubbo使用该包下的@Service直接,后来为了防止与spring混淆改为了@DubboService。
配置文件application.properties,将Java服务(本地)配置为Dubbo服务(远程)如下:
#Spring应用名称,用于SpringCloud服务注册和发现。该值在Dubbo Spring Cloud加持下被视作dubbo.application.name,因此,无需再显示地配置dubbo.application.name。 spring.application.name=dubbo-sample-provider server.port=3001 #Dubbo服务实现类的扫描基准包路径 dubbo.scan.base-packages=com.zxy.dubbo_sample_provider.service #Dubbo服务暴露的协议配置,其中子属性name为协议名称,port为协议端口(-1 表示自增端口,从 20880 开始) #因为项目中存在多个服务提供端比如商品服务、会员服务等,这里为了方便设置为了-1 dubbo.protocol.name=dubbo dubbo.protocol.port=-1 #Dubbo服务注册中心的配置地址,它的值spring-cloud://localhost表示挂载到Spring Cloud注册中心,不配置的话会提示没有配置注册中心的错误。 dubbo.registry.address=spring-cloud://localhost #nacos #spring.cloud.nacos.discovery定义Nacos服务发现与注册配置,其中子属性server-addr指定Nacos服务器主机和端口。 spring.cloud.nacos.discovery.server-addr=localhost:8848
注意:在暴露Dubbo服务方面,推荐使用外部化配置的方式,即指定Java服务实现类的扫描基准包。
Dubbo Spring Cloud 继承了 Dubbo Spring Boot 的外部化配置特性,也可以通过标注@DubboComponentScan来实现基准包扫描。
创建应用主类,即项目入口DubboSampleProviderApplication.java文件。如果使用SpringBoot自动创建项目该文件是自动创建的不用做任何修改。
package com.zxy.dubbo_sample_provider; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DubboSampleProviderApplication { public static void main(String[] args) { SpringApplication.run(DubboSampleProviderApplication.class, args); } }
运行dubbo-sample-provider项目,将会在Nacos中注册服务。(注意:在运行项目前,先安装并启动Nacos服务端)
(3)消费端(客户端)实现
创建一个SpringBoot项目dubbo-consumer,实现调用以上的服务。
工程依赖pom.xml:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.zxy</groupId> <artifactId>dubbo-consumer</artifactId> <version>1.0</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.7.RELEASE</version> <relativePath/> </parent> <properties> <java.version>1.8</java.version> <spring-cloud.version>Hoxton.SR10</spring-cloud.version> <spring-cloud-alibaba.version>2.2.5.RELEASE</spring-cloud-alibaba.version> </properties> <dependencyManagement> <dependencies> <!-- spring-cloud --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> <!-- spring-cloud-alibaba --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>${spring-cloud-alibaba.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <!-- spring boot --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!-- 热部署 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> </dependency> <!-- web项目 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- Nacos Discovery --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <!-- Dubbo --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-dubbo</artifactId> </dependency> <!-- 健康监控,必须包含spring-boot-starter-actuator包,不然启动会报错 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <!-- dubbo服务接口契约包 --> <dependency> <groupId>com.zxy</groupId> <artifactId>dubbo-sample-api</artifactId> <version>1.0</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
注意:pom中一定要引入定义服务契约接口的API模块的包dubbo-sample-api。
工程配置application.properties:
#服务名称 spring.application.name=dubbo-consumer server.port=4001 #表示要订阅服务的服务名,可以配置'*'代表订阅所有服务(不推荐使用)。若需订阅多应用,使用","分割。 dubbo.cloud.subscribed-services=dubbo-sample-provider #nacos spring.cloud.nacos.discovery.server-addr=localhost:8848 #禁止该服务注册到Nacos服务列表中 spring.cloud.nacos.discovery.register-enabled=false
实现服务调用,新建HelloController.java类:
package com.zxy.dubbo_consumer.control; import com.zxy.dubbo_sample_api.IHelloService; import org.apache.dubbo.config.annotation.DubboReference; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class HelloController { @DubboReference private IHelloService helloService; @GetMapping("/say") public String sayHello(){ return helloService.sayHello("Lily"); } }
注意:这里的@DubboReference注解是Dubbo的org.apache.dubbo.config.annotation.DubboReference,千万不要引用错误。
手写过RPC框架的同学,应该都知道,客户端通过服务端的接口契约可以调用服务端的接口,其实是采用动态代理的方式,dubbo中默认采用javasist,大家不妨debug一下。@DubboReference作用就是将动态代理注入进来。
创建应用主类,即项目入口DubboConsumerApplication.java文件。如果使用SpringBoot自动创建项目该文件是自动创建的不用做任何修改。
package com.zxy.dubbo_consumer; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DubboConsumerApplication { public static void main(String[] args) { SpringApplication.run(DubboConsumerApplication.class, args); } }
(4)测试:
启动Nacoe、启动服务提供项目dubbo-sample-provider、服务消费项目dubbo-consumer、确保接口契约项目已经添加到maven本地仓库中。启动完成后,我们可以访问Nacos控制台的服务列表上已经注册的以上服务。具体参照之前的步骤。
我们打开浏览器访问:http://localhost:4001/say,可以看到页面正常显示Hello World:Lily!表示测试成功。