RPC自定义协议

背景

RPC框架注重性能,目前RPC框架使用的是Vert.xio.vertx.core.http.HttpServer作为服务提供者服务器。

优点是代码简单 ;缺点是底层网络传输使用的是HTTP协议,HTTP协议头部信息和请求响应格式较重,影响网络传输性能。(HTTP协议会有大量请求和响应标头)

基于上述问题,可以自定义一套RPC协议,可以利用TCP等传输层协议,自定义请求响应格式,实现性能更高、更灵活、更安全的RPC协议。

网络传输

网络传输:选择一个高性能通信的网络协议和传输方式

HTTP协议协议头比较大,并且HTTP本身属于无状态协议,每个HTTP请求都是独立的,每次请求/响应都需要重新建立和关闭链接,影响性能

为了解决上述问题,HTTP/1.1引入持久连接Keep-Alive ),允许单个TCP连接上发送多个HTTP请求和响应,避免了每次请求都要重新建立和关闭连接的开销。

HTTP是应用层协议,性能不如传输层TCP协议性能高。因此选择TCP协议完成网络传输。

消息结构

消息结构:用最少的空间传输需要的信息

自定义消息结构时,为了节省空间,需要使用轻量的类型,如byte字节类型,只需要1个字节(8个bit位)。

消息结构

除此之外,还需要

  • 请求id:唯一标识某个请求,TCP双向通信,标识成对出现的请求与响应对;
  • 请求体:发送的消息内容;
  • 请求体数据长度:保证能够完整地获取请求体内容信息。

请求头总长17字节。本质上就是一个字节数组。

代码实现

消息结构

协议消息结构

java 复制代码
/**
 * 协议消息结构
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ProtocolMessage<T> {

    /**
     * 消息头
     */
    private Header header;

    /**
     * 消息体(请求或响应对象)
     */
    private T body;

    /**
     * 协议消息头
     */
    @Data
    public static class Header {

        /**
         * 魔数,保证安全性
         */
        private byte magic;

        /**
         * 版本号
         */
        private byte version;

        /**
         * 序列化器
         */
        private byte serializer;

        /**
         * 消息类型(请求 / 响应)
         */
        private byte type;

        /**
         * 状态
         */
        private byte status;

        /**
         * 请求 id
         */
        private long requestId;

        /**
         * 消息体长度
         */
        private int bodyLength;
    }

}

各类常量与枚举

java 复制代码
/**
 * 协议常量
 */
public interface ProtocolConstant {

    /**
     * 消息头长度
     */
    int MESSAGE_HEADER_LENGTH = 17;

    /**
     * 协议魔数
     */
    byte PROTOCOL_MAGIC = 0x1;

    /**
     * 协议版本号
     */
    byte PROTOCOL_VERSION = 0x1;
}
java 复制代码
/**
 * 协议消息的状态枚举
 */
@Getter
public enum ProtocolMessageStatusEnum {

    OK("ok", 20),
    BAD_REQUEST("badRequest", 40),
    BAD_RESPONSE("badResponse", 50);

    private final String text;

    private final int value;

    ProtocolMessageStatusEnum(String text, int value) {
        this.text = text;
        this.value = value;
    }

    /**
     * 根据 value 获取枚举
     *
     * @param value
     * @return
     */
    public static ProtocolMessageStatusEnum getEnumByValue(int value) {
        for (ProtocolMessageStatusEnum anEnum : ProtocolMessageStatusEnum.values()) {
            if (anEnum.value == value) {
                return anEnum;
            }
        }
        return null;
    }
}
java 复制代码
/**
 * 协议消息的类型枚举
 */
@Getter
public enum ProtocolMessageTypeEnum {

    REQUEST(0),
    RESPONSE(1),
    HEART_BEAT(2),
    OTHERS(3);

    private final int key;

    ProtocolMessageTypeEnum(int key) {
        this.key = key;
    }

