总阅读量:次

摘要: 原创出处 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 项目

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

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

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

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

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

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

public class UserDTO implements Serializable {

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

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

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

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

1 创建 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 相关依赖。

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

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

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

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

2.2.2 UserRpcServiceImpl

cn.iocoder.springboot.lab63.rpc.service 包下,创建 Motan 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 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">

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

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

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

</beans>

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

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

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

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

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

}

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

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

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

2.2.5 简单测试

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

ProviderApplication 启动日志

2 我们来使用 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">

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

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

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

</beans>

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

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

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

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

ConsumerApplication 启动日志

2 我们来使用 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&

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

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

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

项目修改点

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

2 删除 <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 项目,修改如下图所示:

项目修改点

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

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

3.4 简单测试

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

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

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

3 使用浏览器,访问 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 示例项目哟!

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