总阅读量:次

摘要: 原创出处 http://www.iocoder.cn/Spring-Boot/SOFARPC/ 「芋道源码」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注**微信公众号:【芋道源码】**有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言将得到认真回复。甚至不知道如何读源码也可以请教噢
  4. 新的源码解析文章实时收到通知。每周更新一篇左右
  5. 认真的源码交流微信群。

本文在提供完整代码示例,可见 https://github.com/YunaiV/SpringBoot-Labslab-62 目录。

原创不易,给点个 Star 嘿,一起冲鸭!

1. 概述

SOFARPC 是蚂蚁金服开源的一款基于 Java 实现的 RPC 服务框架,为应用之间提供远程服务调用能力,具有高可伸缩性,高容错性。目前,蚂蚁金服所有的业务的相互间的 RPC 调用都是采用 SOFARPC。

旁白君:经过艿艿确认,蚂蚁金服内部真的在使用 SOFARPC,嘿嘿。

  • SOFARPC 为用户提供了负载均衡,流量转发,链路追踪,链路数据透传,故障剔除等功能。

  • SOFARPC 还支持不同的协议,目前包括 bolt,RESTful,dubbo,H2C 协议进行通信。

    旁白君:SOFABolt 是一套基于 Netty 实现的网络通信框架。

    如果胖友想要了解 Netty 在网络通信上的应用,阅读 SOFABolt 源码是个不错的选择。

SOFARPC 的整体架构和 Dubbo 是一致的,如下图所示:

整体架构

  • 1、当一个 SOFARPC 的应用启动的时候,如果发现当前应用需要发布 RPC 服务的话,那么 SOFARPC 会将这些服务注册到服务注册中心上。

    如图中 Service 指向 Registry,对应 1. register service 部分。

  • 2、当引用这个服务的 SOFARPC 应用启动时,会从服务注册中心订阅到相应服务的元数据信息。服务注册中心收到订阅请求后,会将发布方的元数据列表实时推送给服务引用方。

    如图中 Registry 指向 Reference,对应 2. subscribe service3. notify address 部分。

  • 3、当服务引用方拿到地址以后,就可以从中选取地址发起调用了。

    如图中 Reference 指向 Service,对应 4. invoke 部分。

SOFARPC 提供了比较多的配置方式,日常开发中主要使用的是 XML 配置注解配置 。我们分别会在 「2. XML 配置」「3. 注解配置」 小节来入门。

2. XML 配置

示例代码对应仓库:lab-62-sofarpc-xml-demo

本小节的示例,需要创建三个 Maven 项目,如下图所示:

三个 Maven 项目

1 user-rpc-service-api 项目:服务接口,定义 SOFARPC Service API 接口,提供给消费者使用。

详细代码,我们在 「2.1 API」 讲解。

2 user-rpc-service-provider 项目:服务提供者,实现 user-rpc-service-api 项目定义的 SOFARPC Service API 接口,提供相应的服务。

详细代码,我们在 「2.2 Provider」 中讲解。

3 user-rpc-service-consumer 项目:服务消费者,会调用 user-rpc-service-provider 项目提供的 SOFARPC Service 服务。

详细代码,我们在 「2.3 Consumer」 中讲解。

2.1 API

对应 user-rpc-service-api 项目,服务接口,定义 SOFARPC Service API 接口,提供给消费者使用。

2.1.1 UserDTO && UserAddDTO

cn.iocoder.springboot.lab62.rpc.dto 包下,创建用于 SOFARPC Service 传输类。

1 创建 UserDTO 类,用户信息 DTO 。代码如下:

public class UserDTO implements Serializable {

/**
* 用户编号
*/
private Integer id;
/**
* 昵称
*/
private String name;
/**
* 性别
*/
private Integer gender;

// 省略 setter/getter 方法...
}

注意,要实现 java.io.Serializable 接口。因为,SOFARPC 会涉及远程通信,需要序列化和反序列化。

2 创建 UserAddDTO 类,用户添加 DTO。代码如下:

public class UserAddDTO implements Serializable {

/**
* 昵称
*/
private String name;
/**
* 性别
*/
private Integer gender;

// 省略 setter/getter 方法...
}

