大家好,我是冰河~~
2023年我带着大家从零开始架构设计并编码实现了一款高性能RPC框架,如今,这款高性能RPC框架已被投产使用到生产环境,经过调优,性能那是杠杠的,承受住了高并发、大流量的生产环境考验。
也就是说,又一个自己手写轮子被投产了,而且这次的轮子就是在星球带着大家一起从零开始手写的。
打开 https://t.zsxq.com/nEOhO 直接自己手写互联网大厂都在自研的RPC轮子。
今天,给大家简单介绍下如何造这个轮子,以及在实际项目中,又该如何使用这个轮子。
一、造轮子
其实,这个轮子就是我在星球带着大家一起手写的高性能RPC框架,并且实现了很多成熟RPC框架应有的功能,例如,对标Dubbo实现的SPI扩展,注册中心、序列化与反序列化、动态代理、服务注册与发现等等,为了更好的说明我们实现的技术,这里直接来一张图。
可以看到,这款RPC框架实现的功能还是比较完善的。为了实现这些功能,我在星球安排的《RPC手撸专栏》,整整涵盖三十四个大的篇章,120 篇文章。
看这内容,应该是最硬核,内容最全的手写RPC框架专栏了吧?目前,已配套的源码包含:120 源码工程和130个源码Tag分支。
支持同步调用、异步调用、回调、单向调用和泛化调用。
- 同步调用
- 异步调用
- 回调
- 单向调用
另外,值得一提的是:这款RPC框架采用微内核、插件化的架构模式,大量使用了对标Dubbo的自定义SPI技术实现高度可扩展性,各位小伙伴可以根据自己的需要,按照SPI的设计要求添加自己实现的自定义插件。
二、用轮子
说了这么多,如何在自己开发的项目中,使用这款RPC轮子呢?
这里,我以《简易商城脚手架项目》,也就是《SpringCloud Alibaba实战项目》为例,给大家介绍下如何使用我们自己手写的RPC框架来开发微服务项目。
2.1 项目背景
在《SpringCloud Alibaba实战项目》中,选择了大家都比较熟悉的电商项目中的用户、商品和订单模块为例。
一方面是这些模块的业务逻辑比较简单,另一方面,案例最终会以微服务的形式呈现给大家,项目原有的代码是使用Fegin作为远程调用的框架,我们要做的就是将Fegin替换成我们自己手写的bhrpc框架,替换后的项目交互流程如下图所示。
可以看出,用户微服务、商品微服务和订单微服务的交互流程比较简单,服务与服务之间的交互都会采用我们自己手写的bhrpc框架进行实现。
另外,从服务与服务之间的交互流程也可以看出,用户微服务和商品微服务作为服务提供者对外提供服务,订单微服务作为服务消费者来消费用户微服务和商品微服务对外提供的服务。
2.2 整合轮子
接下来,我们就将自己手写的RPC轮子整合到《SpringCloud Alibaba实战项目》中,替换掉项目中原本使用的Fegin框架。
1.新增shop-service-api工程
(1)新增shop-service-api工程
在父工程shop-springcloud-alibaba下新建shop-service-api子工程,并在shop-service-api子工程的pom.xml文件中添加如下配置。
代码语言:javascript复制<dependencies>
<dependency>
<groupId>io.binghe.shop</groupId>
<artifactId>shop-bean</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
shop-service-api子工程的作用就是将shop-user工程中的UserService接口和shop-product工程中的ProductService接口单独分离出来,便于后续整合bhrpc框架。
(2)新增UserService接口
UserService接口的源码详见:shop-service-api工程下的io.binghe.shop.service.UserService,如下所示。
代码语言:javascript复制public interface UserService {
/**
* 根据id获取用户信息
*/
User getUserById(Long userId);
}
删除shop-user工程下的io.binghe.shop.user.service.UserService接口,并修改shop-user工程中的报错信息,将报错类中原本依赖io.binghe.shop.user.service.UserService接口修改成依赖io.binghe.shop.service.UserService接口。
(3)新增ProductService接口
ProductService接口的源码详见:shop-service-api工程下的io.binghe.shop.service.ProductService,如下所示。
代码语言:javascript复制public interface ProductService {
/**
* 根据商品id获取商品信息
*/
Product getProductById(Long pid);
/**
* 扣减商品库存
*/
int updateProductStockById(Integer count, Long id);
}
删除shop-product工程下的io.binghe.shop.product.service.ProductService接口,并修改shop-product工程中的报错信息,将报错类中原本依赖io.binghe.shop.product.service.ProductService接口修改成依赖io.binghe.shop.service.ProductService接口。
2.改造shop-user工程
shop-user工程对应bhrpc框架的服务提供者角色。
(1)添加pom.xml依赖
shop-user工程作为bhrpc框架的服务提供者,在pom.xml需要添加如下依赖。
代码语言:javascript复制<dependency>
<groupId>io.binghe.rpc</groupId>
<artifactId>bhrpc-spring-boot-starter-provider</artifactId>
<version>${bhrpc.version}</version>
</dependency>
<dependency>
<groupId>io.binghe.shop</groupId>
<artifactId>shop-service-api</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
(2)修改UserServiceImpl类
UserServiceImpl类的源码详见:shop-user工程下的io.binghe.shop.user.service.impl.UserServiceImpl,需要将UserServiceImpl类上标注的Spring中的@Service注解,替换成bhrpc框架中的@RpcService注解,修改后的源码如下所示。
代码语言:javascript复制@RpcService(interfaceClass = UserService.class, version = "1.0.0", group = "binghe")
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public User getUserById(Long userId) {
return userMapper.selectById(userId);
}
}
可以看到,在UserServiceImpl类上标注了bhrpc框架中的@RpcService注解,并且指定了interfaceClass、version和group属性。
(3)修改UserStarter类
UserStarter类的源码详见:shop-user工程下的io.binghe.shop.UserStarter,主要是在UserStarter类上添加@ComponentScan注解,修改后的源码如下所示。
代码语言:javascript复制@SpringBootApplication
@ComponentScan(basePackages = {"io.binghe.shop", "io.binghe.rpc"})
@EnableTransactionManagement(proxyTargetClass = true)
@MapperScan(value = { "io.binghe.shop.user.mapper" })
@EnableDiscoveryClient
@EnableAsync
public class UserStarter {
public static void main(String[] args){
SpringApplication.run(UserStarter.class, args);
}
}
可以看到,在UserStarter类上标注了@ComponentScan注解,并指定了扫描的包路径为io.binghe.shop和io.binghe.rpc,使其既能够扫描到微服务项目中包下的类,也能够扫描到bhrpc框架包下的类。
(4)添加配置
由于项目使用了Nacos作为配置中心,所以,需要在Nacos添加shop-user工程作为服务提供者的配置,登录Nacos管理端,找到shop-user工程的配置,如下所示。
点击编辑按钮,在原有配置的基础上,添加如下配置信息。
代码语言:javascript复制bhrpc:
binghe:
provider:
# rpc server
serverAddress: 127.0.0.1:20880
# serverRegistryAddress
serverRegistryAddress: 127.0.0.1:20880
# zookeeper server
registryAddress: 127.0.0.1:2181
# registry center type
registryType: zookeeper
#registry loadbalance type
registryLoadBalanceType: zkconsistenthash
# reflect type
reflectType: cglib
# heartbeatInterval
heartbeatInterval: 30000
可以看到,配置的内容都是bhrpc框架的服务提供者启动时,需要读取的一些参数信息。配置完成后,点击发布按钮进行发布。
至此,shop-user工程改造完成,是不是非常简单呢?我们自己手写的bhrpc框架整合SpringCloud Alibaba项目就是这么简单。
3.改造shop-product工程
shop-product工程对应bhrpc框架的服务提供者角色。改造shop-product工程的步骤与改造shop-user工程的步骤基本相同。
(1)添加pom.xml依赖
shop-product工程同样作为bhrpc框架的服务提供者,在pom.xml需要添加如下依赖。
代码语言:javascript复制<dependency>
<groupId>io.binghe.rpc</groupId>
<artifactId>bhrpc-spring-boot-starter-provider</artifactId>
<version>${bhrpc.version}</version>
</dependency>
<dependency>
<groupId>io.binghe.shop</groupId>
<artifactId>shop-service-api</artifactId>
<version>${project.version}</version>
</dependency>
(2)修改ProductServiceImpl类
ProductServiceImpl类的源码详见:shop-product工程下的io.binghe.shop.product.service.impl.ProductServiceImpl,需要将ProductServiceImpl类上标注的Spring中的@Service注解,替换成bhrpc框架中的@RpcService注解,修改后的源码如下所示。
代码语言:javascript复制@RpcService(interfaceClass = ProductService.class, version = "1.0.0", group = "binghe")
public class ProductServiceImpl implements ProductService {
@Autowired
private ProductMapper productMapper;
@Override
public Product getProductById(Long pid) {
return productMapper.selectById(pid);
}
@Override
public int updateProductStockById(Integer count, Long id) {
return productMapper.updateProductStockById(count, id);
}
}
可以看到,在ProductServiceImpl类上标注了bhrpc框架中的@RpcService注解,并且指定了interfaceClass、version和group属性。
(3)修改ProductStarter类
ProductStarter类的源码详见:shop-product工程下的io.binghe.shop.ProductStarter,主要是在ProductStarter类上添加@ComponentScan注解,修改后的源码如下所示。
代码语言:javascript复制@SpringBootApplication
@ComponentScan(basePackages = {"io.binghe.shop", "io.binghe.rpc"})
@MapperScan(value = { "io.binghe.shop.product.mapper" })
@EnableTransactionManagement(proxyTargetClass = true)
@EnableDiscoveryClient
public class ProductStarter {
public static void main(String[] args){
SpringApplication.run(ProductStarter.class, args);
}
}
可以看到,在ProductStarter类上标注了@ComponentScan注解,并指定了扫描的包路径为io.binghe.shop和io.binghe.rpc,使其既能够扫描到微服务项目中包下的类,也能够扫描到bhrpc框架包下的类。
(4)添加配置
由于项目使用了Nacos作为配置中心,所以,需要在Nacos添加shop-product工程作为服务提供者的配置,登录Nacos管理端,找到shop-product工程的配置,如下所示。
点击编辑按钮,在原有配置的基础上,添加如下配置信息。
代码语言:javascript复制bhrpc:
binghe:
provider:
# rpc server
serverAddress: 127.0.0.1:20881
# serverRegistryAddress
serverRegistryAddress: 127.0.0.1:20881
# zookeeper server
registryAddress: 127.0.0.1:2181
# registry center type
registryType: zookeeper
#registry loadbalance type
registryLoadBalanceType: zkconsistenthash
# reflect type
reflectType: cglib
# heartbeatInterval
heartbeatInterval: 30000
可以看到,配置的内容也都是bhrpc框架的服务提供者启动时,需要读取的一些参数信息。配置完成后,点击发布按钮进行发布。
至此,shop-product工程改造完成,也是非常简单的。
4.改造shop-order工程
shop-order工程对应bhrpc框架的服务消费者角色。
(1)添加pom.xml依赖
shop-order工程作为bhrpc框架的服务消费者,在pom.xml需要添加如下依赖。
代码语言:javascript复制<dependency>
<groupId>io.binghe.rpc</groupId>
<artifactId>bhrpc-spring-boot-starter-consumer</artifactId>
<version>${bhrpc.version}</version>
</dependency>
<dependency>
<groupId>io.binghe.shop</groupId>
<artifactId>shop-service-api</artifactId>
<version>${project.version}</version>
</dependency>
(2)新增OrderServiceV9Impl类
为了不影响整体项目原有的逻辑,复制OrderServiceV8Impl类的代码,新增成为OrderServiceV9Impl类,OrderServiceV9Impl类的源码详见:shop-order工程下的io.binghe.shop.order.service.impl.OrderServiceV9Impl,类框架代码如下所示。
代码语言:javascript复制@Slf4j
@Service("orderServiceV9")
public class OrderServiceV9Impl implements OrderService {
}
(3)改造OrderServiceV9Impl类
将OrderServiceV9Impl类中,原本userService和productService成员变量上标注的Spring中的@Autowired注解替换成bhrpc框架中的@RpcReference注解,替换后的源码如下所示。
代码语言:javascript复制@RpcReference(registryType = "zookeeper", registryAddress = "127.0.0.1:2181", loadBalanceType = "zkconsistenthash", version = "1.0.0", group = "binghe", serializationType = "protostuff", proxy = "cglib", timeout = 30000, async = false)
private UserService userService;
@RpcReference(registryType = "zookeeper", registryAddress = "127.0.0.1:2181", loadBalanceType = "zkconsistenthash", version = "1.0.0", group = "binghe", serializationType = "protostuff", proxy = "cglib", timeout = 30000, async = false)
private ProductService productService;
可以看到,userService和productService成员变量上标注了bhrpc框架中的@RpcReference注解,并且配置了服务消费者启动时需要的一些参数信息。
注意:需要将OrderServiceV9Impl类中的UserService改成引用io.binghe.shop.service.UserService接口,将ProductService改成引用io.binghe.shop.service.ProductService接口,修改OrderServiceV9Impl类中的一些报错信息。
(4)修改OrderStarter类
OrderStarter类的源码详见:shop-order工程下的io.binghe.shop.OrderStarter,主要是在OrderStarter类上添加@ComponentScan注解,修改后的源码如下所示。
代码语言:javascript复制@SpringBootApplication
@ComponentScan(basePackages = {"io.binghe.shop", "io.binghe.rpc"})
@EnableTransactionManagement(proxyTargetClass = true)
@MapperScan(value = { "io.binghe.shop.order.mapper" })
@EnableDiscoveryClient
@EnableFeignClients
public class OrderStarter {
public static void main(String[] args){
SpringApplication.run(OrderStarter.class, args);
}
}
可以看到,在OrderStarter类上标注了@ComponentScan注解,并指定了扫描的包路径为io.binghe.shop和io.binghe.rpc,使其既能够扫描到微服务项目中包下的类,也能够扫描到bhrpc框架包下的类。
(5)添加配置
由于项目使用了Nacos作为配置中心,所以,需要在Nacos添加shop-order工程作为服务消费者的配置,登录Nacos管理端,找到shop-order工程的配置,如下所示。
点击编辑按钮,在原有配置的基础上,添加如下配置信息。
代码语言:javascript复制bhrpc:
binghe:
consumer:
# zookeeper server
registryAddress: 127.0.0.1:2181
# registry center type
registryType: zookeeper
# registry loadbalance type
loadBalanceType: zkconsistenthash
# proxy type
proxy: cglib
# version
version: 1.0.0
# group
group: binghe
# zkconsistenthash
serializationType: zkconsistenthash
# timeout
timeout: 30000
# async
async: false
# oneway
oneway: false
# heartbeatInterval
heartbeatInterval: 15000
# retryInterval
retryInterval: 1000
# retryTimes
retryTimes: 3
可以看到,配置的内容都是bhrpc框架的服务消费者启动时,需要读取的一些参数信息。配置完成后,点击发布按钮进行发布。
(6)修改OrderController类
OrderController类的源码详见:shop-order工程下的io.binghe.shop.order.controller.OrderController,主要是将OrderController类中使用@Qualifier注解标识的orderServiceV8修改成orderServiceV9,如下所示。
代码语言:javascript复制@Autowired
@Qualifier(value = "orderServiceV9")
private OrderService orderService;
至此,shop-order工程改造完成,也是非常简单的。
目前,在项目中整合我们自己手写的RPC框架就完成了,是不是非常简单呢?没错,我们自己手写的bhrpc框架整合微服务项目就是这么简单!
2.3 测试轮子
1.启动服务
按照《SpringCloud Alibaba实战项目》,分别启动Nacos、RocketMQ、Sentinel、ZipKin、Seata和Zookeeper服务,对应服务的版本在源码的README.md文件中有说明。
2.启动工程
按顺序分别启动shop-user工程、shop-product工程、shop-order工程和shop-gateway工程。
- 启动shop-user工程
输出如下信息,没有报错,说明bhrpc框架监听的是20880端口,表示启动成功。
代码语言:javascript复制i.b.r.p.common.server.base.BaseServer : Server started on port 20880
- shop-product工程
输出如下信息,没有报错,说明bhrpc框架监听的是20881端口,表示启动成功。
代码语言:javascript复制i.b.r.p.common.server.base.BaseServer : Server started on port 20881
- shop-order工程
输出如下信息,没有报错,说明启动成功。
代码语言:javascript复制o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8081 (http) with context path '/order'
- shop-gateway工程
输出如下信息,没有报错,说明启动成功。
代码语言:javascript复制io.binghe.shop.GatewayStarter : Started GatewayStarter in 9.604 seconds (JVM running for 10.964)
3.查询数据表数据
(1)打开cmd终端,进入MySQL命令行,并进入shop商城数据库,如下所示。
代码语言:javascript复制C:Usersbinghe>mysql -uroot -p
Enter password: ****
Welcome to the MySQL monitor. Commands end with ; or g.
Your MySQL connection id is 15
Server version: 5.7.35 MySQL Community Server (GPL)
Copyright (c) 2000, 2021, Oracle and/or its affiliates.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or 'h' for help. Type 'c' to clear the current input statement.
mysql> use shop;
Database changed
(2)查看商品数据表,如下所示。
代码语言:javascript复制mysql> select * from t_product;
------ ------------ ------------- -------------
| id | t_pro_name | t_pro_price | t_pro_stock |
------ ------------ ------------- -------------
| 1001 | 华为 | 2399.00 | 100 |
| 1002 | 小米 | 1999.00 | 100 |
| 1003 | iphone | 4999.00 | 100 |
------ ------------ ------------- -------------
3 rows in set (0.00 sec)
这里,我们以id为1001的商品为例,此时发现商品的库存为100。
(3)查询订单数据表,如下所示。
代码语言:javascript复制mysql> select * from t_order;
Empty set (0.00 sec)
可以发现订单数据表为空。
(4)查询订单条目数据表,如下所示。
代码语言:javascript复制mysql> select * from t_order_item;
Empty set (0.00 sec)
可以看到,订单条目数据表为空。
4.访问项目
打开浏览器访问http://localhost:10002/server-order/order/submit_order?userId=1001&productId=1001&count=1
,如下所示。
可以看到,项目返回的结果为success,表示项目执行成功。
5.再次查看数据表数据
(1)查看商品数据表,如下所示。
代码语言:javascript复制mysql> select * from t_product;
------ ------------ ------------- -------------
| id | t_pro_name | t_pro_price | t_pro_stock |
------ ------------ ------------- -------------
| 1001 | 华为 | 2399.00 | 99 |
| 1002 | 小米 | 1999.00 | 100 |
| 1003 | iphone | 4999.00 | 100 |
------ ------------ ------------- -------------
3 rows in set (0.00 sec)
这里,id为1001的商品库存为99,说明库存已经减少了1。
(2)查询订单数据表,如下所示。
代码语言:javascript复制mysql> select * from t_order;
------------------- ----------- ------------- ------------- ----------- ---------------
| id | t_user_id | t_user_name | t_phone | t_address | t_total_price |
------------------- ----------- ------------- ------------- ----------- ---------------
| 96829539832958976 | 1001 | binghe | 13212345678 | 北京 | 2399.00 |
------------------- ----------- ------------- ------------- ----------- ---------------
1 row in set (0.00 sec)
可以看到,在t_order表中新增了一张订单数据表,订单的总金额为2399.00元。
(3)查询订单条目数据表,如下所示。
代码语言:javascript复制mysql> select * from t_order_item;
------------------- ------------------- ---------- ------------ ------------- ----------
| id | t_order_id | t_pro_id | t_pro_name | t_pro_price | t_number |
------------------- ------------------- ---------- ------------ ------------- ----------
| 96829541082861568 | 96829539832958976 | 1001 | 华为 | 2399.00 | 1 |
------------------- ------------------- ---------- ------------ ------------- ----------
1 row in set (0.00 sec)
可以看到,订单条目数据表中条了一条订单条目数据,商品的id为1001,商品名称为华为,商品的价格为2399.00,下单的商品数量为1。
根据测试结果可以看出,我们已经正确在微服务项目中整合了我们自己手写的bhrpc框架。
怎么样,很简单吧?大家都学会了吗?
三、写在最后
在冰河的知识星球除了目前正在热更的高性能网关外,还有其他6个项目,像分布式IM即时通讯系统、Sekill分布式秒杀系统、手写RPC、简易商城系统等等,这些项目的需求、方案、架构、落地等均来自互联网真实业务场景,让你真正学到互联网大厂的业务与技术落地方案,并将其有效转化为自己的知识储备。