    /**
     * 根据 key 获取枚举
     *
     * @param key
     * @return
     */
    public static ProtocolMessageTypeEnum getEnumByKey(int key) {
        for (ProtocolMessageTypeEnum anEnum : ProtocolMessageTypeEnum.values()) {
            if (anEnum.key == key) {
                return anEnum;
            }
        }
        return null;
    }
}
java 复制代码
package com.starlink.codec.protocol;

import cn.hutool.core.util.ObjectUtil;
import lombok.Getter;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 协议消息的序列化器枚举
 */
@Getter
public enum ProtocolMessageSerializerEnum {

    JDK(0, "jdk"),
    JSON(1, "json"),
    KRYO(2, "kryo"),
    HESSIAN(3, "hessian");

    private final int key;

    private final String value;

    ProtocolMessageSerializerEnum(int key, String value) {
        this.key = key;
        this.value = value;
    }

    /**
     * 获取值列表
     *
     * @return
     */
    public static List<String> getValues() {
        return Arrays.stream(values()).map(item -> item.value).collect(Collectors.toList());
    }

    /**
     * 根据 key 获取枚举
     *
     * @param key
     * @return
     */
    public static ProtocolMessageSerializerEnum getEnumByKey(int key) {
        for (ProtocolMessageSerializerEnum anEnum : ProtocolMessageSerializerEnum.values()) {
            if (anEnum.key == key) {
                return anEnum;
            }
        }
        return null;
    }


    /**
     * 根据 value 获取枚举
     *
     * @param value
     * @return
     */
    public static ProtocolMessageSerializerEnum getEnumByValue(String value) {
        if (ObjectUtil.isEmpty(value)) {
            return null;
        }
        for (ProtocolMessageSerializerEnum anEnum : ProtocolMessageSerializerEnum.values()) {
            if (anEnum.value.equals(value)) {
                return anEnum;
            }
        }
        return null;
    }
}

TCP网络传输

1)TCP服务器实现

vertx.createNetServer()

java 复制代码
public class VertxTcpServer implements HttpServer {

    private byte[] handleRequest(byte[] requestData) {
        return "Hello, client!".getBytes();
    }

    @Override
    public void doStart(int port) {
        // 创建 Vert.x 实例
        Vertx vertx = Vertx.vertx();

        // 创建 TCP 服务器
        NetServer server = vertx.createNetServer();

        // 处理请求
        server.connectHandler(socket -> {
            // 处理连接
            socket.handler(buffer -> {
                // 处理接收到的字节数组
                byte[] requestData = buffer.getBytes();
                // 在这里进行自定义的字节数组处理逻辑,比如解析请求、调用服务、构造响应等
                byte[] responseData = handleRequest(requestData);
                // 发送响应
                socket.write(Buffer.buffer(responseData));
            });
        });

        // 启动 TCP 服务器并监听指定端口
        server.listen(port, result -> {
            if (result.succeeded()) {
                System.out.println("TCP server started on port " + port);
            } else {
                System.err.println("Failed to start TCP server: " + result.cause());
            }
        });
    }

    public static void main(String[] args) {
        new VertxTcpServer().doStart(8888);
    }
}

socket.write向连接的客户端发送数据,数据格式为Buffer是Vert.x提供的字节数据缓冲实现

2)TCP客户端实现

vertx.createNetClient()

java 复制代码
public class VertxTcpClient {

    public void start() {
        // 创建 Vert.x 实例
        Vertx vertx = Vertx.vertx();

        vertx.createNetClient().connect(8888, "localhost", result -> {
            if (result.succeeded()) {
                System.out.println("Connected to TCP server");
                io.vertx.core.net.NetSocket socket = result.result();
                // 发送数据
                socket.write("Hello, server!");
                // 接收响应
                socket.handler(buffer -> {
                    System.out.println("Received response from server: " + buffer.toString());
                });
            } else {
                System.err.println("Failed to connect to TCP server");
            }
        });
    }

    public static void main(String[] args) {
        new VertxTcpClient().start();
    }
}

3)编码 / 解码器