2.1.2 UserRpcService

cn.iocoder.springboot.lab62.rpc.api 包下,创建 SOFARPC Service API 接口。

1 创建 UserRpcService 接口,用户服务 RPC Service 接口。代码如下:

// UserRpcService.java

public interface UserRpcService {

/**
* 根据指定用户编号,获得用户信息
*
* @param id 用户编号
* @return 用户信息
*/
UserDTO get(Integer id);

/**
* 添加新用户,返回新添加的用户编号
*
* @param addDTO 添加的用户信息
* @return 用户编号
*/
Integer add(UserAddDTO addDTO);

}

2.2 Provider

对应 user-rpc-service-provider 项目,服务提供者,实现 user-rpc-service-api 项目定义的 SOFARPC Service API 接口,提供相应的服务。

2.2.1 引入依赖

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">
<!-- <1> -->
<parent>
<groupId>com.alipay.sofa</groupId>
<artifactId>sofaboot-dependencies</artifactId>
<version>3.3.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>lab-62-sofarpc-xml-demo-user-rpc-service-provider</artifactId>

<dependencies>
<!-- <2> 引入定义的 SOFARPC API 接口 -->
<dependency>
<groupId>cn.iocoder.springboot.labs</groupId>
<artifactId>lab-62-sofarpc-xml-demo-user-rpc-service-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>

<!-- 引入 Spring Boot 基础 Starter 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>

<!-- <3> 实现对 SOFARPC 的自动化配置 -->
<dependency>
<groupId>com.alipay.sofa</groupId>
<artifactId>rpc-sofa-boot-starter</artifactId>
</dependency>

<!-- <4> 使用 Zookeeper 作为注册中心 -->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
</dependency>

</dependencies>

</project>

<1> 处,设置 sofaboot-dependencies父 POM,管理 SOFARPC 所使用到的依赖。

<2> 处,引入 lab-62-sofarpc-xml-demo-user-rpc-service-api 依赖,定义的 SOFARPC Service API 接口。

<3> 处,引入 rpc-sofa-boot-starter 依赖,实现对 实现对 SOFARPC 的自动化配置。

<4> 处,引入 curator-framework 依赖,因为我们希望使用 Zookeeper 作为注册中心。可能胖友不太了解 Apache Curator 框架,这里我们看一段简介:

FROM https://www.oschina.net/p/curator

Zookeeper 的客户端调用过于复杂,Apache Curator 就是为了简化Zookeeper 客户端调用而生,利用它,可以更好的使用 Zookeeper。

  • 虽然说,目前阿里正在大力推广 Nacos 作为 Dubbo 的注册中心,但是大多数团队,采用的还是 Zookeeper 为主。
  • 对了,如果胖友不知道怎么安装 Zookeeper ,可以看看 《芋道 Zookeeper 极简入门》 文章。

2.2.2 UserRpcServiceImpl

cn.iocoder.springboot.lab62.rpc.service 包下,创建 SOFARPC Service 实现类。

1 创建 UserRpcServiceImpl 类,用户服务 RPC Service 实现类。代码如下:

@Service
public class UserRpcServiceImpl implements UserRpcService {

@Override
public UserDTO get(Integer id) {
return new UserDTO().setId(id)
.setName("没有昵称:" + id)
.setGender(id % 2 + 1); // 1 - 男;2 - 女
}

@Override
public Integer add(UserAddDTO addDTO) {
return (int) (System.currentTimeMillis() / 1000); // 嘿嘿,随便返回一个 id
}

}

友情提示:UserRpcServiceImpl 注册成一个 Spring Bean,还是采用 @Service 注解。当然,胖友也可以选择 Spring XML <bean /> 标签。

2.2.3 应用配置文件

创建 application.yml 配置文件,添加 SOFARPC 相关的配置,如下:

spring:
application:
name: user-service-consumer # 应用名

com.alipay.sofa.rpc.registry.address: zookeeper://127.0.0.1:2181 # SOFARPC 注解中心

1 spring.application.name 配置项,设置应用名,同时注册到注册中心时也使用它作为服务名。

