1.什么是rpc
要知道什么是rpc,就要了解rpc产生的背景,这就涉及到进程通信问题。
在单体时代,系统所有功能都集中一起,本地方法直接调用接口,简单方便。而在微服务时代,原本单独系统被分割为多份,一个系统中的方法不可能直接被另外一个系统调用。为解决这个问题,就引入所谓的远程调用,像socket、tcp/udp、http等都属于远程调用。
tcp和http底层也是基于socket实现,再调用时候,需要做很多工作,像tcp的报文设置、黏包处理,http的请求头设置、返回解析等。虽然能实现远程调用,但使用起来肯定没有本地方法调用那么简单。那有没有这种技术,能把远程调用简单到像本地调用一样呢?有,这就是rpc(远程过程调用)。
rpc强调面向过程调用,把远程调用的很多底层细节都给你做好,整个调用过程对你是透明的。你只需像调用本地方法一样简单。
2.rpc调用过程
以server、client举例。server提供接口方法,client负责调用。过程如下:
client:
client-》本地调用-》方法名、参数类型、参数值打包序列化-》网络传输-》server-》
server:
server-》接收数据-》反序列化-》-》拿到方法名,参数等-》本地方法调用-》将结果和类型序列化-》网络传输-》client-》
client:
client-》拿到结果-》反序列化-》得到数据
更详细的流程,可参考:https://blog.csdn.net/w372426096/article/details/88352833
3.什么是dubbo
从rpc调用过程可知,其中涉及到序列化和反序列化、网络传输、调用策略(负载均衡、熔断等)等。此外,一个完善的rpc框架,还会有服务治理等功能。而这些,都有业内大牛已经给你做好了,直接使用即可。(当然,能力强,自己实现也行)。目前,比较知名的rpc框架有,Dubbo、Motan、Tars、Thrift、gRPC。对,没错,dubbo就是一个rpc框架,这就是dubbo。
4.dubbo原理
这是dubbo官网的图片。整个dubbo调用过程,可以概括为:
1.当服务生产者启动后,会将服务信息(ip、port、接口)等信息注册到注册中心上。
2.服务消费者启动后,会监听注册中心,拿到服务生产者的列表,缓存在本地。
3.后续生产者如果下线,会立刻通知消费者。消费者重新拉取生产者列表。
4.消费者轮询或随机从本地缓存列表中拿出一个,进行接口调用。
5.生产者和消费者将调用数据发送监控中心,做监控使用。
5.springboot集成dubbo
5.1项目结构
项目采用maven父子结构,子模块共四个,分别为:api、consumer、provide、provide1。其中,api是公共接口模块,consumer是消费者模块,provide为生成者模块。截图如下:
子模块结构展开如下:
5.2 api模块代码
5.2.1 公共接口类
package com.qxmz.service;
public interface DemoDubboService {
String showMsg(String str);
}
5.2.2 pom文件内容
<?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">
<!-- <parent>-->
<!-- <artifactId>maven</artifactId>-->
<!-- <groupId>com.qxmz.maven</groupId>-->
<!-- <version>1.0-SNAPSHOT</version>-->
<!-- </parent>-->
<modelVersion>4.0.0</modelVersion>
<groupId>com.qxmz.maven</groupId>
<artifactId>api</artifactId>
<version>1.0-SNAPSHOT</version>
</project>
5.2.3 配置文件
无
5.3 provide模块代码
5.3.1 公共接口实现类
package com.qxmz.service;
import org.apache.dubbo.config.annotation.Service;
/**
* @description:
* @author: junzhang
* @time: 2021/7/21 14:55
*/
@Service
public class DemoDubboServiceImpl implements DemoDubboService {
@Override
public String showMsg(String str) {
return "Hello Dubbo--8030 " + str;
}
}
注意:此处的@Service不是springboot的注解,而是dubbo的注解。
5.3.2 pom文件内容
<?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">
<parent>
<artifactId>maven</artifactId>
<groupId>com.qxmz.maven</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>provide</artifactId>
</project>
5.3.2 配置文件:application.yml
dubbo:
application:
name: myProvider
registry:
address: zookeeper://192.168.88.204:2181?backup=192.168.88.205:2182,192.168.88.206:2183
timeout: 15000
protocol:
name: dubbo
port: 20880
scan:
base-packages: com.qxmz.service
server:
port: 8030
provied1和provide的内容一样,不再单独展示。
5.4 consumer模块代码
5.4.1 公共接口调用类
package com.qxmz.service;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.stereotype.Service;
/**
* @description:
* @author: junzhang
* @time: 2021/7/21 16:39
*/
@Service
public class TestService {
@Reference
DemoDubboService demoDubboService;
public String getMsg(String str) {
return demoDubboService.showMsg(str);
}
}
注意:此处的@Service是springboot的注解,不要与provide中的搞混淆。
5.4.2 controller类
package com.qxmz.controller;
import com.qxmz.service.TestService;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
* @description:
* @author: junzhang
* @time: 2021/7/21 16:38
*/
@RestController
public class TestController {
@Resource
TestService testService;
@RequestMapping("/getMsg")
public String getMsg(String str) {
return testService.getMsg(str);
}
}
5.4.3 pom文件内容
<?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">
<parent>
<artifactId>maven</artifactId>
<groupId>com.qxmz.maven</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>consumer</artifactId>
</project>
5.4.4 配置文件:application.yml
dubbo:
application:
name: myConsumer
registry:
address: zookeeper://192.168.88.204:2181?backup=192.168.88.205:2182,192.168.88.206:2183
timeout: 10000
protocol:
name: dubbo
server:
port: 8032
5.5 父模块中的pom文件内容
子模块中所有的依赖,我都放在父pom文件中了,主要的依赖都是consumer和provide需要的。
<?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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.qxmz.maven</groupId>
<artifactId>maven</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>api</module>
<module>provide</module>
<module>consumer</module>
<module>provide1</module>
</modules>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>2.7.6</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-registry-zookeeper</artifactId>
<version>2.7.4</version>
<exclusions>
<exclusion>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>4.2.0</version>
</dependency>
<dependency>
<groupId>com.qxmz.maven</groupId>
<artifactId>api</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
5.6 注册中心说明
项目中dubbo的注册中心,我使用的是zookeeper,且是集群模式。你也可以使用单机模式,或者使用其他注册中心。
5.7 调用演示
依次重启两个provide和一个consumer,然后在浏览器上直接访问http://localhost:8032/getMsg。多次访问后,可以看到返回值是有变化的。在多个provide存在情况下,dubbo默认是随机调用的。其实,这里就体现了负载均衡的功能。
5.8 zookeeper中的注册信息
生产者和消费者启动后,会向注册中心注册自身信息。如下两个节点中对应的就是消费者和生产者的接口信息。
5.9 问题记录
如果是其他项目要实现公共接口,只需要在他的项目中引入公共接口的依赖就行。针对该项目,依赖如下:
<dependency>
<groupId>com.qxmz.maven</groupId>
<artifactId>api</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
不过,我在本地测试时候遇到一个问题。就是添加依赖后,更新maven也没有报错。但在实现类中,就是导入不了包。
后来排查发现,是在创建api子模块时候,使用的模板是springboot模板。打包时候,就变成了可以运行的jar包,这个时候,就引入不了对应的包名。
后面基于maven,重新建了一个空项目作为api模块,重新构建后就没有这个问题。