Netty 实现dubbo rpc

一、RPC 的基本介绍

  RPC (Remote Procedure Call) 远程过程调用,是一个计算机通信协议。该协议允许运行于一台计算机的程序调用另一台计算机的子程序,而程序员无需额外的为这个交互编程。也就是说可以达到两个或者多个应用程序部署在不同的服务器上,他们之间的调用都像是本地方法调用一样。RPC 的调用如下图。

常用的RPC 框架有阿里的dubbo,Google的gRPC,Go 语言的rpcx,Apache的thrift,Spring的Spring Cloud.

若想了解dubbo与Spring Cloud的区别参考:SpringCloud 与 Dubbo 的区别,终于有人讲明白了...-腾讯云开发者社区-腾讯云

二、RPC 调用的过程

在RPC 中,Client 端叫做服务消费者,Server 叫做服务提供者。

调用流程说明

  • 服务消费方(client)以本地调用方式调用服务
  • client stub 接收到调用后负责将方法、参数等封装成能够进行网络传输的消息体
  • client stub 将消息进行编码并发送到服务端
  • server stub 接收到消息后进行解码
  • server stub 根据解码结果调用本地的服务
  • 本地服务执行并将结果返回给server stub
  • server stub 将返回导入结果进行编码并发送给消费方
  • client stub 接收到消息并进行解码
  • 服务消费方(client) 得到结果
  • 其中,RPC 框架的目标就是把2-8 这些步骤封装起来,用户无需关心这些细节,可以像调用本地方法一样即可完成远程服务调用。

三、dubbo RPC

1.需求说明

dubbo 底层使用了Netty 作为网络通信框架,要求用netty 实现一个简单的RPC框架。

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

2.设计说明

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

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

创建一个消费者,该类需要透明的调用自己不存在的方法,内部需要使用netty请求提供者返回数据。 开发的分析图如下:

3.代码实现

netty用的包:4.1.20.Final。pom.xml如下:

<dependency><groupId>io.netty</groupId><artifactId>netty-all</artifactId><version>4.1.20.Final</version>
</dependency>

1)公共接口

/*** @author: fqtang* @date: 2024/05/05/21:51* @description: 服务提供方和服务消费方都需要*/
public interface HelloService {String say(String mes);
}

2)公共接口实现类

import org.springframework.util.StringUtils;
import com.tfq.netty.dubborpc.publicinterface.HelloService;/*** @author: fqtang* @date: 2024/05/05/21:53* @description: 描述*/
public class HelloServiceImpl implements HelloService {private static int count = 0;/*** 当有消费方调用该方法时就返回一个结果** @param mes 传入消息* @return 返回结果*/@Overridepublic String say(String mes) {System.out.println("收到客户端消息=" + mes);if(StringUtils.isEmpty(mes)) {return "你好客户端,我已经收到你的消息 ";}else{return "你好客户端,我已经收到你的消息:【" + mes+"】,第 "+(++count)+"次。";}}
}

3)服务提供者

