大厨小鲜——基于 Netty 自己动手实现 RPC 框架

2018-04-16 11:10:28 +08:00
 codehole

今天我们要来做一道小菜,这道菜就是 RPC 通讯框架。它使用 netty 作为原料,fastjson 序列化工具作为调料,来实现一个极简的多线程 RPC 服务框架。

我们暂且命名该 RPC 框架为 rpckids。

食用指南

在告诉读者完整的制作菜谱之前,我们先来试试这个小菜怎么个吃法,好不好吃,是不是吃起来很方便。如果读者觉得很难吃,那后面的菜谱就没有多大意义了,何必花心思去学习制作一门谁也不爱吃的大烂菜呢?

例子中我会使用 rpckids 提供的远程 RPC 服务,用于计算斐波那契数和指数,客户端通过 rpckids 提供的 RPC 客户端向远程服务传送参数,并接受返回结果,然后呈现出来。你可以使用 rpckids 定制任意的业务 rpc 服务。

斐波那契数输入输出比较简单,一个 Integer,一个 Long。 指数输入有两个值,输出除了计算结果外还包含计算耗时,以纳秒计算。之所以包含耗时,只是为了呈现一个完整的自定义的输入和输出类。

指数服务自定义输入输出类

// 指数 RPC 的输入
public class ExpRequest {
	private int base;
	private int exp;
    
    // constructor & getter & setter
}

// 指数 RPC 的输出
public class ExpResponse {

	private long value;
	private long costInNanos;

	// constructor & getter & setter
}

斐波那契和指数计算处理

public class FibRequestHandler implements IMessageHandler<Integer> {

	private List<Long> fibs = new ArrayList<>();

	{
		fibs.add(1L); // fib(0) = 1
		fibs.add(1L); // fib(1) = 1
	}

	@Override
	public void handle(ChannelHandlerContext ctx, String requestId, Integer n) {
		for (int i = fibs.size(); i < n + 1; i++) {
			long value = fibs.get(i - 2) + fibs.get(i - 1);
			fibs.add(value);
		}
		// 输出响应
		ctx.writeAndFlush(new MessageOutput(requestId, "fib_res", fibs.get(n)));
	}

}

public class ExpRequestHandler implements IMessageHandler<ExpRequest> {

	@Override
	public void handle(ChannelHandlerContext ctx, String requestId, ExpRequest message) {
		int base = message.getBase();
		int exp = message.getExp();
		long start = System.nanoTime();
		long res = 1;
		for (int i = 0; i < exp; i++) {
			res *= base;
		}
		long cost = System.nanoTime() - start;
		// 输出响应
		ctx.writeAndFlush(new MessageOutput(requestId, "exp_res", new ExpResponse(res, cost)));
	}

}

构建 RPC 服务器

RPC 服务类要监听指定 IP 端口,设定 io 线程数和业务计算线程数,然后注册斐波那契服务输入类和指数服务输入类,还有相应的计算处理器。

public class DemoServer {

	public static void main(String[] args) {
		RPCServer server = new RPCServer("localhost", 8888, 2, 16);
		server.service("fib", Integer.class, new FibRequestHandler())
			  .service("exp", ExpRequest.class, new ExpRequestHandler());
		server.start();
	}

}

构建 RPC 客户端

RPC 客户端要链接远程 IP 端口,并注册服务输出类(RPC 响应类),然后分别调用 20 次斐波那契服务和指数服务,输出结果

public class DemoClient {

	private RPCClient client;

	public DemoClient(RPCClient client) {
		this.client = client;
		// 注册服务返回类型
		this.client.rpc("fib_res", Long.class).rpc("exp_res", ExpResponse.class);
	}

	public long fib(int n) {
		return (Long) client.send("fib", n);
	}

	public ExpResponse exp(int base, int exp) {
		return (ExpResponse) client.send("exp", new ExpRequest(base, exp));
	}

	public static void main(String[] args) {
		RPCClient client = new RPCClient("localhost", 8888);
		DemoClient demo = new DemoClient(client);
		for (int i = 0; i < 20; i++) {
			System.out.printf("fib(%d) = %d\n", i, demo.fib(i));
		}
		for (int i = 0; i < 20; i++) {
			ExpResponse res = demo.exp(2, i);
			System.out.printf("exp2(%d) = %d cost=%dns\n", i, res.getValue(), res.getCostInNanos());
		}
	}

}

运行

先运行服务器,服务器输出如下,从日志中可以看到客户端链接过来了,然后发送了一系列消息,最后关闭链接走了。

server started @ localhost:8888
connection comes
read a message
read a message
...
connection leaves

再运行客户端,可以看到一些列的计算结果都成功完成了输出。

fib(0) = 1
fib(1) = 1
fib(2) = 2
fib(3) = 3
fib(4) = 5
...
exp2(0) = 1 cost=559ns
exp2(1) = 2 cost=495ns
exp2(2) = 4 cost=524ns
exp2(3) = 8 cost=640ns
exp2(4) = 16 cost=711ns
...

牢骚

本以为是小菜一碟,但是编写完整的代码和文章却将近花费了一天的时间,深感写码要比做菜耗时太多了。因为只是为了教学目的,所以在实现细节上还有好多没有仔细去雕琢的地方。如果是要做一个开源项目,力求非常完美的话。至少还要考虑一下几点。

  1. 客户端连接池
  2. 多服务进程负载均衡
  3. 日志输出
  4. 参数校验,异常处理
  5. 客户端流量攻击
  6. 服务器压力极限

如果要参考 grpc 的话,还得实现流式响应处理。如果还要为了节省网络流量的话,又需要在协议上下功夫。这一大堆的问题还是抛给读者自己思考去吧。

关注公众号「码洞」,发送「 RPC 」即可获取以上完整菜谱的 GitHub 开源代码链接

2344 次点击
所在节点    程序员
1 条回复
codehole
2018-04-16 11:12:04 +08:00
怕文章太长,看起来难受,后面的菜谱详情略去了,愿意细读的去掘金看看吧

[大厨小鲜——基于 Netty 自己动手实现 RPC 框架]( https://juejin.im/post/5ad2a99ff265da238d51264d)

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/447130

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX