├── .idea
├── .name
├── compiler.xml
├── inspectionProfiles
│ └── Project_Default.xml
├── libraries
│ ├── Arquillian_JUnit_Release.xml
│ ├── Maven__com_caucho_hessian_4_0_38.xml
│ ├── Maven__com_dyuproject_protostuff_protostuff_api_1_0_8.xml
│ ├── Maven__com_dyuproject_protostuff_protostuff_collectionschema_1_0_8.xml
│ ├── Maven__com_dyuproject_protostuff_protostuff_core_1_0_8.xml
│ ├── Maven__com_dyuproject_protostuff_protostuff_runtime_1_0_8.xml
│ ├── Maven__junit_junit_4_12.xml
│ ├── Maven__org_hamcrest_hamcrest_core_1_3.xml
│ └── Maven__org_objenesis_objenesis_2_6.xml
├── misc.xml
├── modules.xml
├── uiDesigner.xml
├── vcs.xml
└── workspace.xml
├── README.md
├── note
├── img
│ ├── 1.png
│ ├── 2.png
│ ├── 20191108234611174.png
│ ├── 3.png
│ ├── 4.png
│ ├── 5.png
│ ├── 6.png
│ ├── 7.png
│ ├── 8.png
│ ├── 9.png
│ └── image-20191110180140681.png
└── note.md
├── pom.xml
├── soft-rpc.iml
└── src
└── main
├── java
└── softrpc
│ └── framework
│ ├── invoker
│ ├── ClientProxyBeanFactory.java
│ ├── NettyChannelPoolFactory.java
│ ├── NettyClientHandler.java
│ ├── ResponseReceiver.java
│ ├── ResponseReceiverHolder.java
│ └── RevokerServiceCallable.java
│ ├── loadBalance
│ ├── common
│ │ ├── LoadBalanceEngine.java
│ │ └── LoadBalanceStrategyEnum.java
│ └── strategy
│ │ ├── LoadBalanceStategy.java
│ │ └── impl
│ │ ├── HashLoadBalanceStrategyImpl.java
│ │ ├── PollingLoadBalanceStrategyImpl.java
│ │ ├── RandomLoadBalanceStrategyImpl.java
│ │ ├── WeightPollingLoadBalanceStrategyImpl.java
│ │ └── WeightRandomLoadBalanceStrategyImpl.java
│ ├── provider
│ ├── NettyDecodeHandler.java
│ ├── NettyEncodeHandler.java
│ ├── NettyServer.java
│ ├── NettyServerHandler.java
│ └── ServiceProvider.java
│ ├── serialization
│ ├── common
│ │ ├── SerializerEngine.java
│ │ ├── SerializerFactory.java
│ │ └── SerializerType.java
│ ├── message
│ │ ├── RequestMessage.java
│ │ └── ResponseMessage.java
│ └── serializer
│ │ ├── HessianSerializer.java
│ │ ├── JDKSerializer.java
│ │ ├── ProtoStuffSerializer.java
│ │ └── Serializer.java
│ ├── spring
│ ├── factoryBean
│ │ ├── RpcReferenceFactoryBean.java
│ │ └── RpcServiceFactoryBean.java
│ ├── handler
│ │ ├── RpcReferenceNamespaceHandler.java
│ │ └── RpcServiceNamespaceHandler.java
│ └── parser
│ │ ├── RpcReferenceBeanDefinitionParser.java
│ │ └── RpcServiceBeanDefinitionParser.java
│ ├── test
│ └── service
│ │ ├── MainClient.java
│ │ ├── MainServer.java
│ │ ├── Service1.java
│ │ ├── Service2.java
│ │ └── imp
│ │ ├── Service1imp.java
│ │ ├── Service2imp1.java
│ │ └── Service2imp2.java
│ ├── utils
│ ├── IPutil.java
│ └── PropertyConfigUtil.java
│ └── zookeeper
│ ├── RegisterCenter.java
│ ├── RegisterCenter4Governance.java
│ ├── RegisterCenter4Invoker.java
│ ├── RegisterCenter4Provider.java
│ └── message
│ ├── InvokerRegisterMessage.java
│ └── ProviderRegisterMessage.java
└── resources
├── META-INF
├── soft-reference.xsd
├── soft-service.xsd
├── spring.handlers
└── spring.schemas
├── log4j.properties
├── rpc-reference.xml
├── rpc-service.xml
└── soft-rpc.properties
/.idea/.name:
--------------------------------------------------------------------------------
1 | softrpc
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/Project_Default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/.idea/libraries/Arquillian_JUnit_Release.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__com_caucho_hessian_4_0_38.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__com_dyuproject_protostuff_protostuff_api_1_0_8.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__com_dyuproject_protostuff_protostuff_collectionschema_1_0_8.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__com_dyuproject_protostuff_protostuff_core_1_0_8.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__com_dyuproject_protostuff_protostuff_runtime_1_0_8.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__junit_junit_4_12.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_hamcrest_hamcrest_core_1_3.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_objenesis_objenesis_2_6.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/uiDesigner.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | -
6 |
7 |
8 | -
9 |
10 |
11 | -
12 |
13 |
14 | -
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 |
44 |
45 |
46 | -
47 |
48 |
49 |
50 |
51 | -
52 |
53 |
54 |
55 |
56 | -
57 |
58 |
59 |
60 |
61 | -
62 |
63 |
64 |
65 |
66 | -
67 |
68 |
69 |
70 |
71 | -
72 |
73 |
74 | -
75 |
76 |
77 |
78 |
79 | -
80 |
81 |
82 |
83 |
84 | -
85 |
86 |
87 |
88 |
89 | -
90 |
91 |
92 |
93 |
94 | -
95 |
96 |
97 |
98 |
99 | -
100 |
101 |
102 | -
103 |
104 |
105 | -
106 |
107 |
108 | -
109 |
110 |
111 | -
112 |
113 |
114 |
115 |
116 | -
117 |
118 |
119 | -
120 |
121 |
122 |
123 |
124 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Soft-RPC使用文档
2 |
3 | Soft-RPC是一种基本功能完整的RPC框架的实现,供学习交流使用,也可以基于此方案进行二次开发,结合业务需要实现个性化RPC框架的研究和定制。
4 |
5 | Email: xctian@zju.edu.cn
6 |
7 | ## 简介
8 |
9 | Soft-RPC是一种轻量级的RPC框架实现方案,并未达到商用水准,但它具备RPC框架所需的核心功能,实现过程涉及**Netty、Zookeeper、Spring、JUC包下某些类的使用、序列化与反序列化、负载均衡算法、并发编程、网络编程、IO**、**设计模式**等后台开发技能领域,非常适合作为一个学习分布式服务框架、远程过程调用原理等内容的参考项目。以下简要介绍Soft-RPC的功能组成和特性:
10 |
11 | ### 配置文件
12 |
13 | - 使用自定义配置标签,支持高度自定义、可优化
14 | - 【soft-rpc.properties】进行全局配置,如zk超时时间,线程池大小等;【rpc-reference.xml】利用自定义标签进行服务消费者的相关参数配置;【rpc-service.xml】利用自定义标签进行服务提供者相关参数配置
15 | - 大部分配置项支持默认配置和容错配置
16 |
17 | ### 集成Spring
18 |
19 | - 集成Spring,支持“一键启动”:服务端只需要在启动类中加载“rpc-service.xml"文件生成ApplicationContext实例即可将启动服务;客户端只需要在启动类中加载“rpc-reference.xml"文件即可完成客户端初始化
20 | - 将项目打包至本地仓库,即可直接当RPC框架进行使用
21 |
22 | ### 序列化/反序列化方案
23 |
24 | - 支持三种序列化/反序列化协议:JDK原生序列化、ProtoStuff、Hessian,并且支持使用者自行配置
25 | - 支持客户端和服务端从上述协议中自由选择序列化协议
26 | - 为解决TCP传输过程中的粘包、半包问题,对消息进行简单的数据格式定义:消息头+消息体。其中消息头由2个int类型变量组成,分别表示采用的序列化协议code和消息体的长度;消息体则表示所要进行传输的数据。
27 |
28 | ### 负载均衡策略
29 |
30 | - 提供Random(随机)/WeightRandom(加权随机)/Polling(轮询)/WeightPolling(加权轮询)/Hash(IP哈希)五种负载均衡算法的选用
31 | - 支持客户端对负载均衡策略进行指定:在rpc-reference.xml中各客户端可配置不同的负载均衡策略
32 |
33 | ### Zookeeper注册中心
34 |
35 | - 通过自定义标签发布服务和引用服务,可实现服务的自动注册与发现
36 | - 支持服务地址变更自动感知,服务自动下线和自动扩容。监听服务提供者列表并实时推送变化至各客户端
37 |
38 | ### 优化和改进
39 |
40 | - 针对异步通信框架Netty实现结果同步等待机制:使用BlockingQueue实现对Netty异步返回结果的同步等待
41 | - 客户端初始化时,自动获取服务提供者地址并预初始化生成对应的ChannelPool,提高连接速度
42 | - 发布服务时可以对该服务的客户端连接数进行限制(通过信号量机制),以提升系统的稳定性
43 | - 线程池隔离:客户端对不同的服务进行RPC调用时使用不同的线程池,以隔离RPC服务风险(线程池大小可配置)
44 |
45 | ## 使用说明
46 |
47 | ### 先RUN起来
48 |
49 | 本项目在`softrpc.framework.test.service`提供了简单的调用RPC服务测试用例,直接RUN即可运行测试。以下简单介绍如何将本项目在你的电脑上RUN起来:
50 |
51 | 1. 参考[此处](https://www.cnblogs.com/xubao/p/10693202.html),在Windows环境下安装和部署zookeeper,找到bin文件夹下面的zkServer.cmd服务,运行启动。注意使用zookeeper时不要关闭CMD窗口。
52 |
53 | 2. clone本项目到本地,使用IDEA打开,配置maven和本地仓库,等待依赖自动导入完成。
54 |
55 | 3. 找到`softrpc.framework.test.service.MainServer`,运行main方法,可以看到控制台打印服务端的启动日志;再找到`softrpc.framework.test.service.MainClient`,同样运行它的main方法,可以看到控制台打印客户端的运行日志。
56 |
57 | 
58 |
59 | 
60 |
61 |
62 | ### 自定义RUN
63 |
64 | 你也可以设置自己的接口和实现类,但是注意需要在rpc-service.xml和rpc-reference.xml中发布和引用你创建的服务。本项目自定义的配置文件有:soft-rpc.properties / rpc-service.xml / rpc-reference.xml
65 |
66 | #### soft-rpc.properties
67 |
68 | 全局配置文件,涉及ZK地址和相关参数、默认负载均衡策略、序列化/反序列化协议等,具体可以参考该文件中的注释说明。除了以下情况必须配置的参数项,其他配置项都支持缺省配置
69 |
70 | - soft.rpc.zookeeper.address代表的ZK地址必须配置
71 | - 发布服务时, 如果没有在rpc-service.xml配置appName属性的soft:service标签, 那么soft.rpc.server.app.name必须配置
72 | - 引用服务时, 如果没有在rpc-reference.xml配置appName属性的soft:reference标签, 那么spft.rpc.client.app.name必须配置
73 |
74 | ```properties
75 | # 注册中心ZK地址,必须进行配置,无默认值
76 | soft.rpc.zookeeper.address=localhost:2181
77 | # session超时时间,默认500
78 | soft.rpc.zookeeper.session.timeout=3000
79 | # 连接超时时间,默认500
80 | soft.rpc.zookeeper.connection.timeout=3000
81 | # 服务端序列化协议,Default,可选值:Default/Hessian/ProtoStuff
82 | soft.rpc.server.serializer=Default
83 | # 客户端序列化协议,默认Default,可选Hessian/ProtoStuff/Default
84 | soft.rpc.client.serializer=Default
85 | # 负载均衡算法可选值:Random/WeightRandom/Polling/WeightPolling/Hash,若配置有误,自动采用Random算法
86 | soft.rpc.client.clusterStrategy.default=WeightRandom
87 | # 客户端对每个主机的初始化Channel数量,默认10
88 | soft.rpc.client.channelPoolSize=10
89 | # 客户端调用RPC服务线程池的大小,默认10
90 | soft.rpc.client.threadWorkers=10
91 | # 发布服务时默认命名空间(标签没有配置appName时采用)
92 | soft.rpc.server.app.name=test
93 | # 引入服务时采用的默认命名空间(标签没有配置appName时采用)
94 | soft.rpc.client.app.name=test
95 | ```
96 |
97 | #### rpc-service.xml
98 |
99 | 服务发布相关的配置文件,主要涉及soft:service标签,示例如下
100 |
101 | ```xml
102 |
103 |
104 |
113 | ```
114 |
115 | - id:不同的soft:service要求id属性不一样
116 | - appName:该属性如果缺失,就会采用全局配置文件 soft-rpc.properties 中的 soft.rpc.server.app.name 值,如果两者都缺失,则抛出异常
117 | - interface:接口的全限定名(appName + interface是该服务在注册中心的key)
118 | - ref:该接口的实现类bean标签id
119 | - weight:提供服务时该主机的权重值(范围[1,100])
120 | - workThreads:该主机提供该服务的限流数,即同一时刻与客户端建立的最大连接数
121 | - serverPort:该主机发布该服务的端口号
122 | - timeout:服务超时时间
123 | - groupName:应用所属分组名称, 本项目未用到。如果要在ZK中设置更复杂的注册路径,以实现服务治理相关功能,则可以使用
124 |
125 | 本框架支持将多个服务发布在不同的端口,同时也支持对同一个接口,发布不同的服务实现类:
126 |
127 | ```xml
128 |
129 |
130 |
139 | ```
140 |
141 | ```xml
142 |
143 |
144 |
153 | ```
154 |
155 | #### rpc-reference.xml
156 |
157 | 服务引用的相关配置文件,主要涉及soft:reference标签,示例如下
158 |
159 | ```xml
160 |
161 |
167 | ```
168 |
169 | - id:不同的soft:reference要求id属性不一样
170 | - appName:该属性如果缺失,就会采用全局配置文件soft.properties 中的 soft.rpc.client.app.name 值,如果两者都缺失,就会抛出异常
171 | - interface:接口的全限定名(appName + interface是该服务在注册中心的key)
172 | - clusterStrategy:采用的载均衡策略,缺省时就使用全局配置文件 soft-rpc.properties 中的 soft.rpc.client.clusterStrategy.default 值, 如果两者都缺省,就使用框架的默认值(Random)
173 | - timeout:服务超时时间
174 | - groupName:应用所属分组名称, 本项目未用到。如果要在ZK中设置更复杂的注册路径,以实现服务治理相关功能,则可以使用
175 |
176 | ### 依赖RUN
177 |
178 | 该项目是一个RPC框架,它可以作为jar包被其他项目所依赖使用。使用maven的install命令,将项目打包到本地仓库,其他项目依赖时的dependency如下
179 |
180 | ```xml
181 |
182 | com.xctian.rpc
183 | soft-rpc
184 | 1.0-SNAPSHOT
185 |
186 | ```
187 |
188 | 作为依赖在新项目中使用时,必须提供配置文件:soft-rpc.properties / rpc-service.xml / rpc-reference.xml, 且它们都要放在 /resource 根目录下,然后根据自己业务需要创建自己的接口和实现类,并按上述说明提供配置标签即可使用(类似于Dubbo的使用要求)
189 |
190 | ## Soft-RPC开发说明
191 |
192 | [这里](note/note.md)教你如何从零开发Soft-RPC框架,介绍本项目各个功能模块的实现要点,开发思路,以及一些学习笔记。
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
--------------------------------------------------------------------------------
/note/img/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xcNew/soft-RPC/e5c6951704f71306efaee1a3ef365e1dcfad8ace/note/img/1.png
--------------------------------------------------------------------------------
/note/img/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xcNew/soft-RPC/e5c6951704f71306efaee1a3ef365e1dcfad8ace/note/img/2.png
--------------------------------------------------------------------------------
/note/img/20191108234611174.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xcNew/soft-RPC/e5c6951704f71306efaee1a3ef365e1dcfad8ace/note/img/20191108234611174.png
--------------------------------------------------------------------------------
/note/img/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xcNew/soft-RPC/e5c6951704f71306efaee1a3ef365e1dcfad8ace/note/img/3.png
--------------------------------------------------------------------------------
/note/img/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xcNew/soft-RPC/e5c6951704f71306efaee1a3ef365e1dcfad8ace/note/img/4.png
--------------------------------------------------------------------------------
/note/img/5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xcNew/soft-RPC/e5c6951704f71306efaee1a3ef365e1dcfad8ace/note/img/5.png
--------------------------------------------------------------------------------
/note/img/6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xcNew/soft-RPC/e5c6951704f71306efaee1a3ef365e1dcfad8ace/note/img/6.png
--------------------------------------------------------------------------------
/note/img/7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xcNew/soft-RPC/e5c6951704f71306efaee1a3ef365e1dcfad8ace/note/img/7.png
--------------------------------------------------------------------------------
/note/img/8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xcNew/soft-RPC/e5c6951704f71306efaee1a3ef365e1dcfad8ace/note/img/8.png
--------------------------------------------------------------------------------
/note/img/9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xcNew/soft-RPC/e5c6951704f71306efaee1a3ef365e1dcfad8ace/note/img/9.png
--------------------------------------------------------------------------------
/note/img/image-20191110180140681.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xcNew/soft-RPC/e5c6951704f71306efaee1a3ef365e1dcfad8ace/note/img/image-20191110180140681.png
--------------------------------------------------------------------------------
/note/note.md:
--------------------------------------------------------------------------------
1 | ## Soft-RPC介绍
2 |
3 | ### RPC简介
4 |
5 | > RPC(Remote Procedure Call,远程过程调用)用于实现部署在不同机器上的系统之间的方法调用,使得程序可以像访问本地服务一样,通过网络传输调用远程系统提供的服务。
6 |
7 | 具体而言:
8 |
9 | - RPC就是从一台机器(客户端)上通过参数传递的方式调用另一台机器(服务器)上的一个函数或方法(可以统称为服务)并得到返回的结果。
10 | - RPC 会隐藏底层的通讯细节(不需要直接处理Socket通讯或Http通讯) RPC 是一个请求响应模型。
11 | - 客户端发起请求,服务器返回响应(类似于Http的工作方式) RPC 在使用形式上像调用本地函数(或方法)一样去调用远程的函数(或方法)。
12 |
13 | web项目中,**调用本地服务的过程**:编写接口和实现类,然后将实现类托管至Spring容器,之后需要用到该服务时直接使用@Autowired将其注入即可。但在实际的开发环境中,并不是所有的服务都是由我们自己来进行开发,我们经常需要调用由别的开发人员开发的服务,那么通过使用RPC框架,在本地导入其他服务接口的依赖包之后,就可以类似上述过程一样去调用远程的服务。
14 |
15 | - 为什么不直接在本地导入外部服务的接口+实现类的依赖包?
16 |
17 | 实际开发环境中,接口的实现类通常很容易改变,如果该服务被大量的调用者所依赖,那么一旦这个服务的实现类发生改变,就要去发包让所有调用者更新本地的依赖包,系统耦合性增加;如果使用RPC的方式,则实现类只需要交给服务的提供者进行维护即可。
18 |
19 | **调用远程服务的过程(RPC方式):**
20 |
21 |
22 |
23 | 1)服务消费方(client)调用以本地调用方式调用服务;
24 |
25 | 2)client stub接收到调用后负责将方法、参数等组装成能够进行网络传输的消息体;
26 |
27 | 3)client stub找到服务地址,并将消息发送到服务端;
28 |
29 | 4)server stub收到消息后进行解码;
30 |
31 | 5)server stub根据解码结果调用本地的服务;
32 |
33 | 6)本地服务执行并将结果返回给server stub;
34 |
35 | 7)server stub将返回结果打包成消息并发送至消费方;
36 |
37 | 8)client stub接收到消息,并进行解码;
38 |
39 | 9)服务消费方得到最终结果。
40 |
41 | RPC框架的目标就是要2~8这些步骤都封装起来,让用户对这些细节透明。从而使得用户对远程服务的调用可以像本地服务一样。
42 |
43 | ### 序列化及反序列化
44 |
45 | - 序列化(Serialization):将对象的状态信息转换为可存储或传输的形式的过程。
46 | - 反序列化(Deserialization):序列化的逆过程。将字节序列恢复为对象的过程。
47 |
48 | **通过序列化可以解决的问题**
49 |
50 | - 通过将对象序列化为字节数组,使得不共享内存通过网络连接的系统之间能够进行对象的传输;
51 | - 通过将对象序列化为字节数组,可以将对象永久存储到存储设备;
52 | - 解决远程接口调用JVM之间内存无法共享的问题。
53 |
54 | **评价序列化算法优劣的指标**
55 |
56 | - 序列化后码流的大小;
57 | - 序列化本身的速度及系统资源开销大小(内存,CPU)。
58 |
59 | #### 常用的序列化工具介绍
60 |
61 | **JDK默认的序列化工具**
62 |
63 | > JAVA原生序列化方式,主要通过对象输入流ObjectInputStream和对象输出流ObjectOutputStream来实现,序列化对象需要实现Serializable接口。
64 |
65 | - 序列化时,只对对象的状态进行保存,而不管对象的方法;
66 |
67 | - 父类实现序列化时,子类自动实现序列化,不需要显式实现Serializable接口;
68 |
69 | - 一个对象的实例变量引用其他对象时,序列化该对象时也把引用对象进行序列化;
70 |
71 | - 字段被声明为transient后,JDK默认序列化机制会忽略该字段。
72 |
73 | **优点:**
74 |
75 | - Java语言自带,无需引入第三方依赖;
76 | - 与Java有天然的最好的易用性与亲和性。
77 |
78 | **缺点:**
79 |
80 | - 只支持Java语言,不支持跨语言;
81 | - 性能欠佳,序列化后产生的码流大小过大,对引用过深的对象序列化可能导致OOM。
82 |
83 | Java序列化会把要序列化的对象类的元数据和业务数据全部序列化为字节流,而且是把整个继承关系上的东西全部序列化了。它序列化出来的字节流是对那个对象结构到内容的完全描述,包含所有的信息,因此效率较低而且字节流比较大。但是由于确实是序列化了所有内容,所以可以说什么都可以传输,因此也更可用和可靠。
84 |
85 | **Hessian**
86 |
87 | > Hessian是一个轻量级的remoting onhttp工具,使用简单的方法提供了RMI的功能。 相比WebService,Hessian更简单、快捷。采用的是二进制RPC协议,因为采用的是二进制协议,所以它很适合于发送二进制数据。序列化对象需要实现Serializable接口。
88 |
89 | - Hessian相比Java原生序列化, 序列化后的二进制数据量更小, 因此传输速度和解析速度都更快
90 |
91 | - 采用简单的结构化标记, 并且只存储对象数据内容部分, 而JJDK默认的序列化工具还会存一些继承关系之类;
92 | - 采用引用取代重复遇到的对象, 避免了重复编码;
93 | - 支持多种不同语言。
94 |
95 | **ProtoStuff**
96 |
97 | protostuff 基于Google protobuf,但是提供了更多的功能和更简易的用法。其中,protostuff-runtime 实现了无需预编译对java bean进行protobuf序列化/反序列化的能力。protostuff-runtime的局限是序列化前需预先传入schema(可以由代码方法生成, 不需要手动改创建),其中,schema中包含了对象进行序列化和反序列化的逻辑;反序列化不负责对象的创建只负责复制,因而**必须提供默认构造函数**。此外,protostuff 还可以按照protobuf的配置序列化成json/yaml/xml等格式。
98 |
99 | 在性能上,protostuff不输原生的protobuf,甚至有反超之势。
100 |
101 | #### **序列化工具引擎**
102 |
103 | 本项目用到三种:Default / Hessian / ProtoStuff, 一个Serializer接口有多种实现类, 如何优雅的进行选择? 使用可配置化的序列化工具引擎,有两种实现思路:
104 |
105 | - 工厂模式方案:
106 |
107 | 添加一个工厂类, 提供根据名称获取Serializer实现类的方法, 最后用一个Engine类即可以实现优雅的选择。这样做的缺陷是每次序列化/反序列化请求都需要生成新的Serializer,消耗存储空间。
108 |
109 | - 使用Map+Enum枚举类:
110 |
111 | 添加一个枚举类, 其中主要存储代表不同实现类的枚举值。在Engine类里新增常量map, key存储枚举类里的不同枚举,value存储对应具体的Serializer实现类,Engine类加载时在static代码块初始化map,根据这个传入的Serializer名称在map中找对应的实现类对象,执行实际的功能方法。**可以解决单例问题**。
112 |
113 |
114 |
115 | #### 自定义消息格式
116 |
117 | ##### 粘包/半包问题
118 |
119 | 上面介绍的是几种序列化/反序列化工具,RPC调用的底层使用的Netty框架基于TCP/IP,是基于字节流进行传输的,像流水一样连接在一起,TCP底层并无法感知业务数据的具体的含义,无法按照具体的业务含义对消息进行分包,而只会按照TCP缓冲区的实际大小情况来对包进行划分。当业务数据被拆分为多个数据包,这些数据包达到目的端后有以下三种情况:
120 |
121 | - 刚好按照业务数据本身的边界逐个到达目的(业务数据的边界刚好是数据包的边界)
122 |
123 |
124 |
125 | - 多个业务数据组合成为一个数据包,即**粘包**现象(数据包大小刚好等于多个业务数据)
126 |
127 |
128 |
129 | - 到达目的的数据包中只包含了部分不完整的业务数据,数据包大小小于n个业务数据,那么第n个业务数据将被拆分到多个数据包传输,即**半包**现象。
130 |
131 |
132 |
133 | 解决半包/粘包问题的关键是能够区分完整的业务应用的数据边界,能够按照此边界完整地接收Netty传输的数据。
134 |
135 | 本项目中,利用自定义的消息格式,结合Netty自定义编解码器开发,作为半包/粘包问题的简单解决方案。
136 |
137 | ##### 自定义消息格式 (消息编解码规则)
138 |
139 | 消息格式定义如下:serializerCode|dataLength|messageData
140 |
141 | 上述消息格式规定了字节流在传输的时候由三部分组成:
142 |
143 | 1. int类型的serializerCode,它是序列化协议对应枚举中的code(见项目serializer\common\Serializer.java)
144 |
145 | 用于标识该次传输所采用的序列化/反序列化协议
146 |
147 | 2. int类型的消息长度dataLength,它表示需要传输的数据的大小
148 |
149 | 3. 需要传输的业务数据
150 |
151 | **Netty自定义编解码器**
152 |
153 | - MessageToByteEncoder是Netty为消息转换为byte提供的一个抽象类,本项目的编码器只需继承MessageToByteEncoder,并严格按照上述规定的自定义编码格式,重写encode方法如下:
154 |
155 | ```java
156 | public void encode(ChannelHandlerContext channelHandlerContext, Object in, ByteBuf out) throws Exception {
157 | long startTime = System.currentTimeMillis();
158 | // 获取序列化协议code
159 | int serializerCode = serializeType.getSerializeCode();
160 | // 将其写入消息头部第一个int
161 | out.writeInt(serializerCode);
162 | // 将对象进行序列化
163 | byte[] data = SerializerEngine.serialize(in,serializeType);
164 | // 将data长度写入消息头部第二个int
165 | out.writeInt(data.length);
166 | // 将消息体写入
167 | out.writeBytes(data);
168 | }
169 | ```
170 |
171 | > encode方法被调用时将会传入需要被编码的消息in,然后将编码后的消息存入ByteBuf类型的out,该ByteBuf随后会被转发给pipeline中的下一个handler
172 |
173 | 以上实现了按照自定义消息格式(int+int+data)对消息进行编码并写入NettyChannel
174 |
175 | - ByteToMessageEncoder是Netty为byte转换为消息提供的一个抽象类,本项目的解码器只需继承ByteToMessageEncoder,并严格按照上述规定的自定义编码格式,重写decode方法如下:
176 |
177 | ```java
178 | public void decode(ChannelHandlerContext ctx, ByteBuf in, List