Vert.x的TCP服务器收发消息都是Buffer类型,需要编解码器实现Java对象与Buffer进行转换。

交互逻辑如下:

编码器:依次向Buffer缓冲区写入消息对象属性

java 复制代码
public class ProtocolMessageEncoder {

    /**
     * 编码
     */
    public static Buffer encode(ProtocolMessage<?> protocolMessage) throws IOException {
        if (protocolMessage == null || protocolMessage.getHeader() == null) {
            return Buffer.buffer();
        }
        ProtocolMessage.Header header = protocolMessage.getHeader();
        // 依次向缓冲区写入字节
        Buffer buffer = Buffer.buffer();
        buffer.appendByte(header.getMagic());
        buffer.appendByte(header.getVersion());
        buffer.appendByte(header.getSerializer());
        buffer.appendByte(header.getType());
        buffer.appendByte(header.getStatus());
        buffer.appendLong(header.getRequestId());
        // 获取序列化器
        ProtocolMessageSerializerEnum serializerEnum = ProtocolMessageSerializerEnum.getEnumByKey(header.getSerializer());
        if (serializerEnum == null) {
            throw new RuntimeException("序列化协议不存在");
        }
        Serializer serializer = SerializerFactory.getInstance(serializerEnum.getValue());
        byte[] bodyBytes = serializer.serialize(protocolMessage.getBody());
        // 写入 body 长度和数据
        buffer.appendInt(bodyBytes.length);
        buffer.appendBytes(bodyBytes);
        return buffer;
    }
}

解码器:依次从Buffer缓冲区指定位置读取数据,构造完整消息对象

java 复制代码
/**
 * 协议消息解码器
 */
public class ProtocolMessageDecoder {

    /**
     * 解码
     */
    public static ProtocolMessage<?> decode(Buffer buffer) throws IOException {
        // 分别从指定位置读出 Buffer
        ProtocolMessage.Header header = new ProtocolMessage.Header();
        byte magic = buffer.getByte(0);
        // 校验魔数
        if (magic != ProtocolConstant.PROTOCOL_MAGIC) {
            throw new RuntimeException("消息 magic 非法");
        }
        header.setMagic(magic);
        header.setVersion(buffer.getByte(1));
        header.setSerializer(buffer.getByte(2));
        header.setType(buffer.getByte(3));
        header.setStatus(buffer.getByte(4));
        header.setRequestId(buffer.getLong(5));
        header.setBodyLength(buffer.getInt(13));
        // 解决粘包问题,只读指定长度的数据
        byte[] bodyBytes = buffer.getBytes(17, 17 + header.getBodyLength());
        // 解析消息体
        ProtocolMessageSerializerEnum serializerEnum = ProtocolMessageSerializerEnum.getEnumByKey(header.getSerializer());
        if (serializerEnum == null) {
            throw new RuntimeException("序列化消息的协议不存在");
        }
        Serializer serializer = SerializerFactory.getInstance(serializerEnum.getValue());
        ProtocolMessageTypeEnum messageTypeEnum = ProtocolMessageTypeEnum.getEnumByKey(header.getType());
        if (messageTypeEnum == null) {
            throw new RuntimeException("序列化消息的类型不存在");
        }
        switch (messageTypeEnum) {
            case REQUEST:
                RpcRequest request = serializer.deserialize(bodyBytes, RpcRequest.class);
                return new ProtocolMessage<>(header, request);
            case RESPONSE:
                RpcResponse response = serializer.deserialize(bodyBytes, RpcResponse.class);
                return new ProtocolMessage<>(header, response);
            case HEART_BEAT:
            case OTHERS:
            default:
                throw new RuntimeException("暂不支持该消息类型");
        }
    }

}

4)请求处理器(服务提供者)

解码器解码请求,反射调用方法封装返回结果。(每一个都当完整帧处理)

java 复制代码
public class TcpServerHandler implements Handler<NetSocket> {

