目录
介绍
这是基于netty
开发的一个rpc
框架,同时支持非Spring
应用和Spring
应用,且对于Spring boot
应用,提供spring-boot-starter
以支持自动配置。该框架采用类似dubbo
的注解方式,简单易用易上手。同时支持zookeeper
和redis
多注册中心,也支持配置不同的负载均衡策略。
源码与文档
功能列表
- 服务注册
- 服务发现
- 服务调用
- 负载均衡策略
- 调用超时与重试
- 服务降级
- 资源限制
实现过程
由于整个实现过程篇幅很长,这里只列出几个我认为比较重要的几个步骤,完整的代码已经上传至GitHub,GitHub项目地址
一、定义一个应用层传输协议
字段 | 说明 | 大小 |
---|---|---|
magic | 魔术字,固定为fastcall | 8字节 |
version | 协议版本,当前版本为0.1 | 2字节 |
length | 除去magic,version,length三个字段的报文长度 | 4字节 |
type | 消息类型(0-认证请求,1-认证响应,2-心跳请求,3-心跳响应,4-rpc请求,5-rpc响应) | 1字节 |
attachment_size | 头部附加信息个数 | 4字节 |
key_size | 头部附加信息key长度 | 4字节 |
key | 头部附加信息key | 变长 |
value_size | 头部附加信息value长度 | 4字节 |
value | 头部附加信息value | 变长 |
~ | ~ | ~ |
body_size | 消息体长度 | 4字节 |
body | 消息体 | 变长 |
二、定义序列化接口
序列化接口用于对协议的消息体进行序列化,最常见的是对rpc
调用过程中的参数、参数类型,返回值和异常进行序列化
public interface SerializeManager {
byte[] serialize(Object object);
<T> T deserialize(byte[] bytes, Class<T> clazz);
}
三、序列化接口实现
框架默认采用json
实现序列化,抛开性能,因为比较简单易用,也可以采用其他序列化框架比如protobuf
、msgpack
等,只需要实现序列化接口即可
public class JsonSerializeManager implements SerializeManager {
private static final Logger log = LoggerFactory.getLogger(JsonSerializeManager.class);
@Override
public byte[] serialize(Object object) {
byte[] bytes = null;
try {
ObjectMapper mapper = new ObjectMapper();
bytes = mapper.writeValueAsBytes(object);
} catch (Exception e) {
log.error("", e);
}
return bytes;
}
@Override
public <T> T deserialize(byte[] bytes, Class<T> clazz) {
T obj = null;
try {
ObjectMapper mapper = new ObjectMapper();
obj = mapper.readValue(bytes, clazz);
} catch (Exception e) {
log.error("", e);
}
return obj;
}
}
四、自定义netty对协议的编码器和解码器
在netty
中,可以继承MessageToByteEncoder
和ByteToMessageDecoder
将自定义的实体类(也就是我们开头定义的协议)与ByteBuf
之间进行相互转换,这里的解码器继承了ByteToMessageDecoder
的子类LengthFieldBasedFrameDecoder
来处理粘包和半包
1、编码器
public class MessageEncoder extends MessageToByteEncoder<Message> {
public static final Logger log = LoggerFactory.getLogger(MessageEncoder.class);
private final SerializeManager serializeManager;
public MessageEncoder(SerializeManager serializeManager){
this.serializeManager = serializeManager;
}
@Override
protected void encode(ChannelHandlerContext channelHandlerContext, Message message, ByteBuf buff) throws Exception {
Header header = message.getHeader();
buff.writeBytes(header.getMagic());
buff.writeShort(header.getVersion());
buff.writeInt(header.getLength());
buff.writeByte(header.getType());
buff.writeInt(header.getAttachment().size());
for (Map.Entry<String, String> entry : header.getAttachment().entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
byte[] keyBytes = key.getBytes("UTF-8");
buff.writeInt(keyBytes.length);
buff.writeBytes(keyBytes);
byte[] valueBytes = value.getBytes("UTF-8");
buff.writeInt(valueBytes.length);
buff.writeBytes(valueBytes);
}
if (message.getBody() != null) {
Object body = message.getBody();
byte[] bodyBytes = serializeManager.serialize(body);
buff.writeInt(bodyBytes.length);
buff.writeBytes(bodyBytes);
} else {
buff.writeInt(0);
}
message.getHeader().setLength(buff.readableBytes() - 14);
buff.setInt(10, message.getHeader().getLength());
log.trace("Send: {}", message);
}
}
2、解码器
public class MessageDecoder extends LengthFieldBasedFrameDecoder {
public static final Logger log = LoggerFactory.getLogger(MessageDecoder.class);
private final SerializeManager serializeManager;
public MessageDecoder(SerializeManager serializeManager) {
super(1024 * 1024, 10, 4, 0, 0);
this.serializeManager = serializeManager;
}
@Override
protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
ByteBuf frame = (ByteBuf) super.decode(ctx, in);
if (frame == null) {
return null;
}
Message message = new Message();
Header header = new Header();
frame.readBytes(header.getMagic(), 0, 8);
header.setVersion(frame.readShort());
header.setLength(frame.readInt());
header.setType(frame.readByte());
int size = frame.readInt();
Map<String, String> attachment = new HashMap<>();
for (int i = 0; i < size; i++) {
int keySize = frame.readInt();
byte[] keyBytes = new byte[keySize];
frame.readBytes(keyBytes, 0, keySize);
String key = new String(keyBytes, "UTF-8");
int valueSize = frame.readInt();
byte[] valueBytes = new byte[valueSize];
frame.readBytes(valueBytes, 0, valueSize);
String value = new String(keyBytes, "UTF-8");
attachment.put(key, value);
}
header.setAttachment(attachment);
message.setHeader(header);
int bodySize = frame.readInt();
if (bodySize > 0) {
byte[] bodyBytes = new byte[bodySize];
frame.readBytes(bodyBytes, 0, bodySize);
if (header.getType() == MessageType.AUTH_RESPONSE.value()) {
byte body = serializeManager.deserialize(bodyBytes, byte.class);
message.setBody(body);
} else if (header.getType() == MessageType.BUSINESS_REQUEST.value()) {
RpcRequest request = serializeManager.deserialize(bodyBytes, RpcRequest.class);
message.setBody(request);
} else if (header.getType() == MessageType.BUSINESS_RESPONSE.value()) {
RpcResponse response = serializeManager.deserialize(bodyBytes, RpcResponse.class);
message.setBody(response);
}
}
log.trace("Receive: {}", message);
frame.release();
return message;
}
}
五、服务端的绑定与客户端的连接
所有与网络通信相关的代码,都可以抽象为一个传输层子系统,方便以后扩展其他应用层协议,如http
、rmi
等,定义子系统接口
public interface TransportManager {
/**
* 绑定本地端口
*
* @param localSocketAddress
*/
void bind(InetSocketAddress localSocketAddress);
/**
* 发送报文给远程地址
*
* @param remoteSocketAddress
* @param request
*/
ResponseFuture sendTo(InetSocketAddress remoteSocketAddress, RpcRequest request);
/**
* 关闭所有连接与线程池
*/
void close();
}
1、服务端绑定
以下是FastcallTransportManager
中bind
方法默认实现,serverExecutorService
是一个Executors.newSingleThreadExecutor
线程池,一次只能启动一个服务,注意pipeline
中添加的几个handler
的顺序,分别是解码器,编码器,消息超时Handler,认证处理Handler,心跳包检测Handler,Rpc请求处理Handler。
@Override
public void bind(InetSocketAddress localSocketAddress) {
serverExecutorService.execute(() -> {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workGroup = new NioEventLoopGroup();
try {
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) {
ChannelPipeline pipeline = socketChannel.pipeline();
pipeline.addLast(new MessageDecoder(serializeManager));
pipeline.addLast(new MessageEncoder(serializeManager));
pipeline.addLast(new ReadTimeoutHandler(TIMEOUT));
pipeline.addLast(new ServerAuthHandler());
pipeline.addLast(new ServerHeatBeatHandler());
pipeline.addLast(new ServerRpcHandler(serializeManager, rpcExecutorService));
}
});
ChannelFuture channelFuture = bootstrap.bind(localSocketAddress).sync();
log.info("[L:{}] TransportManager bind success", localSocketAddress.getAddress().getHostAddress() + ":" + localSocketAddress.getPort());
sChannel = channelFuture.channel();
sChannel.closeFuture().sync();
} catch (InterruptedException e) {
log.error("[L:{}] TransportManager fail to bind", localSocketAddress.getAddress().getHostAddress() + ":" + localSocketAddress.getPort(), e);
} finally {
log.info("[L:{}] TransportManager closed", localSocketAddress.getAddress().getHostAddress() + ":" + localSocketAddress.getPort());
bossGroup.shutdownGracefully();
workGroup.shutdownGracefully();
}
});
}
上面的重点在于ServerRpcHandler
,他传入了两个参数,一个是序列化接口,一个是rpc请求执行的业务线程池用于控制并发,我们来看ServerRpcHandler
的源码,RpcRequestHandler
是它的一个内部类,继承了Runnable
接口,在channelRead
中,判断了协议头中的报文类型是否属于rpc请求,如果是则由业务线程池去执行一个rpc处理的线程,处理的逻辑也很简单,就是从本地的一个服务容器中取出服务对象,通过反射去执行rpc调用的方法,然后将返回值或异常通过channel发送给服务消费者。
public class ServerRpcHandler extends ChannelInboundHandlerAdapter {
private static final Logger log = LoggerFactory.getLogger(ServerRpcHandler.class);
private final SerializeManager serializeManager;
private final ExecutorService rpcExecutorService;
public ServerRpcHandler(SerializeManager serializeManager, ExecutorService rpcExecutorService) {
this.serializeManager = serializeManager;
this.rpcExecutorService = rpcExecutorService;
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
Message message = (Message) msg;
Header header = message.getHeader();
if (header.getType() == MessageType.BUSINESS_REQUEST.value()) {
RpcRequest request = (RpcRequest) message.getBody();
Runnable runnable = new RpcRequestHandler(request, ctx.channel(), serializeManager);
rpcExecutorService.execute(runnable);
}
super.channelRead(ctx, msg);
}
private static class RpcRequestHandler implements Runnable{
private static final Logger log = LoggerFactory.getLogger(RpcRequestHandler.class);
private final RpcRequest request;
private final Channel channel;
private final SerializeManager serializeManager;
public RpcRequestHandler(RpcRequest request, Channel channel, SerializeManager serializeManager) {
this.request = request;
this.channel = channel;
this.serializeManager = serializeManager;
}
@Override
public void run() {
RpcResponse response = handlerRequest(request);
log.trace("[L:{} R:{}] RpcRequestHandler set response, responseId:{}, responseCode:{}", channel.localAddress(), channel.remoteAddress(), response.getId(), response.getCode());
channel.writeAndFlush(new Message(MessageType.BUSINESS_RESPONSE, response));
}
private RpcResponse handlerRequest(RpcRequest request) {
RpcResponse rpcResponse = null;
List<Object> params = request.getParams();
List<Class<?>> paramsType = request.getParamsType();
BeanContext context = BeanContext.getInstance();
Object obj = context.getBean(request.getInterfaceType(), request.getGroup(), request.getVersion());
if (obj == null) {
return new RpcResponse(request.getId(), -1, null, null, BeanNotFoundException.class, serializeManager.serialize(new BeanNotFoundException()));
}
try {
Method method = obj.getClass().getMethod(request.getMethod(), paramsType.toArray(new Class[0]));
Object response = method.invoke(obj, params == null ? null : params.toArray());
rpcResponse = new RpcResponse(request.getId(), 0, response.getClass(), serializeManager.serialize(response), null, null);
} catch (InvocationTargetException e) {
rpcResponse = new RpcResponse(request.getId(), -1, null, null, e.getTargetException().getClass(), serializeManager.serialize(e.getTargetException()));
} catch (Throwable throwable) {
rpcResponse = new RpcResponse(request.getId(), -1, null, null, throwable.getClass(), serializeManager.serialize(throwable));
}
return rpcResponse;
}
}
}
2、客户端建立连接
我们来看sendTo
方法的实现源码,首先他会先生成一个ResponseFuture
对象,该对象是一个未来响应的结果,在调用该对象的getResponse
方法时,如果远程调用还没有传回响应结果,则会一直阻塞住,也可以设置调用超时时间,内部原理很简单,采用wait
和notifyAll
实现,可参考源码。
@Override
public ResponseFuture sendTo(InetSocketAddress remoteSocketAddress, RpcRequest request) {
ResponseFutureContext context = ResponseFutureContext.getInstance();
ResponseFuture future = context.createFuture(request.getId());
String host = remoteSocketAddress.getAddress().getHostAddress();
Integer port = remoteSocketAddress.getPort();
String key = host + ":" + port;
Channel channel = channelPool.get(key);
if (channel == null || !channel.isActive()) {
channel = connect(remoteSocketAddress);
}
channel.writeAndFlush(new Message(MessageType.BUSINESS_REQUEST, request));
log.trace("[L:{} R:{}] TransportManager send request, responseId:{}", channel.localAddress(), channel.remoteAddress(), request.getId());
return future;
}
接着往下看sendTo
源码,我们将ip和port端口作为key,从channelPool
中获取channel
,channelPool
是一个通道池,本质上就是一个map
对象,由于我们的rpc是一个长连接的实现,我们在发送rpc请求之前需要先在通道池中查看有没有已经建立的连接,如果有则直接拿出来使用,如果没有则通过connect
建立一个新的连接,并把它放到channelPool
中,我们看connect
的源码
private synchronized Channel connect(InetSocketAddress remoteSocketAddress) {
String host = remoteSocketAddress.getAddress().getHostAddress();
Integer port = remoteSocketAddress.getPort();
String key = host + ":" + port;
Channel channel = channelPool.get(key);
if (channel == null || !channel.isActive()) {
CountDownLatch countDownLatch = new CountDownLatch(1);
connectionExecutorService.execute(() -> {
Bootstrap bootstrap = new Bootstrap();
EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
try {
bootstrap.group(eventLoopGroup)
.channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) {
ChannelPipeline pipeline = socketChannel.pipeline();
pipeline.addLast(new MessageDecoder(serializeManager));
pipeline.addLast(new MessageEncoder(serializeManager));
pipeline.addLast(new ReadTimeoutHandler(TIMEOUT));
pipeline.addLast(new ClientAuthHandler());
pipeline.addLast(new ClientHeatBeatHandler());
pipeline.addLast(new ClientRpcHandler());
}
});
ChannelFuture channelFuture = bootstrap.connect(remoteSocketAddress).sync();
Channel chl = channelFuture.channel();
log.info("[L:{} R:{}] TransportManager connect success", chl.localAddress(), chl.remoteAddress());
channelPool.put(key, chl);
countDownLatch.countDown();
chl.closeFuture().sync();
} catch (InterruptedException e) {
log.error("[R:{}] TransportManager fail to connect", host + ":" + port, e);
} finally {
log.info("[R:{}] TransportManager closed", host + ":" + port);
eventLoopGroup.shutdownGracefully();
}
});
try {
countDownLatch.await();
} catch (InterruptedException e) {
log.error("[R:{}] TransportManager fail to init channel", host + ":" + port, e);
}
}
return channelPool.get(key);
}
重点依旧在于pipeline
中的最后一个handler,我们看它的源码,channelRead
方法中,我们判断了协议头部中的类型是否该报文是rpc响应报文,如果是,我们就从ResponseFutureContext
上下文中通过一个唯一标识requestId
获取ResponseFuture
对象,并把响应给set进去,这时候ResponseFuture
内部会调用notifyAll
方法,将等待rpc响应结果的线程给唤醒。
public class ClientRpcHandler extends ChannelInboundHandlerAdapter {
private static final Logger log = LoggerFactory.getLogger(ClientRpcHandler.class);
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
Message message = (Message) msg;
Header header = message.getHeader();
if (header.getType() == MessageType.BUSINESS_RESPONSE.value()) {
RpcResponse response = (RpcResponse) message.getBody();
ResponseFutureContext context = ResponseFutureContext.getInstance();
context.setResponse(response);
log.trace("[L:{} R:{}] Client set response, responseId:{}, responseCode:{}", ctx.channel().localAddress(), ctx.channel().remoteAddress(), response.getId(), response.getCode());
}
super.channelRead(ctx, msg);
}
}
六、服务注册与发现
为了适配多注册中心,我们将服务的注册与发现过程抽象为一个服务注册子系统,先定义子系统接口
public interface RegistryManager {
/**
* 注册服务
*
* @param definition 服务定义
*/
void register(ServiceDefinition definition);
/**
* 获取服务
*
* @param clazz 接口Class对象
* @param group 所属分组
* @param version 版本号
* @return
*/
List<ServiceDefinition> getService(Class<?> clazz, String group, String version) throws ServiceNotFoundException;
/**
* 订阅服务
*/
void subscribe();
/**
* 更新本地缓存
*/
void updateCache();
/**
* 关闭连接
*/
void close();
}
由于服务注册子系统中,从本地缓存中过滤可用服务地址的实现是相同的,我们定义了一个抽象类,实现了该接口中的getService
方法
public abstract class AbstractRegistryManager implements RegistryManager {
protected final RegistryCache cache;
public AbstractRegistryManager() {
this.cache = new RegistryCache();
}
/**
* 获取服务地址
*
* @param clazz 接口Class对象
* @param group 所属分组
* @param version 版本号
* @return
*/
@Override
public List<ServiceDefinition> getService(Class<?> clazz, String group, String version) {
List<ServiceDefinition> definitions = cache.get(clazz.getName(), group, version);
if (definitions != null && definitions.size() > 0) {
return definitions;
} else {
throw new ServiceNotFoundException(clazz.getName(), group, String.format("Service not found, interfaceName:%s, group:%s, version:%s", clazz.getName(), group, version));
}
}
}
1、ServiceDefinition
ServiceDefinition
这个类里标明了我们需要向服务注册中心注册的数据包括哪些,我们看源码,这里面的数据包括服务的接口名,服务的所属分组和版本号(对dubbo
熟悉的朋友应该对这两个字段不陌生),本地的ip和端口。
public class ServiceDefinition {
private String group;
private String version;
private String interfaceName;
private String host;
private Integer port;
public ServiceDefinition() {
}
/**
* 构造方法
*
* @param group 分组
* @param interfaceName 接口名称
* @param version 版本号
* @param host 服务ip
* @param port 服务端口
*/
public ServiceDefinition(String group, String version, String interfaceName, String host, Integer port) {
this.group = group;
this.version = version;
this.interfaceName = interfaceName;
this.host = host;
this.port = port;
}
//省略get set toString equals方法
......
}
2、zookeeper注册中心实现
目前该框架支持zookeeper
和redis
注册中心,我们拿默认的zookeeper
注册中心的实现来讲,看源码之前,我们先讲一下注册到zookeeper
的路径结构
/fastcall 根路径
|
|_ /net.stackoverflow.SayService 注册上来的接口
|
|_ /service_0000000000 暴露该服务接口的服务节点(临时顺序节点),以上的ServiceDefinition就存储在这个节点里
|_ /service_0000000001
ZookeeperRegistryManager
实现源码,构造方法里先调用了connect
方法连接到zookeeper
,接着调用了updateCache
方法,该方法遍历了zookeeper
上的所有节点,并将所有远程服务地址缓存到了本地,最后调用了subscribe
方法,订阅了所有服务,当服务路径发生变化时(有新的服务加入,或有已注册上的服务挂掉),会通知连接到zookeeper
的客户端,客户端就会更新本地缓存。
public class ZooKeeperRegistryManager extends AbstractRegistryManager {
private static final Logger log = LoggerFactory.getLogger(ZooKeeperRegistryManager.class);
private static final String ROOT_PATH = "/fastcall";
private ZooKeeper zookeeper;
private final String host;
private final Integer port;
private final Integer sessionTimeout;
private Watcher serviceWatcher;
public ZooKeeperRegistryManager(String host, Integer port, Integer sessionTimeout) {
this.host = host;
this.port = port;
this.sessionTimeout = sessionTimeout;
this.serviceWatcher = new ServiceWatcher(this);
this.connect();
this.updateCache();
this.subscribe();
}
private void connect() {
String connection = host + ":" + port;
CountDownLatch countDownLatch = new CountDownLatch(1);
try {
ZooKeeper zooKeeper = new ZooKeeper(connection, sessionTimeout, new InitWatcher(countDownLatch));
countDownLatch.await();
this.zookeeper = zooKeeper;
} catch (Exception e) {
log.error("RegistryManager fail to connected zookeeper", e);
}
this.checkPathAndCreate(ROOT_PATH);
}
@Override
public void register(ServiceDefinition definition) {
String path = ROOT_PATH + "/" + definition.getInterfaceName();
this.checkPathAndCreate(path);
try {
String json = JsonUtils.bean2json(definition);
zookeeper.create(path + "/service_", json.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
log.info("RegistryManager register service: {}", definition);
} catch (InterruptedException | KeeperException e) {
log.error("RegistryManager fail to register service: {}", definition, e);
}
}
@Override
public synchronized void subscribe() {
try {
List<String> itfChildPaths = zookeeper.getChildren(ROOT_PATH, serviceWatcher);
log.debug("RegistryManager watched children of path {}", ROOT_PATH);
for (String itfChildPath : itfChildPaths) {
String itfPath = ROOT_PATH + "/" + itfChildPath;
List<String> serviceChildPaths = zookeeper.getChildren(itfPath, serviceWatcher);
Collections.sort(serviceChildPaths);
log.debug("RegistryManager watched children of path {}", itfPath);
}
} catch (Exception e) {
log.error("RegistryManager fail to subscribe", e);
}
}
@Override
public synchronized void updateCache() {
try {
Map<String, List<ServiceDefinition>> latestCache = new ConcurrentHashMap<>();
List<String> itfChildPaths = zookeeper.getChildren(ROOT_PATH, false);
for (String itfChildPath : itfChildPaths) {
String itfPath = ROOT_PATH + "/" + itfChildPath;
List<String> serviceChildPaths = zookeeper.getChildren(itfPath, false);
Collections.sort(serviceChildPaths);
List<ServiceDefinition> definitions = new ArrayList<>();
latestCache.put(itfChildPath, definitions);
for (String serviceChildPath : serviceChildPaths) {
String servicePath = itfPath + "/" + serviceChildPath;
byte[] bytes = zookeeper.getData(servicePath, false, new Stat());
String json = new String(bytes);
ServiceDefinition definition = JsonUtils.json2bean(json, ServiceDefinition.class);
definitions.add(definition);
}
}
cache.setCache(latestCache);
} catch (Exception e) {
log.error("RegistryManager fail to update cache", e);
}
}
@Override
public void close() {
try {
zookeeper.close();
log.info("RegistryManager closed");
} catch (InterruptedException e) {
log.error("RegistryManager fail to close zookeeper", e);
}
}
/**
* 检查节点是否存在,不存在则创建
*/
private void checkPathAndCreate(String path) {
try {
String[] paths = path.split("/");
StringBuilder sb = new StringBuilder();
for (int i = 1; i < paths.length; i++) {
sb.append("/").append(paths[i]);
if (zookeeper.exists(sb.toString(), false) == null) {
zookeeper.create(sb.toString(), "".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
}
} catch (Exception e) {
log.error("RegistryManager fail to check path {}", path, e);
}
}
}
zookeeper
的订阅功能由Watcher
实现,我们看构造方法中初始化的ServiceWatcher
类,它传入了RegistryManager
自身的实现类,当监听的路径发生变化时,他会收到一个WatchedEvent
事件,这时候,它调用了RegistryManager
接口的updateCache
方法更新本地缓存,同时又调用了一遍subscribe
方法重新订阅(因为zookeeper
收到通知之后,之前的订阅会失效)
public class ServiceWatcher implements Watcher {
private static final Logger log = LoggerFactory.getLogger(ServiceWatcher.class);
private final RegistryManager registryManager;
public ServiceWatcher(RegistryManager registryManager) {
this.registryManager = registryManager;
}
@Override
public void process(WatchedEvent watchedEvent) {
if (watchedEvent.getType() == Event.EventType.NodeChildrenChanged) {
log.debug("ServiceWatcher received message, {}", watchedEvent.getPath());
registryManager.updateCache();
registryManager.subscribe();
}
}
}
3、redis注册中心实现
redis
注册中心实现与zookeeper
基本类似,不过redis
的发布订阅有自己的一套实现方式,这里就不把所有源码贴出了,感兴趣的可以去GitHub项目地址查看所有源码。
七、代理对象的生成
代理对象是整个系统的核心了,服务消费端所有的远程调用都是通过对代理对象来实现的。框架采用了JDK动态代理
来生成代理对象,JDK动态代理
的核心在于实现的InvocationHandler
接口的实现类,来看源码。是实现类的构造方法参数中,重点在于第一个参数FastcallManager
,这是一个接口,默认实现是DefaultFastcallManager
,他是整个框架的一个外观类(这里运用了外观模式Facade
),它封装了子系统:服务注册子系统,序列化子系统,负载均衡子系统,传输层子系统。用外观类的好处是,对外屏蔽了多个子系统,提供了统一的接口,进行解耦。
public class RpcInvocationHandler implements InvocationHandler {
private static final Logger log = LoggerFactory.getLogger(RpcInvocationHandler.class);
private final FastcallManager fastcallManager;
private final String group;
private final String version;
private final Long timeout;
private final Class<?> fallback;
public RpcInvocationHandler(FastcallManager fastcallManager, String group, String version, Long timeout, Class<?> fallback) {
this.fastcallManager = fastcallManager;
this.group = group;
this.version = version;
this.timeout = timeout;
this.fallback = fallback;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
ResponseFuture future = null;
int retry = fastcallManager.config().getRetry();
while (true) {
try {
future = fastcallManager.call(method, args, group, version);
RpcResponse response = future.getResponse(timeout);
if (response != null) {
log.trace("Method: {}, code: {}", method.getName(), response.getCode());
if (response.getCode() == 0) {
byte[] responseBytes = response.getResponseBytes();
Class<?> responseType = response.getResponseType();
Object object = fastcallManager.serializeManager().deserialize(responseBytes, responseType);
return object;
} else {
byte[] throwableBytes = response.getThrowableBytes();
Class<?> throwableType = response.getThrowableType();
Throwable throwable = (Throwable) fastcallManager.serializeManager().deserialize(throwableBytes, throwableType);
throw throwable;
}
} else {
throw new RpcTimeoutException();
}
} catch (RpcTimeoutException exception) {
if (retry > 0) {
log.warn("Proxy execute rpc timeout and retry, retry:{}, requestId:{}", retry, future.getRequestId());
--retry;
} else {
log.error("Proxy execute rpc timeout, requestId:{}", future.getRequestId());
throw exception;
}
} catch (Throwable throwable) {
if (fallback != Void.class) {
log.warn("Proxy execute fallback method, requestId:{}", future.getRequestId());
Object object = fallback.newInstance();
Object response = method.invoke(object, args);
return response;
} else {
log.error("Proxy fail to call rpc, requestId:{}", future.getRequestId());
throw throwable;
}
} finally {
if (future != null) {
ResponseFutureContext.getInstance().removeFuture(future.getRequestId());
}
}
}
}
}
具体看invoke
方法的实现,它调用了外观类的call
方法获取ResponseFuture
对象,然后该对象的getResponse
方法一直阻塞,直到远程调用已经返回或者等待超时。
- 如果是超时的话,会检查是否有设置超时重试,在有可用超时重试的次数的情况下,会重新调用外观类的
call
方法。 - 如果是远程调用发生异常,则会检查是否设置了
服务降级
方法,设置了的话则会转为执行本地的服务降级
方法,否则将抛出异常。 - 如果是正常返回,则从
ResponseFuture
中获取返回值,给代理对象。
接下来,我们重点看一下外观类中的call
方法的具体实现过程:
- 先是构造了一个
RpcRequest
对象,里面包含了远程调用方法的一些参数,分组,版本等。 - 接着调用了服务注册子系统的
getService
方法,通过本地缓存过滤出了可用的远程地址。 - 然后调用了负载均衡子系统,通过配置的负载均衡策略,选择其中的一个远程地址。
- 最后调用传输子系统,指定地址和请求体,将报文发送给远程服务。
@Override
public ResponseFuture call(Method method, Object[] args, String group, String version) {
RpcRequest request = new RpcRequest();
request.setId(UUID.randomUUID().toString());
request.setInterfaceType(method.getDeclaringClass());
request.setMethod(method.getName());
request.setGroup(group);
request.setVersion(version);
request.setParams(args == null ? null : Arrays.asList(args));
request.setParamsType(Arrays.asList(method.getParameterTypes()));
List<ServiceDefinition> definitions = registryManager.getService(request.getInterfaceType(), group, version);
InetSocketAddress address = balanceManager.choose(definitions);
return transportManager.sendTo(address, request);
}
八、对Spring boot
的支持
以上介绍了整个框架的核心模块,对于Spring boot
的支持的介绍先留个坑,以后再写,感兴趣的朋友可以直接看源码中的fastcall-spring-boot-autoconfigure
模块和fastcall-spring-boot-starter
模块,地址在此GitHub项目地址