netty4 protobuf3多类型传输实现

本文介绍了在后台服务中如何优雅地处理多种protobuf协议的传输。通过定义一个`Frame`类作为协议描述,将不同类型的protobuf消息序列化后放入payload字段。Netty提供的protobuf解码器和编码器用于基本的序列化和反序列化,而自定义的二次解码器和编码器负责进一步处理。此外,展示了服务器端的消息接收、解包、业务处理逻辑以及客户端的相应代码实现。
摘要由CSDN通过智能技术生成

做后台服务经常有这样的流程:

如何优雅的完成这个过程呢?

首先要解决的是如何在netty+protobuf中传输多个protobuf协议,这里采取的方案是使用一个类来做为描述协议的方案,也就是需要二次解码的方案,IDL文件如下:

syntax = "proto3";
option java_package = "com.nonpool.proto";
// 生成的时候设置java_multiple_files = true可以让类分开生成,
//更清晰些,也更方便后面利用反射来获取这些类
option java_multiple_files = true;
//Frame为描述协议
message Frame {
    // messageName约定为要发送的message的类全名
    string messageName = 1;
    // 所有消息在发送的时候都序列化成byte数组写入Frame的payload
    bytes payload = 15;
}
message TextMessage {
    string text = 1;
}

message SpeakMessage {
    string text = 1;
    // ID
    int32 id = 2;
}

 

Frame为描述协议,所有消息在发送的时候都序列化成byte数组写入Frame的payload,messageName约定为要发送的message的类名,生成的时候设置java_multiple_files = true可以让类分开生成,更清晰些,也更方便后面利用反射来获取这些类.

生成好了protobuf,我们解包的过程就应该是这样的:

其中protobuf的序列化和反序列化netty已经为我们编写好了对应的解码/编码器,直接调用即可,我们只需要编写二次解码/编码器即可: 

二次编码/解码器代码如下:

package com.tcp.manyproto;

import com.google.protobuf.MessageLite;
import com.nonpool.proto.Frame;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageCodec;

import java.util.List;

/**
 * @Description 二次Protobuf解码器 in:把playload里面的字节数组转换成对象
 * out:把对需要发送的对象包装成Frame
 *  *
 * @Date 2019/10/22 13:23
 * @Author zsj
 * 二次解码/编码器
 */
public class SecondProtobufCodec extends MessageToMessageCodec<Frame, MessageLite> {


    @Override
    protected void encode(ChannelHandlerContext channelHandlerContext, MessageLite messageLite, List<Object> list) throws Exception {
        //messageName 为全类名 方便从jvm中加载类
        list.add(Frame.newBuilder()
                .setMessageName(messageLite.getClass().getName())
                .setPayload(messageLite.toByteString())
                .build());
    }

    @Override
    protected void decode(ChannelHandlerContext channelHandlerContext, Frame frame, List<Object> list) throws Exception {
       // 根据Frame类解析出其中的body
        list.add(ParseFromUtil.parse(frame));
    }


}

解析工具类代码如下

 

package com.tcp.manyproto;
import com.google.protobuf.ByteString;
import com.google.protobuf.MessageLite;
import com.nonpool.proto.Frame;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;


/**
 * @author nonpool
 * @version 1.0
 * @since 2018/1/29
 */
public  class ParseFromUtil {
    /**
     * 根据Frame类解析出其中的body
     *根据全类名加载类
     * @param msg
     * @return
     */
    public static MessageLite parse(Frame msg) throws InvocationTargetException, IllegalAccessException {
        String className = msg.getMessageName();
        ByteString body = msg.getPayload();
        Class clazz = null;
        try {
            // 根据全类名加载类
           clazz = Class.forName(className);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException("unknown Message type :" + className);
        }
        Method method = null;
        try {
            //执行解析方法
            method = clazz.getMethod("parseFrom", ByteString.class);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
            throw new RuntimeException("unknown Message type :" + className);
        }

        return (MessageLite) method.invoke(null, body);
    }
}

 

服务器接收到消息的业务处理逻辑

package com.tcp.manyproto;

import com.google.protobuf.MessageLite;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHa
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

非ban必选

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值