Netty-实现RPC

Netty实现RPC简直不要那么简单,只需要用户实现相应的序列化、反序列化和tcp拆包粘包问题就行

步骤:

  1. 定义服务接口和实现服务接口
  2. 定义RPC请求实体和响应实体
  3. 选择序列化框架
  4. 定义通信协议,解决tcp拆包粘包问题
  5. 定义RPC服务端和RPC客户端,Netty实现

步骤1:

  • 服务接口
public interface CalculatorService {

    double add(double op1, double op2);

    double substract(double op1, double op2);

    double multiply(double op1, double op2);
}
  • 服务接口实现
public class CalculatorServiceImpl implements CalculatorService {

    @Override
    public double add(double op1, double op2) {
        return op1 + op2;
    }

    @Override
    public double substract(double op1, double op2) {
        return op1 - op2;
    }

    @Override
    public double multiply(double op1, double op2) {
        return op1 * op2;
    }

    public static void main(String[] args) {
        CalculatorService service = new CalculatorServiceImpl();
        System.out.println(service.add(1.0, 2.0));
    }

}

步骤2:

  • 请求实体
public class RpcRequest {
    private String id;

    private String serviceName;

    private String methodName;

    private double param1;
    private double param2;

    public RpcRequest() {
        super();
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getServiceName() {
        return serviceName;
    }

    public void setServiceName(String serviceName) {
        this.serviceName = serviceName;
    }

    public String getMethodName() {
        return methodName;
    }

    public void setMethodName(String methodName) {
        this.methodName = methodName;
    }

    public double getParam1() {
        return param1;
    }

    public void setParam1(double param1) {
        this.param1 = param1;
    }

    public double getParam2() {
        return param2;
    }

    public void setParam2(double param2) {
        this.param2 = param2;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((id == null) ? 0 : id.hashCode());
        result = prime * result + ((methodName == null) ? 0 : methodName.hashCode());
        long temp;
        temp = Double.doubleToLongBits(param1);
        result = prime * result + (int) (temp ^ (temp >>> 32));
        temp = Double.doubleToLongBits(param2);
        result = prime * result + (int) (temp ^ (temp >>> 32));
        result = prime * result + ((serviceName == null) ? 0 : serviceName.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        RpcRequest other = (RpcRequest) obj;
        if (id == null) {
            if (other.id != null)
                return false;
        } else if (!id.equals(other.id))
            return false;
        if (methodName == null) {
            if (other.methodName != null)
                return false;
        } else if (!methodName.equals(other.methodName))
            return false;
        if (Double.doubleToLongBits(param1) != Double.doubleToLongBits(other.param1))
            return false;
        if (Double.doubleToLongBits(param2) != Double.doubleToLongBits(other.param2))
            return false;
        if (serviceName == null) {
            if (other.serviceName != null)
                return false;
        } else if (!serviceName.equals(other.serviceName))
            return false;
        return true;
    }

    @Override
    public String toString() {
        return "RpcRequest [id=" + id + ", serviceName=" + serviceName + ", methodName=" + methodName + ", param1="
                + param1 + ", param2=" + param2 + "]";
    }
    
}
  • 响应实体
public class RpcResponse {
    private String id;

    private String serviceName;

    private String methodName;

    private String cause;

    private double result;

    public RpcResponse() {
        super();
    }


    public String getId() {
        return id;
    }


    public String getServiceName() {
        return serviceName;
    }


    public void setServiceName(String serviceName) {
        this.serviceName = serviceName;
    }


    public void setId(String id) {
        this.id = id;
    }


    public String getMethodName() {
        return methodName;
    }

    public void setMethodName(String methodName) {
        this.methodName = methodName;
    }

    public String getCause() {
        return cause;
    }

    public void setCause(String cause) {
        this.cause = cause;
    }

    public double getResult() {
        return result;
    }

    public void setResult(double result) {
        this.result = result;
    }


    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((cause == null) ? 0 : cause.hashCode());
        result = prime * result + ((id == null) ? 0 : id.hashCode());
        result = prime * result + ((methodName == null) ? 0 : methodName.hashCode());
        long temp;
        temp = Double.doubleToLongBits(this.result);
        result = prime * result + (int) (temp ^ (temp >>> 32));
        return result;
    }


    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        RpcResponse other = (RpcResponse) obj;
        if (cause == null) {
            if (other.cause != null)
                return false;
        } else if (!cause.equals(other.cause))
            return false;
        if (id == null) {
            if (other.id != null)
                return false;
        } else if (!id.equals(other.id))
            return false;
        if (methodName == null) {
            if (other.methodName != null)
                return false;
        } else if (!methodName.equals(other.methodName))
            return false;
        if (Double.doubleToLongBits(result) != Double.doubleToLongBits(other.result))
            return false;
        return true;
    }


