netty基础_07.netty 编码和编码应用之protobuf

Google Protobuf

前面已经写了很多完整的demo代码了 后面只写关键代码,不再写完整的了

编码和解码的基本介绍

  1. 编写网络应用程序时,因为数据在网络中传输的都是二进制字节码数据,在发送数据时就需要编码,接收数据时就需要解码[示意图]
  2. codec(编解码器)的组成部分有两个:decoder(解码器)和 encoder(编码器)。encoder 负责把业务数据转换成字节码数据,decoder 负责把字节码数据转换成业务数据

在这里插入图片描述

Netty 本身的编码解码的机制和问题分析

  1. Netty 自身提供了一些 codec(编解码器)
  2. Netty 提供的编码器
    • StringEncoder:对字符串数据进行编码。
    • ObjectEncoder:对Java对象进行编码。
  3. Netty 提供的解码器
    • StringDecoder,对字符串数据进行解码
    • ObjectDecoder,对 Java 对象进行解码
  4. Netty 本身自带的 ObjectDecoderObjectEncoder 可以用来实现 POJO 对象或各种业务对象的编码和解码,底层使用的仍是Java序列化技术,而Java序列化技术本身效率就不高,存在如下问题
    • 无法跨语言
    • 序列化后的体积太大,是二进制编码的5倍多。
    • 序列化性能太低
  5. 引出新的解决方案[GoogleProtobuf]

Protobuf

  1. Protobuf 基本介绍和使用示意图
  2. ProtobufGoogle 发布的开源项目,全称 Google Protocol Buffers,是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化。它很适合做数据存储或 RPC [远程过程调用 remote procedure call ]数据交换格式。目前很多公司 从http + json 转向tcp + protobuf,效率会更高。
  3. 参考文档:https://developers.google.com/protocol-buffers/docs/proto 语言指南
  4. Protobuf 是以 message 的方式来管理数据的.
  5. 支持跨平台、跨语言,即[客户端和服务器端可以是不同的语言编写的](支持目前绝大多数语言,例如 C++C#Javapython 等)
  6. 高性能,高可靠性
  7. 使用 protobuf 编译器能自动生成代码,Protobuf 是将类的定义使用 .proto 文件进行描述。说明,在 idea 中编写 .proto 文件时,会自动提示是否下载 .ptoto 编写插件.可以让语法高亮。
  8. 然后通过 protoc.exe 编译器根据 .proto 自动生成 .java 文件
  9. protobuf 使用示意图

在这里插入图片描述

Protobuf 快速入门实例

编写程序,使用 Protobuf 完成如下功能

  1. 客户端可以发送一个 StudentPoJo 对象到服务器(通过 Protobuf 编码)
  2. 服务端能接收 StudentPoJo 对象,并显示信息(通过 Protobuf 解码)
	    <dependency>
            <groupId>com.google.protobuf</groupId>
            <artifactId>protobuf-java</artifactId>
            <version>3.6.1</version>
        </dependency>

Student.proto

syntax = "proto3"; //版本
option java_outer_classname = "StudentPOJO";//生成的外部类名,同时也是文件名
//protobuf 使用message 管理数据
message Student { //会在 StudentPOJO 外部类生成一个内部类 Student, 他是真正发送的POJO对象
    int32 id = 1; // Student 类中有 一个属性 名字为 id 类型为int32(protobuf类型) 1表示属性序号,不是值
    string name = 2;
}

编译
protoc.exe --java_out=.Student.proto
将生成的 StudentPOJO 放入到项目使用

这个方式比较不友好,我们使用maven插件的方式

生成的StudentPOJO代码太长就不贴在这里了

maven 插件

<properties>
        <grpc.version>1.6.1</grpc.version>
        <protobuf.version>3.3.0</protobuf.version>
    </properties>
    <dependencies>

        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.68.Final</version>
        </dependency>
        <dependency>
            <groupId>com.google.protobuf</groupId>
            <artifactId>protobuf-java</artifactId>
            <version>3.14.0</version>
        </dependency>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-protobuf</artifactId>
            <version>1.46.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-stub</artifactId>
            <version>1.46.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-netty</artifactId>
            <version>1.46.0</version>
        </dependency>

    </dependencies>
    <build>
        <extensions>
            <extension>
                <groupId>kr.motd.maven</groupId>
                <artifactId>os-maven-plugin</artifactId>
                <version>1.5.0.Final</version>
            </extension>
        </extensions>
        <plugins>
            <plugin>
                <groupId>org.xolstice.maven.plugins</groupId>
                <artifactId>protobuf-maven-plugin</artifactId>
                <version>0.5.0</version>
                <configuration>
                    <protocArtifact>com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier}</protocArtifact>
                    <pluginId>grpc-java</pluginId>
                    <pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}</pluginArtifact>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>compile-custom</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