    @Override
    public void handle(NetSocket netSocket) {
        // 处理连接
        netSocket.handler(buffer -> {
            // 接受请求,解码
            ProtocolMessage<RpcRequest> protocolMessage;
            try {
                protocolMessage = (ProtocolMessage<RpcRequest>) ProtocolMessageDecoder.decode(buffer);
            } catch (IOException e) {
                throw new RuntimeException("协议消息解码错误");
            }
            RpcRequest rpcRequest = protocolMessage.getBody();

            // 处理请求
            // 构造响应结果对象
            RpcResponse rpcResponse = new RpcResponse();
            try {
                // 获取要调用的服务实现类,通过反射调用
                Class<?> implClass = LocalRegistry.get(rpcRequest.getServiceName());
                Method method = implClass.getMethod(rpcRequest.getMethodName(), rpcRequest.getParameterTypes());
                Object result = method.invoke(implClass.newInstance(), rpcRequest.getArgs());
                // 封装返回结果
                rpcResponse.setData(result);
                rpcResponse.setDataType(method.getReturnType());
                rpcResponse.setMessage("ok");
            } catch (Exception e) {
                e.printStackTrace();
                rpcResponse.setMessage(e.getMessage());
                rpcResponse.setException(e);
            }

            // 发送响应,编码
            ProtocolMessage.Header header = protocolMessage.getHeader();
            header.setType((byte) ProtocolMessageTypeEnum.RESPONSE.getKey());
            ProtocolMessage<RpcResponse> responseProtocolMessage = new ProtocolMessage<>(header, rpcResponse);
            try {
                Buffer encode = ProtocolMessageEncoder.encode(responseProtocolMessage);
                netSocket.write(encode);
            } catch (IOException e) {
                throw new RuntimeException("协议消息编码错误");
            }
        });
    }
}

5)发送请求(服务消费者)

调整服务消费者发送请求代码,改HTTP请求为TCP请求。(修改服务代理类)

java 复制代码
/**
 * 服务代理(JDK 动态代理)
 */
@Slf4j
public class ServiceProxy implements InvocationHandler {

