{"id":21706912,"url":"https://github.com/tohodog/qsrpc","last_synced_at":"2026-03-06T07:32:32.344Z","repository":{"id":49962448,"uuid":"183993918","full_name":"tohodog/QSRPC","owner":"tohodog","description":"rpc zookeeper netty pool","archived":false,"fork":false,"pushed_at":"2021-11-25T10:21:07.000Z","size":1050,"stargazers_count":4,"open_issues_count":2,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-12T16:15:55.238Z","etag":null,"topics":["netty","rpc","zookeeper"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tohodog.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-04-29T03:32:01.000Z","updated_at":"2021-11-25T10:04:33.000Z","dependencies_parsed_at":"2022-08-27T05:10:32.906Z","dependency_job_id":null,"html_url":"https://github.com/tohodog/QSRPC","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tohodog%2FQSRPC","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tohodog%2FQSRPC/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tohodog%2FQSRPC/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tohodog%2FQSRPC/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tohodog","download_url":"https://codeload.github.com/tohodog/QSRPC/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248594190,"owners_count":21130316,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["netty","rpc","zookeeper"],"created_at":"2024-11-25T22:14:49.143Z","updated_at":"2026-03-06T07:32:31.440Z","avatar_url":"https://github.com/tohodog.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"![logo][logopng]\n\u003cbr/\u003e\n\u003cbr/\u003e\n---\n一个基于 nacos / zookeeper 自动注册扩展服务、使用 netty 长连接池的高性能轻量级RPC框架\n\u003cbr/\u003e\n\n[![netty][nettysvg]][netty] [![nacos][nacossvg]][nacos] [![zk][zksvg]][zk]  [![License][licensesvg]][license]\n\n  * 使用 nacos / zookeeper 服务发现, 自动注册扩展服务\n  * 使用长连接TCP池, netty 作为网络IO, 支持全双工通信, 高性能\n  * 消息发送支持异步/同步\n  * 自动选择符合 action 节点服务器, 支持权重分发消息\n  * 支持 snappy, gzip 压缩\n  * 可进行二次封装开发, [远程调用][qsrpc-starter], 消息路由负载均衡等等\n  * 欢迎学习交流~见[QSRPC项目技术选型及简介]\n\n![ad][adpng]\n## Maven\n```\n\u003cdependency\u003e\n    \u003cgroupId\u003ecom.github.tohodog\u003c/groupId\u003e\n    \u003cartifactId\u003eqsrpc\u003c/artifactId\u003e\n    \u003cversion\u003e1.3.0\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n## Demo\nFirst configured \n[nacos](https://nacos.io/zh-cn/docs/deployment.html)\n/\n[zookeeper](http://mirrors.hust.edu.cn/apache/zookeeper/)\n\n### application.properties\n```\n#nacos\nqsrpc.nacos.addr=192.168.0.100:8848\n#qsrpc.nacos.srvname=qsrpc\n\n#zookeeper\n#qsrpc.zk.ips=127.0.0.1:2181\n#qsrpc.zk.path=/qsrpc\n\n#node server\nqsrpc.node.ip=127.0.0.1\nqsrpc.node.port=19980\nqsrpc.node.action=user,order\n#qsrpc.node.weight=1\n#qsrpc.node.zip=snappy/gzip\n#qsrpc.connect.timeout=60000\n```\n\n### Node\n```\n    //open node server 1 (read application.properties)\n    NodeInfo nodeInfo = NodeRegistry.buildNode();\n    //sync callback\n    NodeLauncher.start(nodeInfo, new MessageListener() {\n        @Override\n        public byte[] onMessage(Async async, byte[] message) {\n        return (\"Hello! node1 callback -\" + new String(message)).getBytes();\n        }\n    });\n\n\n    // open node server 2\n    ServerConfig.RPC_CONFIG.setNacosAddr(\"192.168.0.100:8848\");\n    ServerConfig.RPC_CONFIG.setNacosServiceName(\"qsrpc\");\n    // ServerConfig.RPC_CONFIG.setZkIps(\"127.0.0.1:2181\");\n    // ServerConfig.RPC_CONFIG.setZkPath(\"/qsrpc\");\n    NodeInfo nodeInfo2 = new NodeInfo();\n    nodeInfo2.setAction(\"order\");//node server action\n    nodeInfo2.setIp(\"127.0.0.1\");//node server ip\n    nodeInfo2.setPort(8848);//nodeserver port\n    nodeInfo2.setWeight(2);//request weight\n\n    //async callback\n    NodeLauncher.start(nodeInfo2, new MessageListener() {\n        @Override\n        public byte[] onMessage(final Async async, final byte[] message) {\n        new Thread(new Runnable() {\n            @Override\n            public void run() {\n            async.callBack((\"Hello! node2 callback -\" + new String(message)).getBytes());\n            }\n        }).start();\n        return null;\n        }\n    });\n```\n### Client\n```\n    //async\n    for (int i = 0; i \u003c 9; i++) {\n    \t//Send byte[] based on action\n        RPCClientManager.getInstance().sendAsync(\"user\", \"user\".getBytes(),\n            new Callback\u003cbyte[]\u003e() {\n            @Override\n            public void handleResult(byte[] result) {\n                System.out.println(\"send [user] Result: \" + new String(result));\n            }\n\n            @Override\n            public void handleError(Throwable error) {\n                error.printStackTrace();\n            }\n            });\n    }\n    System.out.println(\"send [user] Done\");\n\n    //sync\n    for (int i = 0; i \u003c 9; i++) {\n        Thread.sleep(1000);\n        byte[] msg_cb = RPCClientManager.getInstance().sendSync(\"order\", \"order\".getBytes());\n        System.out.println(\"send [order] Result: \" + new String(msg_cb));\n    }\n    System.out.println(\"send [order] Done\");\n\n    //future\n    CallFuture\u003cbyte[]\u003e callFuture = RPCClientManager.getInstance().sendAsync(\"user\", \"user\".getBytes());\n    System.out.println(\"send [user] FutureResult: \" + new String(callFuture.get()));\n```\n## Test\nRun [TestConcurrent.java][testjava] (Don't open the console and 360 antivirus etc.)\n\n|  CPU   | request  | time  |qps  |\n|  ----  | ----  |----  |----  |\n| i3-8100(4-core/4-thread) | 100w(8-thread) |7817ms | 127926  |\n| i7-8700(6-core/12-thread) | 100w(8-thread) |3010ms | 332225  |\n\n在4核自发自收的情况下有12万+的并发数,实际会更高 [测试截图1][testpng] [测试截图2][testpng2]\n\n## Future\n  * ~~Support nacos...~~\n  * AIO...\n  \n\n\n## QSRPC项目技术选型及简介\n### 1.TCP通信\n#### 1.1 连接模式:\n　本项目tcp通信使用长连接+全双工通信(两边可以同时收/发消息),可以保证更大的吞吐量/更少的连接数资源占用,理论上使用一个tcp连接即可满足通信(详见pool),如果使用http/1.1协议的请求-响应模式,同一个连接在同一个时刻只能有一个消息进行传输,如果有大量请求将会阻塞或者需要开更多tcp连接来解决\n#### 1.2 协议:\n|TCP|长度|消息ID|协议号|加密/压缩|内容|包尾|\n|:----:|:----:|:----:|:----:|:----:|:----:|:----:|\n| Byte | 4 | 4 | 1 | 1(4bit+4bit)  | n | 2 |\n\n　首先,使用长连接那就需要解决tcp粘包问题,常见的两种方式:  \n * 包头长度:优点最简单,也是最高效的,缺点是无法感知数据包错误,会导致后续所有包错乱\n * 特定包尾:优点能感知包错误,不影响后续包,缺点需要遍历所有字节,且不能与包内容冲突\n \u003cbr/\u003e\n　综上,本框架使用的是包头长度+特定包尾,结合了两者优点,避免了缺点,高效实用,检测到包错误会自动断开.\n没有使用校检码转码等,因为需要考虑实际情况,内网里出错概率非常低,出错了也能重连,对于RPC框架追求性能来说是合适的,即使是外网,后续有需求可以增加校验加密协议\n\u003cbr/\u003e\n　其次,因为支持全双工那就需要解决消息回调问题,本协议使用了一个消息ID,由客户端生成,服务端返回消息带上;由于发送和接收是非连续的,所以客户端需要维护一个回调池,以ID为key,value为此次请求的context(callback),因为是异步的,请求有可能没有响应,所以池需要有超时机制\n\n#### 1.3 压缩/加密:\n　当出现带宽不足而CPU性能有余时,压缩就派上用场了,用时间换空间。目前支持了snappy/gzip两种压缩,snappy应用于google的rpc上,具有高速压缩速度和合理的压缩率,gzip速度次于snappy,但压缩率较高,根据实际情况配置,前提必须是带宽出现瓶颈/要求,否则不需要开启压缩\n\u003cbr/\u003e　加密功能计划中(加盐位算法)\n#### 1.4 IO框架:\n网络IO目前是基于netty搭建的,支持nio,zero-copy等特性,由于本框架连接模式使用长连接,连接数固定且较少,所以本框架性能对于IO模式(BIO/NIO/AIO)并不是很敏感,netty对于http,iot服务这种有大量连接数的优势就很大了\n\n\n### 2. Tcp pool\n　前面说了一个tcp连接即可支撑通信,为啥又用pool了呢,原因有两个:1. netty工作线程对于同一个连接使用同一个线程来处理的,所以如果客户端发送大量请求时,服务端只有一个线程在处理导致性能问题,起初是想服务端再把消息分发到线程池,但后续测试发现此操作在高并发下会导致延迟增大,因为又把消息放回线程池排队了。2. 相对于一条tcp链接,使用pool会更加灵活,且连接数也很少,并没有性能影响; 本框架还基于pool实现了一个[请求-响应]的通信模式*\n\u003cbr\u003e\n　客户端Pool的maxIdle(maxActive)=服务节点配置的CPU线程数*2=服务节点netty的工作线程数,pool采用FIFO先行先出的策略,可以保证在高并发下均匀的使用tcp连接,服务端就不用再次分发消息了\n### 3. 服务注册发现\n　分布式系统中都需要一个配置/服务中心,才能进行统一管理.本框架目前使用zookeeper(已支持nacos)进行服务注册,zookeeper是使用类似文件目录的结构,每个目录都可以存一个data\n\u003cbr\u003e　节点注册是使用[IP:PROT_NAME_TIME]作为目录名,data存了节点的json数据,创建模式为EPHEMERAL_SEQUENTIAL(断开后会删除该目录),这样就达到了自动监听节点上下线的效果,加入时间戳是为了解决当节点快速重启时,注册了两个目录,便于进行区分处理\n\u003cbr\u003e　客户端通过watch目录变化信息,从而获取到所有服务节点信息,同步一个副本到本地Map里(需加上读写锁),客户端就可以实现高效调用对应的服务了\n\n\n## Log\n### v1.3.0(2021-11-25)\n  * 重构服务发现模块\n  * 优化nacos,使用index,增量拉取节点信息\n  * 升级依赖\n  * 其他优化...\n### v1.2.0(2021-04-16)\n  * 支持Nacos 2.0\n  * 优化zk服务发现性能 \n  * 支持代码配置参数\n  * 其他优化...\n### v1.1.2(2020-11-26)\n  * 支持根据IP选择指定节点\n  * 池增加驱逐机制 \n  * 优化选择节点性能\n  * 其他优化...\n### v1.1.1(2020-11-22)\n  * Support compress\n  * Optimization pool log test...\n\n### v1.0.1(2019-09-26)\n  * Support future get\n  * Optimization\n### v1.0.0(2019-09-19)\n  * Open sourse\n\n## Other\n  * 有问题请Add [issues](https://github.com/tohodog/QSRPC/issues)\n  * 如果项目对你有帮助的话欢迎[![starsvg]][star]\n  \n[logopng]: https://gitee.com/sakaue/QSRPC/raw/master/logo.png\n[adpng]: https://gitee.com/sakaue/QSRPC/raw/master/Architecture_diagram.jpg\n[testpng]: https://gitee.com/sakaue/QSRPC/raw/master/test.png\n[testjava]: https://gitee.com/sakaue/QSRPC/raw/master/src/test/java/test/TestConcurrent.java\n[testpng2]: https://gitee.com/sakaue/QSRPC/raw/master/test2.png\n\n\n[nettysvg]: https://img.shields.io/badge/netty-4.1.70-greed.svg\n[netty]: https://github.com/netty/netty\n\n[nacossvg]: https://img.shields.io/badge/nacos-2.0.3-2EBBFB.svg\n[nacos]: https://github.com/alibaba/nacos\n\n\n[zksvg]: https://img.shields.io/badge/zookeeper-3.4.14-FF9900.svg\n[zk]: https://github.com/apache/zookeeper\n\n[licensesvg]: https://img.shields.io/badge/License-Apache--2.0-red.svg\n[license]: https://gitee.com/sakaue/QSRPC/raw/master/LICENSE\n\n[starsvg]: https://img.shields.io/github/stars/tohodog/QSRPC.svg?style=social\u0026label=Stars\n[star]: https://github.com/tohodog/QSRPC\n\n[qsrpc-starter]: https://github.com/tohodog/QSRPC-starter\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftohodog%2Fqsrpc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftohodog%2Fqsrpc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftohodog%2Fqsrpc/lists"}