千家信息网

如何用Netty实现一个简单的RPC

发表于:2025-12-02 作者:千家信息网编辑
千家信息网最后更新 2025年12月02日,本篇文章为大家展示了如何用Netty实现一个简单的RPC,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。众所周知,dubbo 底层使用了 Netty 作为网络通
千家信息网最后更新 2025年12月02日如何用Netty实现一个简单的RPC

本篇文章为大家展示了如何用Netty实现一个简单的RPC,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。


众所周知,dubbo 底层使用了 Netty 作为网络通讯框架,而 Netty 的高性能我们之前也分析过源码,对他也算还是比较了解了。

今天我们就自己用 Netty 实现一个简单的 RPC 框架。

1、需求

模仿 dubbo,消费者和提供者约定接口和协议,消费者远程调用提供者,提供者返回一个字符串,消费者打印提供者返回的数据。底层网络通信使用 Netty 4.1.16。

2、设计

  1. 创建一个接口,定义抽象方法。用于消费者和提供者之间的约定。

  2. 创建一个提供者,该类需要监听消费者的请求,并按照约定返回数据。

  3. 创建一个消费者,该类需要透明的调用自己不存在的方法,内部需要使用 Netty 请求提供者返回数据。

3、 实现

1. 创建 maven 项目,导入 Netty 4.1.16。

cn.thinkinjavarpc-demo1.0-SNAPSHOT        io.netty     netty-all     4.1.16.Final   

2. 项目目录结构如下:

3. 设计接口

===============

一个简单的 hello world:

public interface HelloService {        String hello(String msg);}

4. 提供者相关实现

==================

4.1. 首先实现约定接口,用于返回客户端数据:

/** * 实现类 */public class HelloServiceImpl implements HelloService {  public String hello(String msg) {    return msg != null ? msg + " -----> I am fine." : "I am fine.";  }}

4.2. 实现 Netty 服务端和自定义 handler

启动 Netty Server 代码:

private static void startServer0(String hostName, int port) {    try {     ServerBootstrap bootstrap = new ServerBootstrap();     NioEventLoopGroup eventLoopGroup = new NioEventLoopGroup();     bootstrap.group(eventLoopGroup)         .channel(NioServerSocketChannel.class)         .childHandler(new ChannelInitializer() {            @Override           protected void initChannel(SocketChannel ch) throws Exception {             ChannelPipeline p = ch.pipeline();             p.addLast(new StringDecoder());             p.addLast(new StringEncoder());             p.addLast(new HelloServerHandler());           }         });     bootstrap.bind(hostName, port).sync();   } catch (InterruptedException e) {     e.printStackTrace();   } }

上面的代码中添加了 String类型的编解码 handler,添加了一个自定义 handler。

自定义 handler 逻辑如下:

