Netty学习笔记(四)——实现dubbo的rpc
1、rpc基本介绍
RPC ( Remote Procedure Call) -远程过程调用,是一个计算机通信协议。该协议允许运行于一台计算机的程序调
用另一台计算机的子程序,两个或多个应用程序分布不同的服务器上,而他们之间的调用就像调用本地方法一样
常见的 RPC框架有:比较知名的如阿里的Dubbo、google的gRPC、Go语言的rpex、Apache的thrift,Spring的Spring Cloud。
2、rpc流程

1)服务消费方(client)以本地调用方式调用服务
2)clientstub接收到调用后负责将方法、参数等封装成能够进行网络传输的消息体
3)clientstub将消息进行编码并发送到服务端
4) server stub收到消息后进行解码
5)serverstub根据解码结果调用本地的服务
6)本地服务执行并将结果返回给server stub
7) server stub将返回导入结果进行编码并发送至消费方
8)clientstub接收到消息并进行解码
9)服 务消费方(client)得到结果
3、用Netty 实现一个简单的RPC框架
需求:消费者远程调用提供者的服务,提供者返回一个字符串,消费者接受提供者返回的数据
1》创建一个公共接口
2》创建一个提供者
3》创建一个消费者
示意图:

公共接口:
//公共接口,提供者和消费者都需要
public interface HelloService { public String hello(String msg);
}
provider:服务端
实现类:
public class HelloImp implements HelloService {
    //消费方调用此方法时,返回值
    @Override
    public String hello(String msg) {
        System.out.println("收到客户端信息:"+msg);
       if(msg != null){
           return "收到客户端的信息"+msg+",我是服务端";
       }else{
           return "收到客户端的信息,我是服务端";
       }
    }
}
启动类:
public class MyServerBootstrap {
    
    public static void main(String[] args) {
      //启动服务提供者
        new MyNettyServer().startServer("127.0.0.1",7000);
    }
}
public class MyNettyServer {
    public void startServer(String host , int port){
        startServer0(host,port);
    }
    //初始化并启动服务端
    public void startServer0(String host, int port){
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workGroup = new NioEventLoopGroup();
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        serverBootstrap.group(bossGroup,workGroup)
                .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 MyServerRpcHandler());
                    }
                });
        try {
            ChannelFuture channelFuture = serverBootstrap.bind(host, port).sync();
            channelFuture.addListener((sc)->{
                if(sc.isSuccess()){
                    System.out.println("服务器启动成功");
                }else{
                    System.out.println("服务器启动失败");
                }
            });
            channelFuture.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            bossGroup.shutdownGracefully();
            workGroup.shutdownGracefully();
        }
    }
}
业务处理handler:
public class MyServerRpcHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println("收到的客户端信息:"+msg);
        String message =   msg.toString().substring(msg.toString().lastIndexOf("#")+1);
        String result = new HelloImp().hello(message);
        ctx.writeAndFlush(result);
    }
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.out.println(cause.getMessage());
        ctx.close();
    }
}
consumer:客户端
启动类:
public class MyClientBootStrap {
    private static final String providerName = "helloWorld#";
    public static void main(String[] args) {
      //创建消费者对象
        MyNettyClient consumer = new MyNettyClient();
      //创建代理对象
        HelloService helloService = (HelloService)consumer.getBean(HelloService.class, providerName);
      //通过代理对象调用方法
        String res = helloService.hello("hello 你好 服务端");
        System.out.println("从客户端返回的结果["+res+"]");
    }
}
public class MyNettyClient {
    private static MyClientRpcHandler clientRpcHandler;
    //创建自定义的线程池
    ExecutorService executorService = new ThreadPoolExecutor(
            2,
            5,
            20,
            TimeUnit.SECONDS,
            new LinkedBlockingDeque<>(3),
            new ThreadPoolExecutor.CallerRunsPolicy());
  //使用代理模式,创建一个代理对象
    public Object getBean(final Class<?> serviceClass , final String providerName){
             System.out.println("getBean。。。。被调用");
            return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),new Class<?>[]{serviceClass},(proxy, method, args) -> {
                if(clientRpcHandler == null){
                    initClient();
                }
                System.out.println("clientRpcHandler..."+clientRpcHandler);
                clientRpcHandler.setPara(providerName+args[0]);
                return executorService.submit(clientRpcHandler).get();
            });
    };
    public  void initClient(){
        clientRpcHandler  = new MyClientRpcHandler();
        EventLoopGroup 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(clientRpcHandler);
                    }
                });
        try {
            bootstrap.connect("127.0.0.1", 7000).sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
业务handler:
public class MyClientRpcHandler extends ChannelInboundHandlerAdapter implements Callable{
    private ChannelHandlerContext context;//上下文对象
    private String result;//返回的结果
    private String para;//客户端传给服务端的信息
    //与服务端连接后第一个被调用
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channelActive被调用。。。");
        context = ctx;
        System.out.println("context"+context);
    }
  //读取服务器的数据
    @Override
    public synchronized void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println("MyClientRpcHandler。。。。channelRead被调用");
        result = msg.toString();
        notify();//唤醒等待的线程
    }
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.out.println(cause.getMessage());
        ctx.close();
    }
    @Override
    public synchronized  Object call() throws Exception {
        System.out.println("call被调用。。。");
        context.writeAndFlush(para);//发送数据到服务端
        wait();//等待channelRead获取到服务端返回的结果数据后,唤醒线程
        return result;//服务端返回的数据
    }
    public  void  setPara(String para){
        System.out.println("setPara。。。被调用");
        this.para = para;
    }
}
Netty学习笔记(四)——实现dubbo的rpc的更多相关文章
- Netty学习笔记(四) 简单的聊天室功能之服务端开发
		前面三个章节,我们使用了Netty实现了DISCARD丢弃服务和回复以及自定义编码解码,这篇博客,我们要用Netty实现简单的聊天室功能. Ps: 突然想起来大学里面有个课程实训,给予UDP还是TCP ... 