2 com.alipay.sofa.rpc.registry.address 配置项,设置注册中心的地址。这里,我们采用 Zookeeper。

友情提示:更多配置项,胖友可以查看《SOFARPC 官方文档 —— RPC 应用参数配置》

2.2.4 SOFARPC XML 配置文件

创建 sofarpc.xml 配置文件,添加 SOFARPC 相关的配置,如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:sofa="http://sofastack.io/schema/sofaboot"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://sofastack.io/schema/sofaboot http://sofastack.io/schema/sofaboot.xsd">

<sofa:service ref="userRpcServiceImpl" interface="cn.iocoder.springboot.lab62.rpc.api.UserRpcService">
<sofa:binding.bolt/>
</sofa:service>

</beans>

<sofa:service /> 标签,用于声明 SOFARPC Service 服务的发布

如果胖友想要发布多个协议,可以设置多个 <sofa:binding /> 标签。例如说:

<sofa:service ref="userRpcServiceImpl" interface="cn.iocoder.springboot.lab62.rpc.api.UserRpcService">
<sofa:binding.bolt/>
<sofa:binding.dubbo/> <!-- Dubbo 协议 -->
<sofa:binding.rest/> <!-- Rest 协议 -->
</sofa:service>

友情提示:更多配置项,胖友可以查看《SOFARPC 官方文档 —— SOFABoot 环境发布订阅说明》

2.2.5 ProviderApplication

创建 ProviderApplication 类,用于启动该项目,提供 SOFARPC 服务。代码如下:

@SpringBootApplication
@ImportResource("classpath:sofarpc.xml")
public class ProviderApplication {

public static void main(String[] args) {
// 启动 Spring Boot 应用
SpringApplication.run(ProviderApplication.class, args);
}

}

在类上,添加 @ImportResource 注解,引用 sofarpc.xml XML 配置文件。

2.2.6 简单测试

1 执行 ProviderApplication 的 #main(String[] args) 方法,启动项目。控制台打印日志如下:

// ... 省略其它日志

2020-06-10 08:22:24.882 INFO 37225 --- [ main] c.i.s.lab62.rpc.ProviderApplication : Started ProviderApplication in 2.427 seconds (JVM running for 2.924)

  • 看到该日志内容,意味着启动成功。

2 我们来使用 Zookeeper 客户端,查看 UserRpcService 服务是否注册成功。操作流程如下:

# 使用 Zookeeper 自带的客户端,连接到 Zookeeper 服务器
$ bin/zkCli.sh

# 查看 /sofa-rpc 目录下的所有服务。
# 此时,我们查看到了 UserRpcService 服务
$ ls /sofa-rpc
[cn.iocoder.springboot.lab62.rpc.api.UserRpcService]

# 查看 /sofa-rpc/cn.iocoder.springboot.lab62.rpc.api.UserRpcService 目录下的存储情况。
# 此时,我们看到了 providers 提供者信息
$ ls /sofa-rpc/cn.iocoder.springboot.lab62.rpc.api.UserRpcService
[configs, overrides, providers]

# 查看 UserRpcService 服务的节点列表
# 此时,可以看到有一个节点,就是我们刚启动的服务提供者。
$ ls /sofa-rpc/cn.iocoder.springboot.lab62.rpc.api.UserRpcService/providers
[bolt%3A%2F%2F192.168.43.240%3A12200%3Fversion%3D1.0%26accepts%3D100000%26appName%3Duser-service-provider%26weight%3D100%26language%3Djava%26pid%3D37225%26interface%3Dcn.iocoder.springboot.lab62.rpc.api.UserRpcService%26timeout%3D0%26serialization%3Dhessian2%26protocol%3Dbolt%26delay%3D-1%26dynamic%3Dtrue%26startTime%3D1591755744817%26id%3DuserRpcServiceImpl%26uniqueId%3D%26rpcVer%3D50700]

2.3 Consumer

对应 user-rpc-service-consumer 项目,服务消费者,会调用 user-rpc-service-provider 项目提供的 SOFARPC Service 服务。

2.3.1 引入依赖