    /**
     * 调用代理
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) {
        
        // 构造请求
        String serviceName = method.getDeclaringClass().getName();
        RpcRequest rpcRequest = RpcRequest.builder()
                .serviceName(serviceName)
                .methodName(method.getName())
                .parameterTypes(method.getParameterTypes())
                .args(args)
                .build();
        try {
            // 从注册中心获取服务提供者请求地址
            RpcConfig rpcConfig = RpcApplication.getRpcConfig();
            Registry registry = RegistryFactory.getInstance(rpcConfig.getRegistryConfig().getRegistry());
            ServiceMetaInfo serviceMetaInfo = new ServiceMetaInfo();
            serviceMetaInfo.setServiceName(serviceName);
            serviceMetaInfo.setServiceVersion(RpcConstants.DEFAULT_SERVICE_VERSION);
            List<ServiceMetaInfo> serviceMetaInfoList = registry.serviceDiscovery(serviceMetaInfo.getServiceKey());
            if (CollUtil.isEmpty(serviceMetaInfoList)) {
                throw new RuntimeException("暂无服务地址");
            }
            
            // 负载均衡策略
            ServiceMetaInfo selectedServiceMetaInfo = serviceMetaInfoList.get(0);
            // 发送 TCP 请求
            Vertx vertx = Vertx.vertx();
            NetClient netClient = vertx.createNetClient();
            CompletableFuture<RpcResponse> responseFuture = new CompletableFuture<>();
            netClient.connect(selectedServiceMetaInfo.getServicePort(), selectedServiceMetaInfo.getServiceHost(),
                    result -> {
                        if (result.succeeded()) {
                            log.info("Connected to TCP server");
                            io.vertx.core.net.NetSocket socket = result.result();
                            // 构造消息,发送数据
                            ProtocolMessage<RpcRequest> protocolMessage = new ProtocolMessage<>();
                            ProtocolMessage.Header header = new ProtocolMessage.Header();
                            header.setMagic(ProtocolConstant.PROTOCOL_MAGIC);
                            header.setVersion(ProtocolConstant.PROTOCOL_VERSION);
                            header.setSerializer((byte) ProtocolMessageSerializerEnum.getEnumByValue(RpcApplication.getRpcConfig().getSerializer()).getKey());
                            header.setType((byte) ProtocolMessageTypeEnum.REQUEST.getKey());
                            header.setRequestId(IdUtil.getSnowflakeNextId());
                            protocolMessage.setHeader(header);
                            protocolMessage.setBody(rpcRequest);
                            // 编码请求
                            try {
                                Buffer encodeBuffer = ProtocolMessageEncoder.encode(protocolMessage);
                                socket.write(encodeBuffer);
                            } catch (IOException e) {
                                throw new RuntimeException("协议消息编码错误");
                            }

                            // 接收响应
                            socket.handler(buffer -> {
                                try {
                                    ProtocolMessage<RpcResponse> rpcResponseProtocolMessage = (ProtocolMessage<RpcResponse>) ProtocolMessageDecoder.decode(buffer);
                                    responseFuture.complete(rpcResponseProtocolMessage.getBody());
                                } catch (IOException e) {
                                    throw new RuntimeException("协议消息解码错误");
                                }
                            });
                        } else {
                            log.error("Failed to connect to TCP server");
                        }
                    });

            RpcResponse rpcResponse = responseFuture.get();
            // 关闭连接
            netClient.close();
            return rpcResponse.getData();
        } catch (Exception e) {
            log.error("请求发送失败.");
        }
        return null;
    }
}

为了方便得到响应结果,CompletableFuture转异步为同步。

java 复制代码
CompletableFuture<RpcResponse> responseFuture = new CompletableFuture<>();
netClient.connect(xxx,
    result -> {
        // 完成了响应
        responseFuture.complete(rpcResponseProtocolMessage.getBody());
    });
);
// 阻塞,直到响应完成,才会继续向下执行
RpcResponse rpcResponse = responseFuture.get();

Vert.x解决半包和粘包

Vert.x框架中,RecordParser 可以根据指定的分隔符或者数据长度来解析数据流,确保每次获取完整的数据包。

具体实现思路如下:

解析完整帧

解析完整帧,再处理业务逻辑。

java 复制代码
/**
 * TCP 消息处理器包装
 * 装饰者模式,使用 recordParser 对原有的 buffer 处理能力进行增强
 */
public class TcpBufferWrapperHandler implements Handler<Buffer> {

    /**
     * 解析器,用于解决半包、粘包问题
     */
    private final RecordParser recordParser;

    public TcpBufferWrapperHandler(Handler<Buffer> bufferHandler) {
        recordParser = initRecordParser(bufferHandler);
    }

    @Override
    public void handle(Buffer buffer) {
        // 向解析器提供数据
        recordParser.handle(buffer);
    }

    /**
     * 初始化解析器
     */
    private RecordParser initRecordParser(Handler<Buffer> bufferHandler) {
        // 构造 parser
        RecordParser parser = RecordParser.newFixed(ProtocolConstant.MESSAGE_HEADER_LENGTH);

        parser.setOutput(new Handler<Buffer>() {
            // 初始化
            int bodyLength = -1;
            // 一个完整消息帧
            Buffer messageBuffer = Buffer.buffer();

            @Override
            public void handle(Buffer buffer) {
                // 1. 每次循环,首先读取消息头
                if (-1 == bodyLength) {
                    // 读取消息体长度
                    bodyLength = buffer.getInt(13);
                    parser.fixedSizeMode(bodyLength);
                    // 写入头信息到结果
                    messageBuffer.appendBuffer(buffer);
                } else {
                    // 2. 然后读取消息体
                    // 写入体信息到结果
                    messageBuffer.appendBuffer(buffer);
                    // 已拼接为完整 Buffer,执行处理
                    bufferHandler.handle(messageBuffer);
                    // 重置一轮
                    parser.fixedSizeMode(ProtocolConstant.MESSAGE_HEADER_LENGTH);
                    bodyLength = -1;
                    messageBuffer = Buffer.buffer();
                }
            }
        });

        return parser;
    }
}

请求处理器改造

java 复制代码
public class TcpServerHandler implements Handler<NetSocket> {

