学习之前, 确保有以下知识基础:
Java 网络编程
Socket 传输数据
IO 流
rpc 简介及实现
rpc 是 remote procedure call 的简写, 意思为远程过程调用.
rpc 应用较多的情景是分布式开发, 那什么是分布式开发呢?
原本我也是想自己解释的, 奈何网上大佬解释得很清楚了, 这里就不献丑了, 建议阅读完下面推荐的几篇再继续往下
[转] 分布式架构的演进 (Javaweb)
如何给老婆解释什么是 RPC https://www.jianshu.com/p/2accc2840a1b
如何实现一个简单的 RPC https://www.jianshu.com/p/5b90a4e70783
刚开始的时候, 服务和调用都是在同一机器, 这叫本地过程调用
之后, 由于客户量增长, 一个服务器并不能满足要求, 之后便是把调用和服务分开, 分别部署在不同的机器, 负责调用服务方法的称之为客户机, 负责提供服务方法的称为服务机
上图的原理可能步骤有点多, 但是只要记住一点, 客户机是把数据通过 Socket 或者是其他协议传递到了服务机, 让服务机进行处理, 从而以相同的协议方式把数据传递回来
如何实现一个简单的 RPC https://www.jianshu.com/p/5b90a4e70783 一文中, 大佬已经实现了一个简洁的 rpc 框架, 然后对于这个 rpc 框架, 并提出来的一些可以优化的点:
1. 缺乏通用性
我通过给 Calculator 接口写了一个 CalculatorRemoteImpl, 来实现计算器的远程调用, 下一次要是有别的接口需要远程调用, 是不是又得再写对应的远程调用实现类? 这肯定是很不方便的.
2, 集成 Spring
在实现了代理对象通用化之后, 下一步就可以考虑集成 Spring 的 IoC 功能了, 通过 Spring 来创建代理对象, 这一点就需要对 Spring 的 bean 初始化有一定掌握了.
3, 长连接 or 短连接
总不能每次要调用 RPC 接口时都去开启一个 Socket 建立连接吧? 是不是可以保持若干个长连接, 然后每次有 rpc 请求时, 把请求放到任务队列中, 然后由线程池去消费执行? 只是一个思路, 后续可以参考一下 Dubbo 是如何实现的.
4, 服务端线程池
我们现在的 Server 端, 是单线程的, 每次都要等一个请求处理完, 才能去 accept 另一个 socket 的连接, 这样性能肯定很差, 是不是可以通过一个线程池, 来实现同时处理多个 RPC 请求? 同样只是一个思路.
5, 服务注册中心
正如之前提到的, 要调用服务, 首先你需要一个服务注册中心, 告诉你对方服务都有哪些实例. Dubbo 的服务注册中心是可以配置的, 官方推荐使用 Zookeeper. 如果使用 Zookeeper 的话, 要怎样往上面注册实例, 又要怎样获取实例, 这些都是要实现的.
6, 负载均衡
如何从多个实例里挑选一个出来, 进行调用, 这就要用到负载均衡了. 负载均衡的策略肯定不只一种, 要怎样把策略做成可配置的? 又要如何实现这些策略? 同样可以参考 Dubbo,Dubbo - 负载均衡
7, 结果缓存
每次调用查询接口时都要真的去 Server 端查询吗? 是不是要考虑一下支持缓存?
8, 多版本控制
服务端接口修改了, 旧的接口怎么办?
9, 异步调用
客户端调用完接口之后, 不想等待服务端返回, 想去干点别的事, 可以支持不?
10, 优雅停机
服务端要停机了, 还没处理完的请求, 怎么办?
PS: 使用 rpc 的时候, 需要考虑到网络问题, 需要采用重试机制
由上述的这些问题, 之后便是出现了一些优秀的 rpc 框架, 如 dubbo,spring cloud 等
dubbo 简介
Dubbo 是阿里巴巴开源的基于 Java 的高性能 RPC(远程过程调用) 分布式服务框架 (SOA), 致力于提供高性能和透明化的 RPC 远程服务调用方案, 以及 SOA 服务治理方案, 其内部使用了 Netty,Zookeeper, 保证了高性能高可用性.
dubbo 结构图:
节点 | 角色说明 |
---|---|
Provider | 暴露服务的服务提供方 |
Consumer | 调用远程服务的服务消费方 |
Registry | 服务注册与发现的注册中心 |
Monitor | 统计服务的调用次数和调用时间的监控中心 |
Container | 服务运行容器 |
其中, 注册中心 Registry 和监控中心 Monitor 都是可选的, 所以, 我们下文先简单实现 dubbo(点对点传输数据)
- <dependency>
- <groupId>com.starsone</groupId>
- <artifactId>dubbo-API</artifactId>
- <version>0.0.1</version>
- </dependency>
dubbo 简单实现
项目说明:
项目基于 spring boot, 分为三个部分, API,consumer 和 provider
API 主要是用来声明一些服务的接口 (maven 项目)
provider 则是对服务接口的具体实现 (spring boot 项目, 通过 maven 依赖 API 项目)
consumer 则是远程调用 provider 提供的服务接口 (spring boot 项目, 通过 maven 依赖 API),
本质上 consumer 相当于客户机, 而 provider 相当于客户机
1. 新建项目
使用 IDEA, 建立一个空白的项目, 之后新建 module
之后 IDEA 会弹出一个新建 module 的窗口
依次新建 API,provider,cousmer 三个 module
API 选择 maven 项目, 之后填写相关的包名信息直接新建即可 (不需要选择具体的 maven 结构), 而另外两个则是 spring boot 项目, 选择 spring initializr 新建即可, 同样, 不要勾选其他的依赖, 填写好包名相关信息新建即可
2.API 项目声明服务接口
在 API 项目中, 我们新建一个 CalculatorService 接口, 里面定义一个 add 的方法
- public interface CalculatorService {
- int add(int a,int b);
- }
3. 配置 API 项目的依赖
原本, 之后的 provider 和 consumer 项目都是需要引用 dubbo-spring-boot-starter 这个依赖, dubbo-spring-boot-starter 依赖中已经包含了 dubbo 依赖, 这样就可以不需要写 dubbo 的依赖了
由于之后我们的 provider 和 consumer 项目都是需要引用 API 这个项目, 所以, 我们可以把 provider 和 consumer 所需要的依赖 dubbo-spring-boot-starter 添加到 API 这个项目中
之后的 provider 和 consumer 项目也就是依赖了 API 项目, 也成功依赖了 dubbo-spring-boot-starter
- <dependency>
- <groupId>org.apache.dubbo</groupId>
- <artifactId>dubbo-spring-boot-starter</artifactId>
- <version>2.7.5</version>
- </dependency>
4.provider 和 consumer 项目引用 API 项目依赖
由于之前我们创建的 API 项目是 maven 项目, 所以添加依赖就很简单, 在 provider 和 consumer 各自的 pom.xml 添加依赖即可
5.provider 实现 API 中的服务接口
我们在 provider 项目中新建一个类 CalculatorServiceImpl, 去实现 CalculatorService 接口
- @Service(interfaceName = "calculatorService")
- class CalculatorServiceImpl:CalculatorService {
- override fun add(a: Int, b: Int): Int {
- val result = a+b
- println("$a+$b=$result")
- return result
- }
- }
注意, 这里的 Service 注解是 dubbo 包里面的注解, 而不是 spring 中的 Service, 定义接口名 interfaceName 为 calculatorService, 方便之后容器进行查找
6. 配置 provider 项目
我们需要修改 spring boot 的配置文件, 这里我使用 YAML 的形式进行配置, 阅读比较舒适
- spring:
- application:
- name: dubbo-provider-application
- dubbo:
- scan:
- #扫描指定包是否包含有 dubbo 中 Service 注解的类
- base-packages: com.starsone.provider.service
- protocol:
- name: dubbo #协议, 默认为 dubbo(其他协议 webserovice,Thrift,Hessain,http)
- port: 12345 #端口, 默认为 20880
- registry:
- address: N/A #不需要注册中心
PS: 如果不想在配置文件制定扫描包含有 Service 注解的类, 可以在 provider 项目中的 application 类中添加开启 dubbo 自动扫描的注解 @EnableDubbo
provider 项目结构图:
7. 运行 provider
由于我们没有引入注册中心, 所以得先运行 provider, 获得 ip 地址
之后 consumer 项目中, 才能让 springr 容器去根据 ip 地址 + 端口号去找到对应的实例并自动装载
由输出日志, 我们可以看到 ip 地址
8.consumer 获得 service 对象
- @Component
- class MyRunner:ApplicationRunner {
- @Reference(url ="dubbo://192.168.52.1:12345",interfaceName = "calculatorService" )
- private lateinit var calculatorService: CalculatorService
- override fun run(args: ApplicationArguments?) {
- println(calculatorService.add(5,14))
- }
- }
这里, 由于是为了简单考虑, 没有使用 Web 依赖, 所以, 使用了 ApplicationRunner 这个接口进行测试, spring 容器在加载完成会自动回调此接口
Reference 注解是 dubbo 中的注解, consumer 项目运行之后, consumer 中的 dubbo 就会根据此 url 和一些其他的信息进行数据的传递, 远程调用 provider 中的服务, 之后, provider 接收数据并进行处理, 返回数据给 consumer, 是不是有了 rpc 的感觉?
9. 配置 consumer 及测试
配置的话, 只配置了应用的名称
之后, 我们运行 consumer 的 application, 可以看到结果
同样, 在 provider 项目, 也是打印出了 consumer 项目传递过来的参数
引入注册中心
前面的实现, 是没有注册中心的, 属于一种直连的方式, 但是, 实际上, 分布式开发, 具有多台服务机
客户机应该是向注册中心请求, 由注册中心查询当前空闲的服务机, 并根据某种策略, 选择其中一台服务机, 将其 ip 地址返回给客户机, 之后客户机通过 ip 地址, 与该服务机进行连接, 进行 rpc 操作
dubbo 框架中, 推荐使用 ZooKeeper 作为注册中心
ZooKeeper 是一个分布式的, 开放源码的分布式应用程序协调服务, 是 Google 的 Chubby 一个开源的实现, 是 Hadoop 和 Hbase 的重要组件. 它是一个为分布式应用提供一致性服务的软件, 提供的功能包括: 配置维护, 域名服务, 分布式同步, 组服务等.
1. 下载 zookeeper
http://mirror.bit.edu.cn/apache/zookeeper/
注意, 这里下载的版本最好与项目中的依赖版本一致
2. 导入 zookeeper 依赖
我们需要修改 API 项目中的依赖, 这样 provider 和 consumer 两个项目的依赖也是得以修改
- <dependency>
- <groupId>org.apache.zookeeper</groupId>
- <artifactId>zookeeper</artifactId>
- <version>3.4.14</version>
- <exclusions>
- <exclusion>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-log4j12</artifactId>
- </exclusion>
- <exclusion>
- <groupId>log4j</groupId>
- <artifactId>log4j</artifactId>
- </exclusion>
- <exclusion>
- <groupId>io.netty</groupId>
- <artifactId>netty</artifactId>
- </exclusion>
- </exclusions>
- </dependency>
- <!-- Zookeeper 客户端 -->
- <dependency>
- <groupId>org.apache.curator</groupId>
- <artifactId>curator-recipes</artifactId>
- <version>4.2.0</version>
- </dependency>
3. 配置 provider 和 consumer 的注册中心
provider:
consumer:
4. 取消 consumer 指定 url
由于我们使用的是注册中心, 所以, 不需要指定 url 了, 把 Reference 注解中的 url 删掉
5. 运行 zookeeper
解压下载的 zookeeper 压缩包, 进入到 conf 目录, 把 zoo_sample.cfg 文件改为 zoo.cfg
进入 bin 目录, 点击 zkserver.cmd 文件, 运行 zookeeper
6. 运行 provider 和 consumer
先运行 provider, 之后运行 consumer, 可以看到结果
本篇文章也是折腾了几天, 参考了十几篇文章, 一步步测试才弄成功, 有些知识点并没有太深入, 像 dubbo 控制台, 监控中心等如何搭建, 后期学习的时候再进行补充说明吧
参考
[转] 分布式架构的演进 (JavaWeb)
如何给老婆解释什么是 RPC https://www.jianshu.com/p/2accc2840a1b
如何实现一个简单的 RPC https://www.jianshu.com/p/5b90a4e70783
Dubbo 一篇文章就够了: 从入门到实战 https://segmentfault.com/a/1190000019896723
SpringBoot 整合 Dubbo https://www.jianshu.com/p/841324b3b128
基于 springboot 的 dubbo 简单实现
Dubbo 各知识点介绍 - 简书 https://www.jianshu.com/p/3090d63e9cb3
来源: https://www.cnblogs.com/stars-one/p/12534295.html