netty+zk实现简单的rpc调用(基于传输层)

本文将为您揭晓开发轻量级分布式 RPC 框架的具体过程,该框架基于 TCP 协议,提供了 NIO 特性,提供高效的序列化方式,同时也具备服务注册与发现的能力。

根据以上技术需求,我们可使用如下技术选型:

  1. Spring:它是最强大的依赖注入框架,也是业界的权威标准。
  2. Netty:它使 NIO 编程更加容易,屏蔽了 Java 底层的 NIO 细节。
  3. Protostuff:它基于 Protobuf 序列化框架,面向 POJO,无需编写 .proto 文件。
  4. 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
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值