    @Override
    public String toString() {
        return "RpcResponse [id=" + id + ", methodName=" + methodName + ", cause=" + cause + ", result=" + result + "]";
    }
}

步骤3:

  • 序列化接口
public interface Serializer {

    public <T> Object deserialize(byte[] bytes, Class<T> clazz);

    public <T> byte[] serialize(T obj);
}

  • protobuf序列化
public class ProtobufSerializer implements Serializer {
    private static Objenesis objenesis = new ObjenesisStd(true);
    private static Map<Class<?>, Schema<?>> cachedSchema = new ConcurrentHashMap<Class<?>, Schema<?>>();

    private static <T> Schema<T> getSchema(Class<T> cls) {
        @SuppressWarnings("unchecked")
        Schema<T> schema = (Schema<T>) cachedSchema.get(cls);
        if (schema == null) {
            schema = RuntimeSchema.createFrom(cls);
            if (schema != null) {
                cachedSchema.put(cls, schema);
            }
        }
        return schema;
    }

    @Override
    public <T> byte[] serialize(T obj) {
        @SuppressWarnings("unchecked")
        Class<T> cls = (Class<T>) obj.getClass();
        LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
        try {
            Schema<T> schema = getSchema(cls);
            return ProtostuffIOUtil.toByteArray(obj, schema, buffer);
        } catch (Exception e) {
            throw new RpcException(e.getMessage(), e);
        } finally {
            buffer.clear();
        }
    }

    @Override
    public <T> Object deserialize(byte[] bytes, Class<T> clazz) {
        try {
            T message = objenesis.newInstance(clazz);
            Schema<T> schema = getSchema(clazz);
            ProtostuffIOUtil.mergeFrom(bytes, message, schema);
            return message;
        } catch (Exception e) {
            throw new RpcException(e.getMessage(), e);
        }
    }

    public static void main(String[] args) {
        RpcRequest request = new RpcRequest();
        request.setMethodName("add");
        request.setParam1(1.0);
        request.setParam2(3.0);

        ProtobufSerializer sr = new ProtobufSerializer();
        byte[] bytes = sr.serialize(request);
        System.out.println(bytes.length);

        RpcRequest rr = (RpcRequest) sr.deserialize(bytes, RpcRequest.class);

        System.out.println(rr.toString());
    }
}

步骤4:

  • 基于长度的变长包解决tcp拆包粘包问题
  • 解码
public class RpcDecode extends ByteToMessageDecoder {

    private Class<?> genericClass;
    private Serializer serializer;

    public RpcDecode(Class<?> genericClass, Serializer serializer) {
        this.genericClass = genericClass;
        this.serializer = serializer;
    }

    @Override
    public final void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        if (in.readableBytes() < 4) {
            return;
        }
        in.markReaderIndex();
        int dataLength = in.readInt();
        if (dataLength < 0) {
            ctx.close();
        }
        if (in.readableBytes() < dataLength) {
            in.resetReaderIndex();
            return;
        }
        byte[] data = new byte[dataLength];
        in.readBytes(data);

        Object obj = serializer.deserialize(data, genericClass);
        out.add(obj);
    }
}
  • 编码
public class RpcEncode extends MessageToByteEncoder<Object> {

    private Class<?> genericClass;
    private Serializer serializer;

    public RpcEncode(Class<?> genericClass, Serializer serializer) {
        this.genericClass = genericClass;
        this.serializer = serializer;
    }

    @Override
    protected void encode(ChannelHandlerContext ctx, Object in, ByteBuf out) throws Exception {
        if (genericClass.isInstance(in)) {
            byte[] data = serializer.serialize(in);
            out.writeInt(data.length);
            out.writeBytes(data);
        }
    }
}

