所谓RPC就是远程方法调用(Remote  Process Call ),简单的来说就是通过MQ,TCP,HTTP或者自己写的网络协议来传输我要调用对方的什么接口,对方处理之后再把结果返回给我.就这么简单的一个过程。

运行时,一次客户机对服务器的RPC调用,其内部操作大致有如下十步:
1、调用客户端句柄;执行传送参数
2、调用本地系统内核发送网络消息
3、消息传送到远程主机
4、服务器句柄得到消息并取得参数
5、执行远程过程
6、执行的过程将结果返回服务器句柄
7、服务器句柄返回结果,调用远程系统内核
8、消息传回本地主机
9、客户句柄由内核接收消息
10、客户接收句柄返回的数据

之前一篇文章简单RPC之Socket实现我们通过socket通信实现了简单的RPC调用,接下来我们基于Netty来实现一个简单的RPC调用过程,当然还有很多不完善的地方,只供参考学习RPC使用。

一、首先定义消息传递的实体类

public class ClassInfo implements Serializable {

	private static final long serialVersionUID = -8970942815543515064L;

	private String className;//类名
	private String methodName;//函数名称
	private Class<?>[] types;//参数类型
	private Object[] objects;//参数列表
	public String getClassName() {
		return className;
	}
	public void setClassName(String className) {
		this.className = className;
	}
	public String getMethodName() {
		return methodName;
	}
	public void setMethodName(String methodName) {
		this.methodName = methodName;
	}
	public Class<?>[] getTypes() {
		return types;
	}
	public void setTypes(Class<?>[] types) {
		this.types = types;
	}
	public Object[] getObjects() {
		return objects;
	}
	public void setObjects(Object[] objects) {
		this.objects = objects;
	}
}

二、创建Netty操作的服务端,以及具体操作

(1)服务端

public class RPCServer {
	private int port;
	public RPCServer(int port){
		this.port = port;
	}
	public void start(){
		EventLoopGroup bossGroup = new NioEventLoopGroup();
		EventLoopGroup workerGroup = new NioEventLoopGroup();

		try {
			ServerBootstrap serverBootstrap = new ServerBootstrap().group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
					.localAddress(port).childHandler(new ChannelInitializer<SocketChannel>() {

						@Override
						protected void initChannel(SocketChannel ch) throws Exception {
		                    ChannelPipeline pipeline = ch.pipeline();
		                     pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4));
		                        pipeline.addLast(new LengthFieldPrepender(4));
		                        pipeline.addLast("encoder", new ObjectEncoder());
		                        pipeline.addLast("decoder", new ObjectDecoder(Integer.MAX_VALUE, ClassResolvers.cacheDisabled(null)));
		                        pipeline.addLast(new InvokerHandler());
						}
					}).option(ChannelOption.SO_BACKLOG, 128)
	                .childOption(ChannelOption.SO_KEEPALIVE, true);
			ChannelFuture future = serverBootstrap.bind(port).sync();
	        System.out.println("Server start listen at " + port );
	        future.channel().closeFuture().sync();
		} catch (Exception e) {
			 bossGroup.shutdownGracefully();
	         workerGroup.shutdownGracefully();
		}
	}
	public static void main(String[] args) throws Exception {
        int port;
        if (args.length > 0) {
            port = Integer.parseInt(args[0]);
        } else {
            port = 8080;
        }
        new RPCServer(port).start();
    }
}

(2)服务端操作,由服务端我们看到具体的数据传输操作是进行序列化的,具体的操作还是比较简单的,就是获取发送过来的信息,这样就可以通过反射获得类名,根据函数名和参数值,执行具体的操作,将执行结果发送给客户端。

public class InvokerHandler extends ChannelInboundHandlerAdapter {
	public static ConcurrentHashMap<String, Object> classMap = new ConcurrentHashMap<String,Object>();
	@Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ClassInfo classInfo = (ClassInfo)msg;
        Object claszz = null;
		if(!classMap.containsKey(classInfo.getClassName())){
			try {
				claszz = Class.forName(classInfo.getClassName()).newInstance();
				classMap.put(classInfo.getClassName(), claszz);
			} catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
				e.printStackTrace();
			}
		}else {
			claszz = classMap.get(classInfo.getClassName());
		}
		Method method = claszz.getClass().getMethod(classInfo.getMethodName(), classInfo.getTypes());
        Object result = method.invoke(claszz, classInfo.getObjects());
        ctx.write(result);
        ctx.flush();
        ctx.close();
    }
	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
	     cause.printStackTrace();
	     ctx.close();
	}  

}

