所谓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. 习题9-8 Uva1632

    题意: 给你n个宝藏,然后给出他们的位置a[i]以及存在时间tim[i],如果能全部拿完,求出最短时间: 否则输出No solution 思路: 对于一段区间[i,j],你取完之后肯定是在最左端或者最 ...

  2. bzoj2149拆迁队 斜率优化dp+分治

    2149: 拆迁队 Time Limit: 10 Sec  Memory Limit: 259 MBSubmit: 397  Solved: 177[Submit][Status][Discuss] ...

  3. java开发笔记

    replace与replaceAll的区别 replace的参数是char和CharSequence,即可以支持字符的替换,也支持字符串的替换(CharSequence即字符串序列的意思,说白了也就是 ...

  4. SpringMVC 处理映射

    一.Spring MVC控制器名称处理映射 以下示例展示如何利用Spring MVC 框架使用控制器名称处理程序映射. ControllerClassNameHandlerMapping类是基于约定的 ...

  5. jsp根据参数默认选中radio

    <% int vol = (Integer)request.getAttribute("cardtype") ; %> <input type="rad ...

  6. 读书笔记-《Maven实战》-2018/4/16

    第一章:Maven简介 1:Maven:Maven原本的单词意思为"知识的积累",谷歌翻译为"行家",而作为Apache的开源项目,Maven是一个主要服务于基 ...

  7. 浅谈Log4net在项目中如何记录日志

    一    引入背景 在软件开发周期中,无论是开发中,或是测试中,或是上线后,选择合适的工具监控程序的运行状态至关重要,只有如此,才能更好地排查程序问题和检测程序性能问题等.本篇文章主要与大家分享,如何 ...

  8. Mysql 统一设置utf8字符

    无聊的关于有效配置文件路径的备忘 原来阿里云服务器的mysql 5.5 , 配置/etc/my.cnf是没有任何作用的,需要编辑/etc/mysql/my.cnf 妈的, 就是这一点让我测试了两天, ...

  9. 深入Java虚拟机(2)——Java的平台无关性

    一.平台无关性的好处 Java技术在网络环境下非常有用,其中一个关键理由是,用Java创建的可执行二进制程序,能够不加改变地运行于多个平台. 这样的平台无关性随之带来许多的好处.这将极大地减轻系统管理 ...

  10. 基于hadoop的BI架构

    BI系统,是企业利用数据驱动运营的一个典型系统.BI系统通过发掘企业运行过程中的数据,发现企业的潜在风险.为企业的各项决策提供数据支撑. 传统的BI系统通常构建于关系型数据库之上.随着企业业务量的增大 ...