Netty实战

Netty实战Netty 实战此处观看更加使用 Netty 实现一个简单的 RPC 框架 RPC 是什么 原理是什么网上很多大神都有在总结 我就不再重复 如果对 RPC 还不是很了解的同学不妨先去了解一下基本的概念这个实例仅仅只是学习 Netty 的一个很小的样例 实际上 它离真正的 RPC 还差得远 写这个的目的仅仅只是为了熟悉 Netty 一个真正的 RPC 至少应该具备以下的功能 注册中心网络传输序列化和反序列化动态代理负载均衡传输协议需求 模仿 dubbo 消费者和提供者约定接口和协议 消费者远程调用提供者的服务

Netty实战

此处观看更加

使用Netty实现一个简单的RPC框架

RPC是什么,原理是什么网上很多大神都有在总结,我就不再重复。如果对RPC还不是很了解的同学不妨先去了解一下基本的概念

这个实例仅仅只是学习Netty的一个很小的样例,实际上,它离真正的RPC还差得远,写这个的目的仅仅只是为了熟悉Netty

一个真正的RPC至少应该具备以下的功能:

  1. 注册中心
  2. 网络传输
  3. 序列化和反序列化
  4. 动态代理
  5. 负载均衡
  6. 传输协议

需求:

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

设计说明:

  1. 创建一个接口,定义抽象方法。用于消费者和提供者之间的约
  2. 创建一个提供者,该类需要监听消费者的请求,并按照约定返回数据
  3. 创建一个消费者,该类需要透明的调用自己不存在的方法,内部需要使用 Netty 请求提供者返回数据

1.定义Public接口

package cuit.epoch.pymjl.dubborpc.pulicinterface; / * @author Pymjl * @version 1.0 * @date 2022/4/13 15:07 / public interface HelloService { 
    / * 简单的RPC接口 * @param msg 信息 * @return String */ String sayHello(String msg); } 

2.定义服务提供者

2.1.服务端首先需要实现Public接口,以供消费者调用

package cuit.epoch.pymjl.dubborpc.provider; import cuit.epoch.pymjl.dubborpc.pulicinterface.HelloService; / * @author Pymjl * @version 1.0 * @date 2022/4/13 15:08 / public class HelloServiceImpl implements HelloService { 
    @Override public String sayHello(String msg) { 
    System.out.println("provider receive msg:" + msg); if (msg != null && !"".equals(msg)) { 
    return "Hello Client,I had already received your msg==>{" + msg + "}"; } else { 
    return "provider return msg: hello"; } } } 

2.2.定义Netty服务

因为这只是一个很简单的样例,所以编解码器选择的Netty自带的字符串编解码器

然后我们需要自定义一个Handler负责处理业务

最后将编解码器和Handler一并加入Pipeline中

  1. NettyServer
package cuit.epoch.pymjl.dubborpc.netty; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.codec.string.StringDecoder; import io.netty.handler.codec.string.StringEncoder; / * @author Pymjl * @version 1.0 * @date 2022/4/13 15:13 / public class NettyServer { 
    / * 对外暴露的方法 * * @param hostName 主机名 * @param port 端口号 */ public static void startServer(String hostName, int port) { 
    startServer0(hostName, port); } private static void startServer0(String hostName, int port) { 
    EventLoopGroup bossGroup = new NioEventLoopGroup(1); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { 
    ServerBootstrap serverBootstrap = new ServerBootstrap(); serverBootstrap.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<SocketChannel>() { 
    @Override protected void initChannel(SocketChannel ch) throws Exception { 
    ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast(new StringDecoder()); pipeline.addLast(new StringEncoder()); pipeline.addLast(new NettyServerHandler()); } }); ChannelFuture channelFuture = serverBootstrap.bind(hostName, port).sync(); System.out.println("<===========服务器启动成功,端口号:" + port + "=============>"); channelFuture.channel().closeFuture().sync(); } catch (InterruptedException e) { 
    e.printStackTrace(); } finally { 
    bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } } 
  1. 自定义NettyServerHandler,处理业务逻辑
package cuit.epoch.pymjl.dubborpc.netty; import cuit.epoch.pymjl.dubborpc.provider.HelloServiceImpl; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; / * @author Pymjl * @version 1.0 * @date 2022/4/13 15:20 / public class NettyServerHandler extends ChannelInboundHandlerAdapter { 
    / * 当有客户端连接进来的时候,调用此方法 * * @param ctx 当前连接的上下文 * @param msg 当前连接的消息 * @throws Exception 异常 */ @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { 
    //获取客户端发送的消息,并调用服务 System.out.println("服务端Handler收到客户端消息:{" + msg + "}"); //客户端在调用服务器的api时,需要遵守相应的协议 //协议:每次发的消息必须以"HelloService#hello#"开头 if (msg.toString().startsWith("HelloService#hello#")) { 
    //调用方法 String response = new HelloServiceImpl().sayHello(msg.toString(). substring(msg.toString().lastIndexOf("#") + 1)); ctx.writeAndFlush(response); } } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 
    super.exceptionCaught(ctx, cause); } } 

