Netty笔记(7) - 使用Netty 模仿 Dubbo 实现简单的 远程调用
使用Netty 模仿 Dubbo 实现简单的 远程调用
使用 java的反射 动态代理 加 Netty的远程访问 实现根据接口的RPC 远程调用
定义两个公共接口:
public interface HandlerServiceOne {
String handleOne(String mes);
}
public interface HandlerServiceTwo {
String handleTwo(String mes);
}
服务端
在服务端定义实现 一个给发客户端发来的字符串加上"$"符号 一个加上"*" 号:
public class HandlerServiceOneImpl implements HandlerServiceOne {
@Override
public String handleOne(String mes) {
return"$$$$$$$" + mes + "$$$$$$$$$$$";
}
}
public class HandlerServiceTwoImpl implements HandlerServiceTwo {
@Override
public String handleTwo(String mes) {
return "*****" + mes + "*****";
}
}
启动服务端 并将两个实现类初始化 并注册进Map 中 键为 实现接口的全类名 值为 接口的实例
public class ServerBootstrap {
public static Map<String,Object> serviceMap = new HashMap();
public static void main(String[] args) {
//两个接口的全类名
String handlerServiceTwo = HandlerServiceTwo.class.getName();
String handlerServiceOne = HandlerServiceOne.class.getName();
//注册服务
serviceMap.put(handlerServiceTwo,new HandlerServiceTwoImpl());
serviceMap.put(handlerServiceOne,new HandlerServiceOneImpl());
startServer("127.0.0.1", 7000);
}
private static void startServer(String hostname, int port) {
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
io.netty.bootstrap.ServerBootstrap serverBootstrap = new io.netty.bootstrap.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("服务提供方开始提供服务~~");
channelFuture.channel().closeFuture().sync();
}catch (Exception e) {
e.printStackTrace();
}
finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
服务端处理请求的自定义Handler 根据自定义的规范 解析出调用的 接口全类名 方法名 和参数 使用 反射调用实例
public class NettyServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
//获取客户端发送的消息,并调用服务
System.out.println("msg=" + msg);
//客户端在调用服务器的api 时,我们需要定义一个协议
//比如我们要求 每次发消息是都必须以某个字符串开头 "HandlerServiceTwo#hello#你好"
String[] message = msg.toString().split("#");
String serviceName = message[0];
String methodName = message[1];
String arg = message[2];
Object service = ServerBootstrap.serviceMap.get(serviceName);
Object result = Class.forName(serviceName)
.getMethod(methodName,String.class)
.invoke(service, arg);
ctx.writeAndFlush(result);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close();
}
}
客户端
先来看看客户端自定义的 Handler
public class NettyClientHandler extends ChannelInboundHandlerAdapter implements Callable {
private ChannelHandlerContext context;//上下文
private String result; //返回的结果
private String para; //客户端调用方法时,传入的参数
//与服务器的连接创建后,就会被调用, 这个方法是第一个被调用
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
context = ctx;
}
//收到服务器的数据后,调用方法 (4)
//
@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 {
ctx.close();
}
//被代理对象调用, 发送数据给服务器,-> wait -> 等待被唤醒(channelRead) -> 返回结果 (3)-》5
@Override
public synchronized Object call() throws Exception {
context.writeAndFlush(para);
//进行wait 等待channelRead 方法获取到服务器的结果后,唤醒
wait();
//服务方返回的结果
return result;
}
void setPara(String para) {
this.para = para;
}
}
实现 Callable 的接口 重写 call 方法 , 可以将此类的实例作为一个任务线程执行 并返回 call方法返回的结果
再看看 制造客户端的Factory类 ,此类可以根据接口 返回动态代理的实现类 并持有一个 Netty的客户端 作为此动态代理的实际执行类, 每次调用 接口的代理类时,都会获取 是哪个接口的全类名 , 调用的方法和调用的参数 并拼装在一起(规则可以自定义), 作为字符串 传给Handler的属性 并执行Handler 的call方法 ,
在call方法中 将字符串发送给服务端,,但此时不能直接返回 要等到客户端发送会数据 作为返回值,所以使线程等待 并等到读取到服务端返回的数据时 再进行释放 ,再回到call方法中返回 返回值
public class ClientFactory<T> {
//创建线程池
private static ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
private Class<T> clazz;
private static String host = "127.0.0.1" ;
private static int port = 7000;
public ClientFactory(Class<T> clazz) {
this.clazz = clazz;
}
/**
* 使用动态代理模式,获取一个代理对象
*/
public T getBean() {
//创建 netty客户端 与服务端交互 并让动态代理类持有
final NettyClientHandler clientHandler = this.getClientHandler();
return (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
new Class<?>[]{clazz}, (proxy, method, args) -> {
//设置要发给服务器端的信息 接口的全类名#方法名#参数
clientHandler.setPara(this.initParams(method,args));
return executor.submit(clientHandler).get();
});
}
private String initParams(Method method,Object[] args) {
return method.getDeclaringClass().getName()+"#"+method.getName()+"#"+ args[0];
}
/**
* 初始化客户端
*/
private NettyClientHandler getClientHandler() {
NettyClientHandler nettyClientHandler = new NettyClientHandler();
//创建EventLoopGroup
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(nettyClientHandler);
}
}
);
try {
bootstrap.connect(host,port).sync();
} catch (Exception e) {
e.printStackTrace();
}
return nettyClientHandler;
}
}
调用 根据工厂类 制造两个接口的代理类 并进行调用:
public class ClientBootstrap {
public static void main(String[] args) throws Exception {
//创建代理对象
HandlerServiceTwo handlerServiceTwo = new ClientFactory<>(HandlerServiceTwo.class).getBean();
HandlerServiceOne handlerServiceOne = new ClientFactory<>(HandlerServiceOne.class).getBean();
//通过代理对象调用服务提供者的方法(服务)
System.out.println(handlerServiceTwo.handleTwo("Hello"));
System.out.println(handlerServiceOne.handleOne("Hello"));
}
}
控制台打印:

调用成功,本例有很多待优化的地方 可以自己发挥想象
Netty笔记(7) - 使用Netty 模仿 Dubbo 实现简单的 远程调用的更多相关文章
- dubbo集成zookeeper rpc远程调用
注:下面使用dubbo依赖的是zookeeper注册中心,这里没有详细的介绍.在配置之前,请自行准备好zookeeper环境. 后续如果写zookeeper的配置会补放链接 添加Gradle依赖 co ...
- Docker+Dubbo+Zookeeper实现RPC远程调用
Docker+Dubbo+Zookeeper 1.安装Docker 1.1卸载旧版本的Docker //如果Docker处于与运行状态 未运行可跳过 [root@MrADiao ~]# systemc ...
- 架构师之路-在Dubbo中开发REST风格的远程调用
架构师之路:从无到有搭建中小型互联网公司后台服务架构与运维架构 http://www.roncoo.com/course/view/ae1dbb70496349d3a8899b6c68f7d10b 概 ...
- 【Rest】在Dubbo中开发REST风格的远程调用(RESTful Remoting)
目录 概述 REST的优点 应用场景 快速入门 标准Java REST API:JAX-RS简介 REST服务提供端详解 HTTP POST/GET的实现 Annotation放在接口类还是实现类 J ...
- [转载] 基于Dubbo的Hessian协议实现远程调用
转载自http://shiyanjun.cn/archives/349.html Dubbo基于Hessian实现了自己Hessian协议,可以直接通过配置的Dubbo内置的其他协议,在服务消费方进行 ...
- 基于Dubbo的Hessian协议实现远程调用
Dubbo基于Hessian实现了自己Hessian协议,可以直接通过配置的Dubbo内置的其他协议,在服务消费方进行远程调用,也就是说,服务调用方需要使用Java语言来基于Dubbo调用提供方服务, ...
- 《精通并发与Netty》学习笔记(01 - netty介绍及环境搭建)
一.Netty介绍 Netty是由JBOSS提供的一个java开源框架.Netty提供异步的.事件驱动的网络应用程序框架和工具,用以快速开发高性能.高可靠性的网络服务器和客户端程序. ...
- Java IO学习笔记八:Netty入门
作者:Grey 原文地址:Java IO学习笔记八:Netty入门 多路复用多线程方式还是有点麻烦,Netty帮我们做了封装,大大简化了编码的复杂度,接下来熟悉一下netty的基本使用. Netty+ ...
- 我的Netty笔记
pom.xml <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w ...
- Netty学习第一节Netty的总体概况
一.Netty简介 什么是Netty? 1.高性能事件驱动,异步非阻塞的IO加载开源框架. 它是由JBoss提供,用于建立TCP等底层链接.基于Netty可以建立高性能的HTTP服务器,快速开发高性能 ...
随机推荐
- 果然,基于 VictoriaMetrics 修改而来的 VictoriaLogs 来了!!!
作者:张富春(ahfuzhang),转载时请注明作者和引用链接,谢谢! cnblogs博客 zhihu Github 公众号:一本正经的瞎扯 2022 年 6 月的时候,正在痛苦的研究 grafana ...
- 【JS 逆向百例】有道翻译接口参数逆向
逆向目标 目标:有道翻译接口参数 主页:https://fanyi.youdao.com/ 接口:https://fanyi.youdao.com/translate_o?smartresult=di ...
- Spring框架源码分析
目录 Spring核心思想 Spring源码编译 自定义实现Spring框架IOC与DI Spring源码Ioc核心模块分析 BeanDefinition整体介绍 FactoryBean接口的使用 B ...
- 【一】分布式训练---单机多卡多机多卡(飞桨paddle1.8)
1.分布式训练简介 分布式训练的核心目的: 加快模型的训练速度.通过对训练任务按照一定方法拆分分配到多个计算节点进行计算,再按照一定的方法对需要汇总的信息进行聚合,从而实现加快训练速度的目的. 1.1 ...
- 2.6 CE修改器:代码注入功能
从本关开始,各位会初步接触到CE的反汇编功能,这也是CE最强大的功能之一.在第6关的时候我们说到指针的找法,用基址定位动态地址.但这一关不用指针也可以进行修改,即使对方是动态地址,且功能更加强大.代码 ...
- Linux 系统Apache配置SSL证书
在Centos7系列系统下,配置Apache服务器,给服务器增加SSL证书功能,让页面访问是不再提示不安全,具体操作流程如下. 1.第一步首先需要安装mod_ssl模块,执行yum install - ...
- SpringBoot不再需要@Autowired来注入属性
实操部分 需要lombok依赖 在对应需要注入属性的类上添加注解 @RequiredArgsConstructor 所有需要注入的属性改为final修饰 为什么 lombok的@RequiredArg ...
- 尴尬:在Excel中为指定数据插入饼图失败
本来是非常非常简单的一个需求,即便不会,随便百度下也都有说明. 可自己却在一次紧急工作中因此耽误了时间,需求是需要插入一个饼图但因操作错误一直无法正确显示饼图数据,非常尴尬,干脆记录下这一刻. 尴尬1 ...
- 《ASP.NET Core 微服务实战》-- 读书笔记(第10章)
第 10 章 应用和微服务安全 云应用意味着应用运行所在的基础设施无法掌控,因此安全不能再等到事后再考虑,也不能只是检查清单上毫无意义的复选框 由于安全与云原生应用密切相关,本章将讨论安全话题,并用示 ...
- NC20154 [JSOI2007]建筑抢修
题目链接 题目 题目描述 小刚在玩JSOI提供的一个称之为"建筑抢修"的电脑游戏:经过了一场激烈的战斗,T部落消灭了所有z部落的入侵者.但是T部落的基地里已经有N个建筑设施受到了严 ...