随着公司的规模的扩大,以及业务量的激增,单体应用逐步演化成服务/微服务的架构模式,因此会产生不同服务之间相互调用的情况,而服务之间的调用大多采用RPC的方式调用。
PRC需要达到的要求是:客户端在不知道调用细节的的情况下,调用存在于远程计算机上的某个对象,就像调用本地应用程序中的对象一样。
概述
RPC(Rmote procedure Call) 远程过程调用,有人说RPC是一种协议,需要满足一定的规范,因为不是说一个服务调用了另外一个服务就算是RPC。比如我们可以通过RestTempate 调用另外一个服务的rest接口,这也算是一种服务调用了另外一个服务,但是这不叫RPC;
PRC需要达到的要求是:客户端在不知道调用细节的的情况下,调用存在于远程计算机上的某个对象,就像调用本地应用程序中的对象一样。
满足三个条件:
● 网路协议和网络IO模型透明
● 信息格式对其透明
● 应该有跨语言能力
Dubbo
Apache Dubbo 是一款 RPC 服务开发框架,用于解决微服务架构下的服务治理与通信问题,官方提供了 Java、Golang 等多语言 SDK 实现。使用 Dubbo 开发的微服务原生具备相互之间的远程地址发现与通信能力, 利用 Dubbo 提供的丰富服务治理特性,可以实现诸如服务发现、负载均衡、流量调度等服务治理诉求。Dubbo 被设计为高度可扩展,用户可以方便的实现流量拦截、选址的各种定制逻辑。
Apache Dubbo 最初是为了解决阿里巴巴内部的微服务架构问题而设计并开发的,在十多年的时间里,它在阿里巴巴公司内部的很多业务系统的到了非常广泛的应用。最早在 2008 年,阿里巴巴就将 Dubbo 捐献到开源社区,它很快成为了国内开源服务框架选型的事实标准框架,得到了业界更广泛的应用。在 2017 年,Dubbo 被正式捐献 Apache 软件基金会并成为 Apache 顶级项目,开始了一段新的征程。
为什么需要 Dubbo,它能做什么?
Dubbo 帮助解决微服务组件之间的通信问题,提供了基于 HTTP、HTTP/2、TCP 等的多种高性能通信协议实现,并支持序列化协议扩展,在实现上解决网络连接管理、数据传输等基础问题。
早期的分布式架构,如下图:
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给了我们这种选择的实现方式。
项目搭建
第一步 导入依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-dubbo</artifactId>
</dependency>
第二步 创建公共子工程 xxxx_api ,添加相关接口
public interface IUserDubboService {
String sayHello(Long id);
}
备注:记得把公共工程做成每个微服务的公共依赖,否则无法勾到此接口
<dependency>
<groupId>org.lanqiao</groupId>
<artifactId>lanqiao-api</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
第三步 创建provider子工程(也可以选取某服务为provider)服务供应方
//服务提供者
@Slf4j
@DubboService
public class UserDubboServiceImpl implements IUserDubboService {
@Override
public String sayHello(Long id) {
System.out.println("已经收到远程客户端的信息编号是"+id);
log.debug("已经收到远程客户端的信息编号是"+id);
return String.valueOf(id+1);
}
}
注意:这里可是使用 @DubboService 也可以使用 新版废弃的@Service , 这里的@Service注解并不是来自Spring的org.springframework.stereotype.Service,而是Dubbo的org.apache.dubbo.config.annotation.Service,千万不要引用错误。为了区分spring的@Service,所以改名@DubboService ,这里的 @DubboService 注解仅声明该Java服务(本地)实现为Dubbo服务。
spring:
application:
name: provider-service-xxxxx
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
dubbo:
scan:
base-packages: com.example.providerservice #可@EnableDubbo代替
protocol:
name: dubbo
port: -1 # random port
registry:
address: spring-cloud://localhost #nacos://localhost:8848注册中心的地址
● spring.application.name:Spring 应用名称,用于 Spring Cloud 服务注册和发现。该值在 Dubbo Spring Cloud 加持下被视作dubbo.application.name,因此,无需再显示地配置dubbo.application.name。
● dubbo.scan.base-packages:指定 Dubbo 服务实现类的扫描基准包,可使用@EnableDubbo代替
● dubbo.protocol:Dubbo服务暴露的协议配置,其中子属性name为协议名称,port为协议端口(-1 表示自增端口,从 20880 开始)
● dubbo.registry:Dubbo 服务注册中心配置,其中子属性address 的值 “spring-cloud://127.0.0.1”,说明挂载到 Spring Cloud 注册中心,其实这个可以不用配置,如果指定某一种的注册中心那么就需要 nacos://localhost:8848 或 zookper://localhost:port
@EnableDubbo
@EnableDiscoveryClient
@SpringBootApplication
public class CloudServiceAApplication {
public static void main(String[] args) {
SpringApplication.run(CloudServiceAApplication.class, args);
}
}
注意:@EnableDubbo =@EnableDubboConfig+@DubboComponentScan,会在main类所在的包以及子包下,自动扫描 @DubboService 注解。你也可以通过@DubboComponentScan中的的basePackages属性指定具体的包路,或者在通过属性配置文件显示的指定。
第四步 创建consumer子工程(也可以选取某服务为consumer),服务消费方
/**
* 远程服务
*/
@DubboReference
IUserDubboService userDubboService;
@GetMapping("/test/dubbo")
public Result dubbo(){
String result = userDubboService.sayHello(10L);
return Result.OK("我是远程dubbo服务IUserDubboService",result);
}
spring:
application:
name: consumer-service-xxxx
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
dubbo:
protocol:
name: dubbo
port: -1 # random port
registry:
address: spring-cloud://localhost #nacos://localhost:8848注册中心的地址
@EnableDubbo
@EnableDiscoveryClient
@SpringBootApplication
public class CloudServiceBApplication {
public static void main(String[] args) {
SpringApplication.run(CloudServiceBApplication.class, args);
}
}