内容简介:简易RPC框架实现
PRC(Remote Procedure Call) 远程过程调用。通俗的讲就是程序通过 RPC
框架调用远程主机的方法就如同调用本地方法一样。 Dubbo
就是这样一个 Rpc
框架,本文主要参考Dubbo的设计思路,简易实现了Rpc框架。 本文涉及到知识点包括:
- Jdk 动态代理
- serialization 序列化
- Netty 相关
- Zookeeper 使用
1、Rpc框架
Rpc 框架一般分为三个部分,Registry(注册中心)、Provider(提供者)、Consumer(消费者)。
- Registry 服务的注册中心,可以通过zookeeper、 redis 等实现。
- Provider 服务提供者被调用方,提供服务供消费者调用
- Consumer 消费者,通过订阅相应的服务获取需要调用服务的ip和端口号调用远程provider提供的服务。
2、代理
java中常见的代理有JDK动态代理、Cglib动态代理、静态代理(ASM等字节码技术)。
2.1、JDK 代理
举个例子
@Override @SuppressWarnings("unchecked") public <T> T createProxyBean(Class<T> rpcServiceInterface) { return (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class<?>[]{rpcServiceInterface}, new AryaRpcInvocationHandler()); }
JDK代理生成代理对象主要通过 java.lang.reflect.Proxy
类的 newProxyInstance
方法。JDK代理需要被代理对象必须实现接口。
2.2、Cglib
Cglib实际上是对ASM的易用性封装,Cglib不需要目标对象必须实现某一个接口,相对JDK动态代理更加灵活。
Enhancer en = new Enhancer(); en.setSuperclass(clazz); en.setCallback(new MethodInterceptor() { @Override public Object intercept(Object arg0, Method method, Object[] args, MethodProxy arg3) throws Throwable { Object o = method.invoke(object, args); return o; } }); return en.create();
2.3、静态代理
通过字节码技术对class文件进行修改,使用和学习成本相对较高,需要对Class的文件结构以及各种符号引用有比较深的认识,才能较好的使用,因为是对字节码的修改所以相对的性能上也比动态代理要好一些。
3、序列化
我们知道数据在网络上传输都是通过二进制流的形式进行进行的。当Consumer调用Provider时传输的参数需要先进行序列化,provider接收到参数时需要进行反序列化才能拿到需要的参数数据,所以序列化的性能对RPC的调用性能有很大的影响。目前主流的序列化方式有很多包括:Kryo、Protostuff、hessian。等
Protostuff是google序列化Protosbuff的开源实现,项目中我们用到它的序列化方式
/** * @author HODO */ public class ProtostuffSerializer implements Serializer { @Override public byte[] serialize(Object object) { Class targetClass = object.getClass(); RuntimeSchema schema = RuntimeSchema.createFrom(targetClass); LinkedBuffer linkedBuffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE); return ProtostuffIOUtil.toByteArray(object, schema, linkedBuffer); } @SuppressWarnings("unchecked") @Override public <T> T deserialize(byte[] bytes, Class<T> targetClass) { RuntimeSchema schema = RuntimeSchema.createFrom(targetClass); T object = (T) schema.newMessage(); ProtostuffIOUtil.mergeFrom(bytes, object, schema); return object; } }
4、Netty
Netty是一个高性能、异步事件驱动的NIO框架,它提供了对TCP、UDP和文件传输的支持。举个例子: Netty 服务端代码
public class NettyServer { private ApplicationContext applicationContext; public NettyServer(ApplicationContext applicationContext) { this.applicationContext = applicationContext; } public void init(int port) { EventLoopGroup boss = new NioEventLoopGroup(); EventLoopGroup worker = new NioEventLoopGroup(); try { ServerBootstrap bootstrap = new ServerBootstrap(); bootstrap.group(boss, worker); bootstrap.channel(NioServerSocketChannel.class); bootstrap.option(ChannelOption.SO_BACKLOG, 1024); bootstrap.option(ChannelOption.TCP_NODELAY, true); bootstrap.childOption(ChannelOption.SO_KEEPALIVE, true); bootstrap.localAddress(port); bootstrap.childHandler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel socketChannel) throws Exception { ChannelPipeline channelPipeline = socketChannel.pipeline(); channelPipeline.addLast(new NettyServerHandler(applicationContext)); } }); ChannelFuture f = bootstrap.bind().sync(); if (f.isSuccess()) { System.out.println("Netty端口号:" + port); } f.channel().closeFuture().sync(); } catch (Exception e) { e.printStackTrace(); } finally { boss.shutdownGracefully(); worker.shutdownGracefully(); } } }
Netty 客服端代码
public class NettyClient { private int port; private String host; private final CountDownLatch countDownLatch = new CountDownLatch(1); SerializerFactory serializerFactory = new SerializerFactory(); Serializer serializer = serializerFactory.getSerialize(ProtostuffSerializer.class); public NettyClient(String host, int port) { this.port = port; this.host = host; } public NettyClient(String inetAddress) { if (inetAddress != null && inetAddress.length() != 0) { String[] strings = inetAddress.split(":"); this.host = strings[0]; this.port = Integer.valueOf(strings[1]); } } public RpcResponse invoker(RpcRequest rpcRequest) throws InterruptedException { EventLoopGroup eventLoopGroup = new NioEventLoopGroup(); try { Bootstrap bootstrap = new Bootstrap(); final NettyClientHandler clientHandler = new NettyClientHandler(); bootstrap.group(eventLoopGroup).channel(NioSocketChannel.class).option(ChannelOption.SO_KEEPALIVE, true).handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { socketChannel.pipeline().addLast(clientHandler); }}); ChannelFuture future = bootstrap.connect(new InetSocketAddress(host, port)).sync(); serializer.serialize(rpcRequest); future.channel().writeAndFlush(Unpooled.buffer().writeBytes(serializer.serialize(rpcRequest))); countDownLatch.await(); // 等待链接关闭 //future.channel().closeFuture().sync(); return clientHandler.getRpcResponse(); } finally { eventLoopGroup.shutdownGracefully(); } } public class NettyClientHandler extends ChannelInboundHandlerAdapter { private RpcResponse rpcResponse; /** * 接收 Rpc 调用结果 * * @param ctx netty 容器 * @param msg 服务端答复消息 * @throws Exception */ @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { ByteBuf buf = (ByteBuf) msg; byte[] req = new byte[buf.readableBytes()]; buf.readBytes(req); rpcResponse = serializer.deserialize(req, RpcResponse.class); countDownLatch.countDown(); } RpcResponse getRpcResponse() { return rpcResponse; } } }
5、注册中心 zookeeper
选用了 zookeeper
作为注册中心,在建议Rpc框架中提供了注册中心的扩展。只要实现 RegistryManager
接口即可。 zookeeper
常用的命令行:
1、客服端脚本连接zookeeper服务器不指定 -server
默认连接本地服务
./zkCli -service ip:port
2、创建
create [-s] [-e] path data acl
创建一个节点 -s
-e
分别指定节点的类型和特性:顺序和临时节点默认创建的是临时节点,acl用于权限控制
3、读取
ls path
只能看指定节点下的一级节点
get path
查看指定节点的数据和属性信息
4、更新
set path data [version]
可以指定更新操作是基于哪一个版本当更新的 path
不存在时报 Node does not exist
5、删除
`delete path [version]``
6、Spring 支持
在框架中还提供了两个注解 @RpcConsumer
和 RpcProvider
在项目中只要引入
<dependency> <groupId>com.yoku.arya</groupId> <artifactId>arya</artifactId> <version>1.0-SNAPSHOT</version> </dependency>
在provider端容器注入
@Bean public RpcProviderProcessor rpcProviderProcessor() { return new RpcProviderProcessor(); }
在comsumer端容器注入
@Bean public RpcConsumerProcessor rpcConsumerProcessor() { return new RpcConsumerProcessor(); }
项目完整的代码 arya github.com/hoodoly/ary…
框架使用Demo github.com/hoodoly/ary…
欢迎 star
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 简易 MVC框架 弃坑
- ByxAOP:简易 AOP 框架
- Go 实现简易 RPC 框架
- 使用反射实现简易的 RPC 框架
- Java多线程之Executor框架和手写简易的线程池
- 记一次手写简易MVC框架的过程 附源代码
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Producter 让产品从0到1
周楷雯 / 人民邮电出版社 / 2016-12-25 / CNY 69.00
这是一本以App Store首页推荐的成功App为例阐述如何完成一款App产品的设计、开发和营销的书。在这本书之后,作者的《一炷香》和《字里行间》两款产品也接连被App Store首页推荐。 《Producter 让产品从0到1》从产品的设计、产品的实现、产品的迭代、产品的营销、产品的进阶等几个角度,全面讲解了产品设计的基本原则、设计的重要性、设计的感觉、实用的设计工具、简单的iOS开发、产......一起来看看 《Producter 让产品从0到1》 这本书的介绍吧!