步骤5:

  • RPC服务端
public class NettyServer {
    private static final Logger logger = LoggerFactory.getLogger(NettyServer.class);
    private Thread thread;
    private final int port;
    public NettyServer(int port) {
        this.port = port;
    }

    public void start(final RpcBuilder rpcBuilder) throws Exception {

        thread = new Thread(new Runnable() {
            @Override
            public void run() {
                EventLoopGroup bossGroup = new NioEventLoopGroup();
                EventLoopGroup workerGroup = new NioEventLoopGroup();
                try {
                    ServerBootstrap bootstrap = new ServerBootstrap();
                    bootstrap.group(bossGroup, workerGroup)
                            .channel(NioServerSocketChannel.class)
                            .childHandler(new ChannelInitializer<SocketChannel>() {
                                @Override
                                public void initChannel(SocketChannel channel) throws Exception {
                                    channel.pipeline()
                                            .addLast(new RpcDecode(RpcRequest.class, SerializerFactory.getSerializer()))
                                            .addLast(new RpcEncode(RpcResponse.class, SerializerFactory.getSerializer()))
                                            .addLast(new NettyServerHandler(rpcBuilder));
                                }
                            })
                            .option(ChannelOption.SO_TIMEOUT, 100)
                            .option(ChannelOption.SO_BACKLOG, 128)
                            .option(ChannelOption.TCP_NODELAY, true)
                            .option(ChannelOption.SO_REUSEADDR, true)
                            .childOption(ChannelOption.SO_KEEPALIVE, true);
                    ChannelFuture future = bootstrap.bind(port).sync();

                    logger.info("接口服务器端启动成功......");

                    Channel serviceChannel = future.channel().closeFuture().sync().channel();
                } catch (InterruptedException e) {
                    logger.error(e.getMessage(), e);
                } finally {
                    workerGroup.shutdownGracefully();
                    bossGroup.shutdownGracefully();
                }
            }
        });
        thread.start();

    }

    public Thread getThread() {
        return this.thread;
    }

    public void stop() throws Exception {
        if (thread != null && thread.isAlive()) {
            thread.interrupt();
        }
        logger.info("接口服务器端停止......");
    }
}

  • RPC客户端
public class NettyClient {
    private Channel channel;
    private static ConcurrentHashMap<String, SynchronousQueue<RpcResponse>> mapInfo = new ConcurrentHashMap<>();
   
    public static void putSunchronousQuee(String id, SynchronousQueue<RpcResponse> queue) {
        mapInfo.put(id, queue);
    }

    public static SynchronousQueue<RpcResponse> getSynchronousQueue(String id) {
        return mapInfo.get(id);
    }

    public static void removeById(String id) {
        mapInfo.remove(id);
    }

    public void sendRpcRequest(RpcRequest rpcRequest) throws Exception {
        try {
            this.channel.writeAndFlush(rpcRequest).sync();
        } catch (Exception e) {
            throw e;
        }
    }

    public void init(String host, int port, final Serializer serializer) throws Exception {
        EventLoopGroup group = new NioEventLoopGroup();
        Bootstrap bootstrap = new Bootstrap();
        bootstrap.group(group)
                .channel(NioSocketChannel.class)
                .handler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    public void initChannel(SocketChannel channel) throws Exception {
                        channel.pipeline()
                                .addLast(new RpcEncode(RpcRequest.class, serializer))
                                .addLast(new RpcDecode(RpcResponse.class, serializer))
                                .addLast(new NettyClientHandler());
                    }
                })
                .option(ChannelOption.TCP_NODELAY, true)
                .option(ChannelOption.SO_REUSEADDR, true)
                .option(ChannelOption.SO_KEEPALIVE, true);
        this.channel = bootstrap.connect(host, port).sync().channel();
        System.out.println("接口服务端连接成功......");
    }

    public boolean isValidate() {
        if (this.channel != null) {
            return this.channel.isActive();
        }
        return false;
    }

    public void close() {
        if (this.channel != null) {
            if (this.channel.isOpen()) {
                this.channel.close();
            }
        }
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值