Day475.GoogleProtobuf&Netty编解码器和handler 的调用机制 -netty

Posted 阿昌喜欢吃黄桃

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Day475.GoogleProtobuf&Netty编解码器和handler 的调用机制 -netty相关的知识,希望对你有一定的参考价值。

Google Protobuf

一、编码和解码的基本介绍

  1. 编写网络应用程序时,因为数据在网络中传输的都是二进制字节码数据,

发送数据时就需要编码

接收数据时就需要解码

  1. codec(编解码器) 的组成部分有两个:decoder(解码器)和 encoder(编码器)。encoder 负责把业务数据转换成字节码数据,decoder 负责把字节码数据转换成业务数据


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

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

引出 新的解决方案 [Google的Protobuf]


三、Protobuf

1、Protobuf 基本介绍

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

2、protobuf 使用示意图


四、Protobuf 快速入门实例

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

  1. 客户端可以发送一个 Student PoJo 对象到服务器 (通过 Protobuf 编码)
  2. 服务端能接收 Student PoJo 对象,并显示信息(通过 Protobuf 解码)
  • 代码

引入maven依赖

<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类中有一个属性,类型为int32,属性名为id,属性的序号为1(不代表值)。
  string name = 2;

编译

protoc.exe --java_out=. Student.proto

将生成的 StudentPOJO 放入到项目使用

具体代码地址: https://github.com/qq995931576/netty/tree/master/netty-demo/src/main/java/com/achang/netty/codec


五、Protobuf 快速入门实例 2

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

Student.proto文件

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

//protobuf可以使用message来管理其他message
message MyMessage
  //定义一个枚举
  enum DataType
    StudentType=0;//在proto3 要求enum的编号从0开始
    WorkerType=1;
  
  //用data_type来标识传的是哪个枚举类型
  DataType data_type=1;

  //表示每次枚举类型最多只能出现其中的一个,节省空间
  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;


编译的方式跟上面一样

具体代码地址:https://github.com/qq995931576/netty/tree/master/netty-demo/src/main/java/com/achang/netty/codec2


Netty 编解码器和 handler 的调用机制

一、基本说明

  1. netty 的组件设计:Netty 的主要组件有
    1. Channel
    2. EventLoop
    3. ChannelFuture
    4. ChannelHandler
    5. ChannelPipe 等
  2. ChannelHandler 充当了处理入站出站数据的应用程序逻辑的容器。
  3. 例如,实现 ChannelInboundHandler 接口(或ChannelInboundHandlerAdapter),你就可以接收入站事件和数据,这些数据会被业务逻辑处理。当要给客户端发 送 响 应 时 , 也 可 以 从ChannelInboundHandler 冲 刷 数 据 。
  4. 业 务 逻 辑 通 常 写 在 一 个 或 者 多 个ChannelInboundHandler 中。
  5. ChannelOutboundHandler 原理一样,只不过它是用来处理出站数据的
  6. ChannelPipeline 提供了 ChannelHandler 链的容器
  7. 客户端应用程序为例,如果事件的运动方向是从客户端到服务端的,那么我们称这些事件为出站的即客户端发送给服务端的数据会通过 pipeline 中的一系列ChannelOutboundHandler,并被这些 Handler 处理,反之则称为入站

二、编码解码器

  1. 当 Netty 发送或者接受一个消息的时候,就将会发生一次数据转换
  1. 入站消息会被解码:从字节转换为另一种格式(比如 java 对象);
  1. 如果是出站消息,它会被编码成字节。

  1. Netty 提供一系列实用的编解码器,他们都实现了 ChannelInboundHadnler 或者ChannelOutboundHandler 接口。
  1. 在这些类中,channelRead 方法已经被重写了。
  2. 以入站为例,对于每个从入站 Channel 读取的消息,这个方法会被调用。(解码
  3. 随后,它将调用由解码器所提供的 decode()方法进行解码,并将已经解码的字节转发给 ChannelPipeline中的下一个 ChannelInboundHandler

三、解码器-ByteToMessageDecoder

  • 关系继承图

  • 由于不可能知道远程节点是否会一次性发送一个完整的信息,tcp 有可能出现粘包拆包的问题,这个类会对入站数据进行缓冲,直到它准备好被处理.

  • 一个关于 ByteToMessageDecoder 实例分析

四、Netty 的 handler 链的调用机制

  • 实例要求:

    使用自定义的编码器和解码器来说明 Netty 的 handler 调用机制
    客户端发送 long -> 服务器
    服务端发送 long -> 客户端

代码地址:https://github.com/qq995931576/netty/tree/master/netty-demo/src/main/java/com/achang/netty/InboundandoutboundHandler

  • 结论
    • 不论解码器 handler 还是编码器 handler 即接收的消息类型必须与待处理的消息类型一致,否则该 handler 不会被执行
    • 在解码器进行数据解码时,需要判断缓存区(ByteBuf)的数据是否足够,否则接收到的结果会期望结果可能不一致

五、解码器-ReplayingDecoder

  1. public abstract class ReplayingDecoder< S > extends ByteToMessageDecoder
  2. ReplayingDecoder 扩展了 ByteToMessageDecoder 类,使用这个类, 我们不必调用 readableBytes() 方法。
  3. 参数 S指定了用户状态管理的类型,其中 Void 代表不需要状态管理

应用实例

使用 ReplayingDecoder 编写解码器,对前面的案例进行简化

public class MyByteToLongDecoder2 extends ReplayingDecoder<Void> 
    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception 
        System.out.println("MyByteToLongDecoder2 被调用");
        //在 ReplayingDecoder 不需要判断数据是否足够读取,内部会进行处理判断
        out.add(in.readLong());
    

ReplayingDecoder 使用方便,但它也有一些局限性

  • 不 是 所 有 的 ByteBuf 操 作 都 被 支 持 , 如 果 调 用 了 一 个 不 被 支 持 的 方 法 , 将 会 抛 出 一 个UnsupportedOperationException
  • ReplayingDecoder 在某些情况下可能稍慢于 ByteToMessageDecoder,例如网络缓慢并且消息格式复杂时,消息会被拆成了多个碎片,速度变慢

六、其它编解码器

1、其它解码器

  1. LineBasedFrameDecoder:这个类在 Netty 内部也有使用,它使用行尾控制字符(\\n 或者\\r\\n)作为分隔符来解析数据。
  2. DelimiterBasedFrameDecoder:使用自定义的特殊字符作为消息的分隔符。
  3. HttpObjectDecoder:一个 HTTP 数据的解码器
  4. LengthFieldBasedFrameDecoder:通过指定长度来标识整包消息,这样就可以自动的处理黏包和半包消息。


七、Log4j 整合到 Netty

  • 在 Maven 中添加对 Log4j 的依赖 在 pom.xml
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.25</version>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.7.25</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-simple</artifactId>
    <version>1.7.25</version>
    <scope>test</scope>
</dependency>
  • 配置 Log4j , 在 resources/log4j.properties
log4j.rootLogger=DEBUG, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=[%p] %C1 - %m%n
  • 测试

以上是关于Day475.GoogleProtobuf&Netty编解码器和handler 的调用机制 -netty的主要内容,如果未能解决你的问题,请参考以下文章

[Android开发学习] day07 &amp; day08

.~day06-07_FlinkTable&SQL

Noip 2016 Day 1 & Day 2

按日子来干活——第一个Blog Day&Happy Day

DP&图论 DAY 6 下午 考试

NOIp2016 Day1&Day2 解题报告