-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
yukun
committed
May 30, 2021
1 parent
876ae67
commit f571f06
Showing
81 changed files
with
4,112 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,69 @@ | ||
# seven-rpc | ||
基于Spring+Netty框架构建的简版RPC个人框架 | ||
|
||
#### 介绍 | ||
|
||
``` | ||
用7这个数字,是因为我儿子是七夕生的。开源的rpc框架有很多,公司的、个人的,优秀的很多。那为什么我又要写个呢? | ||
因为spring、springboot、因为分布式服务协调器zk、因为nio,或者说是因为netty。看了spring的源码,也clone了项目 | ||
调了很长一段时间,总想上手一个"框架"来练练手,期间也手写过简易版的mybaits、spring-mvc框架,很不过瘾。用netty | ||
也编码了一段时间,总感觉少了点什么,于是乎,便决定利用平时空闲之余,串联下几大技术,自己也写一个rpc框架,切身感受 | ||
一下站在巨人肩膀上撸代码的兴奋和快感! | ||
首先一个rpc框架基本该有的功能都得有吧,其次模块得鲜明、代码思路得清晰,让人一看就懂:“哦吼,原来 | ||
Spring的BeanPostProcessor可以用的这么嗨皮啊;原来反射写起来也可以让人爱不释手啊;原来动态代理的时机可以结合bean的 | ||
生命周期来玩啊;原来接口类型的bean注入时也是可以往ioc容器里注册beandefinition的啊;原来FactoryBean是这样用的啊; | ||
原来netty的ByteBuf居然这么好用的啊;原来消息编·解码器这么写就可以解决TCP的拆包或粘包的问题啊;原来netty api用起来 | ||
是如此轻松嗨皮的啊;原来用zk搭建一个rpc服务的注册与发现中心只需一个工具类和为数不多行的代码就可以轻松办到的啊...etc” | ||
``` | ||
|
||
#### 软件架构 | ||
|
||
![Seven-Rpc架构图-简](https://gitee.com/appleyk/seven-rpc/raw/master/src/main/resources/static/images/%E6%9E%B6%E6%9E%841.png) | ||
|
||
![Seven-Rpc架构图-详](https://gitee.com/appleyk/seven-rpc/raw/master/src/main/resources/static/images/%E6%9E%B6%E6%9E%842.jpg) | ||
|
||
|
||
|
||
#### 安装`使用教程 | ||
|
||
1. git clone https://gitee.com/appleyk/seven-rpc | ||
2. idea导入父pom,配置maven,并下载相关依赖 | ||
3. idea编译项目,检查是否报错 | ||
4. 运行rpc-use-case模块中对应的案例启动类即可 | ||
5. 具体使用可参考博客:https://blog.csdn.net/Appleyk/article/details/117392129 | ||
|
||
#### 模块说明 | ||
|
||
1. rpc-common : 定义通用工具类和netty消息编`解码器 | ||
2. rpc-core : 定义核心业务模型和公共自定义注解re | ||
3. rpc-registry: 服务注册与发现中心模块,主要是基于zookeeper curator框架实现的 | ||
4. rpc-client : rpc-netty客户端模块,主要对接口进行动态代理,并封装请求,与rpc-netty服务端建立连接并进行通信 | ||
5. rpc-server : rpc-netty服务端模块,主要对接口实现模块中的可用服务进行一个注册,信息写入到zk中,同时启动一个netty服务,对客户端连接进行监听 | ||
6. rpc-use-case : rpc功能测试用例模块,其又分为3个模块 | ||
6.1. rpc-demo-api: 定义公共接口 | ||
6.2. rpc-demo-consumer: 服务消费方,主要用来测试rpc服务能力的,依赖rpc-demo-api模块 | ||
6.3. rpc-demo-provider: 服务提供方,用来满足rpc接口功能实现的,依赖rpc-demo-api模块 | ||
|
||
#### 参与贡献 | ||
|
||
1. Fork 本仓库 | ||
2. 新建 Feat_xxx 分支 | ||
3. 提交代码 | ||
4. 新建 Pull Request | ||
5. 如果你提交的request对项目有用,我会merge的 | ||
|
||
|
||
#### 番外 | ||
|
||
1. Spring·Bean全生命周期(简图) | ||
|
||
![Spring·Bean完整生命周期](https://gitee.com/appleyk/seven-rpc/raw/master/src/main/resources/static/images/Spring%C2%B7Bean%E5%AE%8C%E6%95%B4%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F.png) | ||
|
||
2. ZK注册的服务和API节点 | ||
|
||
![ZK服务节点](https://gitee.com/appleyk/seven-rpc/raw/master/src/main/resources/static/images/ZK%E6%B3%A8%E5%86%8C%E7%9A%84%E6%9C%8D%E5%8A%A1%E5%92%8CAPI%E8%8A%82%E7%82%B9.jpg) | ||
|
||
3. Netty Reactor模型 | ||
|
||
![Netty Reactor设计模型](https://gitee.com/appleyk/seven-rpc/raw/master/src/main/resources/static/images/netty%C2%B7reactor.png) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
<?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"> | ||
|
||
<modelVersion>4.0.0</modelVersion> | ||
<groupId>com.appleyk</groupId> | ||
<artifactId>seven-rpc</artifactId> | ||
<packaging>pom</packaging> | ||
<version>0.1.1-SNAPSHOT</version> | ||
<description>手写RPC框架·父工程</description> | ||
<modules> | ||
<module>rpc-common</module> | ||
<module>rpc-registry</module> | ||
<module>rpc-client</module> | ||
<module>rpc-server</module> | ||
<module>rpc-core</module> | ||
<module>rpc-use-case</module> | ||
</modules> | ||
|
||
<properties> | ||
|
||
<java.version>1.8</java.version> | ||
<jdk.version>1.8</jdk.version> | ||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | ||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> | ||
<java.version>1.8</java.version> | ||
<skipTests>true</skipTests> | ||
|
||
<!--定义版本--> | ||
<springboot.version>2.4.0</springboot.version> | ||
<spring.framework.version>5.3.1</spring.framework.version> | ||
<jackson.version>2.11.0</jackson.version> | ||
<netty.version>4.1.42.Final</netty.version> | ||
<lombok.version>1.18.6</lombok.version> | ||
<mysql8.version>8.0.22</mysql8.version> | ||
<curator.version>5.1.0</curator.version> | ||
<protostuff.version>1.1.6</protostuff.version> | ||
<objenesis.version>3.2</objenesis.version> | ||
|
||
</properties> | ||
|
||
|
||
<!--第三方依赖包统一管理--> | ||
<dependencyManagement> | ||
|
||
<dependencies> | ||
|
||
<dependency> | ||
<groupId>org.springframework</groupId> | ||
<artifactId>spring-context</artifactId> | ||
<version>${spring.framework.version}</version> | ||
</dependency> | ||
|
||
<!-- Spring Boot相关依赖 --> | ||
<dependency> | ||
<groupId>org.springframework.boot</groupId> | ||
<artifactId>spring-boot-starter</artifactId> | ||
<version>${springboot.version}</version> | ||
</dependency> | ||
|
||
<!-- Spring Boot相关Web依赖 --> | ||
<dependency> | ||
<groupId>org.springframework.boot</groupId> | ||
<artifactId>spring-boot-starter-web</artifactId> | ||
<version>${springboot.version}</version> | ||
</dependency> | ||
|
||
<!--SpringBoot自动装配--> | ||
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-autoconfigure --> | ||
<dependency> | ||
<groupId>org.springframework.boot</groupId> | ||
<artifactId>spring-boot-autoconfigure</artifactId> | ||
<version>${springboot.version}</version> | ||
</dependency> | ||
|
||
<!--配置类属性在配置文件中自动提示(需编译后才可以看到最新更改的效果)--> | ||
<dependency> | ||
<groupId>org.springframework.boot</groupId> | ||
<artifactId>spring-boot-configuration-processor</artifactId> | ||
<version>${springboot.version}</version> | ||
</dependency> | ||
|
||
<!--参数验证--> | ||
<dependency> | ||
<groupId>org.springframework.boot</groupId> | ||
<artifactId>spring-boot-starter-validation</artifactId> | ||
<version>${springboot.version}</version> | ||
</dependency> | ||
|
||
<!--springboot单元测试--> | ||
<dependency> | ||
<groupId>org.springframework.boot</groupId> | ||
<artifactId>spring-boot-starter-test</artifactId> | ||
<version>${springboot.version}</version> | ||
<scope>test</scope> | ||
</dependency> | ||
|
||
<!--spring aop--> | ||
<dependency> | ||
<groupId>org.springframework.boot</groupId> | ||
<artifactId>spring-boot-starter-aop</artifactId> | ||
<version>${springboot.version}</version> | ||
</dependency> | ||
|
||
<!-- jackson,对象正反序列化,比起阿里fastJson,还是使用这个吧 --> | ||
<dependency> | ||
<groupId>com.fasterxml.jackson.core</groupId> | ||
<artifactId>jackson-databind</artifactId> | ||
<version>${jackson.version}</version> | ||
</dependency> | ||
|
||
<!-- https://mvnrepository.com/artifact/io.netty/netty-all --> | ||
<dependency> | ||
<groupId>io.netty</groupId> | ||
<artifactId>netty-all</artifactId> | ||
<version>${netty.version}</version> | ||
</dependency> | ||
|
||
<!--通过简单注解消除臃肿的重复性的代码工作,比如getter、setter、constructor等--> | ||
<dependency> | ||
<groupId>org.projectlombok</groupId> | ||
<artifactId>lombok</artifactId> | ||
<version>${lombok.version}</version> | ||
</dependency> | ||
|
||
<!--mysql驱动包--> | ||
<dependency> | ||
<groupId>mysql</groupId> | ||
<artifactId>mysql-connector-java</artifactId> | ||
<version>${mysql8.version}</version> | ||
</dependency> | ||
|
||
<!-- zookeeper 客户端 --> | ||
<dependency> | ||
<groupId>org.apache.curator</groupId> | ||
<artifactId>curator-framework</artifactId> | ||
<version>${curator.version}</version> | ||
</dependency> | ||
|
||
<!-- 高性能序列化工具:protostuff ,基于Google PBF,省去了手动编译*.proto文件的过程--> | ||
<dependency> | ||
<groupId>com.dyuproject.protostuff</groupId> | ||
<artifactId>protostuff-core</artifactId> | ||
<version>${protostuff.version}</version> | ||
</dependency> | ||
|
||
<dependency> | ||
<groupId>com.dyuproject.protostuff</groupId> | ||
<artifactId>protostuff-runtime</artifactId> | ||
<version>${protostuff.version}</version> | ||
</dependency> | ||
|
||
<!-- https://mvnrepository.com/artifact/org.objenesis/objenesis --> | ||
<dependency> | ||
<groupId>org.objenesis</groupId> | ||
<artifactId>objenesis</artifactId> | ||
<version>${objenesis.version}</version> | ||
</dependency> | ||
|
||
</dependencies> | ||
|
||
</dependencyManagement> | ||
</project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
<?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>seven-rpc</artifactId> | ||
<groupId>com.appleyk</groupId> | ||
<version>0.1.1-SNAPSHOT</version> | ||
</parent> | ||
|
||
<modelVersion>4.0.0</modelVersion> | ||
<artifactId>rpc-client</artifactId> | ||
<description>rpc客户端,实现代理转发、连接(客户端也可以做服务注册)</description> | ||
|
||
<dependencies> | ||
<dependency> | ||
<groupId>com.appleyk</groupId> | ||
<artifactId>rpc-common</artifactId> | ||
<version>${project.version}</version> | ||
</dependency> | ||
<dependency> | ||
<groupId>com.appleyk</groupId> | ||
<artifactId>rpc-registry-api</artifactId> | ||
<version>${project.version}</version> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.springframework.boot</groupId> | ||
<artifactId>spring-boot-starter-aop</artifactId> | ||
</dependency> | ||
</dependencies> | ||
|
||
<build> | ||
<finalName>${artifactId}</finalName> | ||
<plugins> | ||
<plugin> | ||
<groupId>org.apache.maven.plugins</groupId> | ||
<artifactId>maven-compiler-plugin</artifactId> | ||
<version>3.3</version> | ||
<configuration> | ||
<source>1.8</source> | ||
<target>1.8</target> | ||
</configuration> | ||
</plugin> | ||
</plugins> | ||
</build> | ||
|
||
</project> |
103 changes: 103 additions & 0 deletions
103
rpc-client/src/main/java/com/appleyk/rpc/client/RpcClient.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
package com.appleyk.rpc.client; | ||
|
||
import com.appleyk.rpc.common.util.*; | ||
import com.appleyk.rpc.core.model.result.RpcRequest; | ||
import com.appleyk.rpc.core.model.result.RpcResponse; | ||
import io.netty.bootstrap.Bootstrap; | ||
import io.netty.channel.*; | ||
import io.netty.channel.nio.NioEventLoopGroup; | ||
import io.netty.channel.socket.nio.NioSocketChannel; | ||
|
||
import java.net.InetAddress; | ||
import java.util.Date; | ||
|
||
/** | ||
* <p>Rpc客户端 (负责构建netty客户端启动类,向rpc服务端发送请求)</p> | ||
* Rpc客户端通道处理器,主要负责和服务端建立通信,发送请求然后接收服务端发回来的响应对象并进行处理 | ||
* @author appleyk | ||
* @version V.0.1.1 | ||
* @blob https://blog.csdn.net/appleyk | ||
* @github https://github.com/kobeyk | ||
* @date created on 16:19 2021/5/19 | ||
*/ | ||
public class RpcClient extends SimpleChannelInboundHandler<RpcResponse> { | ||
|
||
private String ip; | ||
private int port; | ||
private RpcResponse response; | ||
|
||
public RpcClient(String ip, int port) { | ||
this.ip = ip; | ||
this.port = port; | ||
} | ||
|
||
public RpcClient(int port) { | ||
this.ip = "127.0.0.1"; | ||
this.port = port; | ||
} | ||
|
||
/** | ||
* 发送(API)请求 | ||
* @param request rpc封装的请求对象 | ||
* @return rpc封装的请求响应对象 | ||
*/ | ||
public RpcResponse send(RpcRequest request){ | ||
/*默认cpu核数*2*/ | ||
EventLoopGroup worker = new NioEventLoopGroup(); | ||
try{ | ||
Bootstrap client = new Bootstrap(); | ||
client.group(worker) | ||
.channel(NioSocketChannel.class) | ||
.handler(new RpcClientChannelInitializer(this)); | ||
/*等待异步执行完,拿到异步结果(变异步为同步)*/ | ||
ChannelFuture channelFuture = client.connect(ip, port).sync(); | ||
System.out.println("Rpc-Client,已与服务器建立连接,接下来准备发送数据..."); | ||
/*获取客户端和服务端的通道*/ | ||
Channel channel = channelFuture.channel(); | ||
/*往通道里发送数据 -- RpcRequest对象*/ | ||
channel.writeAndFlush(request); | ||
channel.closeFuture().sync(); | ||
}catch (InterruptedException e){ | ||
response.setE(e); | ||
response.setDate(new Date()); | ||
}finally { | ||
System.out.println("#响应的结果:"+ JsonUtils.parserJson(response)); | ||
/*关闭线程池及清理内存空间,只发一次请求,不管失败还是异常,结果return之前,都要把线程池给关闭了*/ | ||
worker.shutdownGracefully(); | ||
return response; | ||
} | ||
} | ||
|
||
@Override | ||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { | ||
Channel serverChannel = ctx.channel(); | ||
LoggerUtils.error("服务端:{}发送来的响应处理异常,{}\n",serverChannel.remoteAddress(),cause.getMessage()); | ||
/*关闭通道处理器上下文*/ | ||
ctx.close(); | ||
} | ||
|
||
/**接收解码器的结果,并对响应做出处理 -- 读取*/ | ||
@Override | ||
protected void channelRead0(ChannelHandlerContext ctx, RpcResponse response) throws Exception { | ||
System.out.printf("接收来自服务端:{%s}的响应,时间:%s\n",ctx.channel().remoteAddress(), DateUtils.dateNow2Str()); | ||
this.response = response; | ||
/*如果响应成功返回,则断开与server端的通道*/ | ||
ctx.close(); | ||
} | ||
|
||
/**简单测试*/ | ||
public static void main(String[] args) throws Exception{ | ||
InetAddress addr = InetAddress.getLocalHost(); | ||
String ip = addr.getHostAddress(); | ||
RpcClient client = new RpcClient(GeneralUtils.isEmpty(ip) ? "127.0.0.1":ip,8077); | ||
RpcRequest request = RpcRequest.builder() | ||
.requestId(IdUtils.getId()) | ||
.interfaceName("com.appleyk.rpc.api.CacheService") | ||
.type("mongodb") | ||
.methodName("save") | ||
.parameterTypes(new Class[]{String.class, Object.class}) | ||
.parameters(new Object[]{"name", "Appleyk"}) | ||
.build(); | ||
client.send(request); | ||
} | ||
} |
Oops, something went wrong.