NettyServer

//在pipeline加入ProtoBufDecoder
    //指定对哪种对象进行解码
pipeline.addLast("decoder", new ProtobufDecoder(StudentPOJO.Student.getDefaultInstance()));
pipeline.addLast(new NettyServerHandler());

NettyServerHandler

package com.netty.codec;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;

/**
 * @author wufagang
 * @description
 * @date 2022年06月05日 17:18
 */
public class NettyServerHandler extends SimpleChannelInboundHandler<StudentPOJO.Student> {
    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, StudentPOJO.Student student) throws Exception {

        System.out.println("客户端发送的数据 id=" + student.getId() + " 名字=" + student.getName());
    }
}


NettyClient

 //在pipeline中加入 ProtoBufEncoder
 pipeline.addLast("encoder", new ProtobufEncoder());
  pipeline.addLast(new NettyClientHandler()); //加入自己的处理器

NettyClientHandler

package com.netty.codec;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

/**
 * @author wufagang
 * @description
 * @date 2022年06月05日 17:24
 */
public class NettyClientHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        StudentPOJO.Student student = StudentPOJO.Student.newBuilder().setId(4).setName("智多星 吴用").build();
        //Teacher , Member ,Message
        ctx.writeAndFlush(student);
    }
}


在这里插入图片描述

功能实现了,但是我们发现就适合demo案例,不适合开发使用,固定的类型一个studen
如果传递多个不同的类型

Protobuf 快速入门实例 2

  1. 编写程序,使用 Protobuf 完成如下功能
  2. 客户端可以随机发送 StudentPoJo / WorkerPoJo 对象到服务器(通过 Protobuf 编码)
  3. 服务端能接收 StudentPoJo / WorkerPoJo 对象(需要判断是哪种类型),并显示信息(通过 Protobuf 解码)

proto

syntax = "proto3";
option optimize_for = SPEED; // 加快解析
option java_package="com.atguigu.netty.codec2";   //指定生成到哪个包下
option java_outer_classname="MyDataInfo"; // 外部类名, 文件名


/*
1.protobuf 可以使用message 管理其他的message。最终决定使用哪一个message作为传输对象
2.假设你某个项目需要传输20个对象,你不可能新建20个proto文件吧。此时你就可以
在一个文件里定义20个message,最后再用一个总的message(比方说这里的MyMessage)
来决定在实际传输时真正需要传输哪一个对象
3.因为你实际传输的时候大部分情况传输的都是一个对象,所以下面用oneof进行了限制
4.是否可以传多个对象呢?我个人认为是可以的,比如可以通过map(目前我也不太了解proto的语法)
 */
message MyMessage {

    //定义一个枚举类型,DataType如果是0则表示一个Student对象实例,DataType这个名称自定义
    enum DataType {
        StudentType = 0; //在proto3 要求enum的编号从0开始
        WorkerType = 1;
    }

    //用data_type 来标识传的是哪一个枚举类型,这里才真正开始定义MyMessage的数据类型
    DataType data_type = 1;  //所有后面的数字都只是编号而已

    /*
    1.oneof关键字 表示每次枚举类型进行传输时,限制最多只能传输一个对象。
    dataBody名称也是自定义的
    2.为什么这里的序号是2呢?因为上面DataType data_type = 1  占了第一个序号了
    3.MyMessage里真正出现的类型只有两个
      ①DataType类型
      ②Student类型或者Worker类型(这两个在真正传输的时候只会有一个出现)
    */
    oneof dataBody {
        Student student = 2;  //注意这后面的数字也都只是编号而已
        Worker worker = 3;
    }


}


message Student {
    int32 id = 1;//Student类的属性
    string name = 2; //
}
message Worker {
    string name=1;
    int32 age=2;
}

NettyServer

 pipeline.addLast("decoder", new ProtobufDecoder(MyDataInfo.MyMessage.getDefaultInstance()));
pipeline.addLast(new NettyServerHandler());

NettyServerHandler

