简易RPC框架实现

写在最前面

PRC(Remote Procedure Call) 远程过程调用。通俗的讲就是程序通过RPC框架调用远程主机的方法就如同调用本地方法一样。Dubbo就是这样一个Rpc框架,本文主要参考Dubbo的设计思路,简易实现了Rpc框架。 本文涉及到知识点包括:

  • Jdk 动态代理
  • serialization 序列化
  • Netty 相关
  • Zookeeper 使用

1、Rpc框架

Rpc 框架一般分为三个部分,Registry(注册中心)、Provider(提供者)、Consumer(消费者)。

  1. Registry 服务的注册中心,可以通过zookeeper、redis等实现。
  2. Provider 服务提供者被调用方,提供服务供消费者调用
  3. 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 {@Overridepublic 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")@Overridepublic <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>() {@Overridepublic 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>() {@Overrideprotected 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*/@Overridepublic 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 支持

在框架中还提供了两个注解@RpcConsumerRpcProvider 在项目中只要引入

    <dependency><groupId>com.yoku.arya</groupId><artifactId>arya</artifactId><version>1.0-SNAPSHOT</version></dependency>
复制代码

在provider端容器注入

  @Beanpublic RpcProviderProcessor rpcProviderProcessor() {return new RpcProviderProcessor();}
复制代码

在comsumer端容器注入

  @Beanpublic RpcConsumerProcessor rpcConsumerProcessor() {return new RpcConsumerProcessor();}
复制代码

项目完整的代码 arya github.com/hoodoly/ary…

框架使用Demo github.com/hoodoly/ary…

欢迎 star

联系方式:gunriky@163.com 有问题可以直接联系

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/539628.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

kafka java获取topic_通过编程方式获取Kafka中Topic的Metadata信息

如果我们需要通过编程的方式来获取到TopicMetadataRequest请求到 def findLeader(topic: String): Unit {val consumer connect("www.iteblog.com", 9092)val req TopicMetadataRequest(TopicMetadataRequest.CurrentVersion,0, kafkaGroupId, List(topic))val to…

redis java 遍历key_java遍历读取整个redis数据库实例

redis提供了灵活的数据查询方式&#xff0c;最牛的就是key的搜索支持正则表达式。jedis.keys(“*”);表示搜索所有keyjedis.keys(“abc*”)表示搜索开头为abc的key数据遍历了key就能遍历到value。其实就是一个setRedisDO rd new RedisDO();rd.open();Set s rd.jedis.keys(&qu…

js学习

为什么80%的码农都做不了架构师&#xff1f;>>> /* my code */ var gArrSpell [ 1111, 2222, 3333, 4444, 5555, 6666, 7777, 8888, 999999, AAAA, bbbb, cccc, dddd, eeee, fffff ];var gArrSplDmg [11,12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24…

代码在eclipse下不报错,在doc命令行下报错--jar file和runable jar file

今天开发一个小工具&#xff0c;引用了Log4j&#xff0c;来记录日志&#xff0c;在eclipse下运行&#xff0c;代码正常&#xff0c;打包成jar放到doc命令行下运行报错&#xff1a; Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/logging/…

gradle java ide_使用Gradle构建Java项目

使用Gradle构建Java项目这个手册将通过一个简单的Java项目向大家介绍如何使用Gradle构建Java项目。我们将要做什么&#xff1f;我们将在这篇文档航中创建一个简单的Java项目&#xff0c;然后使用Gradle构建它。需要准备什么&#xff1f;预留15分钟空闲时间一件称手的兵器(你最喜…

小马激活软件下载,当心伪小马,有病毒

官方的小马激活软件已经停止更新了&#xff0c;下文是官方停更公告。 http://www.pccppc.com/xiaomajihuo-html 所以小马oem7以后的都不是官方的&#xff0c;包含病毒。重装系统后&#xff0c;一般是先激活系统&#xff0c;再安装杀毒软件&#xff0c;这就给“伪小马激活工具”…

truncate,delete,drop之间的区别

TRUNCATE TABLE 在功能上与不带 WHERE 子句的 DELETE 语句相同&#xff1a;二者均删除表中的全部行。 但 TRUNCATE TABLE 比 DELETE 速度快&#xff0c;且使用的系统和事务日志资源少。 DELETE 语句每次删除一行&#xff0c;并在事务日志中为所删除的每行记录一项。 TRUNCATE…

三张图搞懂JavaScript的原型对象与原型链

对于新人来说&#xff0c;JavaScript的原型是一个很让人头疼的事情&#xff0c;一来prototype容易与__proto__混淆&#xff0c;二来它们之间的各种指向实在有些复杂&#xff0c;其实市面上已经有非常多的文章在尝试说清楚&#xff0c;有一张所谓很经典的图&#xff0c;上面画了…

python partial_如何在python多处理模块中使用partial函数?

下面是我如何解决这个问题的一个简单例子from functools import partialfrom multiprocessing import Pooldef VariadicLifter(func, args):return func(*args)def func(x,y,z,a):return x2*y3*z4*aif __name__ __main__:func_ partial( func, 500, 1007)lfunc_ partial( Va…

Mybatis中resultMap

MyBatis中在查询进行select映射的时候&#xff0c;返回类型可以用resultType&#xff0c;也可以用resultMap&#xff0c;resultType是直接 表示返回类型的&#xff0c;而resultMap则是对外部ResultMap的引用&#xff0c;但是resultType跟resultMap不能同时存在。 1.resultType …

超简单的mysql多实例布置

一、基本概念mysql下载&#xff1a;http://mirrors.sohu.com/mysql/MySQL-5.5/1、MySQL多实例就是在一台机器上面开启多个不同的端口&#xff0c;运行多个MySQL服务进程。这些MySQL多实例公用一套安装程序&#xff0c;使用不同的(也可以是相同的)配置文件&#xff0c;启动程序&…

java程序设计计算器_Java程序设计计算器(含代码)

Java程序课程设计任务书实用性计算器的设计与开发1、主要内容&#xff1a;开发一个实用型的计算器程序&#xff0c;实现基本的计算功能同时并进行相应的功能拓展&#xff0c;使其具更加人性化的功能。我们可以用其进行相应的计算功能来方便我们的学习&#xff0c;代替我们进行一…

mybatis配置insert/update/delete同一个模板

insert&#xff0c;update&#xff0c;delete标签只是一个模板&#xff0c;在操作时是以sql语句为核心的&#xff0c; 即在做增/删/改时&#xff0c;insert/update/delete便签可以通用&#xff0c; 但做查询时只能用 select 标签 提倡什么操作就用什么标签 这就是为什么 ex…

Mybatis配置文件resultMap映射啥时候可写可不写?

1、student实体类 public class Student {private Integer id;//编号private String name;//姓名private Double sal;//薪水public Student(){}public Student(Integer id, String name, Double sal) {this.id id;this.name name;this.sal sal;}public Integer getId() {ret…

arithmetic java_Java:Arithmetic

好吧&#xff0c;事实上你有方法设置变量叫get - 这显然不是一个好主意&#xff0c;并且没有缩进......但它应该有效。但是&#xff0c;你还没有展示出你是如何使用它的。也许你实际上并没有被称为setter方法&#xff1f;以下是相同代码但具有不同名称的示例&#xff0c;以及使…

网络框架 Retrofit(三)

简单实现Retrofit&#xff08;替代Okhttp&#xff09; 1.定义注解参数 Documented Target(PARAMETER) Retention(RUNTIME) public interface Field {String value(); } 复制代码Documented Target(METHOD) Retention(RUNTIME) public interface Get {String value() default &q…

Tomcat提示“XDB 的服务器 localhost 要求用户名和密码”

在地址栏输入http://localhost:8080/&#xff08;8080是TOMCAT的端口号)测试&#xff0c;系统提示“XDB 的服务器 localhost 要求用户名和密码”&#xff0c;并弹出输入用户、密码的窗口&#xff0c;查看代码没问题&#xff0c;不解&#xff0c;上网上查资料&#xff0c;是因为…

java jqgrid json格式_jqGrid 数据之 Json

Json数据需要定义jsonReader来跟服务器端返回的数据做对应&#xff0c;其默认值&#xff1a; jsonReader : { root: "rows", page: "page", total: "total", records: "records", repeatitems: true, c…

django 中静态文件项目加载问题

问题描述&#xff1a; django项目中创建了多个app后&#xff0c;每个app中都有对应的static静态文件。整个项目运行时这些静态文件的加载就是一个问题&#xff0c;因为整个项目我只参与了一部分&#xff0c;项目部署之类的并没有参与。我写的部分的js代码遇到点问题&#xff0c…

Erlang/OTP设计原则(文档翻译)

http://erlang.org/doc/design_principles/des_princ.html 图和代码皆源自以上链接中Erlang官方文档&#xff0c;翻译时的版本为20.1。 这个设计原则&#xff0c;其实是说用户在设计系统的时候应遵循的标准和规范。阅读前我一直以为写的是作者在设计 Erlang/OTP 框架时的一些原…