10.netty客户端与服务器使用protobuf传输报文

【README】

本文总结自B站《尚硅谷-netty》,很不错; 内容如下:

  •  netty的编码器与解码器;
  • netty客户端与服务器通过 protobuf  传输报文的开发方式;
  • 文末po出了所有代码;

【1】netty的编码器与解码器 codec

1)编解码器应用场景

  • 编写网络应用程序时,因为数据在网络中传输的都是二进制字节码数据,在发送数据时就需要编码,接收数据时就需要解码 ;如下图

                                                  【图1】 编码与解码流程图

2) codec(编解码器) 的组成部分有两个:decoder(解码器)和encoder(编码器)。

  • encoder 负责把业务数据(如pojo对象)转换成字节码数据;
  • decoder 负责把字节码数据转换成业务数据(如pojo对象);

补充: 编解码器对应的英文“codec”(compress和decompress简化而成的合成词语 ),from wikipedia;

3)java序列化的问题(所以才引入了 protobuf):

  • Netty 本身自带的 ObjectDecoder 和 ObjectEncoder 可以用来实现POJO对象或各种业务对象的编码和解码,底层使用的仍是 Java 序列化技术 , 而Java 序列化技术本身效率就不高,存在如下问题
    •  无法跨语言;
    •  序列化后的体积太大,是二进制编码的 5 倍多;
    •  序列化性能太低;

引入新的解决方案: google 的 Protobuf


【2】Protobuf

【2.1】概述

0) intro2 protobuf, refer2 https://developers.google.com/protocol-buffers ;

1)ProtoBuf定义:

  • 是 Protocol Buffer 的缩写,即协议缓冲区;它是google实现的一种开源跨平台的序列化资料结构的协议

2)ProtoBuf 是google提出的结构数据序列化方法,可简单类比于 XML,其具有以下特点:

  • ① 语言无关、平台无关。即 ProtoBuf 支持 Java、C++、Python 等多种语言,支持多个平台;
  • ② 高效。即比 XML 更小(3 ~ 10倍)、更快(20 ~ 100倍)、更为简单;
  • ③ 扩展性、兼容性好。你可以更新数据结构,而不影响和破坏原有的旧程序;

【小结】

  • 说的直接点, protobuf序列化性能 优于  java序列化
  • 因为protobuf 很适合做数据存储或 RPC[远程过程调用]数据交换格式,所以目前有些公司把远程调用的报文传输方式从 http+json 修改为 tcp+protobuf

3)基于protobuf的编码与解码流程

【图解】 使用protobuf序列化 java对象的步骤;

  • 步骤1:编写 proto 文件;
  • 步骤2:使用 protoc.exe 编译把 proto文件编译为 java文件(pojo文件);
  • 步骤3:创建pojo对象并赋值,发送到服务器;

【补充】关于 protobuf的更多介绍,refer2  https://www.jianshu.com/p/a24c88c0526a   


 【2.2】 netty客户端服务器通过protobuf传输报文

说明:

  • 为了清晰展示 protobuf 的开发方式,下文使用了截图;但 netty通过 protobuf 传输报文的所有代码,会在文末po出;

【步骤1】 引入 protobuf 依赖

<dependency><groupId>com.google.protobuf</groupId>    <artifactId>protobuf-java</artifactId><version>3.6.1</version>
</dependency>

【步骤2】编写 proto 文件 

Student.proto

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

【步骤3】编译 proto文件到 POJO javabean 文件 

编译命令:

protoc.exe --java_out=. Student.proto

【补充】

  • protoc.exe 编译器到这个地方 Central Repository: com/google/protobuf/protoc/3.6.1 下载,并录入到path(系统环境变量)
  • 生成的 javabean文件为:  StudentPOJO.java

【步骤4】 客户端发送 Student 对象到服务器

1)客户端添加 protobuf 编码器 ProtobufEncoder;

2) 客户端处理器

  • 根据 protobuf编译成的 POJO类文件,创建对应javabean对象,并发送到服务器;


【步骤5】 服务器解码protobuf字节码

添加 protobuf解码器 ProtobufDecoder

netty服务器 处理器,接收POJO类对象,pojo类是由protobuf编译而成;

 【演示结果】netty客户端与服务器使用 probobuf传输报文


【3】netty 使用 protobuf传输报文的客户端与服务器代码(源代码)

【3.1】netty服务器