package com.netty.codec2;

import com.netty.codec.StudentPOJO;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;

/**
 * @author wufagang
 * @description
 * @date 2022年06月05日 17:18
 */
public class NettyServerHandler extends SimpleChannelInboundHandler<MyDataInfo.MyMessage> {
    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, MyDataInfo.MyMessage msg) throws Exception {
        MyDataInfo.MyMessage.DataType dataType = msg.getDataType();
        if(dataType == MyDataInfo.MyMessage.DataType.StudentType) {
            MyDataInfo.Student1 student = msg.getStudent();
            System.out.println("学生id=" + student.getId() + " 学生名字=" + student.getName());

        } else if(dataType == MyDataInfo.MyMessage.DataType.WorkerType) {
            MyDataInfo.Worker worker = msg.getWorker();
            System.out.println("工人的名字=" + worker.getName() + " 年龄=" + worker.getAge());
        } else {
            System.out.println("传输的类型不正确");
        }
    }
}


NettyClient

pipeline.addLast("encoder", new ProtobufEncoder());
                            pipeline.addLast(new NettyClientHandler()); //加入自己的处理器

NettyClientHandler

package com.netty.codec2;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

import java.util.Random;

/**
 * @author wufagang
 * @description
 * @date 2022年06月05日 17:24
 */
public class NettyClientHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        //随机的发送Student 或者 Workder 对象
        int random = new Random().nextInt(1);
        MyDataInfo.MyMessage myMessage = null;

        if(0 == random) { //发送Student 对象

            myMessage = MyDataInfo.MyMessage.newBuilder().setDataType(MyDataInfo.MyMessage.DataType.StudentType).setStudent(MyDataInfo.Student1.newBuilder().setId(5).setName("玉麒麟 卢俊义").build()).build();
        } else { // 发送一个Worker 对象

            myMessage = MyDataInfo.MyMessage.newBuilder().setDataType(MyDataInfo.MyMessage.DataType.WorkerType).setWorker(MyDataInfo.Worker.newBuilder().setAge(20).setName("老李").build()).build();
        }

        ctx.writeAndFlush(myMessage);
    }
}


在这里插入图片描述

dubbo 序列化如何实现的??? jd rpc框架jsf 又是如何实现的???

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
对于使用Netty 4.1和Protobuf的WebSocket编码器,你可以按照以下步骤进行设置: 1. 首先,确保你已经添加了NettyProtobuf的依赖到你的项目中。 2. 创建一个WebSocket编码器类,该类将负责将Protobuf消息编码为WebSocket帧。下面是一个示例代码: ```java import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToByteEncoder; public class ProtobufWebSocketEncoder extends MessageToByteEncoder<MessageLite> { @Override protected void encode(ChannelHandlerContext ctx, MessageLite msg, ByteBuf out) throws Exception { byte[] bytes = msg.toByteArray(); out.writeBytes(bytes); } } ``` 3. 在你的Netty初始化代码中,添加WebSocket编码器到你的ChannelPipeline中。下面是一个示例代码: ```java import io.netty.channel.ChannelInitializer; import io.netty.channel.socket.SocketChannel; import io.netty.handler.codec.http.HttpObjectAggregator; import io.netty.handler.codec.http.HttpServerCodec; import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler; public class WebSocketServerInitializer extends ChannelInitializer<SocketChannel> { @Override protected void initChannel(SocketChannel ch) throws Exception { ch.pipeline() .addLast(new HttpServerCodec()) .addLast(new HttpObjectAggregator(65536)) .addLast(new WebSocketServerProtocolHandler("/websocket")) .addLast(new ProtobufWebSocketEncoder()) .addLast(new YourCustomWebSocketHandler()); } } ``` 在上面的代码中,`YourCustomWebSocketHandler`是你自己实现的处理WebSocket消息的处理器。 4. 最后,在你的Netty服务器启动代码中,绑定正确的端口并启动服务器。下面是一个示例代码: ```java import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; public class WebSocketServer { public static void main(String[] args) throws InterruptedException { EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .childHandler(new WebSocketServerInitializer()); ChannelFuture f = b.bind(8080).sync(); f.channel().closeFuture().sync(); } finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } } ``` 确保将端口号8080更改为你实际使用的端口号。 以上就是使用Netty 4.1和Protobuf的WebSocket编码器的基本设置。请根据你的实际需求进行适当的修改和扩展。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值