概述
手写RPC-简易版 ,实现了一个很简单的RPC调用示例,其中还存在很多问题及可优化的点,
这次做个升级,完全重写之前的代码,使得代码逻辑更加规范,同时引入Zookeeper辅助完成服务治理。在代码展示前,先介绍下Zookeeper、服务治理等概念。
Zookeeper
ZooKeeper(简称zk)是一个分布式、开源的应用协调服务,利用和Paxos类似的ZAB选举算法实现分布式一致性服务。有类似于Unix文件目录的节点信息,同时可以针对节点的变更添加watcher监听以能够及时感知到节点信息变更。可提供的功能例如数据发布/订阅、负载均衡、命名服务、分布式协调/通知、集群管理、Master选举、分布式锁和分布式队列等功能。如下图就是DUBBO存储在ZooKeeper的节点数据情况:
在本地启动服务后通过zk客户端连接后也可通过命令查看节点信息,如下图所示。
ZooKeeper包含了4种不同含义的功能节点,在每次创建节点之前都需要明确声明节点类型:
类型 | 定义 | 描述 |
---|---|---|
PERSISTENT | 持久化目录节点 | 客户端与zookeeper断开连接后,该节点依旧存在 |
PERSISTENT_SEQUENTIAL | 持久化顺序编号目录节点 | 客户端与zookeeper断开连接后,该节点依旧存在,只是Zookeeper给该节点名称进行顺序编号 |
EPHEMERAL | 临时目录节点 | 客户端与zookeeper断开连接后,该节点被删除 |
EPHEMERAL_SEQUENTIAL | 临时顺序编号目录节点 | 客户端与zookeeper断开连接后,该节点被删除,只是Zookeeper给该节点名称进行顺序编号 |
ZooKeeper使用之前需要先进行安装,后开启服务端的服务, 我们的服务作为客户端连接ZooKeeper以便于后续的操作。具体可参考官网文档Zookeeper3.5.5 官方文档,在实际的java项目开发中也是可以通过maven引入ZkClient或者Curator开源的客户端,在本文学习笔记中是使用的Curator,因为其已经封装了原始的节点注册、数据获取、添加watcher等功能。具体maven引入的版本如下:
1 | <dependency> |
服务治理
服务治理也就是针对服务进行管理的措施,例如 服务发现、 服务暴露、 负载均衡、 快速上下线等都是服务治理的具体体现。
服务发现:从服务管理中心获取到需要的服务相关信息,例如可以从zk中获取相关服务的机器信息,然后就可以和具体机器直连完成相关功能。
服务暴露:服务提供方可以提供什么样子的功能,经过服务暴露暴露出去,其他使用方就可以通过服务发现发现具体的服务提供方信息。
负载均衡:一般针对的是服务提供方,避免大量请求同时打到一台机器上,采用随机、轮询等措施让请求均分到各个机器上,提供服务效率, 限流, 灰度等也都是类似的操作,通过动态路由、软负载的形式处理分发请求。
快速上线下:以往需要上下线可能需要杀掉机器上的进程,现在只需要让该服务停止暴露即可,实现服务的灵活上下线。
数据处理流程
服务端:服务的提供方,接受网络传输的请求数据、通过网络把应答数据发送给客户端;
客户端:服务的调用方,使用本地代理,通过网络把请求数据发送出去,接受服务端返回的应答数据.
所有的数据传输都是按照上面图片说的流程来的,如果需要添加自定义的序列化工具,则需要在把数据提交到socket的输出流缓冲区之前按照序列化工具完成序列化操作,反序列化则进行反向操作即可。
RPC V2版本
文件夹目录如下图所示:
- balance文件夹:负载均衡有关;
- config文件夹:网络套接字传输的数据模型以及服务暴露、服务发现的数据模型;
- core文件夹:核心文件夹,包含了服务端和客户端的请求处理、代理生成等;
- demo文件夹:测试使用;
- domain文件夹:模型、枚举常量;
- io.protocol文件夹:目前是只有具体的请求对象和网络io的封装;
- register文件夹:服务注册使用,实现了使用zk进行服务注册和服务发现的操作;
- serialize文件夹:序列化、反序列化,实现了Java和Hessian两种。
服务注册&服务发现
ServiceRegister:
1 | package com.springboot.whb.study.rpc.rpc_v2.register; |
ZkServiceRegister
默认使用了CuratorFramework客户端完成zk数据的操作.
1 | package com.springboot.whb.study.rpc.rpc_v2.register; |
负载均衡
LoadBalance
1 | package com.springboot.whb.study.rpc.rpc_v2.balance; |
AbstractLoadBalance
1 | package com.springboot.whb.study.rpc.rpc_v2.balance; |
DefaultLoadBalance
1 | package com.springboot.whb.study.rpc.rpc_v2.balance; |
消息协议
MessageProtocol
1 | package com.springboot.whb.study.rpc.rpc_v2.io.protocol; |
DefaultMessageProtocol
1 | package com.springboot.whb.study.rpc.rpc_v2.io.protocol; |
数据传输模型
ArgumentConfig
1 | package com.springboot.whb.study.rpc.rpc_v2.config; |
BasicConfig
1 | package com.springboot.whb.study.rpc.rpc_v2.config; |
ClientConfig
1 | package com.springboot.whb.study.rpc.rpc_v2.config; |
MethodConfig
1 | package com.springboot.whb.study.rpc.rpc_v2.config; |
ServiceConfig
1 | package com.springboot.whb.study.rpc.rpc_v2.config; |
枚举常量
ServiceType
1 | package com.springboot.whb.study.rpc.rpc_v2.domain; |
序列化、反序列化
SerializeProtocol
1 | package com.springboot.whb.study.rpc.rpc_v2.serialize; |
JavaInnerSerialize
1 | package com.springboot.whb.study.rpc.rpc_v2.serialize; |
HessianSerialize
1 | package com.springboot.whb.study.rpc.rpc_v2.serialize; |
请求、响应对象
RpcRequest
1 | package com.springboot.whb.study.rpc.rpc_v2.core; |
RpcResponse
1 | package com.springboot.whb.study.rpc.rpc_v2.core; |
服务端处理
ServiceConnection
1 | package com.springboot.whb.study.rpc.rpc_v2.core; |
ServiceHandler
1 | package com.springboot.whb.study.rpc.rpc_v2.core; |
RpcService
1 | package com.springboot.whb.study.rpc.rpc_v2.core; |
客户端处理
代理对象ProxyInstance
1 | package com.springboot.whb.study.rpc.rpc_v2.core; |
ClientHandler
1 | package com.springboot.whb.study.rpc.rpc_v2.core; |
RpcClient
1 | package com.springboot.whb.study.rpc.rpc_v2.core; |
测试
测试接口定义Calculate
1 | package com.springboot.whb.study.rpc.rpc_v2.demo; |
测试接口实现类SimpleCalculate
1 | package com.springboot.whb.study.rpc.rpc_v2.demo; |
测试-服务端Service
1 | package com.springboot.whb.study.rpc.rpc_v2.demo; |
测试-客户端Client
1 | package com.springboot.whb.study.rpc.rpc_v2.demo; |
测试结果
zookeeper
服务端
客户端
总结
v2版本相比v1版本修改了整个代码结构,使得结构能够更加明确,引入zookeeper作为服务治理功能,大致介绍了zookeeper的特点以及功能,给服务注册、服务发现、序列化协议等均留下了口子,以便实现自定义的协议,v1的io模型是BIO,v2并没有变化,只是由单线程改造成多线程。
整体而言符合一个简单的rpc框架,依旧还是有很多点可以完善、优化的点,如:
io模型还是没有替换,后面考虑直接整体接入netty;
不应该每次实时从zk获取节点信息,应该先设置一个本地缓存,再利用zookeeper的watcher功能,开启一个异步线程去监听更新本地缓存,降低和zk交互带来的性能损耗;
没有快速失败、重试的功能,客观情况下存在网络抖动的问题,重试就可以了。
整体的各种协议约定并没有明确规范,比较混乱。