城市直播房产教育博客汽车
投稿投诉
汽车报价
买车新车
博客专栏
专题精品
教育留学
高考读书
房产家居
彩票视频
直播黑猫
投资微博
城市上海
政务旅游

从RedisHTTP协议,看Nett协议设计,我发现了个惊天

6月26日 不将就投稿
  1。协议的作用
  TCPIP中消息传输基于流的方式,没有边界
  协议的目的就是划定消息的边界,制定通信双方要共同遵守的通信规则2。Redis协议
  如果我们要向Redis服务器发送一条setnameNyima的指令,需要遵守如下协议该指令一共有3部分,每条指令之后都要添加回车与换行符3r第一个指令的长度是33r第一个指令是set指令setr下面的指令以此类推4rnamer5rNyimar复制代码
  客户端代码如下publicclassRedisClient{staticfinalLoggerlogLoggerFactory。getLogger(StudyServer。class);publicstaticvoidmain(String〔〕args){NioEventLoopGroupgroupnewNioEventLoopGroup();try{ChannelFuturechannelFuturenewBootstrap()。group(group)。channel(NioSocketChannel。class)。handler(newChannelInitializerSocketChannel(){OverrideprotectedvoidinitChannel(SocketChannelch){打印日志ch。pipeline()。addLast(newLoggingHandler(LogLevel。DEBUG));ch。pipeline()。addLast(newChannelInboundHandlerAdapter(){OverridepublicvoidchannelActive(ChannelHandlerContextctx)throwsException{回车与换行符finalbyte〔〕LINE{r,};获得ByteBufByteBufbufferctx。alloc()。buffer();连接建立后,向Redis中发送一条指令,注意添加回车与换行setnameNyimabuffer。writeBytes(3。getBytes());buffer。writeBytes(LINE);buffer。writeBytes(3。getBytes());buffer。writeBytes(LINE);buffer。writeBytes(set。getBytes());buffer。writeBytes(LINE);buffer。writeBytes(4。getBytes());buffer。writeBytes(LINE);buffer。writeBytes(name。getBytes());buffer。writeBytes(LINE);buffer。writeBytes(5。getBytes());buffer。writeBytes(LINE);buffer。writeBytes(Nyima。getBytes());buffer。writeBytes(LINE);ctx。writeAndFlush(buffer);}});}})。connect(newInetSocketAddress(localhost,6379));channelFuture。sync();关闭channelchannelFuture。channel()。close()。sync();}catch(InterruptedExceptione){e。printStackTrace();}finally{关闭groupgroup。shutdownGracefully();}}}复制代码
  控制台打印结果1600〔nioEventLoopGroup21〕DEBUGio。netty。handler。logging。LoggingHandler〔id:0x28c994f1,L:127。0。0。1:60792R:localhost127。0。0。1:6379〕WRITE:34B0123456789abcdef000000002a330d0a24330d0a7365740d0a24340d3。。3。。set。。4。000000100a6e616d650d0a24350d0a4e79696d61。name。。5。。Nyima000000200d0a。。复制代码
  Redis中查询执行结果
  3。HTTP协议
  HTTP协议在请求行请求头中都有很多的内容,自己实现较为困难,可以使用HttpServerCodec作为服务器端的解码器与编码器,来处理HTTP请求HttpServerCodec中既有请求的解码器HttpRequestDecoder又有响应的编码器HttpResponseEncoderCodec(CodeCombine)一般代表该类既作为编码器又作为解码器publicfinalclassHttpServerCodecextendsCombinedChannelDuplexHandlerHttpRequestDecoder,HttpResponseEncoderimplementsHttpServerUpgradeHandler。SourceCodec复制代码
  服务器代码publicclassHttpServer{staticfinalLoggerlogLoggerFactory。getLogger(StudyServer。class);publicstaticvoidmain(String〔〕args){NioEventLoopGroupgroupnewNioEventLoopGroup();newServerBootstrap()。group(group)。channel(NioServerSocketChannel。class)。childHandler(newChannelInitializerSocketChannel(){OverrideprotectedvoidinitChannel(SocketChannelch){ch。pipeline()。addLast(newLoggingHandler(LogLevel。DEBUG));作为服务器,使用HttpServerCodec作为编码器与解码器ch。pipeline()。addLast(newHttpServerCodec());服务器只处理HTTPRequestch。pipeline()。addLast(newSimpleChannelInboundHandlerHttpRequest(){OverrideprotectedvoidchannelRead0(ChannelHandlerContextctx,HttpRequestmsg){获得请求urilog。debug(msg。uri());获得完整响应,设置版本号与状态码DefaultFullHttpResponseresponsenewDefaultFullHttpResponse(msg。protocolVersion(),HttpResponseStatus。OK);设置响应内容byte〔〕bytesh1Hello,World!h1。getBytes(StandardCharsets。UTF8);设置响应体长度,避免浏览器一直接收响应内容response。headers()。setInt(CONTENTLENGTH,bytes。length);设置响应体response。content()。writeBytes(bytes);写回响应ctx。writeAndFlush(response);}});}})。bind(8080);}}复制代码
  服务器负责处理请求并响应浏览器。所以只需要处理HTTP请求即可服务器只处理HTTPRequestch。pipeline()。addLast(newSimpleChannelInboundHandlerHttpRequest()复制代码
  获得请求后,需要返回响应给浏览器。需要创建响应对象DefaultFullHttpResponse,设置HTTP版本号及状态码,为避免浏览器获得响应后,因为获得CONTENTLENGTH而一直空转,需要添加CONTENTLENGTH字段,表明响应体中数据的具体长度获得完整响应,设置版本号与状态码DefaultFullHttpResponseresponsenewDefaultFullHttpResponse(msg。protocolVersion(),HttpResponseStatus。OK);设置响应内容byte〔〕bytesh1Hello,World!h1。getBytes(StandardCharsets。UTF8);设置响应体长度,避免浏览器一直接收响应内容response。headers()。setInt(CONTENTLENGTH,bytes。length);设置响应体response。content()。writeBytes(bytes);复制代码
  运行结果
  浏览器
  控制台请求内容1714〔nioEventLoopGroup22〕DEBUGio。netty。handler。logging。LoggingHandler〔id:0x72630ef7,L:0:0:0:0:0:0:0:1:8080R:0:0:0:0:0:0:0:1:55503〕READ:688B0123456789abcdef00000000474554202f66617669636f6e2e69636fGETfavicon。ico0000001020485454502f312e310d0a486f73743aHTTP1。1。。Host:00000020206c6f63616c686f73743a383038300dlocalhost:8080。000000300a436f6e6e656374696f6e3a206b6565。Connection:kee00000040702d616c6976650d0a507261676d613apalive。。Pragma:。。。。响应内容1716〔nioEventLoopGroup22〕DEBUGio。netty。handler。logging。LoggingHandler〔id:0x72630ef7,L:0:0:0:0:0:0:0:1:8080R:0:0:0:0:0:0:0:1:55503〕WRITE:61B0123456789abcdef00000000485454502f312e3120323030204f4b0dHTTP1。1200OK。000000100a436f6e74656e742d4c656e6774683a。ContentLength:000000202032320d0a0d0a3c68313e48656c6c6f22。。。。h1Hello000000302c20576f726c64213c2f68313e,World!h1复制代码4。自定义协议
  组成要素魔数:用来在第一时间判定接收的数据是否为无效数据包版本号:可以支持协议的升级序列化算法:消息正文到底采用哪种序列化反序列化方式如:json、protobuf、hessian、jdk指令类型:是登录、注册、单聊、群聊跟业务相关请求序号:为了双工通信,提供异步能力正文长度消息正文编码器与解码器publicclassMessageCodecextendsByteToMessageCodecMessage{Overrideprotectedvoidencode(ChannelHandlerContextctx,Messagemsg,ByteBufout)throwsException{设置魔数4个字节out。writeBytes(newbyte〔〕{N,Y,I,M});设置版本号1个字节out。writeByte(1);设置序列化方式1个字节out。writeByte(1);设置指令类型1个字节out。writeByte(msg。getMessageType());设置请求序号4个字节out。writeInt(msg。getSequenceId());为了补齐为16个字节,填充1个字节的数据out。writeByte(0xff);获得序列化后的msgByteArrayOutputStreambosnewByteArrayOutputStream();ObjectOutputStreamoosnewObjectOutputStream(bos);oos。writeObject(msg);byte〔〕bytesbos。toByteArray();获得并设置正文长度长度用4个字节标识out。writeInt(bytes。length);设置消息正文out。writeBytes(bytes);}Overrideprotectedvoiddecode(ChannelHandlerContextctx,ByteBufin,ListObjectout)throwsException{获取魔数intmagicin。readInt();获取版本号byteversionin。readByte();获得序列化方式byteseqTypein。readByte();获得指令类型bytemessageTypein。readByte();获得请求序号intsequenceIdin。readInt();移除补齐字节in。readByte();获得正文长度intlengthin。readInt();获得正文byte〔〕bytesnewbyte〔length〕;in。readBytes(bytes,0,length);ObjectInputStreamoisnewObjectInputStream(newByteArrayInputStream(bytes));Messagemessage(Message)ois。readObject();将信息放入List中,传递给下一个handlerout。add(message);打印获得的信息正文System。out。println(魔数);System。out。println(magic);System。out。println(版本号);System。out。println(version);System。out。println(序列化方法);System。out。println(seqType);System。out。println(指令类型);System。out。println(messageType);System。out。println(请求序号);System。out。println(sequenceId);System。out。println(正文长度);System。out。println(length);System。out。println(正文);System。out。println(message);}}复制代码编码器与解码器方法源于父类ByteToMessageCodec,通过该类可以自定义编码器与解码器,泛型类型为被编码与被解码的类。此处使用了自定义类Message,代表消息publicclassMessageCodecextendsByteToMessageCodecMessage复制代码编码器负责将附加信息与正文信息写入到ByteBuf中,其中附加信息总字节数最好为2n,不足需要补齐。正文内容如果为对象,需要通过序列化将其放入到ByteBuf中解码器负责将ByteBuf中的信息取出,并放入List中,该List用于将信息传递给下一个handler
  编写测试类publicclassTestCodec{staticfinalorg。slf4j。LoggerlogLoggerFactory。getLogger(StudyServer。class);publicstaticvoidmain(String〔〕args)throwsException{EmbeddedChannelchannelnewEmbeddedChannel();添加解码器,避免粘包半包问题channel。pipeline()。addLast(newLengthFieldBasedFrameDecoder(1024,12,4,0,0));channel。pipeline()。addLast(newLoggingHandler(LogLevel。DEBUG));channel。pipeline()。addLast(newMessageCodec());LoginRequestMessageusernewLoginRequestMessage(Nyima,123);测试编码与解码ByteBufbyteBufByteBufAllocator。DEFAULT。buffer();newMessageCodec()。encode(null,user,byteBuf);channel。writeInbound(byteBuf);}}复制代码测试类中用到了LengthFieldBasedFrameDecoder,避免粘包半包问题通过MessageCodec的encode方法将附加信息与正文写入到ByteBuf中,通过channel执行入站操作。入站时会调用decode方法进行解码
  运行结果
  Sharable注解
  为了提高handler的复用率,可以将handler创建为handler对象,然后在不同的channel中使用该handler对象进行处理操作LoggingHandlerloggingHandlernewLoggingHandler(LogLevel。DEBUG);不同的channel中使用同一个handler对象,提高复用率channel1。pipeline()。addLast(loggingHandler);channel2。pipeline()。addLast(loggingHandler);复制代码
  但是并不是所有的handler都能通过这种方法来提高复用率的,例如LengthFieldBasedFrameDecoder。如果多个channel中使用同一个LengthFieldBasedFrameDecoder对象,则可能发生如下问题channel1中收到了一个半包,LengthFieldBasedFrameDecoder发现不是一条完整的数据,则没有继续向下传播此时channel2中也收到了一个半包,因为两个channel使用了同一个LengthFieldBasedFrameDecoder,存入其中的数据刚好拼凑成了一个完整的数据包。LengthFieldBasedFrameDecoder让该数据包继续向下传播,最终引发错误
  为了提高handler的复用率,同时又避免出现一些并发问题,Netty中原生的handler中用Sharable注解来标明,该handler能否在多个channel中共享。
  只有带有该注解,才能通过对象的方式被共享,否则无法被共享自定义编解码器能否使用Sharable注解
  这需要根据自定义的handler的处理逻辑进行分析
  我们的MessageCodec本身接收的是LengthFieldBasedFrameDecoder处理之后的数据,那么数据肯定是完整的,按分析来说是可以添加Sharable注解的
  但是实际情况我们并不能添加该注解,会抛出异常信息ChannelHandlercn。nyimac。study。day8。protocol。MessageCodecisnotallowedtobeshared因为MessageCodec继承自ByteToMessageCodec,ByteToMessageCodec类的注解如下
  这就意味着ByteToMessageCodec不能被多个channel所共享的原因:因为该类的目标是:将ByteBuf转化为Message,意味着传进该handler的数据还未被处理过。所以传过来的ByteBuf可能并不是完整的数据,如果共享则会出现问题
  如果想要共享,需要怎么办呢?
  继承MessageToMessageDecoder即可。该类的目标是:将已经被处理的完整数据再次被处理。传过来的Message如果是被处理过的完整数据,那么被共享也就不会出现问题了,也就可以使用Sharable注解了。实现方式与ByteToMessageCodec类似ChannelHandler。SharablepublicclassMessageSharableCodecextendsMessageToMessageCodecByteBuf,Message{Overrideprotectedvoidencode(ChannelHandlerContextctx,Messagemsg,ListObjectout)throwsException{。。。}Overrideprotectedvoiddecode(ChannelHandlerContextctx,ByteBufmsg,ListObjectout)throwsException{。。。}}
投诉 评论 转载

内蒙古拟推荐5个单位申报国家级非物质文化遗产生产性保护示范基央广网北京12月27日消息据内蒙古自治区文化和旅游厅消息,按照《文化和旅游部办公厅关于开展国家级非物质文化遗产生产性保护示范基地推荐工作的通知》(办非遗发〔2022〕168号)……从RedisHTTP协议,看Nett协议设计,我发现了个惊天1。协议的作用TCPIP中消息传输基于流的方式,没有边界协议的目的就是划定消息的边界,制定通信双方要共同遵守的通信规则2。Redis协议如果我们要向Redis……叮咚买菜三季度大幅减亏,客单价提升25,有望四季度盈利近日,叮咚买菜发布了2022年第三季度业绩报告。该季度公司营收为59。4亿元,同比去年下降4;净亏损3。4亿元,去年同期为20。1亿元,同比大幅减亏。该季度GMV达到65。1亿……三结义晚年辛酸关羽猝逝,张飞不能自理,刘备坎坷94版《三国演义》关羽的扮演者陆树铭猝然离世,让很多观众把记忆切回到那部经典的电视剧中。和87版《红楼梦》一样,94版《三国演义》被认为是高度还原原著的作品,是无数观众心……苏纳克上任第一天,拜登就打电话谈中国,外交部表态美英元首称应对中国挑战中方驳斥文君剑当地时间25日,苏纳克获任命,正式出任英国新任首相他将开始着手组建英国新内阁,并处理之前留下的一系列问题。就在苏纳克上任第……汉族男性能和维族女性结婚吗?听听维吾尔族姑娘怎么说我国是多民族国家,其中新疆是我国面积最大的省份。其中涉及到的旅游景点也非常多,每年都会有众多的游客选择去新疆旅游观光。一方面是为了感受民族特色,另外一方面就是新疆美……第二任南京军区司令,深受林彪器重,10小时瓦弄大捷金庸12字1999年10月7日,黄花岗殡仪馆内,正在为一位名为丁盛的老人召开追悼会。在前来悼唁的人当中有很多都是参加过战争的老红军,部队干部。在人们所敬献的花圈上写着:同志、将军、……小朋友到底要不要提前预习呢?这要根据小朋友的情况而定。如果小孩比较聪明,接受能力强,就不需要提前预习。如果小孩理解能力差,学习上较为吃力,提前预习就很有必要。对于学起来轻松的小孩,如果事先进行预习,……月薪多少才能养一辆十万的车?这笔账很容易算,因为我自己开的就是一辆10万块钱左右的丰田花冠。先算每个月要在车上支付多少费用,再算每年保养,保险上需要多少费用,然后平摊到每个月里,你就知道一年下来平均……创造世界纪录!乌军炮击俄军打出半米精度俄军坦克被迫跳水游泳近期,在北顿涅兹克渡河战斗以后,美国人都在感叹:到底是北约在培训乌克兰炮兵,还是乌克兰炮兵在培训北约炮兵?从乌军命中的俄军浮桥看,乌军炮弹直接命中了6米宽的桥中央,就是有……阳泉东站会开通直达北京太原的火车吗?很大概率是不会的,即便真的通了火车,也只会加速人口外流,没办法,现在阳泉经济已经触底,除了煤炭,别的产业都没啥大的起色,加上地方本身就小,发展规模不可能大,未来随着煤炭资源的枯……朱仙镇好玩吗?去过朱仙镇一次,感觉很失落,也略微有一点儿心酸。朱仙镇是清代四大名镇里、唯一位于北方地区的名镇。不过,昔日的小伙伴们纷纷得道成仙,依然流落凡间的朱仙镇就显得太落寞了……
中国女排联赛结束了,女排国家队马上要冬训,什么时候主教练确定国潮热度不减内需动能强劲双11购物节折射消费市场新趋势毛主席工资594元,周总理工资404元,但都没有宋庆龄的高,长沙有哪些靠近中学的校区房?考研考博太难?这些大学专业,高考考上就能本硕本博连读!快讯!茅台年内含税销售收入破千亿,日均超3亿元萌翻了!瓦屋山小熊猫雪地觅食太可爱刘涛,终因急功近利付出了代价扎实堆料带来均衡体验,标准版旗舰的标杆便是vivoX90杨振宁的原配,中国顶级名媛,父亲是抗日名将,看看她有多优秀在手机影像这件事上,iPhone在安卓面前只是弟弟?平远街大山村的宝藏为何无人识
太极郭剑鑫抻筋拔骨的锻炼未来太阳系会变成双星系统!科学家发现多颗恒星正在靠近尿酸高痛风应该吃什么?给你两个建议远离痛风汶川地震被救少年14年后救火牺牲,他曾许下心愿热评聚热点网 《安徒生童话》读书笔记小蝴蝶三年级作文350字绝佳茶树生长环境的样本金银花水能不能给婴儿喝婴儿长期喝金银花水好不好抗高原反应的药需要提前多久吃全身实体娃娃,半身实体娃娃,充气娃娃,倒模怎么选择?大闸蟹放冰箱吗大闸蟹能不能放冰箱在日本市场上苹果新款iPhoneSE需求强劲

友情链接:中准网聚热点快百科快传网快生活快软网快好知文好找江西南阳嘉兴昆明铜陵滨州广东西昌常德梅州兰州阳江运城金华广西萍乡大理重庆诸暨泉州安庆南充武汉辽宁