2.3.定义服务提供者启动类

package cuit.epoch.pymjl.dubborpc.provider; import cuit.epoch.pymjl.dubborpc.netty.NettyServer; / * 启动一个服务提供者,NettyServer * * @author Pymjl * @version 1.0 * @date 2022/4/13 15:12 / public class ServerBootStrap { 
    public static void main(String[] args) { 
    NettyServer.startServer("127.0.0.1", 8080); } } 

3.定义消费者

3.1.定义NettyClient

Client需要用到代理模式(动态代理),目的是用来屏蔽客户端调用服务端方法的底层细节

package cuit.epoch.pymjl.dubborpc.netty; import io.netty.bootstrap.Bootstrap; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; import io.netty.channel.ChannelPipeline; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.handler.codec.string.StringDecoder; import io.netty.handler.codec.string.StringEncoder; import java.lang.reflect.Proxy; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; / * @author Pymjl * @version 1.0 * @date 2022/4/13 15:50 / public class NettyClient { 
    private static ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); private static NettyClientHandler clientHandler; / * 代理模式 * @param serviceClass 接口类 * @param providerName 协议头 * @return */ public Object getBean(final Class<?> serviceClass, final String providerName) { 
    return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class<?>[]{ 
   serviceClass}, (proxy, method, args) -> { 
    if (clientHandler == null) { 
    initClient(); } //设置要发给服务端的信息 clientHandler.setParam(providerName + args[0]); return executor.submit(clientHandler).get(); }); } / * 初始化客户端 */ private static void initClient() { 
    try { 
    clientHandler = new NettyClientHandler(); NioEventLoopGroup group = new NioEventLoopGroup(); Bootstrap bootstrap = new Bootstrap(); bootstrap.group(group) .channel(NioSocketChannel.class) .option(ChannelOption.TCP_NODELAY, true) .handler(new ChannelInitializer<SocketChannel>() { 
    @Override protected void initChannel(SocketChannel ch) throws Exception { 
    ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast(new StringDecoder()); pipeline.addLast(new StringEncoder()); pipeline.addLast(clientHandler); } }); bootstrap.connect("127.0.0.1", 8080).sync(); } catch (InterruptedException e) { 
    e.printStackTrace(); } } } 

3.2.自定义NettyClientHandler

注意Handler还实现了Callable方法,call方法是被代理对象调用,发送数据给服务端,并等待服务端返回结果,所以这里要注意call方法和channelRead0方法的同步控制,当call方法发送数据之后,应当进入休眠,然后等待服务端返回结果。当服务端返回结果后,应当再唤醒call方法,完成调用

package cuit.epoch.pymjl.dubborpc.netty; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import java.util.concurrent.Callable; / * @author Pymjl * @version 1.0 * @date 2022/4/13 15:38 / public class NettyClientHandler extends ChannelInboundHandlerAdapter implements Callable<String> { 
    / * 上下文 */ private ChannelHandlerContext context; / * 返回的结果 */ private String result; / * 客户端调用服务时传入的方法参数 */ private String param; / * 被代理对象调用,发送数据给服务端,并等待服务端返回结果 * * @return result * @throws Exception 异常 */ @Override public synchronized String call() throws Exception { 
    /* 向服务端发送消息 */ context.writeAndFlush(param); //开始等待,直到服务端返回结果,被唤醒 wait(); return result; } / * 客户端连接成功后,向服务端发送消息 * * @param ctx 上下文 * @throws Exception 异常 */ @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { 
    context = ctx; } / * 客户端接收服务端返回的消息 * * @param ctx 上下文 * @param msg 消息 * @throws Exception 异常 */ @Override public synchronized void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { 
    result = msg.toString(); //唤醒等待的线程 notify(); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 
    super.exceptionCaught(ctx, cause); } public void setParam(String param) { 
    this.param = param; } } 

3.3.定义客户端启动类