1)netty服务器 (注意它添加的protobuf解码器 ProtobufDecoder

public class ProtobufNettyServer76 {public static void main(String[] args) throws InterruptedException {// 创建 BossGroup 和 WorkerGroup// 1. 创建2个线程组 bossGroup, workerGroup// 2 bossGroup 仅处理连接请求; 真正的业务逻辑,交给workerGroup完成;// 3 两个线程组都是无限循环// 4 bossGroup 和 workerGroup 含有的子线程(NIOEventLoop)个数// 默认是 cpu核数 * 2EventLoopGroup boosGroup = new NioEventLoopGroup();EventLoopGroup workerGruop = new NioEventLoopGroup();try {// 创建服务器端的启动对象, 配置参数ServerBootstrap bootstrap = new ServerBootstrap();bootstrap.group(boosGroup, workerGruop) // 设置2个线程组.channel(NioServerSocketChannel.class) // 使用NIOSocketChannel 作为服务器的通道实现.option(ChannelOption.SO_BACKLOG, 128) // 设置线程队列等待连接的个数.childOption(ChannelOption.SO_KEEPALIVE, true) // 设置保持活动连接状态.childHandler(new ChannelInitializer<SocketChannel>() { // 创建一个通道初始化对象// 给 pipeline 设置处理器@Overrideprotected void initChannel(SocketChannel socketChannel) throws Exception {ChannelPipeline pipeline = socketChannel.pipeline();// 添加 protobuf 解码器, 指定对哪种对象进行解码pipeline.addLast("decoder", new ProtobufDecoder(StudentPOJO.Student.getDefaultInstance()));// 添加业务处理器
//                            pipeline.addLast(new ProtobufNettyServerHandler());pipeline.addLast(new ProtobufNettyServerHandler2());}});  // 给我们的workerGroup 的 EventLoop 对应的管道设置处理器System.out.println("... server is ready.");// 启动服务器, 绑定端口并同步处理 ,生成一个 ChannelFuture对象ChannelFuture channelFuture = bootstrap.bind(6668).sync();channelFuture.addListener((future1) -> System.out.println("Finish binding"));// 给 channelFuture 注册监听器,监听我们关心的事件channelFuture.addListener(future -> {if (future.isSuccess()) {System.out.println("监听端口6668 成功");}else {System.out.println("监听端口6668 失败");}});// 对关闭通道进行监听channelFuture.channel().closeFuture().sync();} finally {// 优雅关闭boosGroup.shutdownGracefully();workerGruop.shutdownGracefully();}}
}

2)netty服务器处理器(channelRead0方法中的 StudentPOJO.Student javabean就是由 protobuf编译生成的java类

public class ProtobufNettyServerHandler2 extends SimpleChannelInboundHandler<StudentPOJO.Student> {// 读写数据事件(读取客户端发送的消息)// 1. ChannelHandlerContext ctx: 上下文信息,包括管道pipeline,通道channel,地址// 2. Object msg: 客户端发送的数据,默认是 Object@Overridepublic void channelRead0(ChannelHandlerContext ctx, StudentPOJO.Student student) throws Exception {System.out.println("SimpleChannelInboundHandler子类handler-客户端发送的数据 id=" + student.getId() + " name=" + student.getName());}// 数据读取完毕,回复客户端@Overridepublic void channelReadComplete(ChannelHandlerContext ctx) throws Exception {// writeAndFlush 是 write + flush ;把数据写入到缓冲,并刷新ChannelFuture channelFuture = ctx.writeAndFlush(Unpooled.copiedBuffer("hello, 客户端", StandardCharsets.UTF_8));channelFuture.addListener(future -> System.out.println("回复成功"));}// 处理异常,关闭通道@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {ctx.channel().close();}
}

【3.2】netty客户端

1)netty客户端 (注意其添加的 Protobuf编码器 ProtobufEncoder

public class ProtobufNettyClient76 {public static void main(String[] args) throws InterruptedException {// 客户端需要一个事件循环组EventLoopGroup eventLoopGroup = new NioEventLoopGroup();try {// 创建客户端启动对象, 注意是 BootStrapBootstrap bootstrap = new Bootstrap();// 设置相关参数bootstrap.group(eventLoopGroup) // 设置线程组.channel(NioSocketChannel.class) // 设置客户端通道实现类(反射).handler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel socketChannel) throws Exception {ChannelPipeline pipeline = socketChannel.pipeline();// 添加 ProtobufEncoder 编码器pipeline.addLast(new ProtobufEncoder());// 添加业务处理器pipeline.addLast(new ProtobufNettyClientHandler()); // 加入自己的处理器}});System.out.println("client is ok");// 启动客户端去连接服务器// ChannelFuture, 设计到netty的异步模型ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 6668).sync();// 给关闭通道进行监听channelFuture.channel().closeFuture().sync();} finally {eventLoopGroup.shutdownGracefully();}}
}

2)netty客户端处理器 (注意 channelActive方法中的 StudentPOJO.Student,是由Protobuf编译生成的POJO类) 

public class ProtobufNettyClientHandler extends ChannelInboundHandlerAdapter {// 当通道就绪就会触发该方法@Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception {// 发送一个 StudentPOJO 对象到服务器StudentPOJO.Student student = StudentPOJO.Student.newBuilder().setId(4).setName("zhangsan").build();ctx.writeAndFlush(student);}// 当通道有读取事件时,会触发@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {ByteBuf byteBuf = (ByteBuf) msg;System.out.println("服务器回复消息:" + byteBuf.toString(StandardCharsets.UTF_8) + ", 当前时间=" + DateUtils.getNowTimestamp());System.out.println("服务器地址:" + ctx.channel().remoteAddress());}// 捕获异常@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {cause.printStackTrace();ctx.close();}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/329367.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Oracle入门(十四.5)之识别数据类型

一、PL / SQL数据类型 数据类型指定存储格式&#xff0c;约束和有效的值范围。 PL / SQL支持五类数据类型&#xff1a;二、标量数据类型&#xff08;Scalar&#xff09; &#xff08;1&#xff09;标量数据类型•保持一个值 •没有内部组件 •可以分为四类&#xff1a; - 性格…

TFS2015的CI集成

这篇应该是这个系列的最后一篇了 配置生成代理 配置dotnet cli环境 这步&#xff0c;需要在生成代理的机器上配置cli环境&#xff0c;与本地配置方法一致&#xff0c;可以自行Google 下载及参考地址&#xff1a; https://www.microsoft.com/net/core#windows 配置环境变量 在生…

pivot sqlserver 条件_行转列之SQLSERVERPIVOT与用法详解

在数据库操作中&#xff0c;有些时候我们遇到需要实现“行转列”的需求&#xff0c;例如一下的表为某店铺的一周收入情况表&#xff1a;WEEK_INCOME(WEEK VARCHAR(10),INCOME DECIMAL)我们先插入一些模拟数据&#xff1a;INSERT INTO WEEK_INCOMESELECT 星期一,1000UNION ALLSE…

Oracle入门(十四.6)之使用标量数据类型

一、声明字符变量 字符数据类型包括CHAR&#xff0c;VARCHAR2和LONG。 DECLAREv_emp_job VARCHAR2(9);v_order_no VARCHAR2(6);v_product_id VARCHAR2(10);v_rpt_body_part LONG; …二、声明数字变量 数字数据类型包括NUMBER&#xff0c;PLS_INTEGER&#xff0c;BINARY_INTEGER…

11.netty入站与出站(处理器链调用机制)

【README】 1.本文源代码总结自 B站《netty-尚硅谷》&#xff1b;2.本文部分内容总结自 https://www.baeldung.com/netty3.本文主要介绍了通道管道中多个入栈处理器与多个出站处理器如何执行&#xff1f;并用代码演示执行顺序&#xff1b; 补充&#xff1a;文末附带了 log4j整…

1688推广工具_全面了解1688数字营销

全面了解1688数字营销什么是数字营销&#xff1f;在数字营销中&#xff0c;有一款很重要的工具-一键推广。在我们的日常经营和大促中&#xff0c;通过一键站外推广可以在各大站外平台实现全店、各产品的推广&#xff0c;尤其在大促中&#xff0c;结合活动当天的站内流量&#x…

Entity Framework升级

第三篇是Entity Framework升级 修改project.json 把原来 EntityFramework 的包 换成 Microsoft.EntityFrameworkCore 版本从 7.0.0-rc1-final 改为 1.0.0-rc2-final 对照表如下&#xff1a; RC1 PackageRC2 EquivalentEntityFramework.MicrosoftSqlServer 7.0.0-rc1-finalMicro…

12.netty中tcp粘包拆包问题及解决方法

【README】 1.本文源代码总结自B站《netty-尚硅谷》&#xff1b;2.本文介绍了tcp粘包拆包问题&#xff1b;3.本文po 出了粘包拆包问题解决方案及源代码实现&#xff1b;【1】tcp粘包拆包问题 refer2 How to deal with the problem of packet sticking and unpacking during T…

Oracle入门(十四.7)之良好的编程习惯

一、目的良好的编程实践是可以遵循的技术来创建最佳代码。 编程实践涵盖了从编写更易读的代码到创建具有更快性能的代码。软件工程团队通常会遵循风格指南&#xff0c;以便团队中的每个人都使用相同的技术。 这使得读取和修改其他人编写的代码变得更加容易。二、编程实践您已经…

css 浏览器调试中不可见_前端入门必会的初级调试技巧

本文仅仅针对前端初学者&#xff0c;目的是【用20%不到的时间】 学会【前端最常用的部分调试技巧】&#xff0c;如果需要最详细的调试技巧&#xff0c;包括调试性能优化的相关知识&#xff0c;文末会补充最全的文档&#xff08;chrome devtool的官方文档链接&#xff09;初学一…

升级ASP.Net Core项目

升级完类库项目&#xff0c;第二篇&#xff0c;我们来升级ASP.Net Core项目 修改global.json与project.json 这里可以参照&#xff0c;升级.Net Core RC2的那些事&#xff08;一&#xff09; 这里补充一点就是如果你觉得这样修改复杂&#xff0c;你完全可以新建一个项目&#x…

gophp解释器_【干货】Gisp 解释器 Golang 辅助开发工具

Gisp 是一个提供给 golang 使用的 Lisp 类 DSL 解释器。在 Lisp 的基本语法基础上&#xff0c;针对 go 环境稍作了一点语法糖。主要目标是提供一个尽可能便于与 golang 互操作的微型DSL工具。简介Gisp用go语言编写&#xff0c;是一个DSL 解释器&#xff0c;这个 DSL 基本上就是…

Oracle入门(十四.8)之迭代控制:基本循环Loop

一、迭代控制&#xff1a;LOOP语句 循环多次重复一个语句或一系列语句。 PL / SQL提供了以下几种类型的循环&#xff1a;•没有全面条件执行重复操作的基本循环 •FOR循环&#xff0c;基于计数器执行迭代操作•WHILE循环根据条件执行重复操作二、基本循环LOOP语句的最简单形式…

phpst安装memcache扩展_在 Ubuntu/Debian 下安装 PHP7.3 教程

介绍最近的 PHP 7.3.0 已经在 2018 年12月6日 发布 GA&#xff0c;大家已经可以开始第一时间体验新版本了&#xff0c;这里先放出 PHP7.3 安装的教程以便大家升级。适用系统&#xff1a; Ubuntu 18.04 LTS / Ubuntu 16.04 LTS &#xff0f; Ubuntu 14.04 LTS / Debian 9 stretc…

升级.Net Core RC1的类库项目

微软终于发布了.Net Code RC2了&#xff0c;作为一个软粉当然是第一时间升级了。《升级.Net Core RC2的那些事》系列文章主要是记录本人升级RC2的相关步骤以及遇到过的坑。 第一篇先写类库项目&#xff08;Nuget包项目&#xff09;的升级 升级VS工具 这里只提供一个下载地址&am…

Oracle入门(十四.9)之迭代控制:WHILE和FOR循环

一、WHILE循环您可以使用WHILE循环重复一系列语句&#xff0c;直到控制条件不再为TRUE。 条件在每次迭代开始时进行评估。当条件为FALSE或NULL时&#xff0c;循环终止。 如果条件在循环开始时为FALSE或NULL&#xff0c;则不会执行进一步的迭代。 WHILE condition LOOPstatement…

为TFS配置跨平台的生成服务器Xplat (Ubuntu Linux)

1. 概述 从TFS 2015开始&#xff0c;微软开始支持跨平台的构建代理。你可以使用TFS的Xplat代理&#xff0c;方便的在基于IOS, Unix和Linux的服务器上搭建生成代理&#xff0c;实现构建、发布等功能。本文档已Ubuntu为例&#xff0c;指导如何安装和运行Xplat代理。 2. 配置TFS的…

分数优先遵循志愿php源码_分数优先 遵循志愿

本报讯 昨日&#xff0c;广东省考试院发布2019年我省普通高校招生平行志愿投档及录取实施办法。今年我省依旧实行普通高校招生平行志愿投档录取模式&#xff0c;按照“分数优先、遵循志愿”的原则&#xff0c;根据考生高考成绩高低排序和院校志愿先后顺序投档&#xff0c;投出…

Oracle入门(十四.10)之显式游标简介

一、上下文区域和游标Oracle服务器分配一个称为上下文区域的私有内存区域来存储由SQL语句处理的数据。 每个上下文区域&#xff08;因此每个SQL语句&#xff09;都有一个与其关联的游标。您可以将游标视为上下文区域的标签&#xff0c;或者将其作为指向上下文区域的指针。 事实…

1.(转)canal背景与工作原理

【README】 1.canal是一个工具&#xff0c;由阿里开源&#xff0c;用于解析mysql的binlog增量日志&#xff0c;重放日志还原出业务数据&#xff0c;下游可以送入 es&#xff0c;mysql&#xff0c;hbase等&#xff1b; 2.本文以下内容转自&#xff1a;GitHub - alibaba/canal:…