「2.2.1 引入依赖」一致,胖友可见pom.xml文件。差别主要有两点:

  • 第一,<artifactId /> 改成了 "user-rpc-service-consumer"
  • 第二,引入 spring-boot-starter-web 依赖,因为稍后我们要在 Controller 中调用 SOFARPC Service 服务。

2.3.2 应用配置文件

创建 application.yml 配置文件,添加 SOFARPC 相关的配置,如下:

spring:
application:
name: user-service-consumer # 应用名

com.alipay.sofa.rpc.registry.address: zookeeper://127.0.0.1:2181 # SOFARPC 注解中心

「2.2.3 应用配置文件」基本一致,除了 spring.application.name 配置项修改成了 user-service-consumer

2.3.3 SOFARPC XML 配置文件

创建 sofarpc.xml 配置文件,添加 SOFARPC 相关的配置,如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:sofa="http://sofastack.io/schema/sofaboot"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://sofastack.io/schema/sofaboot http://sofastack.io/schema/sofaboot.xsd">

<sofa:reference id="userRpcService" interface="cn.iocoder.springboot.lab62.rpc.api.UserRpcService">
<sofa:binding.bolt/>
</sofa:reference>

</beans>

<sofa:reference /> 标签,用于声明 SOFARPC Service 服务的引用

  • id 属性:设置引用 SOFARPC Service 服务后,创建成 Spring Bean 的编号。
  • interface 属性:引用服务的 Service 接口。这里,我们设置为「2.1.2 UserRpcService」
  • 内嵌 <sofa:binding.bolt/> 标签,设置引用的服务使用 bolt 协议。

如果胖友想要引用多个协议,可以设置多个 <sofa:binding /> 标签。例如说:

<sofa:reference id="userRpcService" interface="cn.iocoder.springboot.lab62.rpc.api.UserRpcService">
<sofa:binding.bolt/>
<sofa:binding.dubbo/> <!-- Dubbo 协议 -->
<sofa:binding.rest/> <!-- Rest 协议 -->
</sofa:reference>

2.3.4 UserController

创建 UserController 类,提供 SOFARPC Service 调用的示例。代码如下:

@RestController
@RequestMapping("/user")
public class UserController {

@Autowired
private UserRpcService userRpcService; // <1>

@GetMapping("/get")
public UserDTO get(@RequestParam("id") Integer id) {
return userRpcService.get(id); // <2>
}

@GetMapping("/add") // 为了方便测试,实际使用 @PostMapping
public Integer add(@RequestParam("name") String name,
@RequestParam("gender") Integer gender) {
UserAddDTO addDTO = new UserAddDTO().setName(name).setGender(gender);
return userRpcService.add(addDTO); // <3>
}

}

<1> 处,通过 Spring @Autowired 注入一个 UserRpcService Bean,它就是来自「2.3.3 SOFARPC XML 配置文件」中,通过 <sofa:reference /> 标签所创建的。

<2><3> 处,直接调用 UserRpcService 提供的方法即可,如此便实现了 SOFARPC 远程调用。

2.2.5 ConsumerApplication

创建 ConsumerApplication 类,用于启动该项目,调用 Dubbo 服务。代码如下:

@SpringBootApplication
@ImportResource("classpath:sofarpc.xml")
public class ConsumerApplication {

public static void main(String[] args) {
// 启动 Spring Boot 应用
SpringApplication.run(ConsumerApplication.class, args);;
}

}

在类上,添加 @ImportResource 注解,引用 sofarpc.xml XML 配置文件。

2.2.6 简单测试

1 执行 ConsumerApplication 的 #main(String[] args) 方法,启动项目。控制台打印日志如下:

// ... 省略其它日志

2020-06-10 08:32:29.230 INFO 38265 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''

  • 看到该日志内容,意味着启动成功。

2 我们来使用 Zookeeper 客户端,查看 UserRpcService 服务是否注册成功。操作流程如下:

# 使用 Zookeeper 自带的客户端,连接到 Zookeeper 服务器
$ bin/zkCli.sh

# 查看 /sofa-rpc/cn.iocoder.springboot.lab62.rpc.api.UserRpcService 目录下的存储情况。
# 此时,我们看到了 consumers 提供者信息
$ ls /sofa-rpc/cn.iocoder.springboot.lab62.rpc.api.UserRpcService
[configs, consumers, overrides, providers]

