Zookeeper + Netty 简单实现 RPC 框架
- 首先我们需要发网络请求进行调用,这里使用网络框架 Netty
- 然后调用之间的消息需要序列化和反序列化,这里使用序列化框架 protostuff
- 再然后呢我们需要知道服务提供方地址是多少,也就是 服务发现/ 服务注册,我们使用 Zookeeper 来管理服务,使用 Curator 框架来操作 Zookeeper
- 我们使用 Spring 来方便的管理 Bean 进行随意的注入使用,以及配置文件值注入
- 使用 lombok 来精简代码,方便快速开发
- 使用 objenesis 库来优化我们反序列化 请求 / 响应对象的速度
- 使用 cglib 来优化我们接收响应处理执行方法的速度
- 使用 commons.lang3 库中的一些常用工具类
注意:需要提前启动 Zookeeper 才能正常运行
package icu.sunnyc.rpc.demo.api;
/**
* 示例接口
* @author: houcheng
* @date: 2022/5/31 16:26:10
*/
public interface HelloService {
/**
* 示例方法
* @param name 字符串参数
* @return String
*/
String hello(String name);
/**
* 返回这个学生一年后的样子
* @param student 学生对象
* @return 一年后的学生对象
*/
Student oneYearLater(Student student);
}
<dependencies>
<!-- api -->
<dependency>
<groupId>icu.sunnyc</groupId>
<artifactId>simple-rpc-demo-api</artifactId>
<version>${project.version}</version>
</dependency>
<!-- rpc-core -->
<dependency>
<groupId>icu.sunnyc</groupId>
<artifactId>simple-rpc-core</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
在 src/main/resources 下新建 simple-rpc.properties 配置文件,此配置文件名称不能修改,写入以下内容:
# Zookeeper connection string
# 注册中心地址
registry.address=127.0.0.1:2181
# RpcServer port
# 你的 rpc 服务端启动地址
service.address=127.0.0.1:13356
package icu.sunnyc.rpc.demo.producer.impl;
import icu.sunnyc.rpc.core.annotation.RpcService;
import icu.sunnyc.rpc.demo.api.HelloService;
import icu.sunnyc.rpc.demo.api.Student;
/**
* HelloService 实现类
* @author: houcheng
* @date: 2022/5/31 16:30:43
*/
@RpcService(HelloService.class)
public class HelloServiceImpl implements HelloService {
@Override
public String hello(String name) {
return "Hello " + name;
}
@Override
public Student oneYearLater(Student student) {
System.out.println("生产者收到学生信息:" + student);
// 一年后年龄 + 1
student.setAge(student.getAge() + 1);
return student;
}
}
package icu.sunnyc.rpc.demo.producer;
import icu.sunnyc.rpc.core.RpcApplication;
/**
* RPC 服务启动类 启动自动注册服务
* @author: houcheng
* @date: 2022/5/31 16:50:03
*/
public class ProducerApplication {
public static void main(String[] args) {
RpcApplication.run(ProducerApplication.class);
}
}
<dependencies>
<!-- api -->
<dependency>
<groupId>icu.sunnyc</groupId>
<artifactId>simple-rpc-demo-api</artifactId>
<version>${project.version}</version>
</dependency>
<!-- rpc-core -->
<dependency>
<groupId>icu.sunnyc</groupId>
<artifactId>simple-rpc-core</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
在 src/main/resources 下新建 simple-rpc.properties 配置文件,此配置文件名称不能修改,写入以下内容:
# Zookeeper connection string
# 调用端如果不提供服务的话,只需要配置注册中心地址即可
registry.address=127.0.0.1:2181
package icu.sunnyc.rpc.demo.consumer;
import icu.sunnyc.rpc.core.RpcApplication;
import icu.sunnyc.rpc.core.annotation.SimpleReference;
import icu.sunnyc.rpc.demo.api.HelloService;
import icu.sunnyc.rpc.demo.api.Student;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.stereotype.Component;
import java.util.Random;
/**
* RPC 调用测试
* @author: houcheng
* @date: 2022/6/1 16:43:05
*/
@Component
public class ConsumerApplication {
@SimpleReference
private HelloService helloService;
public static void main(String[] args) {
AnnotationConfigApplicationContext context = RpcApplication.run(ConsumerApplication.class);
ConsumerApplication consumerApplication = context.getBean(ConsumerApplication.class);
// 简单参数类型调用
System.out.println(consumerApplication.sayHello("sunnyc"));
// 复杂参数类型调用
consumerApplication.callOneYearLater();
}
public String sayHello(String name) {
return helloService.hello(name);
}
public void callOneYearLater() {
Student student = new Student();
student.setName("sunnyc");
student.setAge(18);
student.setGender("male");
System.out.println("一年前的学生:" + student);
System.out.println("一年后的学生:" + helloService.oneYearLater(student));
}
}
先启动生产者,再启动消费者进行调用,不出意外消费者控制台会出现以下内容:
...省略若干日志
Hello sunnyc
一年前的学生:Student{name='sunnyc', age=18, gender='male'}
...省略调用过程的日志
一年后的学生:Student{name='sunnyc', age=19, gender='male'}