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

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


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

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

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

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

1. 概述

Motan 是一套高性能、易于使用的分布式远程服务调用(RPC)框架。Motan 由微博开源,已支撑其内部千亿级调用。

旁白君:Motan 在设计上参考了 Dubbo,所以整体架构会比较接近。

1.1 功能概述

  • 支持通过 Spring 配置方式集成,无需额外编写代码即可为服务提供分布式调用能力。
  • 支持集成 Consul、Zookeeper 等配置服务组件,提供集群环境的服务发现及治理能力。
  • 支持动态自定义负载均衡、跨机房流量调整等高级服务调度能力。
  • 基于高并发、高负载场景进行优化,保障生产环境下 RPC 服务高可用。

1.2 架构概述

Motan 中分为服务提供方(RPC Server),服务调用方(RPC Client)和服务注册中心(Registry)三个角色。

  • Server 提供服务,向 Registry 注册自身服务,并向注册中心定期发送心跳汇报状态。
  • Client 使用服务,需要向注册中心订阅 RPC 服务,Client 根据Registry 返回的服务列表,与具体的 Sever 建立连接,并进行 RPC 调用。
  • 当 Server 发生变更时,Registry 会同步变更,Client 感知后会对本地的服务列表作相应调整。

三者的交互关系如下图:

交互关系

友情提示:服务调用方也被称为 Consumer 消费者,服务提供方也被称为 Provider 提供者。本文更多采用该说法~

下面,我们来入门 Motan 的使用。因为 Motan 提供了对 Spring 的集成,所以我们会在「2. XML 配置」「3. 注解配置」小节,分别学习两种配置方式。

2. XML 配置

示例代码对应仓库:lab-63-motan-xml-demo

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

三个 Maven 项目

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

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

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

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

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

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

2.1 API

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

2.1.1 UserDTO && UserAddDTO

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

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

public class UserDTO implements Serializable {

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

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

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

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

public class UserAddDTO implements Serializable {

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

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

2.1.2 UserRpcService

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

① 创建 UserRpcService 接口,用户服务 Motan 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 项目定义的 Motan 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">
<parent>
<artifactId>lab-63-motan-xml-demo</artifactId>
<groupId>cn.iocoder.springboot.labs</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>lab-63-motan-xml-demo-user-rpc-service-provider</artifactId>

<properties>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.source>1.8</maven.compiler.source>
<spring.boot.version>2.2.4.RELEASE</spring.boot.version>
<moton.version>1.1.8</moton.version>
</properties>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>
<!-- 引入定义的 Motan API 接口 -->
<dependency>
<groupId>cn.iocoder.springboot.labs</groupId>
<artifactId>lab-63-motan-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>

<!-- 引入 Motan 相关依赖 -->
<dependency>
<groupId>com.weibo</groupId>
<artifactId>motan-core</artifactId>
<version>${moton.version}</version>
</dependency>
<dependency>
<groupId>com.weibo</groupId>
<artifactId>motan-transport-netty4</artifactId>
<version>${moton.version}</version>
</dependency>
<dependency>
<groupId>com.weibo</groupId>
<artifactId>motan-registry-zookeeper</artifactId>
<version>${moton.version}</version>
</dependency>
<dependency>
<groupId>com.weibo</groupId>
<artifactId>motan-springsupport</artifactId>
<version>${moton.version}</version>
</dependency>

</dependencies>

</project>

主要是引入 Motan 相关依赖。

① 引入 motan-core 依赖,Motan 核心库。

② 引入 motan-transport-netty4 依赖,Motan 使用 Netty 作为网络通信。

③ 引入 motan-registry-zookeeper 依赖,Motan 使用 Zookeeper 作为注册中心。

④ 引入 motan-springsupport 依赖,Motan 针对 Spring 的集成支持。

2.2.2 UserRpcServiceImpl

cn.iocoder.springboot.lab63.rpc.service 包下,创建 Motan 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 Motan XML 配置文件

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

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:motan="http://api.weibo.com/schema/motan"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://api.weibo.com/schema/motan http://api.weibo.com/schema/motan.xsd">

<!-- ① 注册中心配置。 -->
<motan:registry name="registry" regProtocol="zookeeper" address="127.0.0.1:2181" connectTimeout="2000" />

<!-- ② Motan 协议配置。-->
<motan:protocol name="motan2" default="true"
maxServerConnection="80000" maxContentLength="1048576"
maxWorkerThread="800" minWorkerThread="20" />

<!-- ③ Motan 服务提供方配置。 -->
<motan:service ref="userRpcServiceImpl" interface="cn.iocoder.springboot.lab63.rpc.api.UserRpcService" export="motan2:8001" />

</beans>

<motan:registry /> 标签,Motan 注册中心配置,用于配置注册中心的注册协议、地址端口、超时时间等。常用属性如下:

  • name:标识配置名称。
  • regProtocol:标识注册中心协议。这里,我们设置为 ZooKeeper 注册中心。
  • address:标识注册中心地址。

<motan:protocol /> 标签,Motan 协议配置,用来配置 Motan服务的协议。常用属性如下:

  • name:标识协议名称。这里,我们设置为 motan2,使用 TCP 长连接模式,基于 Netty 通信,通过 hessian 序列化。

<motan:service /> 标签,Motan 服务提供方配置,用于定义提供给外部调用的接口。常用属性如下:

  • ref:标识服务的实现类,引用具体的 Spring 业务实现对象。这里,我们设置为「2.2.2 UserRpcServiceImpl」
  • interface:标识服务的接口类名。这里,我们设置为「2.1.2 UserRpcService」
  • export:标识服务的暴露方式,格式为 "protocolId:port"(使用的协议及对外提供的端口号),其中protocolId 应与 <motan:protocol /> 中的 id 一致。

友情提示:更多配置项,胖友可以查看《Motan 官方文档 —— 用户指南(配置说明)》

2.2.4 ProviderApplication

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

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

public static void main(String[] args) {
// 启动 Spring Boot 应用
SpringApplication.run(ProviderApplication.class, args);
// 设置 Motan 开启对外服务
MotanSwitcherUtil.setSwitcherValue(MotanConstants.REGISTRY_HEARTBEAT_SWITCHER, true);
}

}

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

② 在应用启动完成后,执行 MotanSwitcherUtil.setSwitcherValue(MotanConstants.REGISTRY_HEARTBEAT_SWITCHER, true) 代码段,设置 Motan 开启对外服务。

Motan 通过这个开关控制是否提供对外 RPC 服务。因为一般正式环境下,需要验证服务正常后才正式对外提供服务,避免有问题的服务直接注册到线上。具体讨论,可见 ISSUE#182 地址。

2.2.5 简单测试

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

ProviderApplication 启动日志

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

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

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

# 查看 /motan/default_rpc/cn.iocoder.springboot.lab63.rpc.api.UserRpcService 目录下的存储情况。
# 此时,我们看到了 server 可用的服务提供者信息,unavailableServer 不可用的服务提供者信息,client 服务消费者信息
$ ls /motan/default_rpc/cn.iocoder.springboot.lab63.rpc.api.UserRpcService
[client, server, unavailableServer]

# 查看 UserRpcService 服务的节点列表
# 此时,可以看到有一个节点,就是我们刚启动的服务提供者。
$ ls /motan/default_rpc/cn.iocoder.springboot.lab63.rpc.api.UserRpcService/server
[10.101.16.12:8001]
$ get /motan/default_rpc/cn.iocoder.springboot.lab63.rpc.api.UserRpcService/server/10.101.16.12:8001
��t_motan2://10.101.16.12:8001/cn.iocoder.springboot.lab63.rpc.api.UserRpcService?minWorkerThread=20&codec=motan-compatible&isDefault=true&maxContentLength=1048576&protocol=motan2&maxWorkerThread=800&refreshTimestamp=1591835535178&id=com.weibo.api.motan.config.springsupport.ServiceConfigBean&nodeType=service&export=motan2:8001&maxServerConnection=80000&

2.3 Consumer

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

2.3.1 引入依赖

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

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

2.3.2 Motan XML 配置文件

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

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:motan="http://api.weibo.com/schema/motan"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://api.weibo.com/schema/motan http://api.weibo.com/schema/motan.xsd">

<!-- ① 注册中心配置。 -->
<motan:registry name="registry" regProtocol="zookeeper" address="127.0.0.1:2181" connectTimeout="2000"/>

<!-- ② Motan 协议配置。-->
<motan:protocol name="motan2" default="true"
haStrategy="failover" loadbalance="roundrobin"
maxClientConnection="10" minClientConnection="2"/>

<!-- ③ Motan 服务调用方配置。 -->
<motan:referer id="userRpcService" interface="cn.iocoder.springboot.lab63.rpc.api.UserRpcService" />

</beans>

<motan:registry /> 标签,Motan 注册中心配置。

<motan:protocol /> 标签,Motan 协议配置。

<motan:referer /> 标签,Motan 服务调用方配置,用于引用内部提供的服务。常用属性如下:

2.3.3 UserController

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

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

@Autowired
private UserRpcService userRpcService;

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

@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);
}

}

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

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