- Netty 学习笔记(1)通信原理
		前言 本文主要从 select 和 epoll 系统调用入手,来打开 Netty 的大门,从认识 Netty 的基础原理 —— I/O 多路复用模型开始. Netty 的通信原理 Netty 底层 ... 
- 零拷贝详解 Java NIO学习笔记四(零拷贝详解)
		转 https://blog.csdn.net/u013096088/article/details/79122671 Java NIO学习笔记四(零拷贝详解) 2018年01月21日 20:20:5 ... 
- go微服务框架kratos学习笔记四(kratos warden-quickstart warden-direct方式client调用)
		目录 go微服务框架kratos学习笔记四(kratos warden-quickstart warden-direct方式client调用) warden direct demo-server gr ... 
- Netty 学习(四):ChannelHandler 的事件传播和生命周期
		Netty 学习(四):ChannelHandler 的事件传播和生命周期 作者: Grey 原文地址: 博客园:Netty 学习(四):ChannelHandler 的事件传播和生命周期 CSDN: ... 
- C#可扩展编程之MEF学习笔记(四):见证奇迹的时刻
		前面三篇讲了MEF的基础和基本到导入导出方法,下面就是见证MEF真正魅力所在的时刻.如果没有看过前面的文章,请到我的博客首页查看. 前面我们都是在一个项目中写了一个类来测试的,但实际开发中,我们往往要 ... 
- IOS学习笔记(四)之UITextField和UITextView控件学习
		IOS学习笔记(四)之UITextField和UITextView控件学习(博客地址:http://blog.csdn.net/developer_jiangqq) Author:hmjiangqq ... 
- java之jvm学习笔记四(安全管理器)
		java之jvm学习笔记四(安全管理器) 前面已经简述了java的安全模型的两个组成部分(类装载器,class文件校验器),接下来学习的是java安全模型的另外一个重要组成部分安全管理器. 安全管理器 ... 
- Learning ROS for Robotics Programming Second Edition学习笔记(四) indigo devices
		中文译著已经出版,详情请参考:http://blog.csdn.net/ZhangRelay/article/category/6506865 Learning ROS for Robotics Pr ... 
随机推荐
- 转载 | Python AI 教学│k-means聚类算法及应用
			关注我们的公众号哦!获取更多精彩哦! 1.问题导入 假如有这样一种情况,在一天你想去某个城市旅游,这个城市里你想去的有70个地方,现在你只有每一个地方的地址,这个地址列表很长,有70个位置.事先肯定要 ... 
- 洛谷P5506 封锁
			题目 一道模拟题,问题不是很大,主要需要读题清晰,且算法的操作顺序要搞明白,比如在每一秒的开始,所有无人机先移动,然后再一步一步操作. 然后就是判断方向是否一致了,细节还是很多的. #include ... 
- vue-cli3配置多页面入口
			假如要单独将登陆页面当成一个项目入口文件: 第一步:创建一个登陆页面的文件 在项目public文件夹下创建一个login.html,其实就是将index.html复制一份,将title改一下: 第二步 ... 
- P1108 低价购买——最长下降子序列+方案数
			P1108 低价购买 最长下降子序列不用多讲:关键是方案数: 在求出f[i]时,我们可以比较前面的f[j]; 如果f[i]==f[j]&&a[i]==a[j] 要将t[j]=0,去重: ... 
- 数据库blob图片文件,多图片打包下载
			数据库存储blob图片文件,前端打包下载 数据库图片文件实体类 package com.cmrh.mspserver.pos.dto; import java.io.Serializable; imp ... 
- zabbix—自动发现端口并监控
			自动批量检查agent开放的端口 PS:如果服务器上的应用都是固定的,不会随机产生的都可以使用自动发现端口来监控: 如果服务器会随机出现端口且每次启动程序都会改变,可以采用第二种方法,来监控指定的端 ... 
- argmin ,argmax函数
			在数学中,ARG MAX(或ARGMAX)代表最大值,即给定参数的点集,给定表达式的值达到其最大值: 换一种说法, 是f(x)具有最大值M的x的值的集合.例如,如果f(x)是1- | x |,那么它在 ... 
- python matplotlib生成图形
			y=2x+3 import matplotlib.pyplot as plt#约定俗成的写法plt #首先定义两个函数(正弦&余弦) import numpy as np #plt.figur ... 
- 4 个用于执行高级数学计算的 JavaScript 库
			在使用JavaScript执行数学方面的任务时,往往要用到浮点运算,且需要精确到某位小数,这就容易造成错误,而且会相当费时.因此,如果你需要做一些高精度的数学计算的编程工作,比如财务或科学计算,那么你 ... 
- Java IO系统--字符流
			字符流:尽管字节流提供了处理任何类型输入/输出操作的足够功能,它们补鞥呢直接操作Unicode字符.字符流层次结构的顶层是Reader和Writer抽象类.类似于InputStream和OutputS ... 