    @Override
    public void handle(NetSocket netSocket) {
        // 处理完整帧后再处理业务逻辑
        netSocket.handler(new TcpBufferWrapperHandler(
                buffer -> {
                    // 接受请求,解码
                    ProtocolMessage<RpcRequest> protocolMessage;
                    try {
                        protocolMessage = (ProtocolMessage<RpcRequest>) ProtocolMessageDecoder.decode(buffer);
                    } catch (IOException e) {
                        throw new RuntimeException("协议消息解码错误");
                    }
                    RpcRequest rpcRequest = protocolMessage.getBody();

                    // 处理请求
                    // 构造响应结果对象
                    RpcResponse rpcResponse = new RpcResponse();
                    try {
                        // 获取要调用的服务实现类,通过反射调用
                        Class<?> implClass = LocalRegistry.get(rpcRequest.getServiceName());
                        Method method = implClass.getMethod(rpcRequest.getMethodName(), rpcRequest.getParameterTypes());
                        Object result = method.invoke(implClass.newInstance(), rpcRequest.getArgs());
                        // 封装返回结果
                        rpcResponse.setData(result);
                        rpcResponse.setDataType(method.getReturnType());
                        rpcResponse.setMessage("ok");
                    } catch (Exception e) {
                        e.printStackTrace();
                        rpcResponse.setMessage(e.getMessage());
                        rpcResponse.setException(e);
                    }

                    // 发送响应,编码
                    ProtocolMessage.Header header = protocolMessage.getHeader();
                    header.setType((byte) ProtocolMessageTypeEnum.RESPONSE.getKey());
                    ProtocolMessage<RpcResponse> responseProtocolMessage = new ProtocolMessage<>(header, rpcResponse);
                    try {
                        Buffer encode = ProtocolMessageEncoder.encode(responseProtocolMessage);
                        netSocket.write(encode);
                    } catch (IOException e) {
                        throw new RuntimeException("协议消息编码错误");
                    }
                }
        ));
    }
}

通信协议SPI

通过配置和SPI动态切换通信协议,主要作用是服务消费者发送请求 以及服务提供者处理逻辑协议区分。

Vetrx服务端

java 复制代码
public interface VertxServer {
    void doStart(int port);
}
java 复制代码
/**
 * Vertx HTTP 服务器
 */
public class VertxHttpServer implements VertxServer {

    private static final Logger logger = LoggerFactory.getLogger(VertxHttpServer.class);

    /**
     * 启动服务器
     *
     * @param port 端口
     */
    public void doStart(int port) {
        // 创建 Vert.x 实例
        Vertx vertx = Vertx.vertx();

        // 创建 HTTP 服务器
        io.vertx.core.http.HttpServer server = vertx.createHttpServer();

        // 处理请求
        server.requestHandler(new HttpServerHandler());

        // 启动 HTTP 服务器并监听指定端口
        server.listen(port, result -> {
            if (result.succeeded()) {
                logger.info("Server is now listening on port " + port);
            } else {
                logger.error("Failed to start server: " + result.cause());
            }
        });
    }
}
java 复制代码
public class VertxTcpServer implements VertxServer {

    @Override
    public void doStart(int port) {
        // 创建 Vert.x 实例
        Vertx vertx = Vertx.vertx();

        // 创建 TCP 服务器
        NetServer server = vertx.createNetServer();

        // 处理请求
        server.connectHandler(new TcpServerHandler());

        // 启动 TCP 服务器并监听指定端口
        server.listen(port, result -> {
            if (result.succeeded()) {
                System.out.println("TCP server started on port " + port);
            } else {
                System.err.println("Failed to start TCP server: " + result.cause());
            }
        });
    }
}
java 复制代码
public class VertxServerFactory {
    static {
        SpiLoader.load(VertxServer.class);
    }