三、客户端,通过代理机制来触发远程调用

(1)客户端,当执行具体的函数时会调用远程操作,将具体操作的类、函数及参数信息发送到服务端

public class RPCProxy {

	@SuppressWarnings("unchecked")
	public static <T> T create(Object target){

		return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(), new InvocationHandler(){

			@Override
			public Object invoke(Object proxy, Method method, Object[] args)
						throws Throwable {

				ClassInfo classInfo = new ClassInfo();
				classInfo.setClassName(target.getClass().getName());
				classInfo.setMethodName(method.getName());
				classInfo.setObjects(args);
				classInfo.setTypes(method.getParameterTypes());

				ResultHandler resultHandler = new ResultHandler();
		        EventLoopGroup group = new NioEventLoopGroup();
		        try {
		            Bootstrap b = new Bootstrap();
		            b.group(group)
		             .channel(NioSocketChannel.class)
		             .option(ChannelOption.TCP_NODELAY, true)
		             .handler(new ChannelInitializer<SocketChannel>() {
		                 @Override
		                 public void initChannel(SocketChannel ch) throws Exception {
		                	 ChannelPipeline pipeline = ch.pipeline();
                             pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4));
                             pipeline.addLast("frameEncoder", new LengthFieldPrepender(4));
                             pipeline.addLast("encoder", new ObjectEncoder());
                             pipeline.addLast("decoder", new ObjectDecoder(Integer.MAX_VALUE, ClassResolvers.cacheDisabled(null)));
                             pipeline.addLast("handler",resultHandler);
		                 }
		             });  

		            ChannelFuture future = b.connect("localhost", 8080).sync();
		            future.channel().writeAndFlush(classInfo).sync();
		            future.channel().closeFuture().sync();
		        } finally {
		            group.shutdownGracefully();
		        }
		        return resultHandler.getResponse();
			}
		});
	}
}

(2)获取远程调用返回的结果值

public class ResultHandler extends ChannelInboundHandlerAdapter {

	private Object response;  

    public Object getResponse() {
    return response;
}  

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        response=msg;
        System.out.println("client接收到服务器返回的消息:" + msg);
    }  

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.out.println("client exception is general");
    }
}

四、接口、实现类及Main操作

接口:

public interface HelloRpc {
	String hello(String name);
}

实现类:

public class HelloRpcImpl implements HelloRpc {

	@Override
	public String hello(String name) {
		return "hello "+name;
	}

}

Main操作:

public class Main {
	public static void main(String [] args){
		HelloRpc helloRpc = new HelloRpcImpl();
		helloRpc = RPCProxy.create(helloRpc);
		System.err.println(helloRpc.hello("rpc"));
	}
}

完整代码地址github

简单RPC实现之Netty实现的更多相关文章

  1. Java实现简单RPC框架(转)

    一.RPC简介 RPC,全称Remote Procedure Call, 即远程过程调用,它是一个计算机通信协议.它允许像本地服务一样调用远程服务.它可以有不同的实现方式.如RMI(远程方法调用).H ...

  2. 从零开始实现简单 RPC 框架 7:网络通信之自定义协议(粘包拆包、编解码)

    当 RPC 框架使用 Netty 通信时,实际上是将数据转化成 ByteBuf 的方式进行传输. 那如何转化呢?可不可以把 请求参数 或者 响应结果 直接无脑序列化成 byte 数组发出去? 答:直接 ...

  3. RPC笔记之初探RPC:DIY简单RPC框架

    一.什么是RPC RPC(Remote Procedure Call)即远程过程调用,简单的说就是在A机器上去调用B机器上的某个方法,在分布式系统中极其常用. rpc原理其实很简单,比较容易理解,在r ...

  4. 从零开始实现简单 RPC 框架 6:网络通信之 Netty

    网络通信的开发,就涉及到一些开发框架:Java NIO.Netty.Mina 等等. 理论上来说,类似于序列化器,可以为其定义一套统一的接口,让不同类型的框架实现,事实上,Dubbo 就是这么干的. ...

  5. PHP实现简单RPC

    1.什么是rpc RPC全称为Remote Procedure Call,翻译过来为“远程过程调用”.目前,主流的平台中都支持各种远程调用技术,以满足分布式系统架构中不同的系统之间的远程通信和相互调用 ...

  6. 简单的Java实现Netty进行通信

    使用Java搭建一个简单的Netty通信例子 看过dubbo源码的同学应该都清楚,使用dubbo协议的底层通信是使用的netty进行交互,而最近看了dubbo的Netty部分后,自己写了个简单的Net ...

  7. 从零开始实现简单 RPC 框架 2:扩展利器 SPI

    RPC 框架有很多可扩展的地方,如:序列化类型.压缩类型.负载均衡类型.注册中心类型等等. 假设框架提供的注册中心只有zookeeper,但是使用者想用Eureka,修改框架以支持使用者的需求显然不是 ...

  8. 从零开始实现简单 RPC 框架 9:网络通信之心跳与重连机制

    一.心跳 什么是心跳 在 TPC 中,客户端和服务端建立连接之后,需要定期发送数据包,来通知对方自己还在线,以确保 TPC 连接的有效性.如果一个连接长时间没有心跳,需要及时断开,否则服务端会维护很多 ...

  9. 简单RPC框架-基于Consul的服务注册与发现

    *:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: 0 !important; } /* ...

随机推荐

  1. ●CodeForces 280D k-Maximum Subsequence Sum

    题链: http://codeforces.com/problemset/problem/280/D 题解: 神题,巨恶心.(把原来的那个dp题升级为:序列带修 + 多次询问区间[l,r]内取不超过k ...

  2. 51nod 1040 最大公约数之和(欧拉函数)

    1040 最大公约数之和 题目来源: rihkddd 基准时间限制:1 秒 空间限制:131072 KB 分值: 80 难度:5级算法题   给出一个n,求1-n这n个数,同n的最大公约数的和.比如: ...

  3. spaCy is a library for advanced natural language processing in Python and Cython:spaCy 工业级自然语言处理工具

    spaCy is a library for advanced natural language processing in Python and Cython. spaCy is built on ...

  4. consul怎么在windows下安装

    1.去官网下载:https://www.consul.io/downloads.html 2.解压: 3.设置环境变量:path添加 E:\programfiles\consul: 4.cmd启动: ...

  5. Docker学习系列(三)Docker搭建gitlab的两种方式

    一.直接下载docker-ce 1.拉取gitlab/gitlab-ce Randy:~ Randy$ docker pull gitlab/gitlab-ce Using default tag: ...

  6. vue--"卡片层叠" 组件 开发小记

    背景:影城移动点餐web App增加会员卡支付功能 需求:确认订单页点击会员卡项弹出会员卡列表,多张会员卡依次叠加覆盖上一张80%的高度,点击任意卡片则改卡片置为当前卡片,只有当前卡片显示全部卡片信息 ...

  7. UIkit复习:UIContorl及子控件的剖析

    1.模块继承关系: 1.UIButton        ->UIControl  -> UIView 2.UILabel          ->UIview 3.UIImageVie ...

  8. 苹果OS系统安装Xcode方法

    打开Xcode系统,在app store 里面找到自己系统对应的可升级的Xcode版本进行下载,下载到本地后,设置存放Xcode存放的文件夹为共享文件夹. 在MAC OS共享文件夹里面找到Xcode安 ...

  9. hiredis异步接口封装并导出到Lua

    hiredis异步接口封装并导出到Lua(金庆的专栏 2017.1)hiredis 不支持 Windows, Windows 下使用 wasppdotorg / hiredis-for-windows ...

  10. Scala:访问修饰符、运算符和循环

    http://blog.csdn.net/pipisorry/article/details/52902234 Scala 访问修饰符 Scala 访问修饰符基本和Java的一样,分别有:privat ...