├── .gitignore ├── LICENSE ├── README.md ├── common ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── polyu │ │ └── rpc │ │ ├── annotation │ │ ├── BRpcConsumer.java │ │ └── BRpcProvider.java │ │ ├── codec │ │ ├── HeartBeat.java │ │ ├── RpcDecoder.java │ │ ├── RpcEncoder.java │ │ ├── RpcRequest.java │ │ └── RpcResponse.java │ │ ├── info │ │ ├── RpcMetaData.java │ │ └── RpcServiceInfo.java │ │ ├── registry │ │ ├── ServiceDiscovery.java │ │ ├── ServiceRegistry.java │ │ └── observation │ │ │ ├── Observer.java │ │ │ └── Subject.java │ │ ├── route │ │ ├── DefaultRpcLoadBalanceHolder.java │ │ ├── MetaDataKeeper.java │ │ ├── RpcLoadBalance.java │ │ └── impl │ │ │ ├── RpcLoadBalanceConsistentHash.java │ │ │ ├── RpcLoadBalanceRandom.java │ │ │ └── RpcLoadBalanceRoundRobin.java │ │ ├── serializer │ │ ├── Serializer.java │ │ └── kryo │ │ │ ├── KryoPoolFactory.java │ │ │ └── KryoSerializer.java │ │ └── util │ │ ├── JsonUtil.java │ │ ├── ServiceUtil.java │ │ └── ThreadPoolUtil.java │ └── resources │ └── log4j2.xml ├── netty-client ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── polyu │ │ └── rpc │ │ └── client │ │ ├── RpcClient.java │ │ ├── connect │ │ ├── ConnectUpdater.java │ │ ├── Connector.java │ │ └── HandlerManager.java │ │ ├── interceptor │ │ ├── Interceptor.java │ │ └── impl │ │ │ ├── CallBackInterceptor.java │ │ │ └── TimeCostInterceptor.java │ │ ├── invoke │ │ ├── Invocation.java │ │ └── InvokeProxy.java │ │ ├── netty │ │ ├── RpcClientInitializer.java │ │ └── handler │ │ │ ├── RpcClientHandler.java │ │ │ └── RpcHeartBeatHandler.java │ │ ├── result │ │ ├── PendingRpcHolder.java │ │ └── future │ │ │ ├── AsyncRPCCallback.java │ │ │ └── RpcFuture.java │ │ └── spring │ │ └── ClientAutoConfig.java │ └── resources │ └── META-INF │ └── spring.factories ├── netty-server ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── polyu │ │ └── rpc │ │ └── server │ │ ├── RpcServer.java │ │ ├── Server.java │ │ ├── netty │ │ ├── NettyServer.java │ │ ├── NettyServerBootstrap.java │ │ ├── RpcServerInitializer.java │ │ └── handler │ │ │ ├── BusinessHandler.java │ │ │ └── HeartBeatHandler.java │ │ ├── reflect │ │ └── ReflectInvoker.java │ │ ├── spring │ │ └── ServerAutoConfig.java │ │ └── task │ │ └── BusinessTask.java │ └── resources │ └── META-INF │ └── spring.factories ├── pom.xml ├── registry ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── polyu │ └── rpc │ └── registry │ ├── RegistryConfigEnum.java │ ├── nacos │ ├── NacosDiscovery.java │ └── NacosRegistry.java │ └── zookeeper │ ├── CuratorClient.java │ ├── ZKDiscovery.java │ └── ZKRegistry.java └── test ├── pom.xml └── src └── main ├── java └── com │ └── polyu │ └── rpc │ └── test │ ├── api │ ├── client │ │ ├── ClientTest.java │ │ └── ConcurrentTest.java │ └── server │ │ └── ServerTest.java │ ├── service │ ├── HelloService.java │ ├── HelloService2.java │ ├── HelloServiceImpl.java │ └── HelloServiceImpl2.java │ └── spring │ ├── client │ └── ClientTest.java │ └── server │ └── ServerTest.java └── resources ├── application.properties ├── client-spring.xml └── server-spring.xml /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | ### Intellij ### 3 | .idea 4 | .idea_modules/ 5 | /out/ 6 | output/ 7 | 8 | ####eclipse####### 9 | .project 10 | .classpath 11 | .settings 12 | 13 | ### log ### 14 | logs/ 15 | log/ 16 | *.log 17 | 18 | *.iws 19 | *.iml 20 | 21 | ### Java ### 22 | *.class 23 | 24 | # Mobile Tools for Java (J2ME) 25 | .mtj.tmp/ 26 | 27 | # Package Files # 28 | *.jar 29 | *.war 30 | *.ear 31 | 32 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 33 | hs_err_pid* 34 | 35 | ### Maven ### 36 | target/ 37 | pom.xml.tag 38 | pom.xml.releaseBackup 39 | pom.xml.versionsBackup 40 | pom.xml.next 41 | release.properties 42 | dependency-reduced-pom.xml 43 | buildNumber.properties 44 | .mvn/timing.properties 45 | 46 | ### Windows ### 47 | # Windows image file caches 48 | Thumbs.db 49 | ehthumbs.db 50 | 51 | # Folder config file 52 | Desktop.ini 53 | 54 | # Recycle Bin used on file shares 55 | $RECYCLE.BIN/ 56 | 57 | # Windows Installer files 58 | *.cab 59 | *.msi 60 | *.msm 61 | *.msp 62 | 63 | # Windows shortcuts 64 | *.lnk 65 | 66 | ### OSX ### 67 | *~ 68 | .DS_Store 69 | .AppleDouble 70 | .LSOverride 71 | 72 | # Icon must end with two \r 73 | Icon 74 | 75 | # Thumbnails 76 | ._* 77 | 78 | # Files that might appear in the root of a volume 79 | .DocumentRevisions-V100 80 | .fseventsd 81 | .Spotlight-V100 82 | .TemporaryItems 83 | .Trashes 84 | .VolumeIcon.icns 85 | 86 | # Directories potentially created on remote AFP share 87 | .AppleDB 88 | .AppleDesktop 89 | Network Trash Folder 90 | Temporary Items 91 | .apdisk 92 | 93 | ### Commitizen ### 94 | node_modules/ 95 | package.json -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2021 YiBin Yan 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # bRPC 2 | GitHub license 3 | GitHub star 4 |
5 | README 中文版本 6 |
7 | 8 | ## 一个基于netty的RPC框架 9 | 10 | 1. 基于netty NIO、IO多路复用。 11 | 2. client与server端建立心跳包保活机制。发生未知断连时,重连保证可靠长连接。 12 | 3. 使用kryo序列化,自定义传输包,及传输格式,避免TCP沾包问题。 13 | 4. 支持zookeeper或nacos做服务注册中心。 14 | 5. 可在注解中配置server端业务线程池核心线程数及最大线程数,客户端可通过注解自由选择接口对应负载均衡策略。 15 | 6. 可轻松整合SpringBoot进行使用。 16 | 17 | ## Getting Start 18 | 19 | ### 启动 20 | #### API式启动 21 | 1. com.polyu.rpc.test.api.server.ServerTest.java 服务端启动 22 | 2. com.polyu.rpc.test.api.client.ClientTest.java 客户端启动 并rpc调用HelloService.hello() 23 | 24 | #### 与SpringBoot整合启动(由于未发布到远程仓库,所以需本地maven install。) 25 | 26 | 1. 客户端 27 | - maven 28 | ```xml 29 | 30 | com.polyu 31 | netty-client 32 | 1.0-SNAPSHOT 33 | 34 | ``` 35 | - springboot 36 | ```java 37 | @Component 38 | public class Test { 39 | 40 | @BRpcConsumer(version = "1.0", loadBalanceStrategy = RpcLoadBalanceConsistentHash.class, timeOutLength = 1000L) 41 | private static HelloService helloService; 42 | 43 | public static void test() throws InterruptedException { 44 | String yyb = helloService.hello("yyb"); 45 | System.out.println("yyb = " + yyb); 46 | } 47 | } 48 | ``` 49 | - application.properties 50 | ```properties 51 | bRPC.client.registry.type=zookeeper 52 | bRPC.client.registry.address=127.0.0.1:2181 53 | bRPC.client.registry.target.name=springbootApplication 54 | bRPC.client.timeout.checkInterval=500 55 | ``` 56 | 2. 服务端 57 | - maven 58 | ```xml 59 | 60 | com.polyu 61 | netty-server 62 | 1.0-SNAPSHOT 63 | 64 | ``` 65 | - springboot 66 | ```java 67 | @BRpcProvider(value = HelloService.class, version = "1.0") 68 | public class HelloServiceImpl implements HelloService { 69 | 70 | @Override 71 | public String hello(String name) throws InterruptedException { 72 | return name; 73 | } 74 | } 75 | ``` 76 | - application.properties 77 | ```properties 78 | bRPC.server.application.name=springbootApplication 79 | bRPC.server.address=127.0.0.1:12277 80 | bRPC.server.registry.type=zookeeper 81 | bRPC.server.registry.address=127.0.0.1:2181 82 | ``` 83 | ### 注册中心部署 84 | #### Docker Zookeeper部署(例 同样支持nacos) 85 | 86 | 1. 拉取zk镜像   指令:docker pull zookeeper:3.4.14 87 | 2. 查看镜像id   指令:docker images 88 | 3. 拉起容器    指令:docker run -d -p 2181:2181 --name b-zookeeper --restart always {imageId} 89 | 4. 查看容器id   指令:docker ps -a 90 | 5. 进入容器    指令:docker exec -it {containerId} /bin/bash 91 | 6. 起注册中心   指令:./bin/zkCli.sh 92 | 93 |
94 |
95 | 96 | ## A netty-based RPC framewor 97 | 98 | 1. Based on netty NIO, IO multiplexing. 99 | 2. The client and server establish a heartbeat packet keep-alive mechanism. When an unknown disconnection occurs, reconnection ensures a reliable long connection. 100 | 3. Use kryo serialization, customize the transmission package, and transmission format to avoid TCP packet contamination problems. 101 | 4. Support zookeeper or nacos as service registration center. 102 | 5. The number of core threads and the maximum number of threads in the server-side business thread pool can be configured in the annotations, and the client can freely choose the load balancing strategy corresponding to the interface through the annotations. 103 | 6. Can easily integrate SpringBoot for use. 104 | 105 | ## Getting Start 106 | 107 | ### Start 108 | #### API startup 109 | 1. com.polyu.rpc.test.api.server.ServerTest.java Server start 110 | 2. com.polyu.rpc.test.api.client.ClientTest.java Client starts and rpc calls HelloService.hello() 111 | 112 | #### Start integrated with SpringBoot (Because it is not published to a remote warehouse, it needs local maven install.) 113 | 114 | 1. client 115 | - maven 116 | ```xml 117 | 118 | com.polyu 119 | netty-client 120 | 1.0-SNAPSHOT 121 | 122 | ``` 123 | - springboot 124 | ```java 125 | @Component 126 | public class Test { 127 | 128 | @BRpcConsumer(version = "1.0", loadBalanceStrategy = RpcLoadBalanceConsistentHash.class, timeOutLength = 1000L) 129 | private static HelloService helloService; 130 | 131 | public static void test() throws InterruptedException { 132 | String yyb = helloService.hello("yyb"); 133 | System.out.println("yyb = " + yyb); 134 | } 135 | } 136 | ``` 137 | - application.properties 138 | ```properties 139 | bRPC.client.registry.type=zookeeper 140 | bRPC.client.registry.address=127.0.0.1:2181 141 | bRPC.client.registry.target.name=springbootApplication 142 | bRPC.client.timeout.checkInterval=500 143 | ``` 144 | 2. server 145 | - maven 146 | ```xml 147 | 148 | com.polyu 149 | netty-server 150 | 1.0-SNAPSHOT 151 | 152 | ``` 153 | - springboot 154 | ```java 155 | @BRpcProvider(value = HelloService.class, version = "1.0") 156 | public class HelloServiceImpl implements HelloService { 157 | 158 | @Override 159 | public String hello(String name) throws InterruptedException { 160 | return name; 161 | } 162 | } 163 | ``` 164 | - application.properties 165 | ```properties 166 | bRPC.server.application.name=springbootApplication 167 | bRPC.server.address=127.0.0.1:12277 168 | bRPC.server.registry.type=zookeeper 169 | bRPC.server.registry.address=127.0.0.1:2181 170 | ``` 171 | ### Registry deployment 172 | #### Docker Zookeeper deployment (example also supports nacos) 173 | 174 | 1. docker pull zookeeper:3.4.14 175 | 2. docker images 176 | 3. docker run -d -p 2181:2181 --name b-zookeeper --restart always {imageId} 177 | 4. docker ps -a 178 | 5. docker exec -it {containerId} /bin/bash 179 | 6. ./bin/zkCli.sh 180 | -------------------------------------------------------------------------------- /common/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | bRPC 7 | com.polyu 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | common 13 | 14 | 15 | 16 | io.netty 17 | netty-all 18 | 19 | 20 | 21 | org.springframework 22 | spring-context 23 | 24 | 25 | 26 | org.apache.curator 27 | curator-recipes 28 | 29 | 30 | 31 | org.projectlombok 32 | lombok 33 | 34 | 35 | 36 | com.esotericsoftware 37 | kryo 38 | 39 | 40 | 41 | com.fasterxml.jackson.core 42 | jackson-databind 43 | 44 | 45 | 46 | org.slf4j 47 | slf4j-api 48 | 49 | 50 | 51 | org.apache.logging.log4j 52 | log4j-slf4j-impl 53 | 54 | 55 | org.apache.logging.log4j 56 | log4j-api 57 | 58 | 59 | org.apache.logging.log4j 60 | log4j-core 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /common/src/main/java/com/polyu/rpc/annotation/BRpcConsumer.java: -------------------------------------------------------------------------------- 1 | package com.polyu.rpc.annotation; 2 | 3 | import com.polyu.rpc.route.impl.RpcLoadBalanceRoundRobin; 4 | 5 | import java.lang.annotation.ElementType; 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.RetentionPolicy; 8 | import java.lang.annotation.Target; 9 | 10 | /** 11 | * 客户端注解 12 | */ 13 | @Target({ElementType.FIELD}) 14 | @Retention(RetentionPolicy.RUNTIME) 15 | public @interface BRpcConsumer { 16 | 17 | /** 18 | * 版本号 19 | */ 20 | String version() default ""; 21 | 22 | /** 23 | * 负载均衡策略设置 24 | * 可选: 25 | * RpcLoadBalanceRoundRobin.class(default) / RpcLoadBalanceRandom.class / RpcLoadBalanceConsistentHash.class 26 | */ 27 | Class loadBalanceStrategy() default RpcLoadBalanceRoundRobin.class; 28 | 29 | /** 30 | * 接口超时时间 31 | */ 32 | long timeOutLength() default 3000L; 33 | } -------------------------------------------------------------------------------- /common/src/main/java/com/polyu/rpc/annotation/BRpcProvider.java: -------------------------------------------------------------------------------- 1 | package com.polyu.rpc.annotation; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | import java.lang.annotation.ElementType; 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.RetentionPolicy; 8 | import java.lang.annotation.Target; 9 | 10 | /** 11 | * 服务端注解 12 | */ 13 | @Target({ElementType.TYPE}) 14 | @Retention(RetentionPolicy.RUNTIME) 15 | @Component 16 | public @interface BRpcProvider { 17 | 18 | Class value(); 19 | 20 | /** 21 | * 版本号 22 | */ 23 | String version() default ""; 24 | 25 | /** 26 | * 要求: 27 | * coreThreadPoolSize > 0 28 | * coreThreadPoolSize <= maxThreadPoolSize 29 | * 否则配置无效 走默认设置参数值 30 | */ 31 | int coreThreadPoolSize() default 30; 32 | 33 | /** 34 | * 要求: 35 | * maxThreadPoolSize > 0 36 | * 否则配置无效 走默认设置参数值 37 | */ 38 | int maxThreadPoolSize() default 65; 39 | } 40 | -------------------------------------------------------------------------------- /common/src/main/java/com/polyu/rpc/codec/HeartBeat.java: -------------------------------------------------------------------------------- 1 | package com.polyu.rpc.codec; 2 | 3 | public final class HeartBeat { 4 | 5 | public static final int BEAT_INTERVAL = 30; 6 | public static final int BEAT_TIMEOUT = 3 * BEAT_INTERVAL; 7 | public static final String BEAT_ID = "BEAT_PING_PONG"; 8 | 9 | public static RpcRequest BEAT_PING; 10 | 11 | static { 12 | BEAT_PING = new RpcRequest() {}; 13 | BEAT_PING.setRequestId(BEAT_ID); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /common/src/main/java/com/polyu/rpc/codec/RpcDecoder.java: -------------------------------------------------------------------------------- 1 | package com.polyu.rpc.codec; 2 | 3 | import com.polyu.rpc.serializer.Serializer; 4 | import io.netty.buffer.ByteBuf; 5 | import io.netty.channel.ChannelHandlerContext; 6 | import io.netty.handler.codec.ByteToMessageDecoder; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import java.util.List; 11 | 12 | /** 13 | * rpc解码 14 | */ 15 | public class RpcDecoder extends ByteToMessageDecoder { 16 | private static final Logger logger = LoggerFactory.getLogger(RpcDecoder.class); 17 | private Class genericClass; 18 | private Serializer serializer; 19 | 20 | public RpcDecoder(Class genericClass, Serializer serializer) { 21 | this.genericClass = genericClass; 22 | this.serializer = serializer; 23 | } 24 | 25 | /** 26 | * 解码 缓冲区头记录消息包载体长度 27 | * 如果不足4字节 int说明未读完 直接返回 28 | * 如果后续包长度不足 重置ByteBuf读取 直接返回 29 | * 30 | * @param ctx 31 | * @param in 缓冲区 32 | * @param out 33 | */ 34 | @Override 35 | public final void decode(ChannelHandlerContext ctx, ByteBuf in, List out) { 36 | if (in.readableBytes() < 4) { 37 | return; 38 | } 39 | in.markReaderIndex(); 40 | int dataLength = in.readInt(); 41 | if (in.readableBytes() < dataLength) { 42 | in.resetReaderIndex(); 43 | return; 44 | } 45 | byte[] data = new byte[dataLength]; 46 | in.readBytes(data); 47 | Object obj; 48 | try { 49 | obj = serializer.deserialize(data, genericClass); 50 | out.add(obj); 51 | } catch (Exception ex) { 52 | logger.error("Decode error: {}", ex.toString()); 53 | } 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /common/src/main/java/com/polyu/rpc/codec/RpcEncoder.java: -------------------------------------------------------------------------------- 1 | package com.polyu.rpc.codec; 2 | 3 | import com.polyu.rpc.serializer.Serializer; 4 | import io.netty.buffer.ByteBuf; 5 | import io.netty.channel.ChannelHandlerContext; 6 | import io.netty.handler.codec.MessageToByteEncoder; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | /** 11 | * rpc编码 12 | */ 13 | public class RpcEncoder extends MessageToByteEncoder { 14 | private static final Logger logger = LoggerFactory.getLogger(RpcEncoder.class); 15 | private Class genericClass; 16 | private Serializer serializer; 17 | 18 | public RpcEncoder(Class genericClass, Serializer serializer) { 19 | this.genericClass = genericClass; 20 | this.serializer = serializer; 21 | } 22 | 23 | /** 24 | * 编码 25 | * @param ctx 26 | * @param in 27 | * @param out 28 | */ 29 | @Override 30 | public void encode(ChannelHandlerContext ctx, Object in, ByteBuf out) { 31 | if (genericClass.isInstance(in)) { 32 | try { 33 | byte[] data = serializer.serialize(in); 34 | out.writeInt(data.length); 35 | out.writeBytes(data); 36 | } catch (Exception ex) { 37 | logger.error("Encode error: {}", ex.toString()); 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /common/src/main/java/com/polyu/rpc/codec/RpcRequest.java: -------------------------------------------------------------------------------- 1 | package com.polyu.rpc.codec; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class RpcRequest { 7 | private String requestId; 8 | private String className; 9 | private String methodName; 10 | private Class[] parameterTypes; 11 | private Object[] parameters; 12 | private String version; 13 | } -------------------------------------------------------------------------------- /common/src/main/java/com/polyu/rpc/codec/RpcResponse.java: -------------------------------------------------------------------------------- 1 | package com.polyu.rpc.codec; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class RpcResponse { 7 | private String requestId; 8 | private String error; 9 | private Object result; 10 | 11 | public boolean isError() { 12 | return error != null; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /common/src/main/java/com/polyu/rpc/info/RpcMetaData.java: -------------------------------------------------------------------------------- 1 | package com.polyu.rpc.info; 2 | 3 | import com.polyu.rpc.util.JsonUtil; 4 | import lombok.Data; 5 | 6 | import java.util.List; 7 | import java.util.Objects; 8 | 9 | /** 10 | * 服务注册、订阅内容载体 11 | */ 12 | @Data 13 | public class RpcMetaData { 14 | 15 | private String host; 16 | private int port; 17 | /** 18 | * 服务列表list 19 | */ 20 | private List serviceInfoList; 21 | 22 | public String toJson() { 23 | return JsonUtil.objectToJson(this); 24 | } 25 | 26 | public static RpcMetaData fromJson(String json) { 27 | return JsonUtil.jsonToObject(json, RpcMetaData.class); 28 | } 29 | 30 | @Override 31 | public boolean equals(Object o) { 32 | if (this == o) return true; 33 | if (o == null || getClass() != o.getClass()) return false; 34 | RpcMetaData that = (RpcMetaData) o; 35 | return port == that.port && 36 | Objects.equals(host, that.host) && 37 | isListEquals(serviceInfoList, that.getServiceInfoList()); 38 | } 39 | 40 | private boolean isListEquals(List thisList, List thatList) { 41 | if (thisList == null && thatList == null) { 42 | return true; 43 | } 44 | if (thisList == null || thatList == null || thisList.size() != thatList.size()) { 45 | return false; 46 | } 47 | return thisList.containsAll(thatList) && thatList.containsAll(thisList); 48 | } 49 | 50 | /** 51 | * 服务列表不一样 即使ip & port相同也不属于同一个zk节点 52 | * @return 53 | */ 54 | @Override 55 | public int hashCode() { 56 | return Objects.hash(host, port, serviceInfoList.hashCode()); 57 | } 58 | 59 | @Override 60 | public String toString() { 61 | return toJson(); 62 | } 63 | 64 | public List getServiceInfoList() { 65 | return serviceInfoList; 66 | } 67 | 68 | public void setServiceInfoList(List serviceInfoList) { 69 | this.serviceInfoList = serviceInfoList; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /common/src/main/java/com/polyu/rpc/info/RpcServiceInfo.java: -------------------------------------------------------------------------------- 1 | package com.polyu.rpc.info; 2 | 3 | import com.polyu.rpc.util.JsonUtil; 4 | import lombok.NoArgsConstructor; 5 | 6 | import java.util.Objects; 7 | 8 | @NoArgsConstructor 9 | public class RpcServiceInfo { 10 | /** 11 | * 服务名 12 | */ 13 | private String serviceName; 14 | 15 | /** 16 | * 接口版本 17 | */ 18 | private String version; 19 | 20 | public RpcServiceInfo(String serviceName, String version) { 21 | this.serviceName = serviceName; 22 | this.version = version; 23 | } 24 | 25 | public String getServiceName() { 26 | return serviceName; 27 | } 28 | 29 | public void setServiceName(String serviceName) { 30 | this.serviceName = serviceName; 31 | } 32 | 33 | public String getVersion() { 34 | return version; 35 | } 36 | 37 | public void setVersion(String version) { 38 | this.version = version; 39 | } 40 | 41 | @Override 42 | public boolean equals(Object o) { 43 | if (this == o) return true; 44 | if (o == null || getClass() != o.getClass()) return false; 45 | RpcServiceInfo that = (RpcServiceInfo) o; 46 | return Objects.equals(serviceName, that.serviceName) && 47 | Objects.equals(version, that.version); 48 | } 49 | 50 | @Override 51 | public int hashCode() { 52 | return Objects.hash(serviceName, version); 53 | } 54 | 55 | public String toJson() { 56 | return JsonUtil.objectToJson(this); 57 | } 58 | 59 | @Override 60 | public String toString() { 61 | return toJson(); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /common/src/main/java/com/polyu/rpc/registry/ServiceDiscovery.java: -------------------------------------------------------------------------------- 1 | package com.polyu.rpc.registry; 2 | 3 | import com.polyu.rpc.registry.observation.Subject; 4 | 5 | public interface ServiceDiscovery extends Subject { 6 | 7 | /** 8 | * 触发服务发现 9 | */ 10 | void discoveryService(); 11 | 12 | /** 13 | * 停止订阅 14 | */ 15 | void stop(); 16 | } 17 | -------------------------------------------------------------------------------- /common/src/main/java/com/polyu/rpc/registry/ServiceRegistry.java: -------------------------------------------------------------------------------- 1 | package com.polyu.rpc.registry; 2 | 3 | import java.util.Map; 4 | 5 | public interface ServiceRegistry { 6 | 7 | /** 8 | * 服务注册 9 | * @param host 服务提供者ip 10 | * @param port 服务提供者端口号 11 | * @param serviceKey2BeanMap serviceKey -> bean 映射 12 | */ 13 | void registerService(String host, int port, Map serviceKey2BeanMap); 14 | 15 | /** 16 | * 注销服务 17 | */ 18 | void unregisterService(); 19 | } 20 | -------------------------------------------------------------------------------- /common/src/main/java/com/polyu/rpc/registry/observation/Observer.java: -------------------------------------------------------------------------------- 1 | package com.polyu.rpc.registry.observation; 2 | 3 | import com.polyu.rpc.info.RpcMetaData; 4 | import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * 观察者模式 观察者 10 | */ 11 | public interface Observer { 12 | 13 | /** 14 | * 观察者进行服务更新 15 | * @param rpcMetaData rpc server信息 16 | * @param type zk 事件类型 17 | */ 18 | void update(List rpcMetaData, PathChildrenCacheEvent.Type type); 19 | 20 | } 21 | -------------------------------------------------------------------------------- /common/src/main/java/com/polyu/rpc/registry/observation/Subject.java: -------------------------------------------------------------------------------- 1 | package com.polyu.rpc.registry.observation; 2 | 3 | import com.polyu.rpc.info.RpcMetaData; 4 | import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * 观察者模式 事件源 10 | */ 11 | public interface Subject { 12 | 13 | /** 14 | * 注册观察者 15 | * @param observer 观察者 16 | */ 17 | void registerObserver(Observer observer); 18 | 19 | /** 20 | * 通知观察者进行update 21 | * @param rpcMetaData rpc server信息 22 | * @param type zk 事件类型 23 | */ 24 | void notifyObserver(List rpcMetaData, PathChildrenCacheEvent.Type type); 25 | } 26 | -------------------------------------------------------------------------------- /common/src/main/java/com/polyu/rpc/route/DefaultRpcLoadBalanceHolder.java: -------------------------------------------------------------------------------- 1 | package com.polyu.rpc.route; 2 | 3 | import com.polyu.rpc.route.impl.RpcLoadBalanceRoundRobin; 4 | 5 | public class DefaultRpcLoadBalanceHolder { 6 | 7 | private static final RpcLoadBalance rpcLoadBalance = new RpcLoadBalanceRoundRobin(); 8 | 9 | /** 10 | * 获取单例 rpcLoadBalance 11 | * @return RpcLoadBalanceRoundRobin 12 | */ 13 | public static RpcLoadBalance getInstance() { 14 | return rpcLoadBalance; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /common/src/main/java/com/polyu/rpc/route/MetaDataKeeper.java: -------------------------------------------------------------------------------- 1 | package com.polyu.rpc.route; 2 | 3 | import com.polyu.rpc.info.RpcMetaData; 4 | import com.polyu.rpc.info.RpcServiceInfo; 5 | import com.polyu.rpc.util.ServiceUtil; 6 | import lombok.Data; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import java.util.*; 11 | import java.util.concurrent.ConcurrentHashMap; 12 | import java.util.concurrent.CopyOnWriteArrayList; 13 | 14 | /** 15 | * 用于route的快速选择 16 | */ 17 | public class MetaDataKeeper { 18 | private static final Logger logger = LoggerFactory.getLogger(MetaDataKeeper.class); 19 | 20 | private static Map key2MetaDatas = new ConcurrentHashMap<>(); 21 | 22 | @Data 23 | private static class RpcMetaDataContainer { 24 | private List rpcMetaData = new CopyOnWriteArrayList<>(); 25 | private Map metaData2Index = new HashMap<>(); 26 | } 27 | 28 | /** 29 | * zk发生加入新的RpcProtocol 时更新key2Protocols 30 | * @param rpcMetaData 注册信息 31 | */ 32 | public synchronized static void addZkChild(RpcMetaData rpcMetaData) { 33 | if (Objects.isNull(rpcMetaData)) { 34 | return; 35 | } 36 | List serviceInfos = rpcMetaData.getServiceInfoList(); 37 | for (RpcServiceInfo serviceInfo : serviceInfos) { 38 | try { 39 | String serviceKey = ServiceUtil.makeServiceKey(serviceInfo.getServiceName(), serviceInfo.getVersion()); 40 | RpcMetaDataContainer rpcMetaDataContainer = key2MetaDatas.get(serviceKey); 41 | if (Objects.isNull(rpcMetaDataContainer)) { 42 | rpcMetaDataContainer = new RpcMetaDataContainer(); 43 | key2MetaDatas.put(serviceKey, rpcMetaDataContainer); 44 | } 45 | List rpcMetaDatas = rpcMetaDataContainer.getRpcMetaData(); 46 | Map protocol2Index = rpcMetaDataContainer.getMetaData2Index(); 47 | 48 | Integer index = protocol2Index.get(rpcMetaData); 49 | // 如果已经存在 移除进行更新 50 | if (Objects.nonNull(index)) { 51 | rpcMetaDatas.remove(index.intValue()); 52 | } 53 | protocol2Index.put(rpcMetaData, rpcMetaDatas.size()); 54 | rpcMetaDatas.add(rpcMetaData); 55 | } catch (Exception e) { 56 | logger.error("addZkChild operation exception, serviceInfo: {}, exception: {}", serviceInfo, e.getMessage()); 57 | } 58 | } 59 | } 60 | 61 | /** 62 | * 删除rpcProtocol 更新key2Protocols 63 | * @param rpcMetaData 64 | */ 65 | public synchronized static void removeZkChild(RpcMetaData rpcMetaData) { 66 | if (Objects.isNull(rpcMetaData)) { 67 | return; 68 | } 69 | List serviceInfos = rpcMetaData.getServiceInfoList(); 70 | for (RpcServiceInfo serviceInfo : serviceInfos) { 71 | try { 72 | String serviceKey = ServiceUtil.makeServiceKey(serviceInfo.getServiceName(), serviceInfo.getVersion()); 73 | RpcMetaDataContainer rpcMetaDataContainer = key2MetaDatas.get(serviceKey); 74 | if (Objects.isNull(rpcMetaDataContainer)) { 75 | continue; 76 | } 77 | Map protocol2Index = rpcMetaDataContainer.getMetaData2Index(); 78 | List rpcMetaDatas = rpcMetaDataContainer.getRpcMetaData(); 79 | 80 | Integer index = protocol2Index.get(rpcMetaData); 81 | if (Objects.isNull(index)) { 82 | continue; 83 | } 84 | rpcMetaDatas.remove(index.intValue()); 85 | protocol2Index.remove(rpcMetaData); 86 | } catch (Exception e) { 87 | logger.error("removeZkChild operation exception, serviceInfo: {}, exception: {}", serviceInfo, e.getMessage()); 88 | } 89 | } 90 | } 91 | 92 | public static List getProtocolsFromServiceKey(String serviceKey) { 93 | RpcMetaDataContainer rpcMetaDataContainer = key2MetaDatas.get(serviceKey); 94 | if (Objects.isNull(rpcMetaDataContainer)) { 95 | logger.warn("there is no service for serviceKey: {}.", serviceKey); 96 | return null; 97 | } 98 | return rpcMetaDataContainer.getRpcMetaData(); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /common/src/main/java/com/polyu/rpc/route/RpcLoadBalance.java: -------------------------------------------------------------------------------- 1 | package com.polyu.rpc.route; 2 | 3 | import com.polyu.rpc.info.RpcMetaData; 4 | 5 | public interface RpcLoadBalance { 6 | 7 | /** 8 | * 以serviceKey 做负载均衡 9 | * @param serviceKey serviceName & version 10 | * @return RpcProtocol 11 | */ 12 | RpcMetaData route(String serviceKey) throws Exception; 13 | 14 | } 15 | -------------------------------------------------------------------------------- /common/src/main/java/com/polyu/rpc/route/impl/RpcLoadBalanceConsistentHash.java: -------------------------------------------------------------------------------- 1 | package com.polyu.rpc.route.impl; 2 | 3 | import com.google.common.hash.Hashing; 4 | import com.polyu.rpc.info.RpcMetaData; 5 | import com.polyu.rpc.route.MetaDataKeeper; 6 | import com.polyu.rpc.route.RpcLoadBalance; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import java.util.List; 11 | 12 | /** 13 | * 一致性哈希 14 | */ 15 | public class RpcLoadBalanceConsistentHash implements RpcLoadBalance { 16 | private static final Logger logger = LoggerFactory.getLogger(RpcLoadBalanceConsistentHash.class); 17 | 18 | private RpcMetaData doRoute(String serviceKey, List addressList) { 19 | int index = Hashing.consistentHash(serviceKey.hashCode(), addressList.size()); 20 | return addressList.get(index); 21 | } 22 | 23 | @Override 24 | public RpcMetaData route(String serviceKey) throws Exception { 25 | logger.debug("RpcLoadBalanceConsistentHash is routing for {}.", serviceKey); 26 | List addressList = MetaDataKeeper.getProtocolsFromServiceKey(serviceKey); 27 | if (addressList == null || addressList.isEmpty()) { 28 | throw new Exception("Can not find connection for service: " + serviceKey); 29 | } 30 | return doRoute(serviceKey, addressList); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /common/src/main/java/com/polyu/rpc/route/impl/RpcLoadBalanceRandom.java: -------------------------------------------------------------------------------- 1 | package com.polyu.rpc.route.impl; 2 | 3 | import com.polyu.rpc.info.RpcMetaData; 4 | import com.polyu.rpc.route.MetaDataKeeper; 5 | import com.polyu.rpc.route.RpcLoadBalance; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import java.util.List; 10 | import java.util.Random; 11 | 12 | /** 13 | * 随机 14 | */ 15 | public class RpcLoadBalanceRandom implements RpcLoadBalance { 16 | 17 | private Random random; 18 | private static final Logger logger = LoggerFactory.getLogger(RpcLoadBalanceRandom.class); 19 | 20 | public RpcLoadBalanceRandom() { 21 | this.random = new Random(); 22 | } 23 | 24 | private RpcMetaData doRoute(List addressList) { 25 | int index = random.nextInt(addressList.size()); 26 | return addressList.get(index); 27 | } 28 | 29 | @Override 30 | public RpcMetaData route(String serviceKey) throws Exception { 31 | logger.debug("RpcLoadBalanceRandom is routing for {}.", serviceKey); 32 | List addressList = MetaDataKeeper.getProtocolsFromServiceKey(serviceKey); 33 | if (addressList != null && addressList.size() > 0) { 34 | return doRoute(addressList); 35 | } else { 36 | throw new Exception("Can not find connection for service: " + serviceKey); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /common/src/main/java/com/polyu/rpc/route/impl/RpcLoadBalanceRoundRobin.java: -------------------------------------------------------------------------------- 1 | package com.polyu.rpc.route.impl; 2 | 3 | 4 | import com.polyu.rpc.info.RpcMetaData; 5 | import com.polyu.rpc.route.MetaDataKeeper; 6 | import com.polyu.rpc.route.RpcLoadBalance; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import java.util.List; 11 | import java.util.concurrent.atomic.AtomicInteger; 12 | 13 | /** 14 | * 轮询 15 | */ 16 | public class RpcLoadBalanceRoundRobin implements RpcLoadBalance { 17 | private AtomicInteger roundRobin; 18 | private static final Logger logger = LoggerFactory.getLogger(RpcLoadBalanceRoundRobin.class); 19 | 20 | public RpcLoadBalanceRoundRobin() { 21 | roundRobin = new AtomicInteger(0); 22 | } 23 | 24 | private RpcMetaData doRoute(List addressList) { 25 | int size = addressList.size(); 26 | nextNumUpdate(); 27 | int index = (this.roundRobin.get() + size) % size; 28 | return addressList.get(index); 29 | } 30 | 31 | /** 32 | * 防止越界 33 | */ 34 | private void nextNumUpdate() { 35 | this.roundRobin.updateAndGet((x) -> { 36 | if (x >= Integer.MAX_VALUE) { 37 | return 0; 38 | } 39 | return x + 1; 40 | }); 41 | } 42 | 43 | @Override 44 | public RpcMetaData route(String serviceKey) throws Exception { 45 | logger.debug("RpcLoadBalanceRoundRobin is routing for {}.", serviceKey); 46 | List addressList = MetaDataKeeper.getProtocolsFromServiceKey(serviceKey); 47 | if (addressList != null && addressList.size() > 0) { 48 | return doRoute(addressList); 49 | } else { 50 | throw new Exception("Can not find connection for service: " + serviceKey); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /common/src/main/java/com/polyu/rpc/serializer/Serializer.java: -------------------------------------------------------------------------------- 1 | package com.polyu.rpc.serializer; 2 | 3 | public interface Serializer { 4 | 5 | /** 6 | * 序列化 7 | * @param obj 对象 8 | * @return 字节数组 9 | */ 10 | byte[] serialize(T obj); 11 | 12 | /** 13 | * 反序列化 14 | * @param bytes 字节数组 15 | * @param clazz .class 类型 16 | * @return 原对象 17 | */ 18 | Object deserialize(byte[] bytes, Class clazz); 19 | 20 | } 21 | -------------------------------------------------------------------------------- /common/src/main/java/com/polyu/rpc/serializer/kryo/KryoPoolFactory.java: -------------------------------------------------------------------------------- 1 | package com.polyu.rpc.serializer.kryo; 2 | 3 | import com.esotericsoftware.kryo.Kryo; 4 | import com.esotericsoftware.kryo.pool.KryoFactory; 5 | import com.esotericsoftware.kryo.pool.KryoPool; 6 | import com.polyu.rpc.codec.RpcResponse; 7 | import org.objenesis.strategy.StdInstantiatorStrategy; 8 | import com.polyu.rpc.codec.RpcRequest; 9 | 10 | class KryoPoolFactory { 11 | private static volatile KryoPoolFactory poolFactory = null; 12 | 13 | private KryoFactory factory = () -> { 14 | Kryo kryo = new Kryo(); 15 | kryo.setReferences(false); 16 | kryo.register(RpcRequest.class); 17 | kryo.register(RpcResponse.class); 18 | Kryo.DefaultInstantiatorStrategy strategy = (Kryo.DefaultInstantiatorStrategy) kryo.getInstantiatorStrategy(); 19 | strategy.setFallbackInstantiatorStrategy(new StdInstantiatorStrategy()); 20 | return kryo; 21 | }; 22 | 23 | private KryoPool pool = new KryoPool.Builder(factory).build(); 24 | 25 | private KryoPoolFactory() { 26 | } 27 | 28 | static KryoPool getKryoPoolInstance() { 29 | if (poolFactory == null) { 30 | synchronized (KryoPoolFactory.class) { 31 | if (poolFactory == null) { 32 | poolFactory = new KryoPoolFactory(); 33 | } 34 | } 35 | } 36 | return poolFactory.getPool(); 37 | } 38 | 39 | private KryoPool getPool() { 40 | return pool; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /common/src/main/java/com/polyu/rpc/serializer/kryo/KryoSerializer.java: -------------------------------------------------------------------------------- 1 | package com.polyu.rpc.serializer.kryo; 2 | 3 | import com.esotericsoftware.kryo.Kryo; 4 | import com.esotericsoftware.kryo.io.Input; 5 | import com.esotericsoftware.kryo.io.Output; 6 | import com.esotericsoftware.kryo.pool.KryoPool; 7 | import com.polyu.rpc.serializer.Serializer; 8 | 9 | import java.io.ByteArrayInputStream; 10 | import java.io.ByteArrayOutputStream; 11 | import java.io.IOException; 12 | 13 | public class KryoSerializer implements Serializer { 14 | private KryoPool pool = KryoPoolFactory.getKryoPoolInstance(); 15 | 16 | /** 17 | * 序列化 18 | * @param obj 对象 19 | * @param 20 | * @return 字节数组 21 | */ 22 | @Override 23 | public byte[] serialize(T obj) { 24 | Kryo kryo = pool.borrow(); 25 | ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); 26 | Output out = new Output(byteArrayOutputStream); 27 | try { 28 | kryo.writeObject(out, obj); 29 | out.close(); 30 | return byteArrayOutputStream.toByteArray(); 31 | } catch (Exception ex) { 32 | throw new RuntimeException(ex); 33 | } finally { 34 | try { 35 | byteArrayOutputStream.close(); 36 | } catch (IOException e) { 37 | throw new RuntimeException(e); 38 | } finally { 39 | pool.release(kryo); 40 | } 41 | } 42 | } 43 | 44 | /** 45 | * 反序列化 46 | * @param bytes 字节数组 47 | * @param clazz 类型信息 48 | * @param 49 | * @return 50 | */ 51 | @Override 52 | public Object deserialize(byte[] bytes, Class clazz) { 53 | Kryo kryo = pool.borrow(); 54 | ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes); 55 | Input in = new Input(byteArrayInputStream); 56 | try { 57 | Object result = kryo.readObject(in, clazz); 58 | in.close(); 59 | return result; 60 | } catch (Exception ex) { 61 | throw new RuntimeException(ex); 62 | } finally { 63 | try { 64 | byteArrayInputStream.close(); 65 | } catch (IOException e) { 66 | throw new RuntimeException(e); 67 | } finally { 68 | pool.release(kryo); 69 | } 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /common/src/main/java/com/polyu/rpc/util/JsonUtil.java: -------------------------------------------------------------------------------- 1 | package com.polyu.rpc.util; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import com.fasterxml.jackson.core.JsonGenerator; 5 | import com.fasterxml.jackson.core.JsonParser; 6 | import com.fasterxml.jackson.core.JsonProcessingException; 7 | import com.fasterxml.jackson.databind.DeserializationFeature; 8 | import com.fasterxml.jackson.databind.JavaType; 9 | import com.fasterxml.jackson.databind.ObjectMapper; 10 | import com.fasterxml.jackson.databind.SerializationFeature; 11 | 12 | import java.io.IOException; 13 | import java.text.SimpleDateFormat; 14 | import java.util.HashMap; 15 | 16 | public class JsonUtil { 17 | private static ObjectMapper objMapper = new ObjectMapper(); 18 | 19 | static { 20 | SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 21 | objMapper.setDateFormat(dateFormat); 22 | objMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); 23 | objMapper.enable(SerializationFeature.INDENT_OUTPUT); 24 | objMapper.configure(JsonGenerator.Feature.AUTO_CLOSE_TARGET, false); 25 | objMapper.configure(JsonGenerator.Feature.AUTO_CLOSE_JSON_CONTENT, false); 26 | objMapper.disable(SerializationFeature.FLUSH_AFTER_WRITE_VALUE); 27 | objMapper.disable(SerializationFeature.CLOSE_CLOSEABLE); 28 | objMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); 29 | objMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); 30 | objMapper.configure(JsonParser.Feature.IGNORE_UNDEFINED, true); 31 | } 32 | 33 | public static byte[] serialize(T obj) { 34 | byte[] bytes; 35 | try { 36 | bytes = objMapper.writeValueAsBytes(obj); 37 | } catch (JsonProcessingException e) { 38 | throw new IllegalStateException(e.getMessage(), e); 39 | } 40 | return bytes; 41 | } 42 | 43 | public static T deserialize(byte[] data, Class cls) { 44 | T obj; 45 | try { 46 | obj = objMapper.readValue(data, cls); 47 | } catch (IOException e) { 48 | throw new IllegalStateException(e.getMessage(), e); 49 | } 50 | return obj; 51 | } 52 | 53 | public static type jsonToObject(String json, Class cls) { 54 | type obj; 55 | JavaType javaType = objMapper.getTypeFactory().constructType(cls); 56 | try { 57 | obj = objMapper.readValue(json, javaType); 58 | } catch (IOException e) { 59 | throw new IllegalStateException(e.getMessage(), e); 60 | } 61 | return obj; 62 | } 63 | 64 | public static type jsonToObjectList(String json, 65 | Class collectionClass, Class... elementClass) { 66 | type obj; 67 | JavaType javaType = objMapper.getTypeFactory().constructParametricType( 68 | collectionClass, elementClass); 69 | try { 70 | obj = objMapper.readValue(json, javaType); 71 | } catch (IOException e) { 72 | throw new IllegalStateException(e.getMessage(), e); 73 | } 74 | return obj; 75 | } 76 | 77 | public static type jsonToObjectHashMap(String json, 78 | Class keyClass, Class valueClass) { 79 | type obj; 80 | JavaType javaType = objMapper.getTypeFactory().constructParametricType(HashMap.class, keyClass, valueClass); 81 | try { 82 | obj = objMapper.readValue(json, javaType); 83 | } catch (IOException e) { 84 | throw new IllegalStateException(e.getMessage(), e); 85 | } 86 | return obj; 87 | } 88 | 89 | public static String objectToJson(Object o) { 90 | String json; 91 | try { 92 | json = objMapper.writeValueAsString(o); 93 | } catch (IOException e) { 94 | throw new IllegalStateException(e.getMessage(), e); 95 | } 96 | return json; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /common/src/main/java/com/polyu/rpc/util/ServiceUtil.java: -------------------------------------------------------------------------------- 1 | package com.polyu.rpc.util; 2 | 3 | import com.polyu.rpc.info.RpcServiceInfo; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | import java.util.Map; 10 | 11 | /** 12 | * Service相关工具类 13 | */ 14 | public class ServiceUtil { 15 | private static final Logger logger = LoggerFactory.getLogger(ServiceUtil.class); 16 | 17 | private static final String SERVICE_CONCAT_TOKEN = "#"; 18 | 19 | /** 20 | * 服务serviceKey生成 21 | * @param interfaceName 接口名 22 | * @param version 版本 23 | * @return key字符串 24 | */ 25 | public static String makeServiceKey(String interfaceName, String version) { 26 | String serviceKey = interfaceName; 27 | if (version != null && version.trim().length() > 0) { 28 | serviceKey += SERVICE_CONCAT_TOKEN.concat(version); 29 | } 30 | return serviceKey; 31 | } 32 | 33 | /** 34 | * 由 serviceKey2BeanMap 生成 RpcServiceInfo List 35 | * @param serviceKey2BeanMap serviceKey -> 实现类 bean 36 | * @return 服务信息列表 37 | */ 38 | public static List beanMap2RpcServiceInfos(Map serviceKey2BeanMap) { 39 | List serviceInfoList = new ArrayList<>(); 40 | for (String key : serviceKey2BeanMap.keySet()) { 41 | String[] serviceInfo = key.split(ServiceUtil.SERVICE_CONCAT_TOKEN); 42 | if (serviceInfo.length > 0) { 43 | RpcServiceInfo rpcServiceInfo = new RpcServiceInfo(); 44 | rpcServiceInfo.setServiceName(serviceInfo[0]); 45 | if (serviceInfo.length == 2) { 46 | rpcServiceInfo.setVersion(serviceInfo[1]); 47 | } else { 48 | rpcServiceInfo.setVersion(""); 49 | } 50 | logger.info("Register new service: {}.", key); 51 | serviceInfoList.add(rpcServiceInfo); 52 | } else { 53 | logger.warn("Can not get service name and version: {}.", key); 54 | } 55 | } 56 | return serviceInfoList; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /common/src/main/java/com/polyu/rpc/util/ThreadPoolUtil.java: -------------------------------------------------------------------------------- 1 | package com.polyu.rpc.util; 2 | 3 | import java.util.concurrent.LinkedBlockingQueue; 4 | import java.util.concurrent.ThreadFactory; 5 | import java.util.concurrent.ThreadPoolExecutor; 6 | import java.util.concurrent.TimeUnit; 7 | 8 | public class ThreadPoolUtil { 9 | private static final long KEEP_ALIVE_TIME = 60L; 10 | private static final int BLOCKING_QUEUE_CAPACITY = 1000; 11 | 12 | /** 13 | * 构造server线程池 14 | * @param serviceName 服务接口名 15 | * @param corePoolSize 核心线程数 16 | * @param maxPoolSize 最大线程数 17 | * @return 18 | */ 19 | public static ThreadPoolExecutor makeServerThreadPool(final String serviceName, int corePoolSize, int maxPoolSize) { 20 | return new ThreadPoolExecutor( 21 | corePoolSize, 22 | maxPoolSize, 23 | KEEP_ALIVE_TIME, 24 | TimeUnit.SECONDS, 25 | new LinkedBlockingQueue<>(BLOCKING_QUEUE_CAPACITY), 26 | r -> new Thread(r, getThreadName(serviceName, r)), 27 | new ThreadPoolExecutor.AbortPolicy()); 28 | } 29 | 30 | public static ThreadPoolExecutor makeThreadPool(int corePoolSize, int maxPoolSize, long keepAliveTime) { 31 | return new ThreadPoolExecutor( 32 | corePoolSize, 33 | maxPoolSize, 34 | keepAliveTime, 35 | TimeUnit.SECONDS, 36 | new LinkedBlockingQueue<>(BLOCKING_QUEUE_CAPACITY), 37 | Thread::new, 38 | new ThreadPoolExecutor.AbortPolicy()); 39 | } 40 | 41 | private static String getThreadName(final String serviceName, Runnable runnable) { 42 | return "netty-rpc-" + 43 | serviceName + 44 | "-" + 45 | runnable.hashCode(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /common/src/main/resources/log4j2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /netty-client/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | bRPC 7 | com.polyu 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | netty-client 13 | 14 | 15 | com.polyu 16 | common 17 | 1.0-SNAPSHOT 18 | compile 19 | 20 | 21 | com.polyu 22 | registry 23 | 1.0-SNAPSHOT 24 | compile 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /netty-client/src/main/java/com/polyu/rpc/client/RpcClient.java: -------------------------------------------------------------------------------- 1 | package com.polyu.rpc.client; 2 | 3 | import com.polyu.rpc.annotation.BRpcConsumer; 4 | import com.polyu.rpc.client.connect.ConnectUpdater; 5 | import com.polyu.rpc.client.invoke.InvokeProxy; 6 | import com.polyu.rpc.client.result.PendingRpcHolder; 7 | import com.polyu.rpc.registry.ServiceDiscovery; 8 | import com.polyu.rpc.route.RpcLoadBalance; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | import org.springframework.beans.BeansException; 12 | import org.springframework.beans.factory.DisposableBean; 13 | import org.springframework.context.ApplicationContext; 14 | import org.springframework.context.ApplicationContextAware; 15 | 16 | import java.lang.reflect.Field; 17 | import java.lang.reflect.Proxy; 18 | import java.util.concurrent.LinkedBlockingQueue; 19 | import java.util.concurrent.ThreadPoolExecutor; 20 | import java.util.concurrent.TimeUnit; 21 | 22 | public class RpcClient implements ApplicationContextAware, DisposableBean { 23 | private static final Logger logger = LoggerFactory.getLogger(RpcClient.class); 24 | 25 | private ServiceDiscovery serviceDiscovery; 26 | /** 27 | * 异步请求/callback 线程池 28 | */ 29 | private static ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(16, 16, 30 | 600L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1000)); 31 | 32 | /** 33 | * 注册中心地址 & 注册中心选型 34 | * @param serviceDiscovery 注册中心选型 nacos / zk 35 | */ 36 | public RpcClient(ServiceDiscovery serviceDiscovery) { 37 | ConnectUpdater connectUpdater = ConnectUpdater.getAndInitInstance(serviceDiscovery); 38 | this.serviceDiscovery = connectUpdater.getServiceDiscovery(); 39 | this.serviceDiscovery.discoveryService(); 40 | PendingRpcHolder.startTimeoutThreadPool(); 41 | } 42 | 43 | @SuppressWarnings("unchecked") 44 | public static T getProxyInstance(Class interfaceClass, String version, RpcLoadBalance loadBalance, long timeoutLength) { 45 | return (T) Proxy.newProxyInstance( 46 | interfaceClass.getClassLoader(), 47 | new Class[]{interfaceClass}, 48 | new InvokeProxy(version, loadBalance, timeoutLength) 49 | ); 50 | } 51 | 52 | public static void submit(Runnable task) { 53 | threadPoolExecutor.submit(task); 54 | } 55 | 56 | private void stop() { 57 | threadPoolExecutor.shutdown(); 58 | serviceDiscovery.stop(); 59 | ConnectUpdater.getInstance().stop(); 60 | PendingRpcHolder.stop(); 61 | } 62 | 63 | @Override 64 | public void destroy() { 65 | this.stop(); 66 | } 67 | 68 | @Override 69 | public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 70 | String[] beanNames = applicationContext.getBeanDefinitionNames(); 71 | for (String beanName : beanNames) { 72 | Object bean = applicationContext.getBean(beanName); 73 | Field[] fields = bean.getClass().getDeclaredFields(); 74 | try { 75 | for (Field field : fields) { 76 | BRpcConsumer rpcAutowired = field.getAnnotation(BRpcConsumer.class); 77 | if (rpcAutowired != null) { 78 | String version = rpcAutowired.version(); 79 | RpcLoadBalance loadBalance = (RpcLoadBalance) rpcAutowired.loadBalanceStrategy().newInstance(); 80 | long timeoutLength = rpcAutowired.timeOutLength(); 81 | field.setAccessible(true); 82 | field.set(bean, getProxyInstance(field.getType(), version, loadBalance, timeoutLength)); 83 | } 84 | } 85 | } catch (Exception e) { 86 | logger.error(e.toString()); 87 | } 88 | } 89 | } 90 | } 91 | 92 | -------------------------------------------------------------------------------- /netty-client/src/main/java/com/polyu/rpc/client/connect/ConnectUpdater.java: -------------------------------------------------------------------------------- 1 | package com.polyu.rpc.client.connect; 2 | 3 | import com.polyu.rpc.registry.observation.Observer; 4 | import com.polyu.rpc.registry.ServiceDiscovery; 5 | import com.polyu.rpc.route.MetaDataKeeper; 6 | import com.polyu.rpc.info.RpcMetaData; 7 | import com.polyu.rpc.client.netty.handler.RpcClientHandler; 8 | import lombok.Data; 9 | import lombok.NoArgsConstructor; 10 | import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent; 11 | import org.slf4j.Logger; 12 | import org.slf4j.LoggerFactory; 13 | 14 | import java.util.*; 15 | import java.util.concurrent.*; 16 | 17 | public class ConnectUpdater implements Observer { 18 | private static final Logger logger = LoggerFactory.getLogger(ConnectUpdater.class); 19 | 20 | private CopyOnWriteArraySet rpcMetaDataSet = new CopyOnWriteArraySet<>(); 21 | 22 | private volatile boolean isRunning = true; 23 | private ServiceDiscovery serviceDiscovery; 24 | 25 | /** 26 | * 观察者模式 持有事件 27 | * @param serviceDiscovery 服务发现 28 | */ 29 | private ConnectUpdater(ServiceDiscovery serviceDiscovery) { 30 | this.serviceDiscovery = serviceDiscovery; 31 | } 32 | 33 | /** 34 | * zk 根据事件进行更新 35 | * nacos 没有事件类型更新 36 | * @param rpcMetaDatas rpc server信息 37 | * @param type 更新类型(nacos更新类型以及zk全量更新为null) 38 | */ 39 | @Override 40 | public void update(List rpcMetaDatas, PathChildrenCacheEvent.Type type) { 41 | if (type == null) { 42 | updateConnectedServer(rpcMetaDatas); 43 | return; 44 | } 45 | RpcMetaData rpcMetaData = rpcMetaDatas.get(0); 46 | updateConnectedServer(rpcMetaData, type); 47 | } 48 | 49 | /** 50 | * dcl单例 51 | */ 52 | private static class SingletonHolder { 53 | private static volatile ConnectUpdater instance; 54 | 55 | static ConnectUpdater getInstance(ServiceDiscovery discovery) { 56 | if (instance == null) { 57 | synchronized (SingletonHolder.class) { 58 | if (instance == null) { 59 | instance = new ConnectUpdater(discovery); 60 | // 注册观察事件 61 | instance.getServiceDiscovery().registerObserver(instance); 62 | } 63 | } 64 | } 65 | return instance; 66 | } 67 | 68 | } 69 | 70 | /** 71 | * 获取并初始化实例 72 | * @return 73 | */ 74 | public static ConnectUpdater getAndInitInstance(ServiceDiscovery discovery) { 75 | return SingletonHolder.getInstance(discovery); 76 | } 77 | 78 | /** 79 | * 初始化后获取单例方法 80 | * @return 81 | */ 82 | public static ConnectUpdater getInstance() { 83 | return SingletonHolder.instance; 84 | } 85 | 86 | /** 87 | * 全量更新连接 88 | * @param serviceList 89 | */ 90 | private void updateConnectedServer(List serviceList) { 91 | if (serviceList != null && serviceList.size() > 0) { 92 | HashSet serviceSet = new HashSet<>(serviceList.size()); 93 | serviceSet.addAll(serviceList); 94 | 95 | // 加入 & 连接原来没有的 96 | for (final RpcMetaData rpcMetaData : serviceSet) { 97 | if (!rpcMetaDataSet.contains(rpcMetaData)) { 98 | Connector.getInstance().connectServerNode(rpcMetaData); 99 | } 100 | } 101 | 102 | // 关闭 & 删除现在去处的 103 | for (RpcMetaData rpcMetaData : rpcMetaDataSet) { 104 | if (!serviceSet.contains(rpcMetaData)) { 105 | logger.info("Remove invalid service: {}.", rpcMetaData.toJson()); 106 | Connector.getInstance().removeAndCloseHandler(rpcMetaData); 107 | } 108 | } 109 | } else { 110 | logger.error("No available service!"); 111 | for (RpcMetaData rpcMetaData : rpcMetaDataSet) { 112 | Connector.getInstance().removeAndCloseHandler(rpcMetaData); 113 | } 114 | } 115 | } 116 | 117 | /** 118 | * 根据类型增量更新连接 119 | * @param rpcMetaData 元数据 120 | * @param type 更新类型(zk特有) 121 | */ 122 | public void updateConnectedServer(RpcMetaData rpcMetaData, PathChildrenCacheEvent.Type type) { 123 | if (rpcMetaData == null) { 124 | return; 125 | } 126 | if (type == PathChildrenCacheEvent.Type.CHILD_ADDED && !rpcMetaDataSet.contains(rpcMetaData)) { 127 | Connector.getInstance().connectServerNode(rpcMetaData); 128 | } else if (type == PathChildrenCacheEvent.Type.CHILD_UPDATED) { 129 | // 对于主机ip & port没有改变的zk child更新,不进行重新连接。直接更新connectedServerNodes 130 | RpcMetaDataChanger rpcMetaDataChanger = serverHostUnChange(rpcMetaData); 131 | if (rpcMetaDataChanger.isNeedChange()) { 132 | RpcMetaData oldProtocol = rpcMetaDataChanger.getOldMetaData(); 133 | Map connectedServerNodes = Connector.getInstance().getConnectedServerNodes(); 134 | RpcClientHandler rpcClientHandler = connectedServerNodes.get(oldProtocol); 135 | connectedServerNodes.put(rpcMetaData, rpcClientHandler); 136 | connectedServerNodes.remove(oldProtocol); 137 | 138 | rpcMetaDataSet.add(rpcMetaData); 139 | rpcMetaDataSet.remove(oldProtocol); 140 | 141 | MetaDataKeeper.removeZkChild(oldProtocol); 142 | MetaDataKeeper.addZkChild(rpcMetaData); 143 | return; 144 | } 145 | Connector.getInstance().removeAndCloseHandler(rpcMetaData); 146 | Connector.getInstance().connectServerNode(rpcMetaData); 147 | } else if (type == PathChildrenCacheEvent.Type.CHILD_REMOVED) { 148 | Connector.getInstance().removeAndCloseHandler(rpcMetaData); 149 | } else { 150 | throw new IllegalArgumentException("Unknown type: " + type); 151 | } 152 | } 153 | 154 | /** 155 | * 判断zk节点变更时 host ip & port是否改变 156 | * 返回是否需要改变判断 & 需要替换的protocol 157 | * @return 158 | */ 159 | private RpcMetaDataChanger serverHostUnChange(RpcMetaData rpcMetaData) { 160 | for (RpcMetaData presentProtocol : rpcMetaDataSet) { 161 | String presentHost = presentProtocol.getHost(); 162 | int presentPort = presentProtocol.getPort(); 163 | if (presentHost != null && !"".equals(presentHost)) { 164 | if (presentHost.equals(rpcMetaData.getHost()) && presentPort == rpcMetaData.getPort()) { 165 | return new RpcMetaDataChanger(true, presentProtocol); 166 | } 167 | } 168 | } 169 | return new RpcMetaDataChanger(false); 170 | } 171 | 172 | @Data 173 | @NoArgsConstructor 174 | private static class RpcMetaDataChanger { 175 | boolean needChange; 176 | RpcMetaData oldMetaData; 177 | 178 | RpcMetaDataChanger(boolean needChange, RpcMetaData oldMetaData) { 179 | this.needChange = needChange; 180 | this.oldMetaData = oldMetaData; 181 | } 182 | 183 | RpcMetaDataChanger(boolean needChange) { 184 | this.needChange = needChange; 185 | } 186 | } 187 | 188 | public CopyOnWriteArraySet getRpcMetaDataSet() { 189 | return rpcMetaDataSet; 190 | } 191 | 192 | /** 193 | * 获取服务发现实例 194 | * @return serviceDiscoveryImpl 195 | */ 196 | public ServiceDiscovery getServiceDiscovery() { 197 | return serviceDiscovery; 198 | } 199 | 200 | /** 201 | * 获取运行状态 202 | * @return boolean 203 | */ 204 | boolean isRunning() { 205 | return isRunning; 206 | } 207 | 208 | public void stop() { 209 | isRunning = false; 210 | for (RpcMetaData rpcMetaData : rpcMetaDataSet) { 211 | Connector.getInstance().removeAndCloseHandler(rpcMetaData); 212 | } 213 | HandlerManager.signalAvailableHandler(); 214 | Connector.getInstance().getConnectionThreadPool().shutdown(); 215 | Connector.getInstance().getEventLoopGroup().shutdownGracefully(); 216 | } 217 | 218 | } 219 | -------------------------------------------------------------------------------- /netty-client/src/main/java/com/polyu/rpc/client/connect/Connector.java: -------------------------------------------------------------------------------- 1 | package com.polyu.rpc.client.connect; 2 | 3 | import com.polyu.rpc.client.netty.RpcClientInitializer; 4 | import com.polyu.rpc.client.netty.handler.RpcClientHandler; 5 | import com.polyu.rpc.info.RpcMetaData; 6 | import com.polyu.rpc.info.RpcServiceInfo; 7 | import com.polyu.rpc.route.MetaDataKeeper; 8 | import com.polyu.rpc.util.ThreadPoolUtil; 9 | import io.netty.bootstrap.Bootstrap; 10 | import io.netty.channel.ChannelFuture; 11 | import io.netty.channel.ChannelFutureListener; 12 | import io.netty.channel.EventLoopGroup; 13 | import io.netty.channel.nio.NioEventLoopGroup; 14 | import io.netty.channel.socket.nio.NioSocketChannel; 15 | import io.netty.util.NettyRuntime; 16 | import lombok.Data; 17 | import org.slf4j.Logger; 18 | import org.slf4j.LoggerFactory; 19 | 20 | import java.net.InetSocketAddress; 21 | import java.util.Map; 22 | import java.util.concurrent.ConcurrentHashMap; 23 | import java.util.concurrent.ThreadPoolExecutor; 24 | 25 | @Data 26 | public class Connector { 27 | private static final Logger logger = LoggerFactory.getLogger(Connector.class); 28 | 29 | /** 30 | * client建立连线程池 31 | */ 32 | private ThreadPoolExecutor connectionThreadPool = ThreadPoolUtil.makeThreadPool(4, 8, 600L); 33 | 34 | private Map connectedServerNodes = new ConcurrentHashMap<>(); 35 | private EventLoopGroup eventLoopGroup = new NioEventLoopGroup(NettyRuntime.availableProcessors() / 2); 36 | private static volatile Connector connector; 37 | 38 | public static Connector getInstance() { 39 | if (connector == null) { 40 | synchronized (Connector.class) { 41 | if (connector == null) { 42 | connector = new Connector(); 43 | } 44 | } 45 | } 46 | return connector; 47 | } 48 | 49 | /** 50 | * 连接peer host 51 | * @param rpcMetaData peer server 元信息 52 | */ 53 | public void connectServerNode(RpcMetaData rpcMetaData) { 54 | if (rpcMetaData.getServiceInfoList() == null || rpcMetaData.getServiceInfoList().isEmpty()) { 55 | logger.info("No service on node, host: {}, port: {}.", rpcMetaData.getHost(), rpcMetaData.getPort()); 56 | return; 57 | } 58 | ConnectUpdater.getInstance().getRpcMetaDataSet().add(rpcMetaData); 59 | logger.info("New service node, host: {}, port: {}.", rpcMetaData.getHost(), rpcMetaData.getPort()); 60 | for (RpcServiceInfo serviceProtocol : rpcMetaData.getServiceInfoList()) { 61 | logger.info("New service info, name: {}, version: {}.", serviceProtocol.getServiceName(), serviceProtocol.getVersion()); 62 | } 63 | final InetSocketAddress remotePeer = new InetSocketAddress(rpcMetaData.getHost(), rpcMetaData.getPort()); 64 | connectionThreadPool.submit(new Runnable() { 65 | @Override 66 | public void run() { 67 | Bootstrap b = new Bootstrap(); 68 | b.group(eventLoopGroup) 69 | .channel(NioSocketChannel.class) 70 | .handler(new RpcClientInitializer()); 71 | 72 | ChannelFuture channelFuture = b.connect(remotePeer); 73 | channelFuture.addListener(new ChannelFutureListener() { 74 | @Override 75 | public void operationComplete(final ChannelFuture channelFuture) { 76 | if (channelFuture.isSuccess()) { 77 | logger.info("Successfully connect to remote server, remote peer = {}.", remotePeer); 78 | RpcClientHandler rpcClientHandler = channelFuture.channel().pipeline().get(RpcClientHandler.class); 79 | connectedServerNodes.put(rpcMetaData, rpcClientHandler); 80 | rpcClientHandler.setRpcMetaData(rpcMetaData); 81 | rpcClientHandler.setIntentionalClose(false); 82 | // 方便后续快速选择 在此记录 83 | MetaDataKeeper.addZkChild(rpcMetaData); 84 | HandlerManager.signalAvailableHandler(); 85 | } else { 86 | // 失败进行回收 87 | removeConnectRecord(rpcMetaData); 88 | logger.error("Can not connect to remote server, remote peer = {}.", remotePeer); 89 | } 90 | } 91 | }); 92 | } 93 | }); 94 | } 95 | 96 | /** 97 | * 关闭 & 移除 连接 98 | * @param rpcMetaData peer server 信息 99 | */ 100 | public void removeAndCloseHandler(RpcMetaData rpcMetaData) { 101 | RpcClientHandler handler = connectedServerNodes.get(rpcMetaData); 102 | removeConnectRecord(rpcMetaData); 103 | if (handler != null) { 104 | handler.setIntentionalClose(true); 105 | handler.close(); 106 | } 107 | } 108 | 109 | /** 110 | * 连接失败 移除连接记录 111 | * @param rpcMetaData server information 112 | */ 113 | public void removeConnectRecord(RpcMetaData rpcMetaData) { 114 | ConnectUpdater.getInstance().getRpcMetaDataSet().remove(rpcMetaData); 115 | connectedServerNodes.remove(rpcMetaData); 116 | MetaDataKeeper.removeZkChild(rpcMetaData); 117 | logger.info("Remove one connection, host: {}, port: {}.", rpcMetaData.getHost(), rpcMetaData.getPort()); 118 | } 119 | 120 | } 121 | -------------------------------------------------------------------------------- /netty-client/src/main/java/com/polyu/rpc/client/connect/HandlerManager.java: -------------------------------------------------------------------------------- 1 | package com.polyu.rpc.client.connect; 2 | 3 | import com.polyu.rpc.client.netty.handler.RpcClientHandler; 4 | import com.polyu.rpc.info.RpcMetaData; 5 | import com.polyu.rpc.route.RpcLoadBalance; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import java.util.Map; 10 | import java.util.concurrent.TimeUnit; 11 | import java.util.concurrent.locks.Condition; 12 | import java.util.concurrent.locks.ReentrantLock; 13 | 14 | public class HandlerManager { 15 | private static final Logger logger = LoggerFactory.getLogger(HandlerManager.class); 16 | 17 | private static ReentrantLock lock = new ReentrantLock(); 18 | private static Condition condition = lock.newCondition(); 19 | /** 20 | * 当没有可用handler时 重试时间间隔 21 | */ 22 | private static final long HANDLER_RETRY_TIME_INTERVAL = 5000L; 23 | 24 | /** 25 | * 选择handler 进行发送 26 | * @param serviceKey 服务名 & 版本标识 27 | * @param loadBalance 负载均衡实例 28 | * @return handler 29 | * @throws Exception Client close 30 | */ 31 | public static RpcClientHandler chooseHandler(String serviceKey, RpcLoadBalance loadBalance) throws Exception { 32 | Map connectedServerNodes = Connector.getInstance().getConnectedServerNodes(); 33 | while (connectedServerNodes.values().size() <= 0) { 34 | if (!ConnectUpdater.getInstance().isRunning()) { 35 | throw new RuntimeException("Client is closed."); 36 | } 37 | try { 38 | waitingForHandler(); 39 | } catch (InterruptedException e) { 40 | logger.error("Waiting for available service is interrupted!", e); 41 | } 42 | } 43 | RpcMetaData rpcMetaData = loadBalance.route(serviceKey); 44 | RpcClientHandler handler = connectedServerNodes.get(rpcMetaData); 45 | if (handler == null) { 46 | throw new Exception("Can not get available connection."); 47 | } 48 | return handler; 49 | } 50 | 51 | /** 52 | * 唤醒被阻塞的线程 53 | */ 54 | static void signalAvailableHandler() { 55 | lock.lock(); 56 | try { 57 | condition.signalAll(); 58 | } finally { 59 | lock.unlock(); 60 | } 61 | } 62 | 63 | /** 64 | * 等待handler 65 | */ 66 | private static void waitingForHandler() throws InterruptedException { 67 | lock.lock(); 68 | try { 69 | logger.warn("Waiting for available service."); 70 | condition.await(HANDLER_RETRY_TIME_INTERVAL, TimeUnit.MILLISECONDS); 71 | } finally { 72 | lock.unlock(); 73 | } 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /netty-client/src/main/java/com/polyu/rpc/client/interceptor/Interceptor.java: -------------------------------------------------------------------------------- 1 | package com.polyu.rpc.client.interceptor; 2 | 3 | import com.polyu.rpc.client.invoke.Invocation; 4 | 5 | /** 6 | * 拦截器 Interceptor 7 | */ 8 | public interface Interceptor { 9 | 10 | /** 11 | * 前处理 12 | * @param invocation invoke 实例 13 | */ 14 | void beforeInvoke(Invocation invocation); 15 | 16 | /** 17 | * 拦截调用 18 | * @param invocation invoke 实例 19 | * @return request result 20 | */ 21 | Object intercept(Invocation invocation); 22 | 23 | /** 24 | * 后处理 25 | * @param invocation invoke 实例 26 | */ 27 | void afterInvoke(Invocation invocation); 28 | } 29 | -------------------------------------------------------------------------------- /netty-client/src/main/java/com/polyu/rpc/client/interceptor/impl/CallBackInterceptor.java: -------------------------------------------------------------------------------- 1 | package com.polyu.rpc.client.interceptor.impl; 2 | 3 | import com.polyu.rpc.client.interceptor.Interceptor; 4 | import com.polyu.rpc.client.invoke.Invocation; 5 | import com.polyu.rpc.client.result.future.RpcFuture; 6 | 7 | public class CallBackInterceptor implements Interceptor { 8 | 9 | @Override 10 | public void beforeInvoke(Invocation invocation) { 11 | 12 | } 13 | 14 | @Override 15 | public Object intercept(Invocation invocation) { 16 | this.beforeInvoke(invocation); 17 | Object invokeResult = invocation.invoke(); 18 | this.afterInvoke(invocation); 19 | return invokeResult; 20 | } 21 | 22 | /** 23 | * 调用回调 24 | * @param invocation invoke 实例 25 | */ 26 | @Override 27 | public void afterInvoke(Invocation invocation) { 28 | RpcFuture rpcFuture = invocation.getRpcFuture(); 29 | rpcFuture.invokeCallbacks(); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /netty-client/src/main/java/com/polyu/rpc/client/interceptor/impl/TimeCostInterceptor.java: -------------------------------------------------------------------------------- 1 | package com.polyu.rpc.client.interceptor.impl; 2 | 3 | import com.polyu.rpc.client.interceptor.Interceptor; 4 | import com.polyu.rpc.client.invoke.Invocation; 5 | import com.polyu.rpc.client.result.future.RpcFuture; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | /** 10 | * 保证此拦截器顺序 如果超时则不执行的逻辑在该拦截器之前加入 11 | */ 12 | public class TimeCostInterceptor implements Interceptor { 13 | private static final Logger logger = LoggerFactory.getLogger(TimeCostInterceptor.class); 14 | 15 | @Override 16 | public void beforeInvoke(Invocation invocation) { 17 | 18 | } 19 | 20 | @Override 21 | public Object intercept(Invocation invocation) { 22 | this.beforeInvoke(invocation); 23 | Object invokeResult = invocation.invoke(); 24 | this.afterInvoke(invocation); 25 | return invokeResult; 26 | } 27 | 28 | /** 29 | * 超时异常处理 30 | * 惰性删除 or 提前释放 31 | * 都会在此抛 timeout 异常 32 | * @param invocation 调用实例 33 | */ 34 | @Override 35 | public void afterInvoke(Invocation invocation) { 36 | RpcFuture rpcFuture = invocation.getRpcFuture(); 37 | long timeEnd = System.currentTimeMillis(); 38 | long timeStart = rpcFuture.getStartTime(); 39 | if (timeEnd - timeStart > invocation.getTimeoutLength()) { 40 | logger.error("Invoke Timeout. timeStart: {}, timeEnd: {}.", timeStart, timeEnd); 41 | rpcFuture.setTimeoutException(); 42 | throw rpcFuture.getTimeoutException(); 43 | } 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /netty-client/src/main/java/com/polyu/rpc/client/invoke/Invocation.java: -------------------------------------------------------------------------------- 1 | package com.polyu.rpc.client.invoke; 2 | 3 | import com.polyu.rpc.client.connect.HandlerManager; 4 | import com.polyu.rpc.client.interceptor.Interceptor; 5 | import com.polyu.rpc.client.interceptor.impl.CallBackInterceptor; 6 | import com.polyu.rpc.client.interceptor.impl.TimeCostInterceptor; 7 | import com.polyu.rpc.client.netty.handler.RpcClientHandler; 8 | import com.polyu.rpc.client.result.future.RpcFuture; 9 | import com.polyu.rpc.codec.RpcRequest; 10 | import com.polyu.rpc.route.DefaultRpcLoadBalanceHolder; 11 | import com.polyu.rpc.route.RpcLoadBalance; 12 | import com.polyu.rpc.util.ServiceUtil; 13 | import lombok.Data; 14 | import org.slf4j.Logger; 15 | import org.slf4j.LoggerFactory; 16 | 17 | import java.util.ArrayList; 18 | import java.util.List; 19 | 20 | @Data 21 | public class Invocation { 22 | private static final Logger logger = LoggerFactory.getLogger(Invocation.class); 23 | 24 | private List interceptors = new ArrayList<>(); 25 | private int index = 0; 26 | private RpcRequest rpcRequest; 27 | private RpcLoadBalance loadBalance; 28 | private long timeoutLength; 29 | private RpcFuture rpcFuture; 30 | 31 | public Invocation(RpcRequest rpcRequest, RpcLoadBalance loadBalance, long timeoutLength) { 32 | this.rpcRequest = rpcRequest; 33 | this.loadBalance = loadBalance; 34 | this.timeoutLength = timeoutLength; 35 | 36 | this.interceptors.add(new CallBackInterceptor()); 37 | this.interceptors.add(new TimeCostInterceptor()); 38 | } 39 | 40 | /** 41 | * invoke with interceptor 42 | * @return rpc result 43 | */ 44 | public Object invoke() { 45 | if (index == interceptors.size()) { 46 | return this.sendRequest(); 47 | } 48 | Interceptor interceptor = interceptors.get(index++); 49 | return interceptor.intercept(this); 50 | } 51 | 52 | /** 53 | * 发送请求 54 | * @return result 55 | */ 56 | private Object sendRequest() { 57 | Object res = null; 58 | try { 59 | String serviceKey = ServiceUtil.makeServiceKey(rpcRequest.getClassName(), rpcRequest.getVersion()); 60 | RpcClientHandler handler = HandlerManager.chooseHandler(serviceKey, loadBalance == null ? DefaultRpcLoadBalanceHolder.getInstance() : loadBalance); 61 | RpcFuture rpcFuture = handler.sendRequest(this.rpcRequest, this.timeoutLength); 62 | this.rpcFuture = rpcFuture; 63 | res = rpcFuture.get(); 64 | } catch (Exception e) { 65 | logger.error("Invoke exception, exception: {}.", e.getMessage(), e); 66 | } 67 | return res; 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /netty-client/src/main/java/com/polyu/rpc/client/invoke/InvokeProxy.java: -------------------------------------------------------------------------------- 1 | package com.polyu.rpc.client.invoke; 2 | 3 | import com.polyu.rpc.codec.RpcRequest; 4 | import com.polyu.rpc.route.RpcLoadBalance; 5 | 6 | import java.lang.reflect.InvocationHandler; 7 | import java.lang.reflect.Method; 8 | import java.util.UUID; 9 | 10 | public class InvokeProxy implements InvocationHandler { 11 | 12 | private String version; 13 | private RpcLoadBalance loadBalance; 14 | private long timeoutLength; 15 | 16 | public InvokeProxy(String version, RpcLoadBalance loadBalance, long timeoutLength) { 17 | this.version = version; 18 | this.loadBalance = loadBalance; 19 | this.timeoutLength = timeoutLength; 20 | } 21 | 22 | /** 23 | * 动态代理调用 24 | * @param proxy 代理 25 | * @param method 方法 26 | * @param args 参数 27 | * @return 28 | * @throws Throwable 29 | */ 30 | @Override 31 | public Object invoke(Object proxy, Method method, Object[] args) { 32 | RpcRequest request = new RpcRequest(); 33 | request.setRequestId(UUID.randomUUID().toString()); 34 | request.setClassName(method.getDeclaringClass().getName()); 35 | request.setMethodName(method.getName()); 36 | request.setParameterTypes(method.getParameterTypes()); 37 | request.setParameters(args); 38 | request.setVersion(version); 39 | 40 | Invocation invocation = new Invocation(request, this.loadBalance, this.timeoutLength); 41 | return invocation.invoke(); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /netty-client/src/main/java/com/polyu/rpc/client/netty/RpcClientInitializer.java: -------------------------------------------------------------------------------- 1 | package com.polyu.rpc.client.netty; 2 | 3 | import com.polyu.rpc.client.netty.handler.RpcClientHandler; 4 | import com.polyu.rpc.client.netty.handler.RpcHeartBeatHandler; 5 | import com.polyu.rpc.codec.*; 6 | import com.polyu.rpc.serializer.Serializer; 7 | import com.polyu.rpc.serializer.kryo.KryoSerializer; 8 | import io.netty.channel.ChannelInitializer; 9 | import io.netty.channel.ChannelPipeline; 10 | import io.netty.channel.socket.SocketChannel; 11 | import io.netty.handler.timeout.IdleStateHandler; 12 | 13 | import java.util.concurrent.TimeUnit; 14 | 15 | 16 | public class RpcClientInitializer extends ChannelInitializer { 17 | @Override 18 | protected void initChannel(SocketChannel socketChannel) throws Exception { 19 | Serializer serializer = KryoSerializer.class.newInstance(); 20 | ChannelPipeline cp = socketChannel.pipeline(); 21 | cp.addLast(new IdleStateHandler(0, 0, HeartBeat.BEAT_INTERVAL, TimeUnit.SECONDS)); 22 | cp.addLast(new RpcEncoder(RpcRequest.class, serializer)); 23 | cp.addLast(new RpcHeartBeatHandler()); 24 | cp.addLast(new RpcDecoder(RpcResponse.class, serializer)); 25 | cp.addLast(new RpcClientHandler()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /netty-client/src/main/java/com/polyu/rpc/client/netty/handler/RpcClientHandler.java: -------------------------------------------------------------------------------- 1 | package com.polyu.rpc.client.netty.handler; 2 | 3 | import com.polyu.rpc.client.connect.Connector; 4 | import com.polyu.rpc.client.result.PendingRpcHolder; 5 | import com.polyu.rpc.codec.RpcRequest; 6 | import com.polyu.rpc.codec.RpcResponse; 7 | import com.polyu.rpc.info.RpcMetaData; 8 | import com.polyu.rpc.client.result.future.RpcFuture; 9 | import io.netty.buffer.Unpooled; 10 | import io.netty.channel.*; 11 | import org.slf4j.Logger; 12 | import org.slf4j.LoggerFactory; 13 | 14 | 15 | public class RpcClientHandler extends SimpleChannelInboundHandler { 16 | private static final Logger logger = LoggerFactory.getLogger(RpcClientHandler.class); 17 | 18 | private volatile Channel channel; 19 | private RpcMetaData rpcMetaData; 20 | 21 | private volatile boolean intentionalClose; 22 | 23 | @Override 24 | public void channelRegistered(ChannelHandlerContext ctx) throws Exception { 25 | super.channelRegistered(ctx); 26 | this.channel = ctx.channel(); 27 | } 28 | 29 | @Override 30 | public void channelRead0(ChannelHandlerContext ctx, RpcResponse response) { 31 | String requestId = response.getRequestId(); 32 | logger.debug("Receive response: {}.", requestId); 33 | RpcFuture rpcFuture = PendingRpcHolder.getPendingRPC().get(requestId); 34 | if (rpcFuture == null) { 35 | return; 36 | } 37 | PendingRpcHolder.getPendingRPC().remove(requestId); 38 | rpcFuture.done(response); 39 | } 40 | 41 | @Override 42 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 43 | logger.error("Client caught exception: {}.", cause.getMessage()); 44 | ctx.close(); 45 | } 46 | 47 | public void close() { 48 | channel.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE); 49 | } 50 | 51 | /** 52 | * 发送请求 53 | * @param request RpcRequest 54 | * @return result future 55 | */ 56 | public RpcFuture sendRequest(RpcRequest request, long timeoutLength) { 57 | RpcFuture rpcFuture = new RpcFuture(request, timeoutLength); 58 | PendingRpcHolder.getPendingRPC().put(request.getRequestId(), rpcFuture); 59 | try { 60 | ChannelFuture channelFuture = channel.writeAndFlush(request).sync(); 61 | if (!channelFuture.isSuccess()) { 62 | logger.error("Send request {} error.", request.getRequestId()); 63 | } 64 | } catch (InterruptedException e) { 65 | logger.error("Send request exception: {}.", e.getMessage()); 66 | } 67 | return rpcFuture; 68 | } 69 | 70 | public void setRpcMetaData(RpcMetaData rpcMetaData) { 71 | this.rpcMetaData = rpcMetaData; 72 | } 73 | 74 | /** 75 | * server端超时主动关闭 76 | * 触发client端重连 以此机制保持长链接 77 | * 主动关闭则不进行重连接 78 | * @param ctx 79 | */ 80 | @Override 81 | public void channelInactive(ChannelHandlerContext ctx) throws Exception { 82 | if (isIntentionalClose()) { 83 | super.channelInactive(ctx); 84 | return; 85 | } 86 | logger.info("Connection to server lose, active reconnect mechanism."); 87 | Connector connector = Connector.getInstance(); 88 | try { 89 | connector.connectServerNode(rpcMetaData); 90 | } catch (Exception e) { 91 | connector.removeConnectRecord(rpcMetaData); 92 | } 93 | } 94 | 95 | 96 | private boolean isIntentionalClose() { 97 | return intentionalClose; 98 | } 99 | 100 | /** 101 | * 主动关闭时设置 102 | * @param intentionalClose boolean 是否主动关闭 103 | */ 104 | public void setIntentionalClose(boolean intentionalClose) { 105 | this.intentionalClose = intentionalClose; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /netty-client/src/main/java/com/polyu/rpc/client/netty/handler/RpcHeartBeatHandler.java: -------------------------------------------------------------------------------- 1 | package com.polyu.rpc.client.netty.handler; 2 | 3 | import com.polyu.rpc.codec.HeartBeat; 4 | import io.netty.channel.Channel; 5 | import io.netty.channel.ChannelHandlerContext; 6 | import io.netty.channel.ChannelInboundHandlerAdapter; 7 | import io.netty.handler.timeout.IdleStateEvent; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | import java.net.SocketAddress; 12 | 13 | public class RpcHeartBeatHandler extends ChannelInboundHandlerAdapter { 14 | private static final Logger logger = LoggerFactory.getLogger(RpcHeartBeatHandler.class); 15 | 16 | private SocketAddress remotePeer; 17 | private volatile Channel channel; 18 | 19 | @Override 20 | public void channelActive(ChannelHandlerContext ctx) throws Exception { 21 | super.channelActive(ctx); 22 | this.remotePeer = this.channel.remoteAddress(); 23 | } 24 | 25 | @Override 26 | public void channelRegistered(ChannelHandlerContext ctx) throws Exception { 27 | super.channelRegistered(ctx); 28 | this.channel = ctx.channel(); 29 | } 30 | 31 | /** 32 | * 心跳事件进行处理 33 | * 34 | * @param ctx context 35 | * @param evt event 36 | * @throws Exception 37 | */ 38 | @Override 39 | public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { 40 | if (evt instanceof IdleStateEvent) { 41 | IdleStateEvent event = (IdleStateEvent) evt; 42 | switch (event.state()) { 43 | case WRITER_IDLE: 44 | logger.info("heart beat WRITER_IDLE event triggered."); 45 | break; 46 | case READER_IDLE: 47 | logger.info("heart beat READER_IDLE event triggered."); 48 | break; 49 | case ALL_IDLE: 50 | sendHeartBeatPackage(); 51 | logger.info("heart beat ALL_IDLE event triggered."); 52 | break; 53 | } 54 | } 55 | } 56 | 57 | /** 58 | * 发送心跳包 59 | */ 60 | private void sendHeartBeatPackage() { 61 | logger.info("Client send beat-ping to {}.", remotePeer); 62 | try { 63 | channel.writeAndFlush(HeartBeat.BEAT_PING); 64 | } catch (Exception e) { 65 | logger.error("Send heartBeatPackage exception: {}.", e.getMessage()); 66 | } 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /netty-client/src/main/java/com/polyu/rpc/client/result/PendingRpcHolder.java: -------------------------------------------------------------------------------- 1 | package com.polyu.rpc.client.result; 2 | 3 | import com.polyu.rpc.client.result.future.RpcFuture; 4 | 5 | import java.util.concurrent.ConcurrentHashMap; 6 | import java.util.concurrent.Executors; 7 | import java.util.concurrent.ScheduledExecutorService; 8 | import java.util.concurrent.TimeUnit; 9 | 10 | public class PendingRpcHolder { 11 | 12 | private static final ConcurrentHashMap pendingRPC = new ConcurrentHashMap<>(); 13 | private static final ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(); 14 | private static long timeoutCheckInterval = 1000L; 15 | 16 | /** 17 | * 启动超时检查线程 18 | */ 19 | public static void startTimeoutThreadPool() { 20 | scheduledExecutorService.scheduleAtFixedRate(new Runnable() { 21 | @Override 22 | public void run() { 23 | for (String requestId : pendingRPC.keySet()) { 24 | RpcFuture rpcFuture = pendingRPC.get(requestId); 25 | if (rpcFuture == null || !rpcFuture.isTimeout()) { 26 | continue; 27 | } 28 | rpcFuture.cancel(true); 29 | pendingRPC.remove(requestId); 30 | } 31 | } 32 | }, 3000L, timeoutCheckInterval, TimeUnit.MILLISECONDS); 33 | } 34 | 35 | /** 36 | * client 关闭同时调用关闭线程池资源 37 | */ 38 | public static void stop() { 39 | scheduledExecutorService.shutdown(); 40 | } 41 | 42 | /** 43 | * 获取pendingRPC 44 | * @return map 45 | */ 46 | public static ConcurrentHashMap getPendingRPC() { 47 | return pendingRPC; 48 | } 49 | 50 | /** 51 | * 设置巡查时间间隔 52 | * @param timeoutCheckInterval 时间间隔 53 | */ 54 | public static void setTimeoutCheckInterval(long timeoutCheckInterval) { 55 | PendingRpcHolder.timeoutCheckInterval = timeoutCheckInterval; 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /netty-client/src/main/java/com/polyu/rpc/client/result/future/AsyncRPCCallback.java: -------------------------------------------------------------------------------- 1 | package com.polyu.rpc.client.result.future; 2 | 3 | /** 4 | * callback process 5 | */ 6 | public interface AsyncRPCCallback { 7 | 8 | /** 9 | * success invoke 10 | * @param result result 11 | */ 12 | void success(Object result); 13 | 14 | /** 15 | * failure invoke 16 | * @param e exception 17 | */ 18 | void fail(Exception e); 19 | } 20 | -------------------------------------------------------------------------------- /netty-client/src/main/java/com/polyu/rpc/client/result/future/RpcFuture.java: -------------------------------------------------------------------------------- 1 | package com.polyu.rpc.client.result.future; 2 | 3 | import com.polyu.rpc.client.RpcClient; 4 | import com.polyu.rpc.codec.RpcRequest; 5 | import com.polyu.rpc.codec.RpcResponse; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import javax.annotation.Nonnull; 10 | import java.util.List; 11 | import java.util.concurrent.*; 12 | 13 | 14 | public class RpcFuture implements Future { 15 | private static final Logger logger = LoggerFactory.getLogger(RpcFuture.class); 16 | 17 | private Semaphore semaphore; 18 | private RpcRequest request; 19 | private RpcResponse response; 20 | private long startTime; 21 | private long responseTimeThreshold; 22 | private List pendingCallbacks = new CopyOnWriteArrayList<>(); 23 | private volatile CancellationException timeoutException; 24 | 25 | public RpcFuture(RpcRequest request, long responseTimeThreshold) { 26 | this.semaphore = new Semaphore(0); 27 | this.request = request; 28 | this.startTime = System.currentTimeMillis(); 29 | this.responseTimeThreshold = responseTimeThreshold; 30 | } 31 | 32 | @Override 33 | public boolean isDone() { 34 | return this.response != null || this.timeoutException != null; 35 | } 36 | 37 | @Override 38 | public Object get() throws InterruptedException { 39 | semaphore.acquire(1); 40 | if (this.response == null) { 41 | return null; 42 | } 43 | return this.response.getResult(); 44 | } 45 | 46 | @Override 47 | public Object get(long timeout, @Nonnull TimeUnit unit) throws InterruptedException { 48 | boolean success = semaphore.tryAcquire(1, timeout, unit); 49 | if (success) { 50 | if (this.response == null) { 51 | return null; 52 | } 53 | return this.response.getResult(); 54 | } else { 55 | throw new RuntimeException("Timeout exception. Request id: " + this.request.getRequestId() 56 | + ". Request class name: " + this.request.getClassName() 57 | + ". Request method: " + this.request.getMethodName()); 58 | } 59 | } 60 | 61 | /** 62 | * 超时异常设置 63 | */ 64 | public void setTimeoutException() { 65 | if (this.timeoutException == null) { 66 | this.timeoutException = new CancellationException("Response timeout for request: " + this.request.getRequestId()); 67 | } 68 | } 69 | 70 | /** 71 | * 是否超时 72 | * @return boolean 73 | */ 74 | public boolean isTimeout() { 75 | return System.currentTimeMillis() - this.startTime > responseTimeThreshold; 76 | } 77 | 78 | @Override 79 | public boolean isCancelled() { 80 | return this.timeoutException != null; 81 | } 82 | 83 | /** 84 | * 超时取消 释放线程 85 | */ 86 | @Override 87 | public boolean cancel(boolean mayInterruptIfRunning) { 88 | semaphore.release(1); 89 | return true; 90 | } 91 | 92 | /** 93 | * 完成 设置结果 94 | */ 95 | public void done(RpcResponse response) { 96 | this.response = response; 97 | semaphore.release(1); 98 | } 99 | 100 | /** 101 | * 为保证性能 this.response 无volatile 102 | * 需要确保 addCallback 后调用请求发送 103 | * @param callback 回调 104 | * @return this 105 | */ 106 | public RpcFuture addCallback(AsyncRPCCallback callback) { 107 | try { 108 | if (isDone()) { 109 | runCallback(callback); 110 | } else { 111 | this.pendingCallbacks.add(callback); 112 | } 113 | } catch (Exception e) { 114 | logger.error("addCallback failed. exception: {}.", e.getMessage(), e); 115 | } 116 | return this; 117 | } 118 | 119 | public void invokeCallbacks() { 120 | try { 121 | for (final AsyncRPCCallback callback : pendingCallbacks) { 122 | runCallback(callback); 123 | } 124 | } catch (Exception e) { 125 | logger.error("invokeCallbacks failed. exception: {}.", e.getMessage(), e); 126 | } 127 | } 128 | 129 | private void runCallback(final AsyncRPCCallback callback) { 130 | final RpcResponse res = this.response; 131 | RpcClient.submit(new Runnable() { 132 | @Override 133 | public void run() { 134 | if (!res.isError()) { 135 | callback.success(res.getResult()); 136 | } else { 137 | callback.fail(new RuntimeException("Response error.", new Throwable(res.getError()))); 138 | } 139 | } 140 | }); 141 | } 142 | 143 | /** 144 | * 获取启动时间 145 | * @return 146 | */ 147 | public long getStartTime() { 148 | return startTime; 149 | } 150 | 151 | /** 152 | * 获取超时异常 153 | * @return 154 | */ 155 | public CancellationException getTimeoutException() { 156 | return timeoutException; 157 | } 158 | 159 | @Override 160 | public String toString() { 161 | return "RpcFuture{" + 162 | "request=" + request + 163 | ", response=" + response + 164 | ", startTime=" + startTime + 165 | ", responseTimeThreshold=" + responseTimeThreshold + 166 | ", pendingCallbacks=" + pendingCallbacks + 167 | ", timeoutException=" + timeoutException + 168 | '}'; 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /netty-client/src/main/java/com/polyu/rpc/client/spring/ClientAutoConfig.java: -------------------------------------------------------------------------------- 1 | package com.polyu.rpc.client.spring; 2 | 3 | import com.polyu.rpc.client.RpcClient; 4 | import com.polyu.rpc.client.result.PendingRpcHolder; 5 | import com.polyu.rpc.registry.ServiceDiscovery; 6 | import com.polyu.rpc.registry.nacos.NacosDiscovery; 7 | import com.polyu.rpc.registry.zookeeper.ZKDiscovery; 8 | import org.springframework.context.annotation.Bean; 9 | import org.springframework.beans.factory.annotation.Value; 10 | 11 | public class ClientAutoConfig { 12 | 13 | private static final String NACOS_CONFIG_TYPE = "nacos"; 14 | private static final String ZK_CONFIG_TYPE = "zookeeper"; 15 | 16 | /** 17 | * 订阅目标服务应用名 18 | */ 19 | @Value("${bRPC.client.registry.target.name}") 20 | private String targetApplicationName; 21 | 22 | @Value("${bRPC.client.registry.type}") 23 | private String registryCenter; 24 | 25 | @Value("${bRPC.client.registry.address}") 26 | private String registryAddress; 27 | 28 | @Value("${bRPC.client.timeout.checkInterval:#{1500L}}") 29 | private Long timeoutCheckInterval; 30 | 31 | @Bean 32 | public RpcClient createRpcClientBean() throws Exception { 33 | ServiceDiscovery serviceDiscovery = null; 34 | PendingRpcHolder.setTimeoutCheckInterval(this.timeoutCheckInterval); 35 | if (registryCenter != null && !"".equals(registryAddress)) { 36 | switch (registryCenter) { 37 | case NACOS_CONFIG_TYPE: 38 | serviceDiscovery = new NacosDiscovery(registryAddress, targetApplicationName); 39 | break; 40 | case ZK_CONFIG_TYPE: 41 | serviceDiscovery = new ZKDiscovery(registryAddress, targetApplicationName); 42 | break; 43 | default: 44 | throw new Exception("Wrong type of registry type for " + registryCenter); 45 | } 46 | } 47 | return new RpcClient(serviceDiscovery); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /netty-client/src/main/resources/META-INF/spring.factories: -------------------------------------------------------------------------------- 1 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ 2 | com.polyu.rpc.client.spring.ClientAutoConfig -------------------------------------------------------------------------------- /netty-server/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | bRPC 7 | com.polyu 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | netty-server 13 | 14 | 15 | com.polyu 16 | common 17 | 1.0-SNAPSHOT 18 | compile 19 | 20 | 21 | com.polyu 22 | registry 23 | 1.0-SNAPSHOT 24 | compile 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /netty-server/src/main/java/com/polyu/rpc/server/RpcServer.java: -------------------------------------------------------------------------------- 1 | package com.polyu.rpc.server; 2 | 3 | import com.polyu.rpc.annotation.BRpcProvider; 4 | import com.polyu.rpc.registry.ServiceRegistry; 5 | import com.polyu.rpc.server.netty.NettyServer; 6 | import lombok.NoArgsConstructor; 7 | import org.springframework.beans.BeansException; 8 | import org.springframework.beans.factory.DisposableBean; 9 | import org.springframework.beans.factory.InitializingBean; 10 | import org.springframework.context.ApplicationContext; 11 | import org.springframework.context.ApplicationContextAware; 12 | 13 | import java.util.Map; 14 | import java.util.concurrent.atomic.AtomicBoolean; 15 | 16 | @NoArgsConstructor 17 | public class RpcServer extends NettyServer implements ApplicationContextAware, InitializingBean, DisposableBean { 18 | 19 | public RpcServer(String serverAddress, ServiceRegistry serviceRegistry) { 20 | super(serverAddress, serviceRegistry); 21 | } 22 | 23 | public RpcServer(String serverAddress, ServiceRegistry serviceRegistry, int coreThreadPoolSize, int maxThreadPoolSize) { 24 | super(serverAddress, serviceRegistry, coreThreadPoolSize, maxThreadPoolSize); 25 | } 26 | 27 | @Override 28 | public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 29 | Map beansMap = applicationContext.getBeansWithAnnotation(BRpcProvider.class); 30 | AtomicBoolean threadPoolSetting = new AtomicBoolean(false); 31 | beansMap.forEach((key, value) -> { 32 | BRpcProvider annotation = value.getClass().getAnnotation(BRpcProvider.class); 33 | String serviceName = annotation.value().getName(); 34 | String version = annotation.version(); 35 | int coreThreadPoolSize = annotation.coreThreadPoolSize(); 36 | int maxThreadPoolSize = annotation.maxThreadPoolSize(); 37 | super.addService(serviceName, version, value); 38 | if (maxThreadPoolSize >= coreThreadPoolSize && !threadPoolSetting.get()) { 39 | threadPoolSetting.set(true); 40 | super.setCoreThreadPoolSize(coreThreadPoolSize); 41 | super.setMaxThreadPoolSize(maxThreadPoolSize); 42 | } 43 | }); 44 | } 45 | 46 | @Override 47 | public void afterPropertiesSet() { 48 | super.start(); 49 | } 50 | 51 | @Override 52 | public void destroy() { 53 | super.stop(); 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /netty-server/src/main/java/com/polyu/rpc/server/Server.java: -------------------------------------------------------------------------------- 1 | package com.polyu.rpc.server; 2 | 3 | public abstract class Server { 4 | 5 | public abstract void start() throws Exception; 6 | 7 | public abstract void stop() throws Exception; 8 | 9 | } -------------------------------------------------------------------------------- /netty-server/src/main/java/com/polyu/rpc/server/netty/NettyServer.java: -------------------------------------------------------------------------------- 1 | package com.polyu.rpc.server.netty; 2 | 3 | 4 | import com.polyu.rpc.registry.ServiceRegistry; 5 | import com.polyu.rpc.server.Server; 6 | import com.polyu.rpc.util.ServiceUtil; 7 | import lombok.NoArgsConstructor; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | import java.util.HashMap; 12 | import java.util.Map; 13 | 14 | @NoArgsConstructor 15 | public class NettyServer extends Server { 16 | private static final Logger logger = LoggerFactory.getLogger(NettyServer.class); 17 | 18 | /** 19 | * 业务线程池核心线程数 20 | */ 21 | private int coreThreadPoolSize = 30; 22 | /** 23 | * 业务线程池最大线程数 24 | */ 25 | private int maxThreadPoolSize = 65; 26 | 27 | private Thread thread; 28 | private String serverAddress; 29 | private ServiceRegistry serviceRegistry; 30 | private Map serviceKey2BeanMap = new HashMap<>(); 31 | 32 | public NettyServer(String serverAddress, ServiceRegistry serviceRegistry) { 33 | this.serverAddress = serverAddress; 34 | this.serviceRegistry = serviceRegistry; 35 | } 36 | 37 | public NettyServer(String serverAddress, ServiceRegistry serviceRegistry, int coreThreadPoolSize, int maxThreadPoolSize) { 38 | this.serverAddress = serverAddress; 39 | this.serviceRegistry = serviceRegistry; 40 | this.coreThreadPoolSize = coreThreadPoolSize; 41 | this.maxThreadPoolSize = maxThreadPoolSize; 42 | } 43 | 44 | /** 45 | * 异步启动netty服务 46 | */ 47 | public void start() { 48 | NettyServerBootstrap nettyServerBootstrap = new NettyServerBootstrap( 49 | coreThreadPoolSize, 50 | maxThreadPoolSize, 51 | NettyServer.class.getSimpleName(), 52 | serverAddress, 53 | serviceKey2BeanMap, 54 | serviceRegistry); 55 | thread = new Thread(nettyServerBootstrap); 56 | thread.start(); 57 | } 58 | 59 | /** 60 | * 关闭server 61 | */ 62 | public void stop() { 63 | if (thread != null && thread.isAlive()) { 64 | thread.interrupt(); 65 | } 66 | } 67 | 68 | /** 69 | * 添加服务到serviceMap 70 | * @param interfaceName 接口名 71 | * @param version 版本 72 | * @param serviceBean 服务实现类 73 | */ 74 | public void addService(String interfaceName, String version, Object serviceBean) { 75 | logger.info("Adding service, interface: {}, version: {}, bean:{}", interfaceName, version, serviceBean); 76 | String serviceKey = ServiceUtil.makeServiceKey(interfaceName, version); 77 | serviceKey2BeanMap.put(serviceKey, serviceBean); 78 | } 79 | 80 | protected void setCoreThreadPoolSize(int coreThreadPoolSize) { 81 | if (coreThreadPoolSize <= 0) { 82 | return; 83 | } 84 | this.coreThreadPoolSize = coreThreadPoolSize; 85 | } 86 | 87 | protected void setMaxThreadPoolSize(int maxThreadPoolSize) { 88 | if (maxThreadPoolSize <= 0) { 89 | return; 90 | } 91 | this.maxThreadPoolSize = maxThreadPoolSize; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /netty-server/src/main/java/com/polyu/rpc/server/netty/NettyServerBootstrap.java: -------------------------------------------------------------------------------- 1 | package com.polyu.rpc.server.netty; 2 | 3 | import com.polyu.rpc.registry.ServiceRegistry; 4 | import com.polyu.rpc.util.ThreadPoolUtil; 5 | import io.netty.bootstrap.ServerBootstrap; 6 | import io.netty.channel.ChannelFuture; 7 | import io.netty.channel.ChannelOption; 8 | import io.netty.channel.EventLoopGroup; 9 | import io.netty.channel.nio.NioEventLoopGroup; 10 | import io.netty.channel.socket.nio.NioServerSocketChannel; 11 | import lombok.NoArgsConstructor; 12 | import org.slf4j.Logger; 13 | import org.slf4j.LoggerFactory; 14 | 15 | import java.util.Map; 16 | import java.util.concurrent.ThreadPoolExecutor; 17 | 18 | @NoArgsConstructor 19 | public class NettyServerBootstrap implements Runnable { 20 | private static final Logger logger = LoggerFactory.getLogger(NettyServerBootstrap.class); 21 | 22 | private int CORE_THREAD_POOL_SIZE = 35; 23 | private int MAX_THREAD_POOL_SIZE = 70; 24 | 25 | private String serverAddress; 26 | 27 | private ServiceRegistry serviceRegistry; 28 | 29 | private Map serviceKey2BeanMap; 30 | 31 | private ThreadPoolExecutor businessTaskThreadPool; 32 | 33 | NettyServerBootstrap( 34 | int corePoolSize, int maxPoolSize, 35 | String serverName, 36 | String serverAddress, 37 | Map serviceKey2BeanMap, 38 | ServiceRegistry serviceRegistry) { 39 | this.serverAddress = serverAddress; 40 | this.serviceKey2BeanMap = serviceKey2BeanMap; 41 | this.serviceRegistry = serviceRegistry; 42 | this.CORE_THREAD_POOL_SIZE = corePoolSize; 43 | this.MAX_THREAD_POOL_SIZE = maxPoolSize; 44 | this.businessTaskThreadPool = ThreadPoolUtil.makeServerThreadPool( 45 | serverName, 46 | CORE_THREAD_POOL_SIZE, 47 | MAX_THREAD_POOL_SIZE 48 | ); 49 | } 50 | 51 | NettyServerBootstrap( 52 | String serverName, 53 | String serverAddress, 54 | Map serviceKey2BeanMap, 55 | ServiceRegistry serviceRegistry) { 56 | this.serverAddress = serverAddress; 57 | this.serviceKey2BeanMap = serviceKey2BeanMap; 58 | this.serviceRegistry = serviceRegistry; 59 | this.businessTaskThreadPool = ThreadPoolUtil.makeServerThreadPool( 60 | serverName, 61 | CORE_THREAD_POOL_SIZE, 62 | MAX_THREAD_POOL_SIZE 63 | ); 64 | } 65 | 66 | /** 67 | * 异步启动netty server 68 | */ 69 | @Override 70 | public void run() { 71 | EventLoopGroup bossGroup = new NioEventLoopGroup(1); 72 | EventLoopGroup workerGroup = new NioEventLoopGroup(); 73 | try { 74 | ServerBootstrap bootstrap = new ServerBootstrap(); 75 | bootstrap.group(bossGroup, workerGroup) 76 | .channel(NioServerSocketChannel.class) 77 | .childHandler(new RpcServerInitializer(serviceKey2BeanMap, businessTaskThreadPool)) 78 | .option(ChannelOption.SO_BACKLOG, 128) 79 | .childOption(ChannelOption.SO_KEEPALIVE, true); 80 | 81 | String[] array = serverAddress.split(":"); 82 | String host = array[0]; 83 | int port = Integer.parseInt(array[1]); 84 | ChannelFuture future = bootstrap.bind(port).sync(); 85 | // 服务注册 86 | registerService(host, port); 87 | logger.info("Server started on port {}.", port); 88 | future.channel().closeFuture().sync(); 89 | } catch (Exception e) { 90 | if (e instanceof InterruptedException) { 91 | logger.info("Rpc server remoting server stop."); 92 | } else { 93 | logger.error("Rpc server remoting server error", e); 94 | } 95 | } finally { 96 | try { 97 | assert serviceRegistry != null; 98 | serviceRegistry.unregisterService(); 99 | workerGroup.shutdownGracefully(); 100 | bossGroup.shutdownGracefully(); 101 | } catch (Exception ex) { 102 | logger.error(ex.getMessage(), ex); 103 | } 104 | } 105 | } 106 | 107 | /** 108 | * 服务注册 109 | * @param host 主机地址 110 | * @param port 端口号 111 | * @throws Exception 112 | */ 113 | private void registerService(String host, int port) throws Exception { 114 | if (serviceRegistry == null) { 115 | logger.warn("serviceRegistry is null."); 116 | return; 117 | } 118 | try { 119 | serviceRegistry.registerService(host, port, serviceKey2BeanMap); 120 | } catch (Exception e) { 121 | logger.error("Rpc server register server failed. host: {}, port: {}", host, port); 122 | throw new Exception("register failed."); 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /netty-server/src/main/java/com/polyu/rpc/server/netty/RpcServerInitializer.java: -------------------------------------------------------------------------------- 1 | package com.polyu.rpc.server.netty; 2 | 3 | import com.polyu.rpc.serializer.Serializer; 4 | import com.polyu.rpc.serializer.kryo.KryoSerializer; 5 | import com.polyu.rpc.server.netty.handler.BusinessHandler; 6 | import com.polyu.rpc.server.netty.handler.HeartBeatHandler; 7 | import com.polyu.rpc.codec.*; 8 | import io.netty.channel.ChannelInitializer; 9 | import io.netty.channel.ChannelPipeline; 10 | import io.netty.channel.socket.SocketChannel; 11 | import io.netty.handler.timeout.IdleStateHandler; 12 | 13 | import java.util.Map; 14 | import java.util.concurrent.ThreadPoolExecutor; 15 | import java.util.concurrent.TimeUnit; 16 | 17 | public class RpcServerInitializer extends ChannelInitializer { 18 | private Map serviceKey2BeanMap; 19 | private ThreadPoolExecutor businessTaskThreadPool; 20 | 21 | RpcServerInitializer(Map serviceKey2BeanMap, ThreadPoolExecutor businessTaskThreadPool) { 22 | this.serviceKey2BeanMap = serviceKey2BeanMap; 23 | this.businessTaskThreadPool = businessTaskThreadPool; 24 | } 25 | 26 | @Override 27 | public void initChannel(SocketChannel channel) throws Exception { 28 | Serializer serializer = KryoSerializer.class.newInstance(); 29 | ChannelPipeline cp = channel.pipeline(); 30 | cp.addLast(new IdleStateHandler(0, 0, HeartBeat.BEAT_TIMEOUT, TimeUnit.SECONDS)); 31 | cp.addLast(new RpcDecoder(RpcRequest.class, serializer)); 32 | cp.addLast(new RpcEncoder(RpcResponse.class, serializer)); 33 | cp.addLast(new HeartBeatHandler()); 34 | cp.addLast(new BusinessHandler(serviceKey2BeanMap, businessTaskThreadPool)); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /netty-server/src/main/java/com/polyu/rpc/server/netty/handler/BusinessHandler.java: -------------------------------------------------------------------------------- 1 | package com.polyu.rpc.server.netty.handler; 2 | 3 | import com.polyu.rpc.codec.RpcRequest; 4 | import com.polyu.rpc.server.task.BusinessTask; 5 | import io.netty.channel.ChannelHandlerContext; 6 | import io.netty.channel.ChannelInboundHandlerAdapter; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import java.util.Map; 11 | import java.util.concurrent.ThreadPoolExecutor; 12 | 13 | /** 14 | * netty server business logic process handler 15 | */ 16 | public class BusinessHandler extends ChannelInboundHandlerAdapter { 17 | 18 | private static final Logger logger = LoggerFactory.getLogger(BusinessHandler.class); 19 | 20 | private final Map serviceKey2BeanMap; 21 | 22 | /** 23 | * 业务线程池 24 | */ 25 | private final ThreadPoolExecutor businessTaskThreadPool; 26 | 27 | public BusinessHandler(Map serviceKey2BeanMap, final ThreadPoolExecutor businessTaskThreadPool) { 28 | this.serviceKey2BeanMap = serviceKey2BeanMap; 29 | this.businessTaskThreadPool = businessTaskThreadPool; 30 | } 31 | 32 | /** 33 | * 获取rpcRequest进行业务处理 34 | * @param ctx 35 | * @param msg 信息 36 | */ 37 | @Override 38 | public void channelRead(ChannelHandlerContext ctx, Object msg) { 39 | businessTaskThreadPool.execute(new BusinessTask((RpcRequest) msg, serviceKey2BeanMap, ctx)); 40 | } 41 | 42 | @Override 43 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { 44 | logger.warn("Server caught exception: " + cause.getMessage()); 45 | ctx.close(); 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /netty-server/src/main/java/com/polyu/rpc/server/netty/handler/HeartBeatHandler.java: -------------------------------------------------------------------------------- 1 | package com.polyu.rpc.server.netty.handler; 2 | 3 | import com.polyu.rpc.codec.HeartBeat; 4 | import com.polyu.rpc.codec.RpcRequest; 5 | import io.netty.channel.ChannelHandlerContext; 6 | import io.netty.channel.ChannelInboundHandlerAdapter; 7 | import io.netty.handler.timeout.IdleStateEvent; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | public class HeartBeatHandler extends ChannelInboundHandlerAdapter { 12 | private static final Logger logger = LoggerFactory.getLogger(HeartBeatHandler.class); 13 | 14 | /** 15 | * 拦截client发送的心跳 16 | * 17 | * @param ctx 18 | * @param msg 19 | * @throws Exception 20 | */ 21 | @Override 22 | public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { 23 | if (msg instanceof RpcRequest) { 24 | RpcRequest request = (RpcRequest) msg; 25 | if (HeartBeat.BEAT_ID.equalsIgnoreCase(request.getRequestId())) { 26 | logger.info("Server read heartbeat ping from {}", ctx.channel().remoteAddress().toString()); 27 | return; 28 | } 29 | } 30 | super.channelRead(ctx, msg); 31 | } 32 | 33 | /** 34 | * 空闲时间超时则主动关闭连接 35 | * @param ctx 36 | * @param evt 37 | * @throws Exception 38 | */ 39 | @Override 40 | public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { 41 | if (evt instanceof IdleStateEvent) { 42 | ctx.channel().close(); 43 | logger.warn("Channel idle in last {} seconds, close it", HeartBeat.BEAT_TIMEOUT); 44 | } else { 45 | super.userEventTriggered(ctx, evt); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /netty-server/src/main/java/com/polyu/rpc/server/reflect/ReflectInvoker.java: -------------------------------------------------------------------------------- 1 | package com.polyu.rpc.server.reflect; 2 | 3 | import com.polyu.rpc.codec.RpcRequest; 4 | 5 | import java.lang.reflect.Method; 6 | 7 | public class ReflectInvoker { 8 | 9 | /** 10 | * jdk反射调用 11 | * @param request 请求 12 | * @param serviceBean 实现类 13 | * @return 14 | * @throws Throwable 15 | */ 16 | public static Object handle(RpcRequest request, Object serviceBean) throws Throwable { 17 | Class serviceClass = serviceBean.getClass(); 18 | String methodName = request.getMethodName(); 19 | Class[] parameterTypes = request.getParameterTypes(); 20 | Object[] parameters = request.getParameters(); 21 | 22 | Method method = serviceClass.getMethod(methodName, parameterTypes); 23 | method.setAccessible(true); 24 | return method.invoke(serviceBean, parameters); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /netty-server/src/main/java/com/polyu/rpc/server/spring/ServerAutoConfig.java: -------------------------------------------------------------------------------- 1 | package com.polyu.rpc.server.spring; 2 | 3 | import com.polyu.rpc.registry.ServiceRegistry; 4 | import com.polyu.rpc.registry.nacos.NacosDiscovery; 5 | import com.polyu.rpc.registry.nacos.NacosRegistry; 6 | import com.polyu.rpc.registry.zookeeper.ZKDiscovery; 7 | import com.polyu.rpc.registry.zookeeper.ZKRegistry; 8 | import com.polyu.rpc.server.RpcServer; 9 | import org.springframework.beans.factory.annotation.Value; 10 | import org.springframework.context.annotation.Bean; 11 | 12 | public class ServerAutoConfig { 13 | 14 | private static final String NACOS_CONFIG_TYPE = "nacos"; 15 | private static final String ZK_CONFIG_TYPE = "zookeeper"; 16 | 17 | /** 18 | * 应用名 19 | */ 20 | @Value("${bRPC.server.application.name}") 21 | private String applicationName; 22 | 23 | /** 24 | * 服务地址 ip:port 25 | */ 26 | @Value("${bRPC.server.address}") 27 | private String serverAddress; 28 | 29 | /** 30 | * 注册中心类型 31 | */ 32 | @Value("${bRPC.server.registry.type}") 33 | private String registryCenter; 34 | 35 | /** 36 | * 注册中心地址 ip:port 37 | */ 38 | @Value("${bRPC.server.registry.address}") 39 | private String registryAddress; 40 | 41 | /** 42 | * 是否启用业务线程池自定义设置 43 | */ 44 | @Value("${bRPC.server.enableThreadPoolSize:#{false}}") 45 | private Boolean enableThreadPoolSize; 46 | 47 | @Value("${bRPC.server.coreThreadPoolSize:#{null}}") 48 | private Integer coreThreadPoolSize; 49 | 50 | @Value("${bRPC.server.maxThreadPoolSize:#{null}}") 51 | private Integer maxThreadPoolSize; 52 | 53 | @Bean 54 | public RpcServer createRpcServerBean() throws Exception { 55 | ServiceRegistry serviceRegistry = null; 56 | if (registryCenter != null && !"".equals(registryAddress)) { 57 | switch (registryCenter) { 58 | case NACOS_CONFIG_TYPE: 59 | serviceRegistry = new NacosRegistry(registryAddress, applicationName); 60 | break; 61 | case ZK_CONFIG_TYPE: 62 | serviceRegistry = new ZKRegistry(registryAddress, applicationName); 63 | break; 64 | default: 65 | throw new Exception("Wrong type of registry type for " + registryCenter); 66 | } 67 | } 68 | if (!enableThreadPoolSize) { 69 | return new RpcServer(serverAddress, serviceRegistry); 70 | } 71 | return new RpcServer(serverAddress, serviceRegistry, coreThreadPoolSize, maxThreadPoolSize); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /netty-server/src/main/java/com/polyu/rpc/server/task/BusinessTask.java: -------------------------------------------------------------------------------- 1 | package com.polyu.rpc.server.task; 2 | 3 | import com.polyu.rpc.codec.RpcRequest; 4 | import com.polyu.rpc.codec.RpcResponse; 5 | import com.polyu.rpc.server.reflect.ReflectInvoker; 6 | import com.polyu.rpc.util.ServiceUtil; 7 | import io.netty.channel.ChannelFuture; 8 | import io.netty.channel.ChannelFutureListener; 9 | import io.netty.channel.ChannelHandlerContext; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | 13 | import java.util.Map; 14 | 15 | public class BusinessTask implements Runnable { 16 | private static final Logger logger = LoggerFactory.getLogger(BusinessTask.class); 17 | 18 | private RpcRequest request; 19 | private final Map serviceKey2BeanMap; 20 | private final ChannelHandlerContext ctx; 21 | 22 | public BusinessTask(RpcRequest request, Map serviceKey2BeanMap, ChannelHandlerContext ctx) { 23 | this.request = request; 24 | this.serviceKey2BeanMap = serviceKey2BeanMap; 25 | this.ctx = ctx; 26 | } 27 | 28 | @Override 29 | public void run() { 30 | task(); 31 | } 32 | 33 | /** 34 | * 写出任务 35 | */ 36 | private void task() { 37 | logger.info("Receive request {}.", request.getRequestId()); 38 | RpcResponse response = new RpcResponse(); 39 | response.setRequestId(request.getRequestId()); 40 | try { 41 | Object result = handle(request); 42 | response.setResult(result); 43 | } catch (Throwable t) { 44 | response.setError(t.toString()); 45 | logger.error("RPC Server handle request error.", t); 46 | } 47 | ctx.writeAndFlush(response).addListener(new ChannelFutureListener() { 48 | @Override 49 | public void operationComplete(ChannelFuture channelFuture) throws Exception { 50 | logger.info("Send response for request {}.", request.getRequestId()); 51 | } 52 | }); 53 | } 54 | 55 | /** 56 | * 根据service标识获取bean进行调用 57 | * @param request 58 | * @return 59 | * @throws Throwable 60 | */ 61 | private Object handle(RpcRequest request) throws Throwable { 62 | String className = request.getClassName(); 63 | String version = request.getVersion(); 64 | String serviceKey = ServiceUtil.makeServiceKey(className, version); 65 | Object serviceBean = serviceKey2BeanMap.get(serviceKey); 66 | if (serviceBean == null) { 67 | logger.error("Can not find service implement with interface name: {} and version: {}.", className, version); 68 | return null; 69 | } 70 | return ReflectInvoker.handle(request, serviceBean); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /netty-server/src/main/resources/META-INF/spring.factories: -------------------------------------------------------------------------------- 1 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ 2 | com.polyu.rpc.server.spring.ServerAutoConfig -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.polyu 8 | bRPC 9 | pom 10 | 1.0-SNAPSHOT 11 | 12 | common 13 | netty-server 14 | test 15 | netty-client 16 | registry 17 | 18 | 19 | 20 | UTF-8 21 | 4.13.1 22 | 1.7.7 23 | 2.17.1 24 | 5.2.20.RELEASE 25 | 4.1.71.Final 26 | 4.0.2 27 | 2.13.0 28 | 4.1 29 | 2.1 30 | 2.12.6.1 31 | 1.18.4 32 | 1.4.1 33 | 34 | 35 | 36 | 37 | 38 | 39 | org.slf4j 40 | slf4j-api 41 | ${slf4j.version} 42 | 43 | 44 | 45 | 46 | org.apache.logging.log4j 47 | log4j-slf4j-impl 48 | ${logging-log4j.version} 49 | 50 | 51 | org.apache.logging.log4j 52 | log4j-api 53 | ${logging-log4j.version} 54 | 55 | 56 | org.apache.logging.log4j 57 | log4j-core 58 | ${logging-log4j.version} 59 | 60 | 61 | 62 | 63 | org.springframework 64 | spring-context 65 | ${spring.version} 66 | 67 | 68 | 69 | 70 | io.netty 71 | netty-all 72 | ${netty.version} 73 | 74 | 75 | 76 | 77 | com.esotericsoftware 78 | kryo 79 | ${kryo.version} 80 | 81 | 82 | 83 | 84 | org.apache.curator 85 | curator-recipes 86 | ${curator.version} 87 | 88 | 89 | 90 | 91 | com.fasterxml.jackson.core 92 | jackson-databind 93 | ${jackson.version} 94 | 95 | 96 | 97 | 98 | org.projectlombok 99 | lombok 100 | ${lombok.version} 101 | 102 | 103 | 104 | 105 | com.alibaba.nacos 106 | nacos-client 107 | ${nacos.version} 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | org.apache.maven.plugins 117 | maven-compiler-plugin 118 | 3.2 119 | 120 | 1.8 121 | 1.8 122 | UTF-8 123 | true 124 | 125 | 126 | 127 | 128 | 129 | -------------------------------------------------------------------------------- /registry/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | bRPC 7 | com.polyu 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | registry 13 | 14 | 15 | com.polyu 16 | common 17 | 1.0-SNAPSHOT 18 | compile 19 | 20 | 21 | 22 | com.alibaba.nacos 23 | nacos-client 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /registry/src/main/java/com/polyu/rpc/registry/RegistryConfigEnum.java: -------------------------------------------------------------------------------- 1 | package com.polyu.rpc.registry; 2 | 3 | /** 4 | * 注册中心配置枚举 5 | */ 6 | public enum RegistryConfigEnum { 7 | 8 | ZK_REGISTRY_PATH("/registry/"), 9 | ZK_NAME_SPACE("bRPC"), 10 | ZK_SESSION_TIMEOUT(5000), 11 | ZK_CONNECTION_TIMEOUT(5000), 12 | 13 | NACOS_REGISTRY_PATH("bRPC."); 14 | 15 | 16 | private String value; 17 | private int timeOutLength; 18 | 19 | RegistryConfigEnum(String value) { 20 | this.value = value; 21 | } 22 | 23 | RegistryConfigEnum(int timeOutLength) { 24 | this.timeOutLength = timeOutLength; 25 | } 26 | 27 | public String getValue() { 28 | return value; 29 | } 30 | 31 | public int getTimeOutLength() { 32 | return timeOutLength; 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /registry/src/main/java/com/polyu/rpc/registry/nacos/NacosDiscovery.java: -------------------------------------------------------------------------------- 1 | package com.polyu.rpc.registry.nacos; 2 | 3 | import com.alibaba.nacos.api.naming.NamingFactory; 4 | import com.alibaba.nacos.api.naming.NamingService; 5 | import com.alibaba.nacos.api.naming.listener.Event; 6 | import com.alibaba.nacos.api.naming.listener.EventListener; 7 | import com.alibaba.nacos.api.naming.listener.NamingEvent; 8 | import com.alibaba.nacos.api.naming.pojo.Instance; 9 | import com.polyu.rpc.registry.RegistryConfigEnum; 10 | import com.polyu.rpc.registry.observation.Observer; 11 | import com.polyu.rpc.info.RpcMetaData; 12 | import com.polyu.rpc.registry.ServiceDiscovery; 13 | import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent; 14 | import org.slf4j.Logger; 15 | import org.slf4j.LoggerFactory; 16 | 17 | import java.util.ArrayList; 18 | import java.util.List; 19 | import java.util.Map; 20 | 21 | public class NacosDiscovery implements ServiceDiscovery { 22 | private static final Logger logger = LoggerFactory.getLogger(NacosDiscovery.class); 23 | 24 | private NamingService namingService; 25 | 26 | /** 27 | * 订阅目标server服务名 28 | */ 29 | private String targetApplicationName; 30 | 31 | /** 32 | * 观察者引用 33 | */ 34 | private List observers = new ArrayList<>(); 35 | 36 | public NacosDiscovery(String registryAddress, String applicationName) { 37 | try { 38 | this.namingService = NamingFactory.createNamingService(registryAddress); 39 | } catch (Exception e) { 40 | logger.error("Nacos namingService creation failed. exception: {}", e.getMessage()); 41 | } 42 | this.targetApplicationName = applicationName; 43 | } 44 | 45 | public NacosDiscovery(String registryAddress) { 46 | try { 47 | this.namingService = NamingFactory.createNamingService(registryAddress); 48 | } catch (Exception e) { 49 | logger.error("Nacos namingService creation failed. exception: {}", e.getMessage()); 50 | } 51 | this.targetApplicationName = "DefaultApplication"; 52 | } 53 | 54 | /** 55 | * 服务发现 56 | * 首次拉取订阅信息 然后对变更进行监听 57 | */ 58 | @Override 59 | public void discoveryService() { 60 | try { 61 | String serviceName = RegistryConfigEnum.NACOS_REGISTRY_PATH.getValue().concat(this.targetApplicationName); 62 | List instances = namingService.selectInstances(serviceName, true); 63 | List rpcMetaData = instances2RpcProtocols(instances); 64 | // 观察者模式 进行通知 65 | notifyObserver(rpcMetaData, null); 66 | logger.info("Nacos service discovery first pull. data: {}.", rpcMetaData); 67 | 68 | namingService.subscribe(serviceName, new EventListener() { 69 | @Override 70 | public void onEvent(Event event) { 71 | if (event instanceof NamingEvent) { 72 | try { 73 | NamingEvent namingEvent = (NamingEvent) event; 74 | List instances = namingEvent.getInstances(); 75 | List rpcMetaData = instances2RpcProtocols(instances); 76 | logger.info("service changed, new server info: {}.", rpcMetaData); 77 | // 观察者模式 进行通知 78 | notifyObserver(rpcMetaData, null); 79 | } catch (Exception e) { 80 | logger.error("service update failed. exception: {}.", e.getMessage()); 81 | } 82 | } 83 | } 84 | }); 85 | } catch (Exception e) { 86 | logger.error("discoveryService failed, exception: {}.", e.getMessage()); 87 | } 88 | } 89 | 90 | /** 91 | * 终止服务订阅 92 | */ 93 | @Override 94 | public void stop() { 95 | try { 96 | namingService.shutDown(); 97 | } catch (Exception e) { 98 | logger.error("NacosDiscovery shutDown failed. exception: {}", e.getMessage()); 99 | } 100 | } 101 | 102 | /** 103 | * 转换 nacos instance列表 为 RpcProtocol 列表 104 | * @param instances nacos 服务实例 105 | * @return 106 | */ 107 | private List instances2RpcProtocols(List instances) { 108 | List res = new ArrayList<>(instances.size()); 109 | for (Instance instance : instances) { 110 | if (!instance.isHealthy()) { 111 | continue; 112 | } 113 | Map metadata = instance.getMetadata(); 114 | String rpcProtocolJson = metadata.get("rpcProtocol"); 115 | RpcMetaData rpcMetaData = RpcMetaData.fromJson(rpcProtocolJson); 116 | res.add(rpcMetaData); 117 | } 118 | return res; 119 | } 120 | 121 | @Override 122 | public void registerObserver(Observer observer) { 123 | observers.add(observer); 124 | } 125 | 126 | @Override 127 | public void notifyObserver(List protocols, PathChildrenCacheEvent.Type type) { 128 | for (Observer observer : observers) { 129 | observer.update(protocols, type); 130 | } 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /registry/src/main/java/com/polyu/rpc/registry/nacos/NacosRegistry.java: -------------------------------------------------------------------------------- 1 | package com.polyu.rpc.registry.nacos; 2 | 3 | import com.alibaba.nacos.api.naming.NamingFactory; 4 | import com.alibaba.nacos.api.naming.NamingService; 5 | import com.alibaba.nacos.api.naming.pojo.Instance; 6 | import com.polyu.rpc.info.RpcMetaData; 7 | import com.polyu.rpc.info.RpcServiceInfo; 8 | import com.polyu.rpc.registry.RegistryConfigEnum; 9 | import com.polyu.rpc.registry.ServiceRegistry; 10 | import com.polyu.rpc.util.ServiceUtil; 11 | import org.slf4j.Logger; 12 | import org.slf4j.LoggerFactory; 13 | 14 | import java.util.HashMap; 15 | import java.util.List; 16 | import java.util.Map; 17 | 18 | public class NacosRegistry implements ServiceRegistry { 19 | private static final Logger logger = LoggerFactory.getLogger(NacosRegistry.class); 20 | 21 | private NamingService namingService; 22 | private String applicationName; 23 | private String nacosNameSpace; 24 | 25 | private String ip; 26 | private int port; 27 | 28 | public NacosRegistry(String registryAddress, String applicationName) { 29 | try { 30 | this.namingService = NamingFactory.createNamingService(registryAddress); 31 | } catch (Exception e) { 32 | logger.error("Nacos namingService creation failed. exception: {}", e.getMessage()); 33 | } 34 | this.applicationName = applicationName; 35 | } 36 | 37 | public NacosRegistry(String registryAddress) { 38 | try { 39 | this.namingService = NamingFactory.createNamingService(registryAddress); 40 | } catch (Exception e) { 41 | logger.error("Nacos namingService creation failed. exception: {}", e.getMessage()); 42 | } 43 | this.applicationName = "DefaultApplication"; 44 | } 45 | 46 | /** 47 | * 注册服务 48 | * @param host 主机地址 49 | * @param port 端口地址 50 | * @param serviceKey2BeanMap serviceKey -> bean 51 | */ 52 | @Override 53 | public void registerService(String host, int port, Map serviceKey2BeanMap) { 54 | List serviceInfoList = ServiceUtil.beanMap2RpcServiceInfos(serviceKey2BeanMap); 55 | this.ip = host; 56 | this.port = port; 57 | try { 58 | RpcMetaData rpcMetaData = new RpcMetaData(); 59 | rpcMetaData.setHost(host); 60 | rpcMetaData.setPort(port); 61 | rpcMetaData.setServiceInfoList(serviceInfoList); 62 | String serviceData = rpcMetaData.toJson(); 63 | this.nacosNameSpace = RegistryConfigEnum.NACOS_REGISTRY_PATH.getValue().concat(applicationName); 64 | Instance serviceInstance = new Instance(); 65 | serviceInstance.setIp(host); 66 | serviceInstance.setPort(port); 67 | Map instanceMeta = new HashMap<>(); 68 | instanceMeta.put("rpcProtocol", serviceData); 69 | serviceInstance.setMetadata(instanceMeta); 70 | namingService.registerInstance(nacosNameSpace, serviceInstance); 71 | logger.info("Register {} new service, host: {}, port: {}.", serviceInfoList.size(), host, port); 72 | } catch (Exception e) { 73 | logger.error("Register service fail, exception: {}.", e.getMessage()); 74 | } 75 | } 76 | 77 | /** 78 | * 注销服务 79 | */ 80 | @Override 81 | public void unregisterService() { 82 | logger.info("Unregister service."); 83 | try { 84 | this.namingService.deregisterInstance(nacosNameSpace, this.ip, this.port); 85 | } catch (Exception e) { 86 | logger.error("Delete service path error: {}.", e.getMessage()); 87 | } finally { 88 | try { 89 | this.namingService.shutDown(); 90 | } catch (Exception ex) { 91 | logger.error("NamingService shutDown error: {}.", ex.getMessage()); 92 | } 93 | } 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /registry/src/main/java/com/polyu/rpc/registry/zookeeper/CuratorClient.java: -------------------------------------------------------------------------------- 1 | package com.polyu.rpc.registry.zookeeper; 2 | 3 | import com.polyu.rpc.registry.RegistryConfigEnum; 4 | import org.apache.curator.framework.CuratorFramework; 5 | import org.apache.curator.framework.CuratorFrameworkFactory; 6 | import org.apache.curator.framework.recipes.cache.PathChildrenCache; 7 | import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener; 8 | import org.apache.curator.framework.recipes.cache.TreeCache; 9 | import org.apache.curator.framework.recipes.cache.TreeCacheListener; 10 | import org.apache.curator.framework.state.ConnectionStateListener; 11 | import org.apache.curator.retry.ExponentialBackoffRetry; 12 | import org.apache.zookeeper.CreateMode; 13 | import org.apache.zookeeper.Watcher; 14 | 15 | import java.util.List; 16 | 17 | /** 18 | * zk客户端 19 | */ 20 | public class CuratorClient { 21 | private CuratorFramework client; 22 | 23 | private CuratorClient(String connectString, String namespace, int sessionTimeout, int connectionTimeout) { 24 | client = CuratorFrameworkFactory.builder().namespace(namespace).connectString(connectString) 25 | .sessionTimeoutMs(sessionTimeout).connectionTimeoutMs(connectionTimeout) 26 | .retryPolicy(new ExponentialBackoffRetry(1000, 10)) 27 | .build(); 28 | client.start(); 29 | } 30 | 31 | CuratorClient(String connectString, int timeout) { 32 | this(connectString, RegistryConfigEnum.ZK_NAME_SPACE.getValue(), timeout, timeout); 33 | } 34 | 35 | CuratorClient(String connectString) { 36 | this( 37 | connectString, 38 | RegistryConfigEnum.ZK_NAME_SPACE.getValue(), 39 | RegistryConfigEnum.ZK_SESSION_TIMEOUT.getTimeOutLength(), 40 | RegistryConfigEnum.ZK_CONNECTION_TIMEOUT.getTimeOutLength() 41 | ); 42 | } 43 | 44 | void addConnectionStateListener(ConnectionStateListener connectionStateListener) { 45 | client.getConnectionStateListenable().addListener(connectionStateListener); 46 | } 47 | 48 | String createPathData(String path, byte[] data) throws Exception { 49 | return client.create().creatingParentsIfNeeded() 50 | .withMode(CreateMode.EPHEMERAL_SEQUENTIAL) 51 | .forPath(path, data); 52 | } 53 | 54 | void updatePathData(String path, byte[] data) throws Exception { 55 | client.setData().forPath(path, data); 56 | } 57 | 58 | void deletePath(String path) throws Exception { 59 | client.delete().forPath(path); 60 | } 61 | 62 | void watchNode(String path, Watcher watcher) throws Exception { 63 | client.getData().usingWatcher(watcher).forPath(path); 64 | } 65 | 66 | byte[] getData(String path) throws Exception { 67 | return client.getData().forPath(path); 68 | } 69 | 70 | List getChildren(String path) throws Exception { 71 | return client.getChildren().forPath(path); 72 | } 73 | 74 | void watchTreeNode(String path, TreeCacheListener listener) { 75 | TreeCache treeCache = new TreeCache(client, path); 76 | treeCache.getListenable().addListener(listener); 77 | } 78 | 79 | void watchPathChildrenNode(String path, PathChildrenCacheListener listener) throws Exception { 80 | PathChildrenCache pathChildrenCache = new PathChildrenCache(client, path, true); 81 | pathChildrenCache.start(PathChildrenCache.StartMode.BUILD_INITIAL_CACHE); 82 | pathChildrenCache.getListenable().addListener(listener); 83 | } 84 | 85 | void close() { 86 | client.close(); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /registry/src/main/java/com/polyu/rpc/registry/zookeeper/ZKDiscovery.java: -------------------------------------------------------------------------------- 1 | package com.polyu.rpc.registry.zookeeper; 2 | 3 | import com.polyu.rpc.registry.RegistryConfigEnum; 4 | import com.polyu.rpc.registry.observation.Observer; 5 | import com.polyu.rpc.info.RpcMetaData; 6 | import com.polyu.rpc.registry.ServiceDiscovery; 7 | import org.apache.curator.framework.CuratorFramework; 8 | import org.apache.curator.framework.recipes.cache.ChildData; 9 | import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent; 10 | import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener; 11 | import org.slf4j.Logger; 12 | import org.slf4j.LoggerFactory; 13 | 14 | import java.nio.charset.StandardCharsets; 15 | import java.util.ArrayList; 16 | import java.util.Collections; 17 | import java.util.List; 18 | 19 | public class ZKDiscovery implements ServiceDiscovery { 20 | private static final Logger logger = LoggerFactory.getLogger(ZKDiscovery.class); 21 | 22 | private CuratorClient curatorClient; 23 | 24 | /** 25 | * 订阅目标server服务名 26 | */ 27 | private String targetApplicationName; 28 | 29 | /** 30 | * 观察者引用 31 | */ 32 | private List observers = new ArrayList<>(); 33 | 34 | public ZKDiscovery(String registryAddress, String targetApplicationName) { 35 | this.curatorClient = new CuratorClient(registryAddress); 36 | this.targetApplicationName = targetApplicationName; 37 | } 38 | 39 | public ZKDiscovery(String registryAddress) { 40 | this.curatorClient = new CuratorClient(registryAddress); 41 | this.targetApplicationName = "DefaultApplication"; 42 | } 43 | 44 | @Override 45 | public void discoveryService() { 46 | try { 47 | logger.info("Get initial service info."); 48 | getServiceAndUpdateServer(); 49 | curatorClient.watchPathChildrenNode(RegistryConfigEnum.ZK_REGISTRY_PATH.getValue().concat(this.targetApplicationName), 50 | new PathChildrenCacheListener() { 51 | @Override 52 | public void childEvent(CuratorFramework curatorFramework, PathChildrenCacheEvent pathChildrenCacheEvent) { 53 | PathChildrenCacheEvent.Type type = pathChildrenCacheEvent.getType(); 54 | ChildData childData = pathChildrenCacheEvent.getData(); 55 | switch (type) { 56 | case CONNECTION_RECONNECTED: 57 | logger.info("Reconnected to zk, try to get latest service list."); 58 | getServiceAndUpdateServer(); 59 | break; 60 | case CHILD_ADDED: 61 | getServiceAndUpdateServer(childData, PathChildrenCacheEvent.Type.CHILD_ADDED); 62 | break; 63 | case CHILD_UPDATED: 64 | getServiceAndUpdateServer(childData, PathChildrenCacheEvent.Type.CHILD_UPDATED); 65 | break; 66 | case CHILD_REMOVED: 67 | getServiceAndUpdateServer(childData, PathChildrenCacheEvent.Type.CHILD_REMOVED); 68 | break; 69 | } 70 | } 71 | }); 72 | } catch (Exception ex) { 73 | logger.error("Watch node exception: " + ex.getMessage()); 74 | } 75 | } 76 | 77 | private void getServiceAndUpdateServer() { 78 | try { 79 | List nodeList = curatorClient.getChildren(RegistryConfigEnum.ZK_REGISTRY_PATH.getValue().concat(this.targetApplicationName)); 80 | List dataList = new ArrayList<>(); 81 | for (String node : nodeList) { 82 | logger.debug("Service node: {}.", node); 83 | byte[] bytes = curatorClient.getData(RegistryConfigEnum.ZK_REGISTRY_PATH.getValue().concat(this.targetApplicationName) + "/" + node); 84 | String json = new String(bytes); 85 | RpcMetaData rpcMetaData = RpcMetaData.fromJson(json); 86 | dataList.add(rpcMetaData); 87 | } 88 | logger.debug("Service node data: {}.", dataList); 89 | updateConnectedServer(dataList); 90 | } catch (Exception e) { 91 | logger.error("Get node exception: {}.", e.getMessage()); 92 | } 93 | } 94 | 95 | private void getServiceAndUpdateServer(ChildData childData, PathChildrenCacheEvent.Type type) { 96 | String path = childData.getPath(); 97 | String data = new String(childData.getData(), StandardCharsets.UTF_8); 98 | logger.info("Child data is updated, path:{}, type:{}, data:{}.", path, type, data); 99 | RpcMetaData rpcMetaData = RpcMetaData.fromJson(data); 100 | updateConnectedServer(rpcMetaData, type); 101 | } 102 | 103 | private void updateConnectedServer(List dataList) { 104 | notifyObserver(dataList, null); 105 | } 106 | 107 | 108 | private void updateConnectedServer(RpcMetaData rpcMetaData, PathChildrenCacheEvent.Type type) { 109 | notifyObserver(Collections.singletonList(rpcMetaData), type); 110 | } 111 | 112 | @Override 113 | public void stop() { 114 | this.curatorClient.close(); 115 | } 116 | 117 | /** 118 | * 注册观察者 119 | * @param observer 120 | */ 121 | @Override 122 | public void registerObserver(Observer observer) { 123 | observers.add(observer); 124 | } 125 | 126 | /** 127 | * 事件通知 128 | * @param rpcMetaData 129 | */ 130 | @Override 131 | public void notifyObserver(List rpcMetaData, PathChildrenCacheEvent.Type type) { 132 | for (Observer observer : observers) { 133 | observer.update(rpcMetaData, type); 134 | } 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /registry/src/main/java/com/polyu/rpc/registry/zookeeper/ZKRegistry.java: -------------------------------------------------------------------------------- 1 | package com.polyu.rpc.registry.zookeeper; 2 | 3 | import com.polyu.rpc.info.RpcMetaData; 4 | import com.polyu.rpc.info.RpcServiceInfo; 5 | import com.polyu.rpc.registry.RegistryConfigEnum; 6 | import com.polyu.rpc.registry.ServiceRegistry; 7 | import com.polyu.rpc.util.ServiceUtil; 8 | import org.apache.curator.framework.state.ConnectionState; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | 12 | import java.util.List; 13 | import java.util.Map; 14 | 15 | public class ZKRegistry implements ServiceRegistry { 16 | private static final Logger logger = LoggerFactory.getLogger(ZKRegistry.class); 17 | private static final int TIME_OUT_LENGTH = 5000; 18 | 19 | /** 20 | * zk客户端 21 | */ 22 | private CuratorClient zkClient; 23 | private String zkPath; 24 | 25 | /** 26 | * 应用名 27 | */ 28 | private String applicationName; 29 | 30 | public ZKRegistry(String registryAddress) { 31 | this.zkClient = new CuratorClient(registryAddress, TIME_OUT_LENGTH); 32 | this.applicationName = "DefaultApplication"; 33 | } 34 | 35 | public ZKRegistry(String registryAddress, String applicationName) { 36 | this.zkClient = new CuratorClient(registryAddress, TIME_OUT_LENGTH); 37 | this.applicationName = applicationName; 38 | } 39 | 40 | /** 41 | * 服务注册 42 | * @param host 主机地址 43 | * @param port 端口 44 | * @param serviceKey2BeanMap 提供服务信息 45 | */ 46 | @Override 47 | public void registerService(String host, int port, Map serviceKey2BeanMap) { 48 | List serviceInfoList = ServiceUtil.beanMap2RpcServiceInfos(serviceKey2BeanMap); 49 | try { 50 | RpcMetaData rpcMetaData = new RpcMetaData(); 51 | rpcMetaData.setHost(host); 52 | rpcMetaData.setPort(port); 53 | rpcMetaData.setServiceInfoList(serviceInfoList); 54 | String serviceData = rpcMetaData.toJson(); 55 | byte[] bytes = serviceData.getBytes(); 56 | String path = RegistryConfigEnum.ZK_REGISTRY_PATH.getValue().concat(this.applicationName) + "/data-" + rpcMetaData.hashCode(); 57 | path = this.zkClient.createPathData(path, bytes); 58 | this.zkPath = path; 59 | logger.info("Register {} new service, host: {}, port: {}.", serviceInfoList.size(), host, port); 60 | } catch (Exception e) { 61 | logger.error("Register service fail, exception: {}.", e.getMessage()); 62 | } 63 | 64 | zkClient.addConnectionStateListener((curatorFramework, connectionState) -> { 65 | if (connectionState == ConnectionState.RECONNECTED) { 66 | logger.info("Connection state: {}, register service after reconnected.", connectionState); 67 | registerService(host, port, serviceKey2BeanMap); 68 | } 69 | }); 70 | } 71 | 72 | /** 73 | * 注销服务 74 | */ 75 | @Override 76 | public void unregisterService() { 77 | logger.info("Unregister service."); 78 | try { 79 | this.zkClient.deletePath(zkPath); 80 | } catch (Exception ex) { 81 | logger.error("Delete service path error: {}.", ex.getMessage()); 82 | } 83 | this.zkClient.close(); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /test/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | bRPC 7 | com.polyu 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | test 13 | 14 | 15 | com.polyu 16 | common 17 | 1.0-SNAPSHOT 18 | compile 19 | 20 | 21 | com.polyu 22 | netty-client 23 | 1.0-SNAPSHOT 24 | compile 25 | 26 | 27 | com.polyu 28 | netty-server 29 | 1.0-SNAPSHOT 30 | compile 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /test/src/main/java/com/polyu/rpc/test/api/client/ClientTest.java: -------------------------------------------------------------------------------- 1 | package com.polyu.rpc.test.api.client; 2 | 3 | import com.polyu.rpc.test.service.HelloService; 4 | import com.polyu.rpc.client.RpcClient; 5 | import com.polyu.rpc.registry.zookeeper.ZKDiscovery; 6 | import com.polyu.rpc.route.impl.RpcLoadBalanceRoundRobin; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | public class ClientTest { 11 | 12 | private static final Logger logger = LoggerFactory.getLogger(ClientTest.class); 13 | 14 | public static void main(String[] args) throws Exception { 15 | new RpcClient(new ZKDiscovery("127.0.0.1:2181", "testYYB")); 16 | HelloService helloService = RpcClient.getProxyInstance(HelloService.class, "1.0", new RpcLoadBalanceRoundRobin(), 3000L); 17 | String str = helloService.hello("yyb"); 18 | logger.info("str = {}", str); 19 | logger.error("str = {}", str); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /test/src/main/java/com/polyu/rpc/test/api/client/ConcurrentTest.java: -------------------------------------------------------------------------------- 1 | package com.polyu.rpc.test.api.client; 2 | 3 | import com.polyu.rpc.test.service.HelloService; 4 | import com.polyu.rpc.client.RpcClient; 5 | import com.polyu.rpc.registry.zookeeper.ZKDiscovery; 6 | import com.polyu.rpc.route.impl.RpcLoadBalanceRandom; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import java.util.concurrent.Semaphore; 11 | 12 | public class ConcurrentTest { 13 | 14 | private static final Logger logger = LoggerFactory.getLogger(ConcurrentTest.class); 15 | 16 | private static HelloService helloService; 17 | 18 | private static Semaphore semaphore; 19 | 20 | public static void main(String[] args) throws Exception { 21 | new RpcClient(new ZKDiscovery("127.0.0.1:2181", "testYYB")); 22 | helloService = RpcClient.getProxyInstance(HelloService.class, "1.0", new RpcLoadBalanceRandom(), 3000L); 23 | for (int i = 0; i < 50; i++) { 24 | String res = helloService.hello("Yan Yibin"); 25 | logger.info(res); 26 | } 27 | 28 | for (int i = 1; i <= 20; i++) { 29 | Thread[] ts = new Thread[i]; 30 | for (int j = 1; j <= i; j++) { 31 | ts[j - 1] = new Thread(new Task()); 32 | } 33 | semaphore = new Semaphore(0); 34 | long s = System.currentTimeMillis(); 35 | for (int j = 1; j <= i; j++) { 36 | ts[j - 1].start(); 37 | } 38 | semaphore.acquire(i); 39 | long e = System.currentTimeMillis(); 40 | System.out.println("qps = " + i * 7000 / ((e - s) / 1000)); 41 | } 42 | } 43 | 44 | static class Task implements Runnable { 45 | 46 | @Override 47 | public void run() { 48 | for (int i = 0; i < 7000; i++) { 49 | String res = helloService.hello("Yan Yibin"); 50 | logger.info(res); 51 | } 52 | semaphore.release(1); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /test/src/main/java/com/polyu/rpc/test/api/server/ServerTest.java: -------------------------------------------------------------------------------- 1 | package com.polyu.rpc.test.api.server; 2 | 3 | import com.polyu.rpc.test.service.HelloService; 4 | import com.polyu.rpc.test.service.HelloService2; 5 | import com.polyu.rpc.test.service.HelloServiceImpl; 6 | import com.polyu.rpc.test.service.HelloServiceImpl2; 7 | import com.polyu.rpc.registry.ServiceRegistry; 8 | import com.polyu.rpc.registry.zookeeper.ZKRegistry; 9 | import com.polyu.rpc.server.netty.NettyServer; 10 | 11 | public class ServerTest { 12 | 13 | public static void main(String[] args) throws Exception { 14 | // String serverAddress = "127.0.0.1:18877"; 15 | // String serverAddress = "127.0.0.1:18876"; 16 | // String serverAddress = "127.0.0.1:18875"; 17 | String serverAddress = "127.0.0.1:18874"; 18 | 19 | // zk 20 | String registryAddress = "127.0.0.1:2181"; 21 | // nacos 22 | // String registryAddress = "127.0.0.1:8848"; 23 | ServiceRegistry serviceRegistry = new ZKRegistry(registryAddress, "springbootApplication"); 24 | NettyServer rpcServer = new NettyServer(serverAddress, serviceRegistry); 25 | HelloService helloService1 = new HelloServiceImpl(); 26 | HelloServiceImpl2 helloService2 = new HelloServiceImpl2(); 27 | rpcServer.addService(HelloService.class.getName(), "1.0", helloService1); 28 | rpcServer.addService(HelloService2.class.getName(), "1.0", helloService2); 29 | try { 30 | rpcServer.start(); 31 | } catch (Exception ex) { 32 | ex.printStackTrace(); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /test/src/main/java/com/polyu/rpc/test/service/HelloService.java: -------------------------------------------------------------------------------- 1 | package com.polyu.rpc.test.service; 2 | 3 | public interface HelloService { 4 | String hello(String name); 5 | } 6 | -------------------------------------------------------------------------------- /test/src/main/java/com/polyu/rpc/test/service/HelloService2.java: -------------------------------------------------------------------------------- 1 | package com.polyu.rpc.test.service; 2 | 3 | public interface HelloService2 { 4 | String hello(String name); 5 | } 6 | -------------------------------------------------------------------------------- /test/src/main/java/com/polyu/rpc/test/service/HelloServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.polyu.rpc.test.service; 2 | 3 | import com.polyu.rpc.annotation.BRpcProvider; 4 | 5 | @BRpcProvider(value = HelloService.class, version = "1.0", coreThreadPoolSize = 10, maxThreadPoolSize = 70) 6 | public class HelloServiceImpl implements HelloService { 7 | 8 | @Override 9 | public String hello(String name) { 10 | return name; 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /test/src/main/java/com/polyu/rpc/test/service/HelloServiceImpl2.java: -------------------------------------------------------------------------------- 1 | package com.polyu.rpc.test.service; 2 | 3 | import com.polyu.rpc.annotation.BRpcProvider; 4 | 5 | @BRpcProvider(value = HelloService2.class, version = "1.0", coreThreadPoolSize = 10, maxThreadPoolSize = 70) 6 | public class HelloServiceImpl2 implements HelloService2 { 7 | @Override 8 | public String hello(String name) { 9 | try { 10 | Thread.sleep(3000L); 11 | } catch (Exception e) { 12 | e.printStackTrace(); 13 | } 14 | return name; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /test/src/main/java/com/polyu/rpc/test/spring/client/ClientTest.java: -------------------------------------------------------------------------------- 1 | package com.polyu.rpc.test.spring.client; 2 | 3 | import com.polyu.rpc.test.service.HelloService; 4 | import com.polyu.rpc.test.service.HelloService2; 5 | import com.polyu.rpc.annotation.BRpcConsumer; 6 | import com.polyu.rpc.route.impl.RpcLoadBalanceRandom; 7 | import org.springframework.context.support.ClassPathXmlApplicationContext; 8 | import org.springframework.stereotype.Component; 9 | 10 | @Component 11 | public class ClientTest { 12 | 13 | @BRpcConsumer(version = "1.0", loadBalanceStrategy = RpcLoadBalanceRandom.class) 14 | static HelloService2 helloService2; 15 | 16 | @BRpcConsumer(version = "1.0") 17 | static HelloService helloService; 18 | 19 | /** 20 | * api 21 | * @param args 22 | */ 23 | public static void main(String[] args) throws InterruptedException { 24 | ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("client-spring.xml"); 25 | String name1 = helloService.hello("yyb"); 26 | System.out.println("name = " + name1); 27 | // String name2 = helloService2.hello("cyx"); 28 | // System.out.println("name = " + name2); 29 | classPathXmlApplicationContext.close(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /test/src/main/java/com/polyu/rpc/test/spring/server/ServerTest.java: -------------------------------------------------------------------------------- 1 | package com.polyu.rpc.test.spring.server; 2 | 3 | import org.springframework.context.support.ClassPathXmlApplicationContext; 4 | 5 | public class ServerTest { 6 | 7 | public static void main(String[] args) { 8 | new ClassPathXmlApplicationContext("server-spring.xml"); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /test/src/main/resources/application.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vincentbin/bRPC/27e931c606e937dc44331e77b92989ca10e66ff0/test/src/main/resources/application.properties -------------------------------------------------------------------------------- /test/src/main/resources/client-spring.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /test/src/main/resources/server-spring.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | --------------------------------------------------------------------------------