import com.tfq.netty.dubborpc.netty.NettyServer;/*** @author: fqtang* @date: 2024/05/05/21:57* @description: 启动服务提供者,就是NettyServer*/
public class ServerBootstrap {public static void main(String[] args) {String hostName="127.0.0.1";int port = 8001;NettyServer.startServer(hostName,port);}}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: fqtang* @date: 2024/05/05/21:59* @description: 描述*/
public class NettyServer {public static void startServer(String hostName,int port){startServer0(hostName,port);}/*** 编写一个方法,完成对Netty Server的初始化工作和启动* @param hostName* @param 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>() {@Overrideprotected 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("服务提供方开始提供服务~~~");channelFuture.channel().closeFuture().sync();}catch(Exception e){e.printStackTrace();}finally {bossGroup.shutdownGracefully();workerGroup.shutdownGracefully();}}
}import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import com.tfq.netty.dubborpc.consumer.ClientBootstrap;
import com.tfq.netty.dubborpc.provider.HelloServiceImpl;/*** @author: fqtang* @date: 2024/05/05/22:03* @description: 描述*/
public class NettyServerHandler extends ChannelInboundHandlerAdapter {@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {//获取客户端调用的消息,并调用服务System.out.println("msg = " + msg);//客户端在调用服务器的时候,需要定义一个协议。比如我们要求每次发消息时,都必须以某个字符器开头//比如:dubboserver#hello#xxxxif(msg.toString().startsWith(ClientBootstrap.ProtocolHeader)) {String res = new HelloServiceImpl().say(msg.toString().substring(msg.toString().lastIndexOf("#") + 1));ctx.writeAndFlush(res);}}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {ctx.close();}
}

4)消费者

import com.tfq.netty.dubborpc.netty.NettyClient;
import com.tfq.netty.dubborpc.publicinterface.HelloService;/*** @author: fqtang* @date: 2024/05/05/23:26* @description: 消费者*/
public class ClientBootstrap {/*** 这里定义协议头*/public static final String ProtocolHeader = "dubboserver#say#";public static void main(String[] args) throws InterruptedException {//创建一个消费者NettyClient customer = new NettyClient();//创建代理对象HelloService helloService = (HelloService) customer.getBean(HelloService.class, ProtocolHeader);while(true) {Thread.sleep(10 * 1000);//通过代理对象调用提供者的方法(服务)String res = helloService.say("你好 dubbo~");System.out.println("调用的结果 res = " + res);}}
}import java.lang.reflect.Proxy;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
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;/*** @author: fqtang* @date: 2024/05/05/23:04* @description: 描述*/
public class NettyClient {//创建一个线程池private static ExecutorService executorService = Executors.newFixedThreadPool(2);private static NettyClientHandler clientHandler;/*** 编写方法使用代理模式,获取一个代理对象* @param serviceClass* @param protocolHeader* @return*/public Object getBean(final Class<?> serviceClass, final String protocolHeader) {return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),new Class<?>[]{serviceClass}, (proxy, method, args) -> {if(clientHandler == null) {initClient("127.0.0.1", 8001);}//设置要发送给服务器端的信息,protocolHeader为协议头[dubboserver#hello#],//args[0] 就是客户端调用api say(???),参数clientHandler.setParam(protocolHeader + args[0]);return executorService.submit(clientHandler).get();});}private static void initClient(String hostName, int port) {EventLoopGroup worker = new NioEventLoopGroup();try {clientHandler = new NettyClientHandler();Bootstrap bootstrap = new Bootstrap();bootstrap.group(worker).channel(NioSocketChannel.class).option(ChannelOption.TCP_NODELAY, true).handler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) throws Exception {ChannelPipeline channelPipeline = ch.pipeline();channelPipeline.addLast(new StringDecoder());channelPipeline.addLast(new StringEncoder());channelPipeline.addLast(clientHandler);}});ChannelFuture channelFuture = bootstrap.connect(hostName, port).sync();/*channelFuture.channel().closeFuture().sync();*/} catch(InterruptedException e) {e.printStackTrace();} /*finally {worker.shutdownGracefully();}*/}
}package com.tfq.netty.dubborpc.netty;import java.util.concurrent.Callable;import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;/*** @author: fqtang* @date: 2024/05/05/22:48* @description: 描述*/
public class NettyClientHandler extends ChannelInboundHandlerAdapter implements Callable {private ChannelHandlerContext context;/*** 返回的结果*/private String result;/*** 客户端调用方法返回的参数*/private String param;/*** 与服务器的连接创建后,就会被调用,这个方法被第一个,调用(1)* @param ctx* @throws Exception*/@Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception {//因为在其他方法会使用到这个ctxcontext = ctx;System.out.println("调用(1) channelActive--->连接到服务器");}/***  被调用(4)* 收到服务器的数据后,调用方法* @param ctx* @param msg* @throws Exception*/@Overridepublic synchronized void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {result = (String) msg;System.out.println("调用(4)channelRead--->从服务器读取到数据:"+result);//唤醒等待的线程notify();System.out.println("调用(4)channelRead---notify()---->从服务器读取到数据后唤醒线程.....");}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {ctx.close();}/*** 被调用(3), 被调用(5)* 被代理对象调用,发送数据给服务器,--->wait ---> 等待被唤醒 --->返回结果* @return* @throws Exception*/@Overridepublic synchronized Object call() throws Exception {context.writeAndFlush(param);System.out.println("调用(3) call()--->被代理对象调用,发送数据给服务器.....");//进行wait,等待channelRead 方法获取到服务器的结果后,唤醒wait();System.out.println("调用(5) call()--->wait() 等待channelRead 方法获取到服务器的结果后.....");return result;}/*** 被调用(2)* @param param*/void setParam(String param){System.out.println("调用(2) setParam()--->被代理对象调用,发送数据给服务器.....");this.param = param;}
}

若有问题请留言。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/8935.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

HOOPS Visualize:工业级3D可视化SDK,打造移动端和PC端工程应用程序

HOOPS Visualize是一种高性能的软件开发工具包&#xff08;SDK&#xff09;&#xff0c;旨在帮助开发人员轻松构建和集成高质量的3D可视化功能。这是一种全功能的&#xff0c;以工程为重点的场景图技术&#xff0c;我们称为Core Graphics。Core Graphics可集成到一个框架中&…

webpack从零到1 构建 vue3

为什么要手写webpack 不用cli &#xff08;无的放矢&#xff09;并不是 其实是为了加深我们对webpack 的了解方便以后灵活运用webpack 的技术 初始化项目结构&#xff08;跟cli 结构保持一致&#xff09; 新建 public src 等文件夹npm init -y 创建package.json文件tsc --init…

opencv基础篇 ——(十一)常用照片处理函数

改善图像的亮度(illuminationChange) 用于改善光照条件不佳导致的图像对比度低下或局部过暗/过亮的问题。该函数通过模拟全局和局部光照变化&#xff0c;旨在提高图像的整体视觉质量&#xff0c;特别是在低光照条件下&#xff0c;使得图像中的重要细节更加清晰可见。 函数原型…

IDEA Maven项目,控制台出现乱码的问题

前言 使用idea进行maven项目的编译时&#xff0c;控制台输出中文的时候出现乱码的情况。 通常出现这样的问题&#xff0c;都是因为编码格式不一样导致的。既然是maven出的问题&#xff0c;我们在idea中查找下看可以如何设置文件编码。 第一种方式 在pom.xml文件中&#xff…

nginx--系统参数优化telenct

系统参数 在生产环境中&#xff0c;根据自己的需求在/etc/sysctl.conf来更改内核参数 net.ipv4.ip_nonlocal_bind 1 允许非本地IP地址socket监听 net.ipv4.ip_forward 1 开启IPv4转发 net.ipv4.tcp_timestamps 0 是否开启数据包时间戳 net.ipv4.tcp_tw_reuse 0 端⼝口复⽤…

企业外贸邮箱有哪些?国内五大外贸邮箱排行榜

外贸公司在进行跨国业务的时候&#xff0c;需要一个稳定安全的企业邮箱。国内的企业外贸邮箱提供商有很多&#xff0c;目前排行在前五的有Zoho Mail企业邮箱、阿里企业邮箱、网易企业邮箱、腾讯企业邮箱、新浪企业邮箱&#xff0c;今天我们就来详细了解下这些邮箱产品。 一、Z…

【yolov8 项目打包】pyinstaller 打包pyQt5 界面为exe

创建一篇博客文章&#xff0c;介绍如何使用PyInstaller将PyQt5界面打包为exe文件&#xff0c;并且处理与YOLOv8模型相关的文件&#xff0c;可以按照以下结构进行&#xff1a; 标题&#xff1a;使用PyInstaller将PyQt5界面与YOLOv8模型打包为Windows可执行文件 引言 在机器学习…

Unity Material(材质)、Texture(纹理)、Shader(着色器)简介

文章目录 一、概念二、Rendering Mode三、Main Maps三、参考文章 一、概念 Material(材质)&#xff1a;物体的“色彩”、“纹理”、“光滑度”、“透明度”、“反射率”、“折射率”、“发光度”等&#xff0c;材质的本质是shader的实例(载体)Texture(贴图)&#xff1a;附件到…

Python通过定义类实现增删改查(期末考试)

python高级编程期末测试 别看我挣的少&#xff0c;但是我省的多&#xff0c;昨天法拉利又省下两百多万。 一、通过创建自己类来实现增删改查 我们已经利用模型实现单表的增删改查了 现在 我们不想使用模型来操作数据库 我们可以自己定义模型 那么 如何通过自己创建的类实现增…

计算机SCI期刊,IF=9.657,1区TOP,2周内出版!

一、期刊名称 Neural Networks 二、期刊简介概况 期刊类型&#xff1a;SCI 学科领域&#xff1a;计算机科学 影响因子&#xff1a;7.8 中科院分区&#xff1a;1区TOP 出版方式&#xff1a;订阅模式/开放出版 版面费&#xff1a;选择开放出版需支付$3350 三、期刊简介 神…

美国原装二手keysight86122c安捷伦86122C波长计

是德KEYSIGHT 86122C 波长计 主要功能和规格 特征&#xff1a; 增强型&#xff0c;稳定的HeNe参考激光器&#xff0c;使用寿命更长 包括五年保修&#xff0c;涵盖整个仪器&#xff0c;所有零件 绝对波长精度&#xff1a;0.2 ppm 差分波长精度&#xff1a;0.15 ppm 不到0.…

Springboot+vue项目影城管理系统

摘 要 本论文主要论述了如何使用JAVA语言开发一个影城管理系统&#xff0c;本系统将严格按照软件开发流程进行各个阶段的工作&#xff0c;采用B/S架构&#xff0c;面向对象编程思想进行项目开发。在引言中&#xff0c;作者将论述影城管理系统的当前背景以及系统开发的目的&…

正点原子Linux学习笔记(六)在 LCD 上显示 jpeg 图像

在 LCD 上显示 jpeg 图像 20.1 JPEG 简介20.2 libjpeg 简介20.3 libjpeg 移植下载源码包编译源码安装目录下的文件夹介绍移植到开发板 20.4 libjpeg 使用说明错误处理创建解码对象设置数据源读取 jpeg 文件的头信息设置解码处理参数开始解码读取数据结束解码释放/销毁解码对象 …

【动态规划】子数组、子串系列I|最大子数组和|环形子数组的最大和|乘积最大子数组|乘积为正数的最长子数组长度

一、最大子数组和 最大子数组和 算法原理&#xff1a; &#x1f4a1;细节&#xff1a; 1.返回值为dp表每个位置的最大值&#xff0c;而不是只看最后一个位置&#xff0c;因为可能最后一个位置都不选 2.可以直接在填dp表的时候就进行返回值的比较 3.如果初始化选择多开一个位…

2024最新版JavaScript逆向爬虫教程-------基础篇之无限debugger的原理与绕过

目录 一、无限debugger的原理与绕过1.1 案例介绍1.2 实现原理1.3 绕过debugger方法1.3.1 禁用所有断点1.3.2 禁用局部断点1.3.3 替换文件1.3.4 函数置空与hook 二、补充2.1 改写JavaScript文件2.2 浏览器开发者工具中出现的VM开头的JS文件是什么&#xff1f; 一、无限debugger的…

520送男士内裤给男朋友好吗?五大男士内裤测评种草

相信有很多朋友都选在520这个特殊的日子里为心爱的人挑选一份特别的礼物吧&#xff01;如果送礼给男朋友或老公&#xff0c;一份实用的礼物肯定是最佳选择哦&#xff01;很多男性朋友每条内裤都穿很久&#xff0c;如果给男朋友挑选合适的男士内裤&#xff0c;也是一种关心体贴的…

[华为OD]BFS C卷 200 智能驾驶

题目&#xff1a; 有一辆汽车需要从m*n的地图的左上角(起点)开往地图的右下角(终点)&#xff0c;去往每一个地区都需 要消耗一定的油量&#xff0c;加油站可进行加油 请你计算汽车确保从起点到达终点时所需的最少初始油量说明&#xff1a; (1)智能汽车可以上下左右四个方向…

C++ 复习2 输入输出 基本数据类型

输入输出 标准输出流 ( cout ) cout 代表标准输出流&#xff0c;通常用于向屏幕输出数据。 使用操作符 << &#xff08;插入操作符&#xff09;向 cout 发送数据。 例如&#xff0c; std::cout << "Hello, world!" << std::endl; 会在屏幕上打印 …

本地搭建AI环境

本地搭建AI 这几天刚刚看到好兄弟分享的一段关于本地搭建AI的短视频&#xff0c;于是我按照视频里的讲解&#xff0c;进行了实践。感觉非常棒&#xff01;&#xff01;&#xff0c;马上整理成文字与大家分享一下。 在本地启动并运行大型语言模型&#xff0c;运行llama3、phi3…

自然语言处理(NLP)技术有哪些运用?

目录 一、自然语言处理&#xff08;NLP&#xff09;技术有哪些运用&#xff1f; 二、Python进行文本的情感分析 1、NLTK库: 2、TextBlob库: 三、错误排除 一、自然语言处理&#xff08;NLP&#xff09;技术有哪些运用&#xff1f; 自然语言处理&#xff08;NLP&#xff09…