当前位置: 首页 > news >正文

ProtoBuf介绍

1 编码和解码

编写网络应用程序时,因为数据在网络传输的都是二进制字节码数据,在发送数据时进行编码,在接受数据时进行解码

codec(编码器)的组成部分有2个:decoder(解码器)和encoder(编码器)。encoder负责将数据转换为字节码数据,而decoder负责将字节码数据转为业务数据

2 Netty的编码和解码机制问题

Netty提供的编码器:

StringEncoder:对字符串数据进行编码

ObjectEncoder:对java对象进行编码

Netty提供的解码器:

StringDecoder:对字符串数据进行编码

ObjectDecoder:对java对象进行编码

Netty本身自带的ObjectDecoder和ObjectEncoder可以实现用来对JOPO对象或各种业务对象的编码和解码。底层使用的依然是Java序列化技术,而java序列化技术本身就存在问题

  1. 无法跨语言

  1. 序列化的体积太大,是二进制编码的5倍多

  1. 序列化的性能太低

所以就出现了Google的Protobuf。

3 ProtoBuf

3.1基本介绍:

1) ProtoBuf是Google发布的开源项目,是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化,它很适合做数据存储或RPC[远程过程调用]数据交换格式,目前很多公司正在由http+json->tcp+protobuf

  1. Protobuf是以message的方式来管理数据的

  1. 支持跨平台,跨语言,即客户端和服务端可以是不同语言编写的

  1. 高性能,可读性高

  1. 使用protobuf编译器能够自动生成代码,ProtoBuf是将类的定义使用.protobuf文件进行描述(注意:在idea中编写.proto文件时,会自动提示是否下载.protot编写插件,可以让语法高亮)

  1. 然后通过.protoc.exe编译器根据.protp自动生成java文件

3.2 ProtoBuf生成类(单种类型)

实例要求:客户端发送一个Student PoJo对象到服务器(通过ProtoBuf编码),服务端接收Student PoJo对象,并显示信息(通过ProtoBuf解码)

步骤一:在maven中引入ProtoBuf坐标,下载相关jar包

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

步骤二:github下载相关protoc文件(注意:下载文件需和pom文件中版本对应,否则生成java文件时会报错)

下载链接:https://github.com/protocolbuffers/protobuf/tags

然后进行相关idea配置:https://blog.csdn.net/Xin_101/article/details/123332526?

步骤三:编写代码

服务端:

public class NettyServer {public static void main(String[] args) throws Exception {//1.创建2个线程组bossGroup和workerGroup//2  bossGroup只是处理连接请求,workerGroup真正的和客户端进行业务处理//3 两个都是无限循环NioEventLoopGroup bossGroup = new NioEventLoopGroup();NioEventLoopGroup workerGroup = new NioEventLoopGroup();try {//创建服务器端的启动对象,配置参数ServerBootstrap bootstrap = new ServerBootstrap();bootstrap.group(bossGroup,workerGroup)//设置两个线程组.channel(NioServerSocketChannel.class) //使用nioSocketChannel作为服务器的通道实现.option(ChannelOption.SO_BACKLOG,128)//设置线程队列得到连接.childOption(ChannelOption.SO_KEEPALIVE,true)//设置保持活动连接状态.childHandler(new ChannelInitializer<SocketChannel>() {//给pipeline设置处理器@Overrideprotected void initChannel(SocketChannel ch) throws Exception {//向pipeline加入ProtobufDecoder,指定对哪种对象进行解码ch.pipeline().addLast("decoder",new ProtobufDecoder(StudentPOJO.Student.getDefaultInstance()));ch.pipeline().addLast(new NettyServerHandler());}});//给workerGroup的EventLoop对应的管道设置处理器System.out.println("....服务器 is ready...");//绑定一个端口并且同步,生成了一个ChannelFuture对象//启动服务器(并绑定端口)ChannelFuture cf = bootstrap.bind(6668).sync();//对关联通道进行监听cf.channel().closeFuture().sync();}finally {bossGroup.shutdownGracefully();workerGroup.shutdownGracefully();}}}

服务端处理器:

public class NettyServerHandler extends ChannelInboundHandlerAdapter {//读取数据的事件(这里读取客户端发送的消息)/**** @param ctx ChannelHandlerContext ctx:上下文对象,含有管道pipeline,通道channel,地址* @param msg 就是客户端发送的消息* @throws Exception*/@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {//读取从客户端发送的StudentPojo,StudentStudentPOJO.Student student = (StudentPOJO.Student)msg;System.out.println("客户端发送的数据 id="+student.getId() + " 名字="+student.getName());}@Overridepublic void channelReadComplete(ChannelHandlerContext ctx) throws Exception {ctx.writeAndFlush(Unpooled.copiedBuffer(LocalDateTime.now()+" :hello,客户端~",CharsetUtil.UTF_8));}//处理异常,一般需要关闭通道@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {ctx.close();}}

客户端:

public class NettyClient {public static void main(String[] args) throws Exception{//客户端需要一个循环组NioEventLoopGroup group = new NioEventLoopGroup();try {//创建客户端的启动对象//注意客户端使用的是Bootstrap不是ServerBootstrapBootstrap bootstrap = new Bootstrap();bootstrap.group(group) //设置线程组.channel(NioSocketChannel.class) //设置客户端通道的实现类.handler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) throws Exception {//在pipeline中加入ProtobufEncoderch.pipeline().addLast("encoder",new ProtobufEncoder());ch.pipeline().addLast(new NettyClientHandler()); //加入自己的处理器}});System.out.println("客户端 ok ...");//启动客户端连接服务器 ChannelFuture涉及到netty的异步模型ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 6668).sync();//给关闭通道进行监听channelFuture.channel().closeFuture().sync();}finally {group.shutdownGracefully();}}}

客户端处理器:

public class NettyClientHandler extends ChannelInboundHandlerAdapter {//当通道就绪会触发该方法@Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception {//发送一个Student对象到服务器StudentPOJO.Student student = StudentPOJO.Student.newBuilder().setId(4).setName("王五").build();ctx.writeAndFlush(student);}@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {ByteBuf buf = (ByteBuf)msg;System.out.println("服务器回复的消息:"+buf.toString(CharsetUtil.UTF_8));System.out.println("服务器的地址:"+ctx.channel().remoteAddress());}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {cause.printStackTrace();ctx.close();}}

如果不想在channelRead()方法中进行强转,可以将处理器实现SimpleChannelInboundHandler类型,从而实现channelRead0()方法

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;}

将proto文件转为java文件结果如下:

先启动客户端,再次启动服务端:

客户端控制台:

服务端控制台:

发现,已经用protobuf实现了数据的传输。

3.3 ProtoBuf生成类(多种类型)

实例要求:客户端发送一个Student PoJo \Worker PoJo对象到服务器(通过ProtoBuf编码),服务端接收Student PoJo\Worker PoJo对象,并显示信息(通过ProtoBuf解码)

服务端代码;

package com.liubujun.codec2;import com.liubujun.codec.StudentPOJO;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.protobuf.ProtobufDecoder;/*** @Author: liubujun* @Date: 2023/2/15 10:24*/public class NettyServer {public static void main(String[] args) throws Exception {//1.创建2个线程组bossGroup和workerGroup//2  bossGroup只是处理连接请求,workerGroup真正的和客户端进行业务处理//3 两个都是无限循环NioEventLoopGroup bossGroup = new NioEventLoopGroup();NioEventLoopGroup workerGroup = new NioEventLoopGroup();try {//创建服务器端的启动对象,配置参数ServerBootstrap bootstrap = new ServerBootstrap();bootstrap.group(bossGroup,workerGroup)//设置两个线程组.channel(NioServerSocketChannel.class) //使用nioSocketChannel作为服务器的通道实现.option(ChannelOption.SO_BACKLOG,128)//设置线程队列得到连接.childOption(ChannelOption.SO_KEEPALIVE,true)//设置保持活动连接状态.childHandler(new ChannelInitializer<SocketChannel>() {//给pipeline设置处理器@Overrideprotected void initChannel(SocketChannel ch) throws Exception {//向pipeline加入ProtobufDecoder,指定对哪种对象进行解码ch.pipeline().addLast("decoder",new ProtobufDecoder(MyDataInfo.MyMessage.getDefaultInstance()));ch.pipeline().addLast(new NettyServerHandler());}});//给workerGroup的EventLoop对应的管道设置处理器System.out.println("....服务器 is ready...");//绑定一个端口并且同步,生成了一个ChannelFuture对象//启动服务器(并绑定端口)ChannelFuture cf = bootstrap.bind(6668).sync();//对关联通道进行监听cf.channel().closeFuture().sync();}finally {bossGroup.shutdownGracefully();workerGroup.shutdownGracefully();}}}

服务端处理器:

package com.liubujun.codec2;import com.liubujun.codec.StudentPOJO;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.CharsetUtil;import java.time.LocalDateTime;/*** @Author: liubujun* @Date: 2023/2/15 10:25*/public class NettyServerHandler extends SimpleChannelInboundHandler<MyDataInfo.MyMessage> {@Overrideprotected void channelRead0(ChannelHandlerContext channelHandlerContext, MyDataInfo.MyMessage msg) throws Exception {//根据dataType 来显示不同的信息MyDataInfo.MyMessage.DataType dataType = msg.getDataType();if (dataType == MyDataInfo.MyMessage.DataType.StudentType){MyDataInfo.Student 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.getAge() +" 工人姓名="+worker.getName());}else {System.out.println("传输的类型不正确");}}@Overridepublic void channelReadComplete(ChannelHandlerContext ctx) throws Exception {ctx.writeAndFlush(Unpooled.copiedBuffer(LocalDateTime.now() + " :hello,客户端~", CharsetUtil.UTF_8));}//处理异常,一般需要关闭通道@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {ctx.close();}}

客户端:

package com.liubujun.codec2;import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.protobuf.ProtobufEncoder;/*** @Author: liubujun* @Date: 2023/2/15 10:27*/public class NettyClient {public static void main(String[] args) throws Exception{//客户端需要一个循环组NioEventLoopGroup group = new NioEventLoopGroup();try {//创建客户端的启动对象//注意客户端使用的是Bootstrap不是ServerBootstrapBootstrap bootstrap = new Bootstrap();bootstrap.group(group) //设置线程组.channel(NioSocketChannel.class) //设置客户端通道的实现类.handler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) throws Exception {//在pipeline中加入ProtobufEncoderch.pipeline().addLast("encoder",new ProtobufEncoder());ch.pipeline().addLast(new NettyClientHandler()); //加入自己的处理器}});System.out.println("客户端 ok ...");//启动客户端连接服务器 ChannelFuture涉及到netty的异步模型ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 6668).sync();//给关闭通道进行监听channelFuture.channel().closeFuture().sync();}finally {group.shutdownGracefully();}}}

客户端处理器:

package com.liubujun.codec2;import com.liubujun.codec.StudentPOJO;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;import java.util.Random;/*** @Author: liubujun* @Date: 2023/2/15 10:28*/public class NettyClientHandler extends ChannelInboundHandlerAdapter {//当通道就绪会触发该方法@Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception {//随机的发送Student 或者 Worker对象int random = new Random().nextInt(3);MyDataInfo.MyMessage myMessage = null;if (0 == random){ //发送Student对象myMessage = MyDataInfo.MyMessage.newBuilder().setDataType(MyDataInfo.MyMessage.DataType.StudentType).setStudent(MyDataInfo.Student.newBuilder().setId(5).setName("小飞棍来喽").build()).build();}else { //发送一个Worker对象myMessage =  MyDataInfo.MyMessage.newBuilder().setDataType(MyDataInfo.MyMessage.DataType.WorkerType).setWorker(MyDataInfo.Worker.newBuilder().setAge(5).setName("来了 老李").build()).build();}ctx.writeAndFlush(myMessage);}@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {ByteBuf buf = (ByteBuf)msg;System.out.println("服务器回复的消息:"+buf.toString(CharsetUtil.UTF_8));System.out.println("服务器的地址:"+ctx.channel().remoteAddress());}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {cause.printStackTrace();ctx.close();}}

proto文件:

syntax = "proto3";
option optimize_for = SPEED; //加快解析
option java_package = "com.liubujun.codec2";//指定生成到哪个包下
option java_outer_classname = "MyDataInfo"; //外部类名称//proto可以使用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; //Studnet类的属性string name = 2;
}
message Worker{string name = 1;int32 age = 2;
}

先启动服务端,再启动多个客户端:发现数据正常传输

相关文章:

ProtoBuf介绍

1 编码和解码编写网络应用程序时&#xff0c;因为数据在网络传输的都是二进制字节码数据&#xff0c;在发送数据时进行编码&#xff0c;在接受数据时进行解码codec&#xff08;编码器&#xff09;的组成部分有2个&#xff1a;decoder&#xff08;解码器&#xff09;和encoder&a…...

数据结构:完全二叉树开胃菜小练习

目录 一.前言 二.完全二叉树的重要结构特点 三.完全二叉树开胃菜小练习 1.一个重要的数学结论 2.简单的小练习 一.前言 关于树及完全二叉树的基础概念(及树结点编号规则)参见:http://t.csdn.cn/imdrahttp://t.csdn.cn/imdra 完全二叉树是一种非常重要的数据结构: n个结点的…...

mybatis与jpa

1、官方文档 mybatis&#xff1a;mybatis-spring – jpa&#xff1a;https://springdoc.cn/spring-data-jpa/ 应用文档 jpa详解_java菜鸟1的博客-CSDN博客 JPA简介及其使用详解_Tourist-xl的博客-CSDN博客_jpa的作用 2、使用比较 mybatis一般用于互联网性质的项目&#x…...

js 求解《初级算法》66. 加一

一、题目描述 给定一个由 整数 组成的 非空 数组所表示的非负整数&#xff0c;在该数的基础上加一。最高位数字存放在数组的首位&#xff0c; 数组中每个元素只存储单个数字。你可以假设除了整数 0 之外&#xff0c;这个整数不会以零开头。 示例 1&#xff1a; 输入&#xff1a…...

力扣-游戏玩法分析

大家好&#xff0c;我是空空star&#xff0c;本篇带大家了解一道简单的力扣sql练习题。 文章目录前言一、题目&#xff1a;511. 游戏玩法分析二、解题1.正确示范①提交SQL运行结果2.正确示范②提交SQL运行结果3.正确示范③提交SQL运行结果4.正确示范④提交SQL运行结果5.其他总结…...

ZZNUOJ_用C语言编写程序实现1186 : 奖学金(结构体专题)(附完整源码)

题目描述 某校发放奖学金共5种,获取条件各不同: 1.阳明奖学金,每人8000,期末平均成绩>80,且在本学期发表论文大于等于1篇; 2.梨洲奖学金,每人4000,期末平均成绩>85,且班级评议成绩>80; 3.成绩优秀奖,每人2000,期末平均成绩>90; 4.西部奖学金,…...

加油站ai系统视频监测 yolov5

加油站ai系统视频监测通过yolov5网络模型深度学习边缘计算技术&#xff0c;加油站ai系统视频监测对现场卸油过程中人员违规离岗、现场灭火器没有按要求正确摆放、以及卸油前需要遵守静电释放15分钟、打电话、明火烟雾情况、抽烟行为进行自动识别。YOLO系列算法是一类典型的one-…...

【JDK8新特性之Stream流-Stream结果收集案例实操】

一.JDK8新特性之Stream流-Stream结果收集以及案例实操 二.Stream结果收集(collect函数)-实例实操 2.1 结果收集到集合中 /*** Stream将结果收集到集合中以及具体的实现 collect*/Testpublic void test01(){// 收集到List中 接口List<Integer> list Stream.of(1, 2, 3…...

Fiddler 抓包工具

HTTP代理所谓的http代理&#xff0c;其实就是代理客户机的http访问&#xff0c;主要代理浏览器访问页面。代理服务器是介于浏览器和web服务器之间的一台服务器&#xff0c;有了它之后&#xff0c;浏览器不是直接到Web服务器去取回网页而是向代理服务器发出请求&#xff0c;Requ…...

2023最新版网络安全保姆级指南,手把手带你从零基础进阶渗透攻防工程师

前言 一份网络攻防渗透测试的学习路线&#xff0c;不藏私了&#xff01; 1、学习编程语言(phpmysqljshtml) 原因&#xff1a; phpmysql可以帮助你快速的理解B/S架构是怎样运行的&#xff0c;只有理解了他的运行原理才能够真正的找到问题/漏洞所在。所以对于国内那些上来就说…...

排序基础之选择排序法

目录 前言 一、什么是选择排序 二、实现选择排序 三、使用泛型扩展 四、使用自定义类型测试 前言 今天天气不错&#xff0c;这么好的天气不干点啥实在是有点可惜了&#xff0c;于是乎&#xff0c;拿出键盘撸一把&#xff01; 来&#xff0c;今天来学习一下排序算法中的选…...

2.24测试用例

一.测试模型1.V模型特点:1.明确标注了测试的类型2.明确标注了测试阶段和开发阶段的对应关系缺点:测试后置2.W模型也叫双v模型,测试阶段全流程介入缺点:1.上一阶段完成.下一个阶段才能开始2.开发模型和测试模型也保持着一种线性的前后关系3.重文档,重过程,不支持敏捷模式二.设计…...

面试必刷101 Java题解 -- part 1

练习地址 面试必刷101-牛客1、链表反转2、链表内指定区间反转**3. 链表中的节点每k个一组翻转**4、**合并两个排序的链表**5、**合并k个已排序的链表**6、**判断链表中是否有环****7、链表中环的入口结点**8、链表中倒数最后k个结点**9、删除链表的倒数第n个节点****10、两个链…...

Python---关联与继承

专栏&#xff1a;python 个人主页&#xff1a;HaiFan. 专栏简介&#xff1a;Python在学&#xff0c;希望能够得到各位的支持&#xff01;&#xff01;&#xff01; 关联与继承前言has a关联关系is a继承关系子类不添加__init__子类添加__init__前言 has a关联关系 has - a 是在…...

数据库行业的 “叛逆者”:大数据已“死”,MotherDuck 当立

“大数据”已死——现今我们最重要的事情不是担心数据大小&#xff0c;而是专注于我们将如何使用它来做出更好的决策。数据库行业发展至今&#xff0c;在数据层面有很多的加速和变革&#xff0c;尤其是过去几年的云数仓爆炸式增长&#xff0c;带来了行业的很多变化。毫无疑问&a…...

Linux->进程优先级

目录 1. 优先级的概念 2. 优先级的运作方式 3. Linux下查看进程优先级以及调整 3.1 查看进程优先级 3.2 修改进程优先级 1. 优先级的概念 1. cpu资源分配的先后顺序&#xff0c;就是指进程的优先权&#xff08;priority&#xff09;。 2. 优先权高的进程有优先执行权利。配…...

loki 日志管理的安装部署使用

loki介绍 Loki是 Grafana Labs 团队最新的开源项目&#xff0c;是一个水平可扩展&#xff0c;高可用性&#xff0c;多租户的日志聚合系统。它的设计非常经济高效且易于操作&#xff0c;因为它不会为日志内容编制索引&#xff0c;而是为每个日志流编制一组标签。 不对日志进行…...

CTFer成长之路之反序列化漏洞

反序列化漏洞CTF 1.访问url&#xff1a; http://91a5ef16-ff14-4e0d-a687-32bdb4f61ecf.node3.buuoj.cn/ 点击下载源码 本地搭建环境并访问url&#xff1a; http://127.0.0.1/www/public/ 构造payload&#xff1a; ?sindex/index/hello&ethanwhoamiPOST的参数&#…...

Python学习-----模块5.0(文件管理大师-->os模块)

目录 前言&#xff1a; 1.os.getcwd() 2. os.listdir(path) 3.os.walk(path) 4.os.path.exists(path) 5.os.mkdir(path) 6.os.makedirs(path,exist_okTrue) 7.os.rmdir(path) 8.os.remove(path) 9.os.path.join(p1,p2) 10.os.path.split(path) 11.os.path.isdi…...

第45届世界技能大赛“网络安全”赛项浙江省选拔赛竞赛任务书

第45届世界技能大赛浙江省选拔赛竞赛任务书 一、竞赛时间 8:00-17:00&#xff0c;共计9小时。 二、竞赛阶段 竞赛阶段 任务阶段 竞赛任务 竞赛时间 分值 模块A 任务1 数据库安全加固 8:00-10:00 50 任务2 文件MD5校验 50 任务3 Linux系统服务渗透测试及安全加…...

基于FPGA的PID算法学习———实现PID比例控制算法

基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容&#xff1a;参考网站&#xff1a; PID算法控制 PID即&#xff1a;Proportional&#xff08;比例&#xff09;、Integral&#xff08;积分&…...

rknn优化教程(二)

文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK&#xff0c;开始写第二篇的内容了。这篇博客主要能写一下&#xff1a; 如何给一些三方库按照xmake方式进行封装&#xff0c;供调用如何按…...

C++:std::is_convertible

C++标志库中提供is_convertible,可以测试一种类型是否可以转换为另一只类型: template <class From, class To> struct is_convertible; 使用举例: #include <iostream> #include <string>using namespace std;struct A { }; struct B : A { };int main…...

Spring Boot 实现流式响应(兼容 2.7.x)

在实际开发中&#xff0c;我们可能会遇到一些流式数据处理的场景&#xff0c;比如接收来自上游接口的 Server-Sent Events&#xff08;SSE&#xff09; 或 流式 JSON 内容&#xff0c;并将其原样中转给前端页面或客户端。这种情况下&#xff0c;传统的 RestTemplate 缓存机制会…...

镜像里切换为普通用户

如果你登录远程虚拟机默认就是 root 用户&#xff0c;但你不希望用 root 权限运行 ns-3&#xff08;这是对的&#xff0c;ns3 工具会拒绝 root&#xff09;&#xff0c;你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案&#xff1a;创建非 roo…...

优选算法第十二讲:队列 + 宽搜 优先级队列

优选算法第十二讲&#xff1a;队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...

Redis的发布订阅模式与专业的 MQ(如 Kafka, RabbitMQ)相比,优缺点是什么?适用于哪些场景?

Redis 的发布订阅&#xff08;Pub/Sub&#xff09;模式与专业的 MQ&#xff08;Message Queue&#xff09;如 Kafka、RabbitMQ 进行比较&#xff0c;核心的权衡点在于&#xff1a;简单与速度 vs. 可靠与功能。 下面我们详细展开对比。 Redis Pub/Sub 的核心特点 它是一个发后…...

springboot整合VUE之在线教育管理系统简介

可以学习到的技能 学会常用技术栈的使用 独立开发项目 学会前端的开发流程 学会后端的开发流程 学会数据库的设计 学会前后端接口调用方式 学会多模块之间的关联 学会数据的处理 适用人群 在校学生&#xff0c;小白用户&#xff0c;想学习知识的 有点基础&#xff0c;想要通过项…...

排序算法总结(C++)

目录 一、稳定性二、排序算法选择、冒泡、插入排序归并排序随机快速排序堆排序基数排序计数排序 三、总结 一、稳定性 排序算法的稳定性是指&#xff1a;同样大小的样本 **&#xff08;同样大小的数据&#xff09;**在排序之后不会改变原始的相对次序。 稳定性对基础类型对象…...

Java求职者面试指南:计算机基础与源码原理深度解析

Java求职者面试指南&#xff1a;计算机基础与源码原理深度解析 第一轮提问&#xff1a;基础概念问题 1. 请解释什么是进程和线程的区别&#xff1f; 面试官&#xff1a;进程是程序的一次执行过程&#xff0c;是系统进行资源分配和调度的基本单位&#xff1b;而线程是进程中的…...