Java 从零开始手写 RPC -序列化

开发 后端
序列化 (Serialization)是将对象的状态信息转换为可以存储或传输的形式的过程。在序列化期间,对象将其当前状态写入到临时或持久性存储区。

[[429947]]

前面几节我们实现了最基础的客户端调用服务端,这一节来学习一下通讯中的对象序列化。

为什么需要序列化

netty 底层都是基于 ByteBuf 进行通讯的。

前面我们通过编码器/解码器专门为计算的入参/出参进行处理,这样方便我们直接使用 pojo。

但是有一个问题,如果想把我们的项目抽象为框架,那就需要为所有的对象编写编码器/解码器。

显然,直接通过每一个对象写一对的方式是不现实的,而且用户如何使用,也是未知的。

序列化的方式

基于字节的实现,性能好,可读性不高。

基于字符串的实现,比如 json 序列化,可读性好,性能相对较差。

ps: 可以根据个人还好选择,相关序列化可参考下文,此处不做展开。

json 序列化框架简介[1]

实现思路

可以将我们的 Pojo 全部转化为 byte,然后 Byte 转换为 ByteBuf 即可。

反之亦然。

代码实现

maven

引入序列化包:

<dependency> 
    <groupId>com.github.houbb</groupId> 
    <artifactId>json</artifactId> 
    <version>0.1.1</version> 
</dependency> 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

服务端

核心

服务端的代码可以大大简化:

serverBootstrap.group(workerGroup, bossGroup) 
    .channel(NioServerSocketChannel.class) 
    // 打印日志 
    .handler(new LoggingHandler(LogLevel.INFO)) 
    .childHandler(new ChannelInitializer<Channel>() { 
        @Override 
        protected void initChannel(Channel ch) throws Exception { 
            ch.pipeline() 
                    .addLast(new RpcServerHandler()); 
        } 
    }) 
    // 这个参数影响的是还没有被accept 取出的连接 
    .option(ChannelOption.SO_BACKLOG, 128) 
    // 这个参数只是过一段时间内客户端没有响应,服务端会发送一个 ack 包,以判断客户端是否还活着。 
    .childOption(ChannelOption.SO_KEEPALIVE, true); 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.

这里只需要一个实现类即可。

RpcServerHandler

服务端的序列化/反序列化调整为直接使用 JsonBs 实现。

package com.github.houbb.rpc.server.handler; 
 
 
import com.github.houbb.json.bs.JsonBs; 
import com.github.houbb.log.integration.core.Log; 
import com.github.houbb.log.integration.core.LogFactory; 
import com.github.houbb.rpc.common.model.CalculateRequest; 
import com.github.houbb.rpc.common.model.CalculateResponse; 
import com.github.houbb.rpc.common.service.Calculator; 
import com.github.houbb.rpc.server.service.CalculatorService; 
 
 
import io.netty.buffer.ByteBuf; 
import io.netty.buffer.Unpooled; 
import io.netty.channel.ChannelHandlerContext; 
import io.netty.channel.SimpleChannelInboundHandler; 
 
 
/** 
 * @author binbin.hou 
 * @since 0.0.1 
 */ 
