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的更多相关文章

  1. Netty学习笔记(四) 简单的聊天室功能之服务端开发

    前面三个章节,我们使用了Netty实现了DISCARD丢弃服务和回复以及自定义编码解码,这篇博客,我们要用Netty实现简单的聊天室功能. Ps: 突然想起来大学里面有个课程实训,给予UDP还是TCP ...

  2. Netty 学习笔记(1)通信原理

    前言 本文主要从 select 和 epoll 系统调用入手,来打开 Netty 的大门,从认识 Netty 的基础原理 —— I/O 多路复用模型开始.   Netty 的通信原理 Netty 底层 ...

  3. 零拷贝详解 Java NIO学习笔记四(零拷贝详解)

    转 https://blog.csdn.net/u013096088/article/details/79122671 Java NIO学习笔记四(零拷贝详解) 2018年01月21日 20:20:5 ...

  4. go微服务框架kratos学习笔记四(kratos warden-quickstart warden-direct方式client调用)

    目录 go微服务框架kratos学习笔记四(kratos warden-quickstart warden-direct方式client调用) warden direct demo-server gr ...

  5. Netty 学习(四):ChannelHandler 的事件传播和生命周期

    Netty 学习(四):ChannelHandler 的事件传播和生命周期 作者: Grey 原文地址: 博客园:Netty 学习(四):ChannelHandler 的事件传播和生命周期 CSDN: ...

  6. C#可扩展编程之MEF学习笔记(四):见证奇迹的时刻

    前面三篇讲了MEF的基础和基本到导入导出方法,下面就是见证MEF真正魅力所在的时刻.如果没有看过前面的文章,请到我的博客首页查看. 前面我们都是在一个项目中写了一个类来测试的,但实际开发中,我们往往要 ...

  7. IOS学习笔记(四)之UITextField和UITextView控件学习

    IOS学习笔记(四)之UITextField和UITextView控件学习(博客地址:http://blog.csdn.net/developer_jiangqq) Author:hmjiangqq ...

  8. java之jvm学习笔记四(安全管理器)

    java之jvm学习笔记四(安全管理器) 前面已经简述了java的安全模型的两个组成部分(类装载器,class文件校验器),接下来学习的是java安全模型的另外一个重要组成部分安全管理器. 安全管理器 ...

  9. Learning ROS for Robotics Programming Second Edition学习笔记(四) indigo devices

    中文译著已经出版,详情请参考:http://blog.csdn.net/ZhangRelay/article/category/6506865 Learning ROS for Robotics Pr ...

随机推荐

  1. 数据结构实验之排序五:归并求逆序数(SDUT 3402)

    归并排序详解(戳我). 以下是搬了别人的. #include<stdio.h> #include<stdlib.h> long long sum = 0; int a[1000 ...

  2. 最大字段和&洛谷11月月赛DIV2 T1

    蒟蒻只能打一打DIV2的基础题 太卑微了 这道题的本质其实是再建一个数组,如果s串i位置是0那么就给a[i]赋值为1,表示要累加个数,如果是1那么就把a[i]赋值为-1,表示个数减一,最后求最大子段和 ...

  3. Gin-Go学习

    笔记一:Hello World https://www.cnblogs.com/tudaogaoyang/p/8056186.html 笔记二:Gin-Web框架 https://www.cnblog ...

  4. Compiling OpenCV: VTK Not Found on Ubuntu 16.04 LTS

    When installing OpenCV: /usr/bin/vtk not found libvtkRenderingPythonTkWidgets.so not found /usr/bin/ ...

  5. CentOS下载与服务器版安装(VMware)

    1. 下载 首先需要选择一个版本,因为华为云最新只提供了CentOS 7.6,所以要选择CentOS 7版本的. 官网只提供了最新的版本,而且服务器在国外,下载速度贼慢. 不过官方提供了分布在各个地区 ...

  6. webpack4-用之初体验

    众所周知,webpack进入第4个大版本已经有2个月的时间了,而且webpack团队升级更新的速度也是非常的惊人 在写下如下内容的时候webpack已经出到了4.6的版本了,剑指5.0应该是指日可待了 ...

  7. Mybatis笔记(二)

    目录 MyBatis 逆向工程 MyBatis Generator 使用 分页插件 1.下载分页插件 2.配置分页插件 3.使用分页插件 SSM整合(spring与springMVC) 1.创建web ...

  8. 网络IPC:套接字接口概述

    网络IPC:套接字接口概述 套接字接口实现了通过网络连接的不同计算机之间的进程相互通信的机制. 套接字描述符(创建套接字) 套接字是通信端点的抽象,为创建套接字,调用socket函数 #include ...

  9. Python之pygame学习绘制文字制作滚动文字

    pygame绘制文字 ✕ 今天来学习绘制文本内容,毕竟游戏中还是需要文字对玩家提示一些有用的信息. 字体常用的不是很多,在pygame中大多用于提示文字,或者记录分数等事件. 字体绘制基本分为以下几个 ...

  10. 《Linux设备驱动程序》编译LDD3的scull驱动问题总结***

    由于Linux内核版本更新的原因,LDD3(v2.6.10)提供的源码无法直接使用,下面是本人编译scull源码时出现的一些问题及解决方法.编译环境:Ubuntu 10.04 LTS(kernel v ...