2.3.4 简单测试

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

ConsumerApplication 启动日志

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

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

# 查看 UserRpcService 服务的节点列表
# 此时,可以看到有一个节点,就是我们刚启动的服务提供者。
$ ls /motan/default_rpc/cn.iocoder.springboot.lab63.rpc.api.UserRpcService/client
[10.101.16.12]
$ get /motan/default_rpc/cn.iocoder.springboot.lab63.rpc.api.UserRpcService/client/10.101.16.12
��tmotan2://10.101.16.12:0/cn.iocoder.springboot.lab63.rpc.api.UserRpcService?singleton=true&protocol=motan2&isDefault=true&haStrategy=failover&maxClientConnection=10&minClientConnection=2&loadbalance=roundrobin&refreshTimestamp=1591837911818&id=userRpcService&nodeType=service&version=1.0&

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

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

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

1591763258

3. 注解配置

示例代码对应仓库:lab-63-motan-annotations-demo

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

项目结构

3.1 API

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

3.2 Provider

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

项目修改点

① 在 UserRpcServiceImpl 类上,添加 @MotanService 注解,声明发布一个 Motan Service 服务。同时,设置 export 属性为 motan2:8001,使用 motan2 协议,并启动服务在 8001 端口。

② 删除 <motan:service /> 标签,并添加 <motan:annotation /> 标签,开启扫描 Motan 注解的功能。因为我们要扫描指定包下声明的 Motan 服务,所以设置 package 属性为 "cn.iocoder.springboot.lab63.rpc.service"

3.3 Consumer

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

项目修改点

① 在 UserController 类的 userRpcService 变量上,添加 @MotanReferer 注解,声明引用一个 Motan Service 服务。

② 删除 <motan:service /> 标签,并添加 <motan:annotation /> 标签,开启扫描 Motan 注解的功能。

3.4 简单测试

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

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

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

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

1591796851

另外,本小节还是保留了 <motan:registry /><motan:protocol /> 等 XML 配置,如果胖友想完全去掉,可以参考《Motan 文档 —— 快速入门(使用注解方式配置 motan)》。如下图所示:

Java Config

666. 彩蛋

因为好奇,所以艿艿简单进行了下 Motan 的入门。后续胖友可以阅读下《Motan 官方文档》写的还是不错的,进行更加深入的了解。

另外,也可以瞅瞅 SOFA 官方提供的 https://github.com/weibocom/motan/tree/master/motan-demo 示例项目哟!

文章目录
  1. 1. 1. 概述
    1. 1.1. 1.1 功能概述
    2. 1.2. 1.2 架构概述
  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 Motan XML 配置文件
      4. 2.2.4. 2.2.4 ProviderApplication
      5. 2.2.5. 2.2.5 简单测试
    3. 2.3. 2.3 Consumer
      1. 2.3.1. 2.3.1 引入依赖
      2. 2.3.2. 2.3.2 Motan XML 配置文件
      3. 2.3.3. 2.3.3 UserController
      4. 2.3.4. 2.3.4 简单测试
  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. 彩蛋