《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 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 还支持不同的协议,目前包括 boltRESTfuldubboH2C 协议进行通信。

    旁白君: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 项目

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

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

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

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

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 传输类。

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

public class UserDTO implements Serializable {

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

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

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

② 创建 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 接口。

① 创建 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 实现类。

① 创建 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 注解中心

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

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 简单测试

① 执行 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)
  • 看到该日志内容,意味着启动成功。

② 我们来使用 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 简单测试

① 执行 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 ''
  • 看到该日志内容,意味着启动成功。

② 我们来使用 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]

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

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

④ 使用浏览器,访问 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 项目,修改如下图所示:

项目修改点

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

② 删除 sofarpc.xml 配置文件。

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

3.3 Consumer

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

项目修改点

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

② 删除 sofarpc.xml 配置文件。

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

3.4 简单测试

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

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

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

③ 使用浏览器,访问 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 示例项目哟!虽然它显示处于归档状态,但是貌似还是它是最全的。

文章目录
  1. 1. 1. 概述
  2. 2. 2. XML 配置
    1. 2.1. 2.1 API
      1. 2.1.1. 2.1.1 UserDTO && UserAddDTO
      2. 2.1.2. 2.1.2 UserRpcService
    2. 2.2. 2.2 Provider
      1. 2.2.1. 2.2.1 引入依赖
      2. 2.2.2. 2.2.2 UserRpcServiceImpl
      3. 2.2.3. 2.2.3 应用配置文件
      4. 2.2.4. 2.2.4 SOFARPC XML 配置文件
      5. 2.2.5. 2.2.5 ProviderApplication
      6. 2.2.6. 2.2.6 简单测试
    3. 2.3. 2.3 Consumer
      1. 2.3.1. 2.3.1 引入依赖
      2. 2.3.2. 2.3.2 应用配置文件
      3. 2.3.3. 2.3.3 SOFARPC XML 配置文件
      4. 2.3.4. 2.3.4 UserController
      5. 2.3.5. 2.2.5 ConsumerApplication
      6. 2.3.6. 2.2.6 简单测试
  3. 3. 3. 注解配置
    1. 3.1. 3.1 API
    2. 3.2. 3.2 Provider
    3. 3.3. 3.3 Consumer
    4. 3.4. 3.4 简单测试
  4. 4. 666. 彩蛋