基于netty实现的一个rpc框架

介绍

这是基于netty开发的一个rpc框架,同时支持非Spring应用和Spring应用,且对于Spring boot应用,提供spring-boot-starter以支持自动配置。该框架采用类似dubbo的注解方式,简单易用易上手。同时支持zookeeperredis多注册中心,也支持配置不同的负载均衡策略。

源码与文档

GitHub项目地址
在线文档

功能列表

  • 服务注册
  • 服务发现
  • 服务调用
  • 负载均衡策略
  • 调用超时与重试
  • 服务降级
  • 资源限制

实现过程

由于整个实现过程篇幅很长,这里只列出几个我认为比较重要的几个步骤,完整的代码已经上传至GitHub,GitHub项目地址

一、定义一个应用层传输协议

字段说明大小
magic魔术字,固定为fastcall8字节
version协议版本,当前版本为0.12字节
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实现序列化,抛开性能,因为比较简单易用,也可以采用其他序列化框架比如protobufmsgpack等,只需要实现序列化接口即可

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中,可以继承MessageToByteEncoderByteToMessageDecoder将自定义的实体类(也就是我们开头定义的协议)与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;
    }
}

五、服务端的绑定与客户端的连接

所有与网络通信相关的代码,都可以抽象为一个传输层子系统,方便以后扩展其他应用层协议,如httprmi等,定义子系统接口

public interface TransportManager {

    /**
     * 绑定本地端口
     *
     * @param localSocketAddress
     */
    void bind(InetSocketAddress localSocketAddress);

    /**
     * 发送报文给远程地址
     *
     * @param remoteSocketAddress
     * @param request
     */
    ResponseFuture sendTo(InetSocketAddress remoteSocketAddress, RpcRequest request);

    /**
     * 关闭所有连接与线程池
     */
    void close();
}
1、服务端绑定

以下是FastcallTransportManagerbind方法默认实现,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方法时,如果远程调用还没有传回响应结果,则会一直阻塞住,也可以设置调用超时时间,内部原理很简单,采用waitnotifyAll实现,可参考源码。

@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中获取channelchannelPool是一个通道池,本质上就是一个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注册中心实现

目前该框架支持zookeeperredis注册中心,我们拿默认的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项目地址

1、spring-boot-autoconfigure模块
2、spring-boot-starter模块
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值