    public static VertxServer getInstance(String key) {
        return SpiLoader.getInstance(VertxServer.class, key);
    }
}

服务提供者启动服务器时从VertxServerFactory加载特定服务器:

java 复制代码
public class ServiceStarter {
    public static void main(String[] args) {
        RpcApplication.init();

        RpcConfig rpcConfig = RpcApplication.getRpcConfig();
        Registry registry = RegistryFactory.getInstance(rpcConfig.getRegistryConfig().getRegistry());


        // 注册服务
        String serviceName = HelloService.class.getName();
        LocalRegistry.register(serviceName, HelloServiceImpl.class);

        ServiceMetaInfo serviceMetaInfo = new ServiceMetaInfo();
        serviceMetaInfo.setServiceHost(rpcConfig.getServerHost());
        serviceMetaInfo.setServicePort(rpcConfig.getServerPort());
        serviceMetaInfo.setServiceName(serviceName);
        serviceMetaInfo.setServiceVersion(rpcConfig.getVersion());
        try {
            registry.register(serviceMetaInfo);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        // 启动服务器
        VertxServer server = VertxServerFactory.getInstance(rpcConfig.getProtocol());
        server.(rpcConfig.getServerPort());
    }
}

Vetrx客户端

java 复制代码
/**
 * 客户端发送请求
 */
public interface VertxClient {
    RpcResponse doRequest(RpcRequest rpcRequest, ServiceMetaInfo serviceMetaInfo) throws Exception;
}
java 复制代码
public class VertxHttpClient implements VertxClient {
    @Override
    public RpcResponse doRequest(RpcRequest rpcRequest, ServiceMetaInfo serviceMetaInfo) throws Exception {

        RpcConfig rpcConfig = RpcApplication.getRpcConfig();
        // 指定序列化器
        final Serializer serializer = SerializerFactory.getInstance(rpcConfig.getSerializer());

        // 序列化
        byte[] bodyBytes = serializer.serialize(rpcRequest);

        HttpResponse httpResponse = HttpRequest.post(serviceMetaInfo.getServiceAddress())
                .body(bodyBytes)
                .execute();
        byte[] result = httpResponse.bodyBytes();
        // 反序列化
        return serializer.deserialize(result, RpcResponse.class);
    }
}
java 复制代码
@Slf4j
public class VertxTcpClient implements VertxClient {

    @Override
    public RpcResponse doRequest(RpcRequest rpcRequest, ServiceMetaInfo serviceMetaInfo) throws Exception {
        // 发送 TCP 请求
        Vertx vertx = Vertx.vertx();
        NetClient netClient = vertx.createNetClient();
        CompletableFuture<RpcResponse> responseFuture = new CompletableFuture<>();
        netClient.connect(serviceMetaInfo.getServicePort(), serviceMetaInfo.getServiceHost(),
                result -> {
                    if (result.succeeded()) {
                        log.info("Connected to TCP server");
                        io.vertx.core.net.NetSocket socket = result.result();
                        // 构造消息,发送数据
                        ProtocolMessage<RpcRequest> protocolMessage = new ProtocolMessage<>();
                        ProtocolMessage.Header header = new ProtocolMessage.Header();
                        header.setMagic(ProtocolConstant.PROTOCOL_MAGIC);
                        header.setVersion(ProtocolConstant.PROTOCOL_VERSION);
                        header.setSerializer((byte) ProtocolMessageSerializerEnum.getEnumByValue(RpcApplication.getRpcConfig().getSerializer()).getKey());
                        header.setType((byte) ProtocolMessageTypeEnum.REQUEST.getKey());
                        header.setRequestId(IdUtil.getSnowflakeNextId());
                        protocolMessage.setHeader(header);
                        protocolMessage.setBody(rpcRequest);
                        // 编码请求
                        try {
                            Buffer encodeBuffer = ProtocolMessageEncoder.encode(protocolMessage);
                            socket.write(encodeBuffer);
                        } catch (IOException e) {
                            throw new RuntimeException("协议消息编码错误");
                        }

                        // 接收响应
                        socket.handler(buffer -> {
                            try {
                                ProtocolMessage<RpcResponse> rpcResponseProtocolMessage = (ProtocolMessage<RpcResponse>) ProtocolMessageDecoder.decode(buffer);
                                responseFuture.complete(rpcResponseProtocolMessage.getBody());
                            } catch (IOException e) {
                                throw new RuntimeException("协议消息解码错误");
                            }
                        });
                    } else {
                        log.error("Failed to connect to TCP server");
                    }
                });

        RpcResponse rpcResponse = responseFuture.get();
        // 关闭连接
        netClient.close();
        return rpcResponse;
    }
}
java 复制代码
public class VertxClientFactory {
    static {
        SpiLoader.load(VertxClient.class);
    }

