簡易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.pswp.cn/news/539628.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/539628.shtml
英文地址,請注明出處:http://en.pswp.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 框架時的一些原…