/*** 用于处理请求数据*/public class HelloServerHandler extends ChannelInboundHandlerAdapter {  @Override public void channelRead(ChannelHandlerContext ctx, Object msg) {    // 如何符合约定,则调用本地方法,返回数据   if (msg.toString().startsWith(ClientBootstrap.providerName)) {     String result = new HelloServiceImpl()         .hello(msg.toString().substring(msg.toString().lastIndexOf("#") + 1));     ctx.writeAndFlush(result);   } }}

这里显示判断了是否符合约定(并没有使用复杂的协议,只是一个字符串判断),然后创建一个具体实现类,并调用方法写回客户端。为什么Netty这么火?为什么?

还需要一个启动类:

public class ServerBootstrap {  public static void main(String[] args) {   NettyServer.startServer("localhost", 8088); }}

好,关于提供者的代码就写完了,主要就是创建一个 netty 服务端,实现一个自定义的 handler,自定义 handler 判断是否符合之间的约定(算是协议吧),如果符合,就创建一个接口的实现类,并调用他的方法返回字符串。

5. 消费者相关实现

消费者有一个需要注意的地方,就是调用需要透明,也就是说,框架使用者不用关心底层的网络实现。这里我们可以使用 JDK 的动态代理来实现这个目的。

思路:客户端调用代理方法,返回一个实现了 HelloService 接口的代理对象,调用代理对象的方法,返回结果。

我们需要在代理中做手脚,当调用代理方法的时候,我们需要初始化 Netty 客户端,还需要向服务端请求数据,并返回数据。

5.1. 首先创建代理相关的类

public class RpcConsumer {  private static ExecutorService executor = Executors     .newFixedThreadPool(Runtime.getRuntime().availableProcessors());  private static HelloClientHandler client;  /**  * 创建一个代理对象  */ public Object createProxy(final Class serviceClass,      final String providerName) {    return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),        new Class[]{serviceClass}, (proxy, method, args) -> {          if (client == null) {           initClient();         }          // 设置参数         client.setPara(providerName + args[0]);          return executor.submit(client).get();       }); }  /**  * 初始化客户端  */ private static void initClient() {   client = new HelloClientHandler();   EventLoopGroup group = new NioEventLoopGroup();   Bootstrap b = new Bootstrap();   b.group(group)       .channel(NioSocketChannel.class)       .option(ChannelOption.TCP_NODELAY, true)       .handler(new ChannelInitializer() {          @Override         public void initChannel(SocketChannel ch) throws Exception {           ChannelPipeline p = ch.pipeline();           p.addLast(new StringDecoder());           p.addLast(new StringEncoder());           p.addLast(client);         }       });    try {     b.connect("localhost", 8088).sync();   } catch (InterruptedException e) {     e.printStackTrace();   } }}

该类有 2 个方法,创建代理和初始化客户端。

初始化客户端逻辑:创建一个 Netty 的客户端,并连接提供者,并设置一个自定义 handler,和一些 String 类型的编解码器。

创建代理逻辑:使用 JDK 的动态代理技术,代理对象中的 invoke 方法实现如下:如果 client 没有初始化,则初始化 client,这个 client 既是 handler ,也是一个 Callback。将参数设置进 client ,使用线程池调用 client 的 call 方法并阻塞等待数据返回。

看看 HelloClientHandler 的实现:

public class HelloClientHandler extends ChannelInboundHandlerAdapter implements Callable {  private ChannelHandlerContext context;  private String result;  private String para;  @Override public void channelActive(ChannelHandlerContext ctx) {   context = ctx; }  /**  * 收到服务端数据,唤醒等待线程  */ @Override public synchronized void channelRead(ChannelHandlerContext ctx, Object msg) {   result = msg.toString();   notify(); }  /**  * 写出数据,开始等待唤醒  */ @Override public synchronized Object call() throws InterruptedException {   context.writeAndFlush(para);   wait();    return result; }  void setPara(String para) {    this.para = para; }}

该类缓存了 ChannelHandlerContext,用于下次使用,有两个属性:返回结果和请求参数。

当成功连接后,缓存 ChannelHandlerContext,当调用 call 方法的时候,将请求参数发送到服务端,等待。当服务端收到并返回数据后,调用 channelRead 方法,将返回值赋值个 result,并唤醒等待在 call 方法上的线程。此时,代理对象返回数据。

再看看设计的测试类:

public class ClientBootstrap {  public static final String providerName = "HelloService#hello#";  public static void main(String[] args) throws InterruptedException {   RpcConsumer consumer = new RpcConsumer();    // 创建一个代理对象   HelloService service = (HelloService) consumer       .createProxy(HelloService.class, providerName);    for (; ; ) {     Thread.sleep(1000);     System.out.println(service.hello("are you ok ?"));   } }}

测试类首先创建了一个代理对象,然后每隔一秒钟调用代理的 hello 方法,并打印服务端返回的结果。

测试结果

成功打印。

看了这么久的 Netty 源码,我们终于实现了一个自己的 Netty 应用,虽然这个应用很简单,甚至代码写的有些粗糙,但功能还是实现了,RPC 的目的就是允许像调用本地服务一样调用远程服务,需要对使用者透明,于是我们使用了动态代理。并使用 Netty 的 handler 发送数据和响应数据,完成了一次简单的 RPC 调用。

上述内容就是如何用Netty实现一个简单的RPC,你们学到知识或技能了吗?如果还想学到更多技能或者丰富自己的知识储备,欢迎关注行业资讯频道。

代理 数据 方法 提供者 服务 客户 客户端 消费者 消费 对象 约定 接口 代码 参数 就是 结果 动态 字符 字符串 底层 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 软件开发笔记本电脑 海康服务器默认密码 调整网络安全工作领导小组的通知 省监狱警察网络安全管理是省直吗 成都旭呈网络技术有限责任公司 合肥ios系统软件开发 服务器上架管理 网络安全践于 网络安全与管理的基本概念 网络安全教育宣传新闻稿 服务器怎么实时查共享文件 衢州学软件开发需要学什么 交通运输网络安全培训什么 ebsco数据库的评价 服务器系统有那些 潮汕正规软件开发公司 服务器大小写区别 数据库数据有重复怎么设置主键 软件开发企业的职业 服务器提示安全密码 吉林潮流软件开发服务推广 软件开发公司一般年度如何考核 软件开发哪里最多 贵州项目软件开发排行榜 部队网络安全利与弊的总结 农村邮政物流供应链数据库 ftp服务器应用协议有哪些 河北erp软件开发服务介绍 服务器系统如何开通远程协助 青岛市财政局局网络安全评估
0