    public static VertxClient getInstance(String key) {
        return SpiLoader.getInstance(VertxClient.class, key);
    }
}

服务代理从VertxClientFactory获取对应客户端实现并发送请求,获取响应:

java 复制代码
public Object invoke(Object proxy, Method method, Object[] args) {

        // 构造请求
        String serviceName = method.getDeclaringClass().getName();
        RpcRequest rpcRequest = RpcRequest.builder()
                .serviceName(serviceName)
                .methodName(method.getName())
                .parameterTypes(method.getParameterTypes())
                .args(args)
                .build();
        try {
            // 从注册中心获取服务提供者请求地址
            RpcConfig rpcConfig = RpcApplication.getRpcConfig();
            Registry registry = RegistryFactory.getInstance(rpcConfig.getRegistryConfig().getRegistry());
            ServiceMetaInfo serviceMetaInfo = new ServiceMetaInfo();
            serviceMetaInfo.setServiceName(serviceName);
            serviceMetaInfo.setServiceVersion(RpcConstants.DEFAULT_SERVICE_VERSION);
            List<ServiceMetaInfo> serviceMetaInfoList = registry.serviceDiscovery(serviceMetaInfo.getServiceKey());
            if (CollUtil.isEmpty(serviceMetaInfoList)) {
                throw new RuntimeException("暂无服务地址");
            }

            // 负载均衡策略
            ServiceMetaInfo selectedServiceMetaInfo = serviceMetaInfoList.get(0);
            RpcResponse rpcResponse = VertxClientFactory.getInstance(rpcConfig.getProtocol())
                    .doRequest(rpcRequest, selectedServiceMetaInfo);

            return rpcResponse.getData();
        } catch (Exception e) {
            log.error("请求发送失败.");
        }
        return null;
    }

最后再配置VertxServer、VertxClient两个接口的SPI配置文件,key都一样,分别为http、tcp。

SPI文件

相关推荐
Hacker_LaoYi25 分钟前
计算机网络:虚拟机虚拟网络配置
网络·计算机网络·php
大丈夫立于天地间2 小时前
OSPF - 1类LSA(Router-LSA)
网络·网络协议·学习·信息与通信
黑客Jack2 小时前
网络安全应急响应技术原理与应用
网络·安全·web安全
黑客K-ing3 小时前
什么是黑客和白帽子
网络·安全·web安全
迷茫的小技术5 小时前
OSPF使能配置
运维·服务器·网络
寻找优秀的自己5 小时前
WebSocket 设计思路
网络·websocket·网络协议·golang
云计算DevOps-韩老师5 小时前
【网络云SRE运维开发】2025第2周-每日【2025/01/07】小测-【第7章 GVRP链路捆绑】理论和实操
服务器·网络·计算机网络·云计算·运维开发
黑客老李7 小时前
BaseCTF scxml 详解
开发语言·网络·数据库·python·sql·安全
碎碎思7 小时前
使用 IP 核和开源库减少 FPGA 设计周期
网络·网络协议·tcp/ip·fpga开发
Hacker_LaoYi8 小时前
[CTF/网络安全] 攻防世界 view_source 解题详析
网络·安全·web安全