本文将为您揭晓开发轻量级分布式 RPC 框架的具体过程,该框架基于 TCP 协议,提供了 NIO 特性,提供高效的序列化方式,同时也具备服务注册与发现的能力。
根据以上技术需求,我们可使用如下技术选型:
- Spring:它是最强大的依赖注入框架,也是业界的权威标准。
- Netty:它使 NIO 编程更加容易,屏蔽了 Java 底层的 NIO 细节。
- Protostuff:它基于 Protobuf 序列化框架,面向 POJO,无需编写 .proto 文件。
- ZooKeeper:提供服务注册与发现功能,开发分布式系统的必备选择,同时它也具备天生的集群能力。
相关 Maven 依赖请见附录。
第一步:编写服务接口
1
2
3
4
5
|
<!-- lang: java -->
public
interface
HelloService {
String hello(String name);
}
|
将该接口放在独立的客户端 jar 包中,以供应用使用。
第二步:编写服务接口的实现类
1
2
3
4
5
6
7
8
9
|
<!-- lang: java -->
@RpcService
(HelloService.
class
)
// 指定远程接口
public
class
HelloServiceImpl
implements
HelloService {
@Override
public
String hello(String name) {
return
"Hello! "
+ name;
}
}
|
使用RpcService注解定义在服务接口的实现类上,需要对该实现类指定远程接口,因为实现类可能会实现多个接口,一定要告诉框架哪个才是远程接口。
RpcService代码如下:
1
2
3
4
5
6
7
8
|
<!-- lang: java -->
@Target
({ElementType.TYPE})
@Retention
(RetentionPolicy.RUNTIME)
@Component
// 表明可被 Spring 扫描
public
@interface
RpcService {
Class<!--?--> value();
}
|
该注解具备 Spring 的Component注解的特性,可被 Spring 扫描。
该实现类放在服务端 jar 包中,该 jar 包还提供了一些服务端的配置文件与启动服务的引导程序。
第三步:配置服务端
服务端 Spring 配置文件名为spring.xml,内容如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
<!-- lang: xml -->
<beans ...=
""
>
<context:component-scan base-
package
=
"com.xxx.rpc.sample.server"
>
<context:property-placeholder location=
"classpath:config.properties"
>
<!-- 配置服务注册组件 -->
<bean
class
=
"com.xxx.rpc.registry.ServiceRegistry"
id=
"serviceRegistry"
>
<constructor-arg name=
"registryAddress"
value=
"${registry.address}"
>
</constructor-arg></bean>
<!-- 配置 RPC 服务器 -->
<bean
class
=
"com.xxx.rpc.server.RpcServer"
id=
"rpcServer"
>
<constructor-arg name=
"serverAddress"
value=
"${server.address}"
>
<constructor-arg name=
"serviceRegistry"
ref=
"serviceRegistry"
>
</constructor-arg></constructor-arg></bean>
</context:property-placeholder></context:component-scan></beans>
|
具体的配置参数在config.properties文件中,内容如下:
1
2
3
4
5
6
|
<!-- lang: java -->
# ZooKeeper 服务器
registry.address=
127.0
.
0.1
:
2181
# RPC 服务器
server.address=
127.0
.
0.1
:
8000
|
以上配置表明:连接本地的 ZooKeeper 服务器,并在 8000 端口上发布 RPC 服务。
第四步:启动服务器并发布服务
为了加载 Spring 配置文件来发布服务,只需编写一个引导程序即可:
1
2
3
4
5
6
7
|
<!-- lang: java -->
public
class
RpcBootstrap {
public
static
void
main(String[] args) {
new
ClassPathXmlApplicationContext(
"spring.xml"
);
}
}
|
运行RpcBootstrap类的main方法即可启动服务端,但还有两个重要的组件尚未实现,它们分别是:ServiceRegistry与RpcServer,下文会给出具体实现细节。
第五步:实现服务注册
使用 ZooKeeper 客户端可轻松实现服务注册功能,ServiceRegistry代码如下:
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
|
<!-- lang: java -->
public
class
ServiceRegistry {
private
static
final
Logger LOGGER = LoggerFactory.getLogger(ServiceRegistry.
class
);
private
CountDownLatch latch =
new
CountDownLatch(
1
);
private
String registryAddress;
public
ServiceRegistry(String registryAddress) {
this
.registryAddress = registryAddress;
}
public
void
register(String data) {
if
(data !=
null
) {
ZooKeeper zk = connectServer();
if
(zk !=
null
) {
createNode(zk, data);
}
}
}
private
ZooKeeper connectServer() {
ZooKeeper zk =
null
;
try
{
zk =
new
ZooKeeper(registryAddress, Constant.ZK_SESSION_TIMEOUT,
new
Watcher() {
@Override
public
void
process(WatchedEvent event) {
if
(event.getState() == Event.KeeperState.SyncConnected) {
latch.countDown();
}
}
});
latch.await();
}
catch
(IOException | InterruptedException e) {
LOGGER.error(
""
, e);
}
return
zk;
}
private
void
createNode(ZooKeeper zk, String data) {
try
{
byte
[] bytes = data.getBytes();
String path = zk.create(Constant.ZK_DATA_PATH, bytes, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
LOGGER.debug(
"create zookeeper node ({} => {})"
, path, data);
}
catch
(KeeperException | InterruptedException e) {
LOGGER.error(
""
, e);
}
}
}
|
其中,通过Constant配置了所有的常量:
1
2
3
4
5
6
7
8
|
<!-- lang: java -->
public
interface
Constant {
int
ZK_SESSION_TIMEOUT =
5000
;
String ZK_REGISTRY_PATH =
"/registry"
;
String ZK_DATA_PATH = ZK_REGISTRY_PATH +
"/data"
;
}
|
注意:首先需要使用 ZooKeeper 客户端命令行创建/registry永久节点,用于存放所有的服务临时节点。
第六步:实现 RPC 服务器
使用 Netty 可实现一个支持 NIO 的 RPC 服务器,需要使用ServiceRegistry注册服务地址,RpcServer代码如下:
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
|