public class RpcServerHandler extends SimpleChannelInboundHandler { 
 
 
    private static final Log log = LogFactory.getLog(RpcServerHandler.class); 
 
 
    @Override 
    public void channelActive(ChannelHandlerContext ctx) throws Exception { 
        final String id = ctx.channel().id().asLongText(); 
        log.info("[Server] channel {} connected " + id); 
    } 
 
 
    @Override 
    protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception { 
        final String id = ctx.channel().id().asLongText(); 
 
 
        ByteBuf byteBuf = (ByteBuf)msg; 
        byte[] bytes = new byte[byteBuf.readableBytes()]; 
        byteBuf.readBytes(bytes); 
        CalculateRequest request = JsonBs.deserializeBytes(bytes, CalculateRequest.class); 
        log.info("[Server] receive channel {} request: {} from ", id, request); 
 
 
        Calculator calculator = new CalculatorService(); 
        CalculateResponse response = calculator.sum(request); 
 
 
        // 回写到 client 端 
        byte[] responseBytes = JsonBs.serializeBytes(response); 
        ByteBuf responseBuffer = Unpooled.copiedBuffer(responseBytes); 
        ctx.writeAndFlush(responseBuffer); 
        log.info("[Server] channel {} response {}", id, response); 
    } 
 
 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.

客户端

核心

客户端可以简化如下:

channelFuture = bootstrap.group(workerGroup) 
    .channel(NioSocketChannel.class) 
    .option(ChannelOption.SO_KEEPALIVE, true
    .handler(new ChannelInitializer<Channel>(){ 
        @Override 
        protected void initChannel(Channel ch) throws Exception { 
            channelHandler = new RpcClientHandler(); 
            ch.pipeline() 
                    .addLast(new LoggingHandler(LogLevel.INFO)) 
                    .addLast(channelHandler); 
        } 
    }) 
    .connect(RpcConstant.ADDRESS, port) 
    .syncUninterruptibly(); 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.

RpcClientHandler

客户端的序列化/反序列化调整为直接使用 JsonBs 实现。

package com.github.houbb.rpc.client.handler; 
 
 
import com.github.houbb.json.bs.JsonBs; 
import com.github.houbb.log.integration.core.Log; 
import com.github.houbb.log.integration.core.LogFactory; 
import com.github.houbb.rpc.client.core.RpcClient; 
import com.github.houbb.rpc.common.model.CalculateResponse; 
 
 
import io.netty.buffer.ByteBuf; 
import io.netty.channel.ChannelHandlerContext; 
import io.netty.channel.SimpleChannelInboundHandler; 
 
 
/** 
 * <p> 客户端处理类 </p> 
 * 
 * <pre> Created: 2019/10/16 11:30 下午  </pre> 
 * <pre> Project: rpc  </pre> 
 * 
 * @author houbinbin 
 * @since 0.0.2 
 */ 
public class RpcClientHandler extends SimpleChannelInboundHandler { 
 
 
    private static final Log log = LogFactory.getLog(RpcClient.class); 
 
 
    /** 
     * 响应信息 
     * @since 0.0.4 
     */ 
    private CalculateResponse response; 
 
 
    @Override 
    protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception { 
        ByteBuf byteBuf = (ByteBuf)msg; 
        byte[] bytes = new byte[byteBuf.readableBytes()]; 
        byteBuf.readBytes(bytes); 
 
 
        this.response = JsonBs.deserializeBytes(bytes, CalculateResponse.class); 
        log.info("[Client] response is :{}", response); 
    } 
 
 
    @Override 
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { 
        // 每次用完要关闭,不然拿不到response,我也不知道为啥(目测得了解netty才行) 
        // 个人理解:如果不关闭,则永远会被阻塞。 
        ctx.flush(); 
        ctx.close(); 
    } 
 
 
    public CalculateResponse getResponse() { 
        return response; 
    } 
 
 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.

 

责任编辑:姜华 来源: 今日头条
相关推荐

2021-10-13 08:21:52

Java websocket Java 基础

2021-10-29 08:07:30

Java timeout Java 基础

2021-10-19 08:58:48

Java 语言 Java 基础

2021-10-21 08:21:10

Java Reflect Java 基础

2019-09-23 19:30:27

reduxreact.js前端

2021-10-14 08:39:17

Java Netty Java 基础

2017-02-10 09:30:33

数据化运营流量

2020-07-02 15:32:23

Kubernetes容器架构

2024-12-06 17:02:26

2018-04-18 07:01:59

Docker容器虚拟机

2019-01-18 12:39:45

云计算PaaS公有云

2015-11-17 16:11:07

Code Review

2018-03-19 10:20:23

Java序列化反序列化

2022-08-06 08:41:18

序列化反序列化Hessian

2021-10-27 08:10:15

Java 客户端 Java 基础

2023-03-06 07:28:57

RPC框架序列化

2013-03-11 13:55:03

JavaJSON

2009-06-14 22:01:27

Java对象序列化反序列化

2024-05-15 14:29:45

2018-09-14 17:16:22

云计算软件计算机网络
点赞
收藏

51CTO技术栈公众号