package cuit.epoch.pymjl.dubborpc.customer; import cuit.epoch.pymjl.dubborpc.netty.NettyClient; import cuit.epoch.pymjl.dubborpc.pulicinterface.HelloService; / * @author Pymjl * @version 1.0 * @date 2022/4/13 16:06 / public class ClientBootStrap { 
    public static final String DEFAULT_PROVIDER_NAME = "HelloService#hello#"; public static void main(String[] args) { 
    //创建一个消费者 NettyClient customer = new NettyClient(); //创建代理对象 HelloService helloService = (HelloService) customer.getBean(HelloService.class, DEFAULT_PROVIDER_NAME); //调用代理对象的方法 String res = helloService.sayHello("hello dubboService"); System.out.println("调用的结果==>" + res); } } 

运行示例

服务端:

image-20220414121422168

客户端:

} 

}

 运行示例 服务端: ![在这里插入图片描述](https://img-blog.csdnimg.cn/img_convert/7e9d93201e0c6ff21ac65.png#pic_center) 客户端: ![image-](https://img-blog.csdnimg.cn/img_convert/4d0534e7477c5592e6ba5fef721deda3.png)OK,到这里简单的rpc调用就已经实现了。但是真正的RPC远不止这些,后面有时间我还会继续深入学习Netty以及Dubbo,通过自己diy一个RPC框架来学习这些框架的底层原理 
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/203128.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月19日 下午10:31
下一篇 2026年3月19日 下午10:31


相关推荐

  • zencart模板分析

    zencart模板分析ZenCart的模板设计说简单其实也挺简单的说复杂也比较复杂,需要一定的时间来熟悉。一旦你了解了它的结构,就会慢慢习惯了。首先要阅读常见问答部分的:如何添加、制作新模板。ZenCart的设计没有什么特别,与以前设计HTML页面是一样的。只是整个页面分成了好几个部分,并加入了php代码。(设计Zencart模板制作需要理解PHP和CSS样式定义)通常,页面分为页眉(header),页

    2022年7月27日
    12
  • Opkg安装问题[通俗易懂]

    Opkg安装问题[通俗易懂]问题1:satisfy_dependencies_for:CannotsatisfythefollowingdependenciesforXXX问题报错如下:root@OpenWrt:/etc#opkginstallkmod-i2c-coreInstallingkmod-i2c-core(3.10.49-1)toroot…Downloadinghttp://downloads.openwrt.org/barrier_breaker/14.07/ramips/mt

    2022年6月1日
    47
  • Android 浏览器分享到APP「建议收藏」

    Android 浏览器分享到APP「建议收藏」1.在配置文件添加权限&lt;uses-permissionandroid:name="com.android.launcher.permission.INSTALL_SHORTCUT"/&gt;2.在配置文件中给分享到APP的界面(Acticiy)添加intent-filter&lt;intent-filter&gt;&lt;actionandroid:name="android…

    2022年5月14日
    47
  • 测试开发工程师常见面试题(随时更新)

    测试开发工程师常见面试题(随时更新)一 功能测试用例的设计举例 一 我想要回家 让你给我买一张票 然后设计测试用例答案 1 确定需求 回家回哪 需要什么票 买什么时候的票 2 开始测试 2 1 功能测试 我去买票 买火车票 飞机票 买到票 什么时候 回来给你 2 2 可靠性测试 我去买票过程中被撞死了 票买不到怎么办 延期了 买那个点的票没了怎么办让我帮他买票的人的身份 比如是否有特殊优待 如军人 1 米

    2026年1月17日
    2
  • pycharm使用gpu运行_降低python程序cpu占用高

    pycharm使用gpu运行_降低python程序cpu占用高本人频繁在pycharm下run程序,经常终止,可能其后台运行的Python程序没有关闭,所以耗尽GPU资源。现象是占用GPU的进场ID为空,即nvidia-smi后,没有进程使用GPU,但每块GPU的内存确被使用很多。。。。。fuser-v/dev/nvidia*会发现很多Python在运行,故粗暴地kill这些进程ID就可以了。。。。。。。ID乍一看很多,杀死一两个就不剩几个了。。。。本方…

    2022年8月29日
    6
  • C语言逗号运算符_c语言逗号运算符优先级最低

    C语言逗号运算符_c语言逗号运算符优先级最低逗号也是运算符?是的,但是其实我更愿意说它是分隔符。在C语言中我们就经常使用逗号,看似逗号是非常平凡的分隔符,但是它关联到一个你必须知道但可能未曾思考的小知识:理论上,每条语句(分号结束),最终都会转换成一个值。例子1:#include<stdio.h>intmain(){ 3,4,5;//这是一条语句 //把上面这条语句的值赋值给变量a inta=(3,4,5); printf(“a=%d\n”,a);}输出结果:a=5因为a的值是整条语句的值,

    2025年7月15日
    7

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号