# 查看 UserRpcService 服务的节点列表
# 此时,可以看到有一个节点,就是我们刚启动的服务消费者。
$ ls /sofa-rpc/cn.iocoder.springboot.lab62.rpc.api.UserRpcService/consumers
[bolt%3A%2F%2F10.101.16.12%3Fversion%3D1.0%26uniqueId%3D%26pid%3D38265%26timeout%3D-1%26id%3DuserRpcService%26generic%3Dfalse%26interface%3Dcn.iocoder.springboot.lab62.rpc.api.UserRpcService%26appName%3Duser-service-consumer%26serialization%3Dhessian2%26startTime%3D1591759947382%26pid%3D38265%26language%3Djava%26rpcVer%3D50605]

3 使用浏览器,访问 http://127.0.0.1:8080/user/get?id=1 地址,成功执行 SOFARPC 调用。返回结果如下:

{
"id": 1,
"name": "没有昵称:1",
"gender": 2
}

4 使用浏览器,访问 http://127.0.0.1:8080/user/add?name=yudaoyuanma&gender=1 地址,成功执行 SOFARPC 调用。返回结果如下:

1591763258

至此,我们已经完成 SOFARPC XML 配置方式的学习,更多可以参考《SOFARPC 文档 —— SOFABoot 环境 XML 配置使用》

3. 注解配置

示例代码对应仓库:lab-62-sofarpc-annotations-demo

本文我们在「2. XML 配置」小节的基础上,改成注解配置的方式。最终项目如下图所示:

项目结构

3.1 API

对应 lab-62-sofarpc-annotations-demo-user-rpc-service-api 项目,无需做任何修改。

3.2 Provider

对应 lab-62-sofarpc-annotations-demo-user-rpc-service-provider 项目,修改如下图所示:

项目修改点

1 在 UserRpcServiceImpl 类上,添加 @SofaService 注解,声明发布一个 SOFARPC Service 服务。同时,设置 bindings 属性为 @SofaServiceBinding 中的 bolt 协议。

2 删除 sofarpc.xml 配置文件。

3 在 ProviderApplication 类上,注释掉对 sofarpc.xml 配置文件的 @ImportResource

3.3 Consumer

对应 lab-62-sofarpc-annotations-demo-user-rpc-service-consumer 项目,修改如下图所示:

项目修改点

1 在 UserController 类的 userRpcService 变量上,添加 @SofaReference 注解,声明引用一个 SOFARPC Service 服务。同时,设置 bindings 属性为 @SofaReferenceBinding 中的 bolt 协议。

2 删除 sofarpc.xml 配置文件。

3 在 ProviderApplication 类上,注释掉对 sofarpc.xml 配置文件的 @ImportResource

3.4 简单测试

1 执行 ProviderApplication 启动服务提供者,执行 ConsumerApplication 启动服务消费者。

2 使用浏览器,访问 http://127.0.0.1:8080/user/get?id=1 地址,成功执行 SOFARPC 调用。返回结果如下:

{
"id": 1,
"name": "没有昵称:1",
"gender": 2
}

3 使用浏览器,访问 http://127.0.0.1:8080/user/add?name=yudaoyuanma&gender=1 地址,成功执行 SOFARPC 调用。返回结果如下:

1591796851

至此,我们已经完成 SOFARPC 注解配置方式的学习,更多可以参考《SOFARPC 文档 —— SOFABoot 环境注解使用》

666. 彩蛋

因为好奇,所以艿艿简单进行了下 SOFARPC 的入门。又瞅了瞅供的《SOFARPC 官方文档 —— SOFABoot 方式快速入门》写的有点简略,所以希望通过本文能够帮助同样感兴趣的胖友。

当然,《SOFARPC 官方文档》写的还是不错的,想要了解更多的胖友可以花点时间通读下。

另外,也可以瞅瞅 SOFA 官方提供的 sofa-boot-samples 示例项目哟!虽然它显示处于归档状态,但是貌似还是它是最全的。

AltStyle によって変換されたページ (->オリジナル) /