├── .gitignore
├── LICENSE
├── README.md
├── pom.xml
├── rpc-client
├── .DS_Store
├── pom.xml
└── src
│ └── main
│ └── java
│ └── cn
│ └── xpleaf
│ └── rpc
│ └── client
│ ├── discovery
│ └── ServiceDiscovery.java
│ ├── netty
│ └── RPCClient.java
│ └── proxy
│ └── RPCProxy.java
├── rpc-common
├── pom.xml
└── src
│ └── main
│ └── java
│ └── cn
│ └── xpleaf
│ └── rpc
│ └── common
│ ├── pojo
│ ├── RPCRequest.java
│ └── RPCResponse.java
│ └── utils
│ ├── RPCDecoder.java
│ ├── RPCEncoder.java
│ └── SerializationUtil.java
└── rpc-server
├── pom.xml
└── src
└── main
└── java
└── cn
└── xpleaf
└── rpc
└── server
├── annotation
└── RPCService.java
├── netty
├── RPCServer.java
└── RPCServerHandler.java
└── registry
└── ServiceRegistry.java
/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled class file
2 | *.class
3 |
4 | # Log file
5 | *.log
6 |
7 | # BlueJ files
8 | *.ctxt
9 |
10 | # Mobile Tools for Java (J2ME)
11 | .mtj.tmp/
12 |
13 | # Package Files #
14 | *.jar
15 | *.war
16 | *.nar
17 | *.ear
18 | *.zip
19 | *.tar.gz
20 | *.rar
21 |
22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
23 | hs_err_pid*
24 |
25 | # 过滤不必要的目录和文件
26 | *.iml
27 | .idea
28 | rpc-common/target/maven-archiver/pom.properties
29 | rpc-server/target/maven-archiver/pom.properties
30 | rpc-client/target/maven-archiver/pom.properties
--------------------------------------------------------------------------------
/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 [yyyy] [name of copyright owner]
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 | # minidubbo
2 | A Full RPC Framework Based on Netty.
3 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 | 4.0.0
6 |
7 | cn.xpleaf.rpc
8 | minidubbo
9 | pom
10 | 1.0-SNAPSHOT
11 |
12 | rpc-common
13 | rpc-server
14 | rpc-client
15 |
16 |
17 | minidubbo
18 |
19 | http://www.example.com
20 |
21 |
22 |
23 | UTF-8
24 | 4.12
25 | 4.2.4.RELEASE
26 | 4.1.21.Final
27 | 1.1.3
28 | 3.4.7
29 | 1.7.10
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | org.springframework
38 | spring-context
39 | ${spring.version}
40 |
41 |
42 | org.springframework
43 | spring-beans
44 | ${spring.version}
45 |
46 |
47 | org.springframework
48 | spring-webmvc
49 | ${spring.version}
50 |
51 |
52 | org.springframework
53 | spring-jdbc
54 | ${spring.version}
55 |
56 |
57 | org.springframework
58 | spring-aspects
59 | ${spring.version}
60 |
61 |
62 | org.springframework
63 | spring-jms
64 | ${spring.version}
65 |
66 |
67 | org.springframework
68 | spring-context-support
69 | ${spring.version}
70 |
71 |
72 |
73 | io.netty
74 | netty-all
75 | ${netty.version}
76 |
77 |
78 |
79 | com.dyuproject.protostuff
80 | protostuff-core
81 | ${protostuff.version}
82 |
83 |
84 | com.dyuproject.protostuff
85 | protostuff-runtime
86 | ${protostuff.version}
87 |
88 |
89 |
90 | org.apache.zookeeper
91 | zookeeper
92 | ${zookeeper.version}
93 |
94 |
95 |
96 | org.slf4j
97 | slf4j-api
98 | ${log4j.version}
99 |
100 |
101 | org.slf4j
102 | slf4j-log4j12
103 | ${log4j.version}
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 | junit
113 | junit
114 | 4.12
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 | org.apache.maven.plugins
123 | maven-compiler-plugin
124 | 2.3.2
125 |
126 | UTF-8
127 | 1.8
128 | 1.8
129 | true
130 |
131 |
132 |
133 |
134 | maven-assembly-plugin
135 |
136 |
137 |
138 | jar-with-dependencies
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 | make-assembly
152 | package
153 |
154 | single
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
--------------------------------------------------------------------------------
/rpc-client/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xpleaf/minidubbo/1ab502dd623a8bcfe8de786ecd8db09ebc1ce2c6/rpc-client/.DS_Store
--------------------------------------------------------------------------------
/rpc-client/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 | minidubbo
7 | cn.xpleaf.rpc
8 | 1.0-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | rpc-client
13 |
14 | rpc-client
15 |
16 | http://www.example.com
17 |
18 |
19 | UTF-8
20 |
21 |
22 |
23 |
24 | cn.xpleaf.rpc
25 | rpc-common
26 | 1.0-SNAPSHOT
27 |
28 |
29 |
30 | org.apache.zookeeper
31 | zookeeper
32 |
33 |
34 |
35 | org.slf4j
36 | slf4j-api
37 |
38 |
39 | org.slf4j
40 | slf4j-log4j12
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/rpc-client/src/main/java/cn/xpleaf/rpc/client/discovery/ServiceDiscovery.java:
--------------------------------------------------------------------------------
1 | package cn.xpleaf.rpc.client.discovery;
2 |
3 | import java.util.List;
4 | import java.util.Random;
5 | import java.util.concurrent.CountDownLatch;
6 |
7 | import org.apache.zookeeper.KeeperException;
8 | import org.apache.zookeeper.WatchedEvent;
9 | import org.apache.zookeeper.Watcher;
10 | import org.apache.zookeeper.ZooKeeper;
11 | import org.slf4j.Logger;
12 | import org.slf4j.LoggerFactory;
13 |
14 | /**
15 | * 服务发现类,用于向zookeeper中查询服务提供者的地址(host:port) 暂时的设计思路是,每执行一次调用都会进行查询,而不是像dubbo那样
16 | * 会将interfaceName和服务地址缓存起来,后面会实现这一点
17 | *
18 | * 目前已经实现了负载均衡服务的功能,算法为随机负载均衡,即如果服务提供者有3个,会随机返回其中一个服务提供者的地址信息
19 | *
20 | * 另外,显然我这里都是使用zookeeper较为原生的API,原因很简单,当初入手zookeeper API时就是先从原生的学起,之后就直接应用在minidubbo上,
21 | * 在我的另外一个项目中[分布式爬虫系统],使用的是较为高层次的API,即curator,如果有兴趣,可以参考一下使用方式:https://github.com/xpleaf/ispider
22 | *
23 | * @author yeyonghao
24 | */
25 | public class ServiceDiscovery {
26 |
27 | // zookeeper中保存服务信息的父节点
28 | private final String parentNode = "/minidubbo";
29 | // zookeeper的地址,由spring构造ServiceDiscovery对象时传入
30 | private String registryAddress;
31 | // 连接zookeeper的超时时间
32 | private int sessionTimeout = 2000;
33 | // 连接zookeeper的客户端
34 | private ZooKeeper zkClient = null;
35 | // 用来确保zookeeper连接成功后才进行后续的操作
36 | private CountDownLatch latch = new CountDownLatch(1);
37 | // log4j日志记录
38 | private Logger logger = LoggerFactory.getLogger(ServiceDiscovery.class);
39 |
40 | /**
41 | * 构造方法
42 | *
43 | * @param registryAddress zookeeper的地址,格式为 host:port
44 | */
45 | public ServiceDiscovery(String registryAddress) {
46 | this.registryAddress = registryAddress;
47 | }
48 |
49 | /**
50 | * 发现服务方法 根据接口名称向zookeeper查询服务提供者的地址
51 | *
52 | * @param interfaceName 接口名称
53 | * @return serverAddress服务提供者的地址,格式为 host:port 如果不存在,则返回null
54 | */
55 | public String discoverService(String interfaceName) {
56 | // 如果zkClient为null,则连接未建立,先建立连接
57 | if (this.zkClient == null) {
58 | logger.info("未连接zookeeper,准备建立连接...");
59 | connectServer();
60 | }
61 | // 构建需要查询的节点的完整名称
62 | String node = parentNode + "/" + interfaceName;
63 | // 获取该节点所对应的服务提供者地址
64 | logger.info("zookeeper连接建立完毕,准备获取服务提供者地址[{}]...", node);
65 | String serverAddress = getServerAddress(node);
66 | logger.info("服务提供者地址获取完毕[{}]...", serverAddress);
67 | // 返回结果
68 | return serverAddress;
69 | }
70 |
71 | /**
72 | * 建立连接
73 | */
74 | private void connectServer() {
75 | try {
76 | zkClient = new ZooKeeper(registryAddress, sessionTimeout, new Watcher() {
77 |
78 | // 注册监听事件,连接成功后会调用process方法
79 | // 此时再调用latch的countDown方法使CountDownLatch计数器减1
80 | // 因为构造CountDownLatch对象时设置的值为1,减1后变为0,所以执行该方法后latch.await()将会中断
81 | // 从而确保连接成功后才会执行后续zookeeper的相关操作
82 | @Override
83 | public void process(WatchedEvent event) {
84 | // 如果状态为已连接,则使用CountDownLatch计数器减1
85 | if (event.getState() == Event.KeeperState.SyncConnected) {
86 | latch.countDown();
87 | }
88 | }
89 | });
90 | latch.await();
91 | } catch (Exception e) {
92 | e.printStackTrace();
93 | }
94 | }
95 |
96 | /**
97 | * 获取对应接口名的服务地址
98 | *
99 | * @param node 接口名对应的完整节点名称
100 | * @return serverAddress,如果为null,说明不存在该节点的服务提供者
101 | */
102 | private String getServerAddress(String node) {
103 | String serverAddress = null;
104 | try {
105 | // 先获取接口名节点的子节点,子节点下是服务器的列表
106 | // 需要注意的是,如果不存在该节点,会有异常,此时下面的代码就不会执行
107 | List children = zkClient.getChildren(node, false);
108 | // 随机负载均衡,会随机返回注册服务列表中的其中一个服务地址
109 | String firstChildren = children.get(getRandomNum(children.size()));
110 | // 构建该服务提供者的完整节点名称
111 | String firstChildrenNode = node + "/" + firstChildren;
112 | // 获取服务提供者节点的数据,得到serverAddress的byte数组
113 | byte[] serverAddressByte = zkClient.getData(firstChildrenNode, false, null);
114 | // 将byte数组转换为字符串,同时赋值给serverAddress
115 | serverAddress = new String(serverAddressByte);
116 | } catch (Exception e) {
117 | logger.error("节点[{}]不存在,无法获取服务提供者地址...", node);
118 | logger.error(e.getMessage());
119 | }
120 |
121 | return serverAddress;
122 | }
123 |
124 | /**
125 | * 返回一个给定范围内的随机数值,用于实现
126 | * @param size
127 | * @return
128 | */
129 | private Integer getRandomNum(int size) {
130 | Random random = new Random();
131 |
132 | return random.nextInt(size);
133 | }
134 |
135 | /*
136 | public static void main(String[] args) throws Exception {
137 | ServiceDiscovery serviceDiscovery = new ServiceDiscovery("192.168.43.132:2181");
138 | serviceDiscovery.connectServer();
139 | String serverAddress = serviceDiscovery.getServerAddress("/minidubbo/cn.xpleaf.app.service.ItemService");
140 | System.out.println("serverAddress: " + serverAddress);
141 | }
142 | */
143 |
144 | }
145 |
--------------------------------------------------------------------------------
/rpc-client/src/main/java/cn/xpleaf/rpc/client/netty/RPCClient.java:
--------------------------------------------------------------------------------
1 | package cn.xpleaf.rpc.client.netty;
2 |
3 | import cn.xpleaf.rpc.common.pojo.RPCRequest;
4 | import cn.xpleaf.rpc.common.pojo.RPCResponse;
5 | import cn.xpleaf.rpc.common.utils.RPCDecoder;
6 | import cn.xpleaf.rpc.common.utils.RPCEncoder;
7 | import io.netty.bootstrap.Bootstrap;
8 | import io.netty.channel.ChannelFuture;
9 | import io.netty.channel.ChannelHandlerContext;
10 | import io.netty.channel.ChannelInitializer;
11 | import io.netty.channel.ChannelOption;
12 | import io.netty.channel.EventLoopGroup;
13 | import io.netty.channel.SimpleChannelInboundHandler;
14 | import io.netty.channel.nio.NioEventLoopGroup;
15 | import io.netty.channel.socket.SocketChannel;
16 | import io.netty.channel.socket.nio.NioSocketChannel;
17 | import org.slf4j.Logger;
18 | import org.slf4j.LoggerFactory;
19 |
20 | /**
21 | * RPC客户端,用于连接RPC服务端,向服务端发送请求
22 | * 主要是netty的模板代码
23 | *
24 | * @author yeyonghao
25 | */
26 | public class RPCClient extends SimpleChannelInboundHandler {
27 |
28 | // RPC服务端的地址
29 | private String host;
30 | // RPC服务端的端口号
31 | private int port;
32 | // RPCResponse响应对象
33 | private RPCResponse response;
34 | // log4j日志记录
35 | private Logger logger = LoggerFactory.getLogger(RPCClient.class);
36 |
37 | /**
38 | * 构造方法
39 | *
40 | * @param host RPC服务端的地址
41 | * @param port RPC服务端的端口号
42 | */
43 | public RPCClient(String host, int port) {
44 | this.host = host;
45 | this.port = port;
46 | }
47 |
48 | /**
49 | * 向RPC服务端发送请求方法
50 | *
51 | * @param request RPC客户端向RPC服务端发送的request对象
52 | * @return
53 | */
54 | public RPCResponse sendRequest(RPCRequest request) throws Exception {
55 |
56 | // 配置客户端NIO线程组
57 | EventLoopGroup group = new NioEventLoopGroup();
58 | try {
59 | Bootstrap b = new Bootstrap();
60 | b.group(group).channel(NioSocketChannel.class).option(ChannelOption.TCP_NODELAY, true)
61 | // 设置TCP连接超时时间
62 | .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 3000)
63 | .handler(new ChannelInitializer() {
64 |
65 | @Override
66 | protected void initChannel(SocketChannel ch) throws Exception {
67 | // 添加解码器,RPC客户端需要解码的是RPCResponse对象,因为需要接收服务端发送过来的响应
68 | ch.pipeline().addLast(new RPCDecoder(RPCResponse.class));
69 | // 添加编码器
70 | ch.pipeline().addLast(new RPCEncoder());
71 | // 添加业务处理handler,本类继承了SimpleChannelInboundHandler
72 | // RPCClient继承了SimpleChannelInboundHandler,所以可以直接传入本类对象
73 | ch.pipeline().addLast(RPCClient.this);
74 | }
75 | });
76 | // 发起异步连接操作(注意服务端是bind,客户端则需要connect)
77 | logger.info("准备发起异步连接操作[{}:{}]", host, port);
78 | ChannelFuture f = b.connect(host, port).sync();
79 |
80 | // 判断连接是否成功的代码
81 | // System.out.println(f.isSuccess());
82 |
83 | // 向RPC服务端发起请求
84 | logger.info("准备向RPC服务端发起请求...");
85 | f.channel().writeAndFlush(request);
86 |
87 |
88 | // 需要注意的是,如果没有接收到服务端返回数据,那么会一直停在这里等待
89 | // 等待客户端链路关闭
90 | logger.info("准备等待客户端链路关闭...");
91 | f.channel().closeFuture().sync();
92 | } finally {
93 | // 优雅退出,释放NIO线程组
94 | logger.info("优雅退出,释放NIO线程组...");
95 | group.shutdownGracefully();
96 | }
97 |
98 | return response;
99 | }
100 |
101 | /**
102 | * 读取RPC服务端的响应结果,并赋值给response对象
103 | */
104 | @Override
105 | protected void channelRead0(ChannelHandlerContext ctx, RPCResponse msg) throws Exception {
106 | logger.info("从RPC服务端接收到响应...");
107 | this.response = msg;
108 | // 关闭与服务端的连接,这样就可以执行f.channel().closeFuture().sync();之后的代码,即优雅退出
109 | // 相当于是主动关闭连接
110 | ctx.close();
111 | }
112 |
113 | /*
114 | public static void main(String[] args) throws Exception {
115 | int port = 21881;
116 | if (args != null && args.length > 0) {
117 | try {
118 | port = Integer.valueOf(port);
119 | } catch (NumberFormatException e) {
120 | // 采用默认值
121 | }
122 | }
123 | new RPCClient("localhost", port).sendRequest(new RPCRequest());
124 | }
125 | */
126 | }
127 |
--------------------------------------------------------------------------------
/rpc-client/src/main/java/cn/xpleaf/rpc/client/proxy/RPCProxy.java:
--------------------------------------------------------------------------------
1 | package cn.xpleaf.rpc.client.proxy;
2 |
3 | import cn.xpleaf.rpc.client.discovery.ServiceDiscovery;
4 | import cn.xpleaf.rpc.client.netty.RPCClient;
5 | import cn.xpleaf.rpc.common.pojo.RPCRequest;
6 | import cn.xpleaf.rpc.common.pojo.RPCResponse;
7 | import org.slf4j.Logger;
8 | import org.slf4j.LoggerFactory;
9 |
10 | import java.lang.reflect.InvocationHandler;
11 | import java.lang.reflect.Method;
12 | import java.lang.reflect.Proxy;
13 | import java.util.UUID;
14 |
15 | /**
16 | * 动态代理对象类,用于根据接口创建动态代理对象
17 | *
18 | * @author yeyonghao
19 | */
20 | public class RPCProxy {
21 |
22 | // 用于发现服务的对象
23 | private ServiceDiscovery serviceDiscovery;
24 |
25 | // log4j日志记录
26 | private Logger logger = LoggerFactory.getLogger(RPCProxy.class);
27 |
28 | /**
29 | * 构造函数,传入ServiceDiscovery对象
30 | */
31 | public RPCProxy(ServiceDiscovery serviceDiscovery) {
32 | this.serviceDiscovery = serviceDiscovery;
33 | }
34 |
35 | /**
36 | * 获得动态代理对象的通用方法,实现思路是,该方法中,并不需要具体的实现类对象 因为在invoke方法中,并不会调用Method
37 | * method这个方法,只是获得其方法的名字 然后将其封装在Netty请求中,发送到Netty服务端中请求远程调用的结果
38 | *
39 | * @param interfaceClass 需要被代理的接口的类型对象
40 | * @return proxy 对应接口的代理对象
41 | */
42 | @SuppressWarnings("unchecked")
43 | public T getProxy(Class> interfaceClass) {
44 |
45 | T proxy = (T) Proxy.newProxyInstance(RPCProxy.class.getClassLoader(), new Class>[]{interfaceClass},
46 | new InvocationHandler() {
47 |
48 | @Override
49 | public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
50 |
51 | logger.info("准备构建RPCRequest对象...");
52 |
53 | // 构建RPCRequest对象
54 | RPCRequest request = new RPCRequest();
55 | // 设置requestId
56 | request.setRequestId(UUID.randomUUID().toString());
57 | // 设置接口名interfaceName
58 | String interfaceName = method.getDeclaringClass().getName();
59 | request.setInterfaceName(interfaceName);
60 | /*
61 | 当时在将其整合到Spring-mvc失败时的调试信息
62 | 前后调试了近两个月才搞定,所以这部分调试信息就不删除了
63 | String interfaceName1 = method.getDeclaringClass().getName();
64 | String interfaceName2 = interfaceClass.getName();
65 | System.out.println("接口名称是:" + interfaceClass.getName());
66 | System.out.println("接口名称是1:" + method.getDeclaringClass().getName());
67 | String proxyName = proxy.getClass().getName();
68 | */
69 | // 设置方法名methodName
70 | request.setMethodName(method.getName());
71 | // 设置参数类型parameterTypes
72 | request.setParameterTypes(method.getParameterTypes());
73 | // 设置参数列表parameters
74 | request.setParameters(args);
75 |
76 | logger.info("RPCRequest对象构建完毕,准备发现服务[{}]...", interfaceName);
77 |
78 | // 发现服务,得到服务地址,格式为 host:port
79 | String serverAddress = serviceDiscovery.discoverService(interfaceName);
80 | // 如果服务不存在,null,否则就构建RPC客户端进行远程调用
81 | if (serverAddress == null) {
82 | logger.error("服务[{}]的提供者不存在,发现服务失败...", interfaceName);
83 | return null;
84 | } else {
85 |
86 | logger.info("发现服务完毕,准备解析服务地址[{}]...", serverAddress);
87 |
88 | // 解析服务地址
89 | String[] array = serverAddress.split(":");
90 | String host = array[0];
91 | int port = Integer.valueOf(array[1]);
92 |
93 | logger.info("服务地址解析完毕,准备构建RPC客户端...");
94 |
95 | // 构建RPC客户端
96 | RPCClient client = new RPCClient(host, port);
97 |
98 | logger.info("RPC客户端构建完毕,准备向RPC服务端发送请求...");
99 |
100 | // 向RPC服务端发送请求
101 | RPCResponse response = client.sendRequest(request);
102 |
103 | // 返回信息
104 | if (response.isError()) {
105 | // 如果进行远程调用时出现异常,[则抛出异常信息]--->直接返回null
106 | // throw response.getError();
107 | logger.error("[{}]远程过程调用出现异常,远程过程调用失败...", interfaceName);
108 | return null;
109 | } else {
110 | // 如果没有异常,则返回调用的结果
111 | logger.info("[{}]远程过程调用完毕,远程过程调用成功...", interfaceName);
112 | return response.getResult();
113 | }
114 | }
115 | }
116 | });
117 |
118 | return proxy;
119 | }
120 |
121 | }
122 |
--------------------------------------------------------------------------------
/rpc-common/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 | minidubbo
7 | cn.xpleaf.rpc
8 | 1.0-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | rpc-common
13 |
14 | rpc-common
15 |
16 | http://www.example.com
17 |
18 |
19 | UTF-8
20 |
21 |
22 |
23 |
24 |
25 | io.netty
26 | netty-all
27 |
28 |
29 |
30 | com.dyuproject.protostuff
31 | protostuff-core
32 |
33 |
34 | com.dyuproject.protostuff
35 | protostuff-runtime
36 |
37 |
38 |
39 | org.slf4j
40 | slf4j-api
41 |
42 |
43 | org.slf4j
44 | slf4j-log4j12
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/rpc-common/src/main/java/cn/xpleaf/rpc/common/pojo/RPCRequest.java:
--------------------------------------------------------------------------------
1 | package cn.xpleaf.rpc.common.pojo;
2 |
3 | /**
4 | * RPCRequest是client向server端发送数据的传输载体,将需要进行传输的pojo对象统一封装到RPCRequest对象中,
5 | * 这样会为编解码工作带来很大的方便性和统一性,同时也可以携带其它信息, 对于后面对程序进行扩展会有非常大的帮助
6 | *
7 | * @author yeyonghao
8 | */
9 | public class RPCRequest {
10 |
11 | // 请求的ID,为UUID
12 | private String requestId;
13 | // 接口名称
14 | private String interfaceName;
15 | // 调用的方法名称
16 | private String methodName;
17 | // 方法的参数类型
18 | private Class>[] parameterTypes;
19 | // 方法的参数值
20 | private Object[] parameters;
21 |
22 | /**
23 | * 上面几个数据就可以唯一地确定某一个类(接口)中的某一个具体的方法
24 | */
25 | public String getRequestId() {
26 | return requestId;
27 | }
28 |
29 | public void setRequestId(String requestId) {
30 | this.requestId = requestId;
31 | }
32 |
33 | public String getInterfaceName() {
34 | return interfaceName;
35 | }
36 |
37 | public void setInterfaceName(String interfaceName) {
38 | this.interfaceName = interfaceName;
39 | }
40 |
41 | public String getMethodName() {
42 | return methodName;
43 | }
44 |
45 | public void setMethodName(String methodName) {
46 | this.methodName = methodName;
47 | }
48 |
49 | public Class>[] getParameterTypes() {
50 | return parameterTypes;
51 | }
52 |
53 | public void setParameterTypes(Class>[] parameterTypes) {
54 | this.parameterTypes = parameterTypes;
55 | }
56 |
57 | public Object[] getParameters() {
58 | return parameters;
59 | }
60 |
61 | public void setParameters(Object[] parameters) {
62 | this.parameters = parameters;
63 | }
64 |
65 | }
66 |
--------------------------------------------------------------------------------
/rpc-common/src/main/java/cn/xpleaf/rpc/common/pojo/RPCResponse.java:
--------------------------------------------------------------------------------
1 | package cn.xpleaf.rpc.common.pojo;
2 |
3 | /**
4 | * RPCResponse是server向client端发送数据的传输载体,将需要进行传输的pojo对象统一封装到RPCResponse对象中,
5 | * 这样会为编解码工作带来很大的方便性和统一性,同时也可以携带其它信息, 对于后面对程序进行扩展会有非常大的帮助
6 | *
7 | * @author yeyonghao
8 | */
9 | public class RPCResponse {
10 |
11 | private String requestId;
12 | private Throwable error;
13 | private Object result;
14 |
15 | public boolean isError() {
16 | return error != null;
17 | }
18 |
19 | public String getRequestId() {
20 | return requestId;
21 | }
22 |
23 | public void setRequestId(String requestId) {
24 | this.requestId = requestId;
25 | }
26 |
27 | public Throwable getError() {
28 | return error;
29 | }
30 |
31 | public void setError(Throwable error) {
32 | this.error = error;
33 | }
34 |
35 | public Object getResult() {
36 | return result;
37 | }
38 |
39 | public void setResult(Object result) {
40 | this.result = result;
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/rpc-common/src/main/java/cn/xpleaf/rpc/common/utils/RPCDecoder.java:
--------------------------------------------------------------------------------
1 | package cn.xpleaf.rpc.common.utils;
2 |
3 | import java.util.List;
4 |
5 | import io.netty.buffer.ByteBuf;
6 | import io.netty.channel.ChannelHandlerContext;
7 | import io.netty.handler.codec.MessageToMessageDecoder;
8 | import org.slf4j.Logger;
9 | import org.slf4j.LoggerFactory;
10 |
11 | /**
12 | * RPCDecoder继承自Netty中的MessageToMessageDecoder类,
13 | * 并重写抽象方法decode(ChannelHandlerContext ctx, ByteBuf msg, List