编解码-protobuf
Google的Protobuf在业界非常流行,很多商业项目选择Protobuf作为编解码框架,Protobuf的优点。
(1)在谷歌内部长期使用,产品成熟度高;
(2)跨语言,支持多种语言,包括C++、Java和Python;
(3)编码后的消息更小,更加有利于存储和传输;
(4)编解码的性能非常高;
(5)支持不同协议版本的前向兼容;
(6)支持定义可选和必选字段。
Protobuf的入门
Protobuf是一个灵活、高效、结构化的数据序列化框架,相比于XML等传统的序列化工具,它更小,更快,更简单。Protobuf支持数据结构化一次可以到处使用,甚至跨语言使用,通过代码生成工具可以自动生成不同语言版本的源代码,甚至可以在使用不同版本的数据结构进程间进行数据传递,实现数据结构的前向兼容。
Netty的Protobuf应用开发
服务端代码示例:
import cn.sf.redis.socket.javaser.SubReqServerHandler;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.protobuf.ProtobufDecoder;
import io.netty.handler.codec.protobuf.ProtobufEncoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler; public class SubReqServer {
public void bind(int port) throws Exception {
// 配置服务端的NIO 线程组
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 100)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer() {
@Override
public void initChannel(Channel ch) {
//首先向ChannelPipeline添加ProtobufVarint32FrameDecoder,它主要用于半包处理
ch.pipeline().addLast(new ProtobufVarint32FrameDecoder());
//随后继续添加ProtobufDecoder解码器,它的参数是com.google.protobuf.MessageLite,
//实际上就是要告诉ProtobufDecoder需要解码的目标类是什么,
//否则仅仅从字节数组中是无法判断出要解码的目标类型信息的。
ch.pipeline().addLast(
new ProtobufDecoder(
SubscribeReqProto.SubscribeReq
.getDefaultInstance()));
ch.pipeline().addLast(
new ProtobufVarint32LengthFieldPrepender());
ch.pipeline().addLast(new ProtobufEncoder());
ch.pipeline().addLast(new SubReqServerHandler());
}
}); // 绑定端口,同步等待成功
ChannelFuture f = b.bind(port).sync(); // 等待服务端监听端口关闭
f.channel().closeFuture().sync();
} finally {
// 优雅退出,释放线程池资源
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
} public static void main(String[] args) throws Exception {
int port = 8080;
if (args != null && args.length > 0) {
try {
port = Integer.valueOf(args[0]);
} catch (NumberFormatException e) {
// 采用默认值
}
}
new SubReqServer().bind(port);
}
} import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext; @ChannelHandler.Sharable
public class SubReqServerHandler extends ChannelHandlerAdapter { @Override
public void channelRead(ChannelHandlerContext ctx, Object msg)
throws Exception {
//由于ProtobufDecoder已经对消息进行了自动解码,因此接收到的订购请求消息可以直接使用。
SubscribeReqProto.SubscribeReq req = (SubscribeReqProto.SubscribeReq) msg;
//对用户名进行校验,校验通过后构造应答消息返回给客户端
if ("Lilinfeng".equalsIgnoreCase(req.getUserName())) {
System.out.println("Service accept client subscribe req : ["+ req.toString() + "]");
//由于使用了ProtobufEncoder,所以不需要对SubscribeRespProto.SubscribeResp进行手工编码。
ctx.writeAndFlush(resp(req.getSubReqID()));
}
} private SubscribeRespProto.SubscribeResp resp(int subReqID) {
SubscribeRespProto.SubscribeResp.Builder builder = SubscribeRespProto.SubscribeResp.newBuilder();
builder.setSubReqID(subReqID);
builder.setRespCode(0);
builder.setDesc("Netty book order succeed, 3 days later, sent to the designated address");
return builder.build();
} @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();// 发生异常,关闭链路
}
}
客户端代码示例:
import cn.sf.redis.socket.javaser.SubReqClientHandler;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.protobuf.ProtobufDecoder;
import io.netty.handler.codec.protobuf.ProtobufEncoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender; public class SubReqClient { public void connect(int port, String host) throws Exception {
// 配置客户端NIO 线程组
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group).channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true)
.handler(new ChannelInitializer() {
@Override
public void initChannel(Channel ch)
throws Exception {
ch.pipeline().addLast(new ProtobufVarint32FrameDecoder());
//客户端需要解码的对象是订购响应,
//所以使用SubscribeResp Proto.SubscribeResp的实例做入参。
ch.pipeline().addLast(
new ProtobufDecoder(
SubscribeRespProto.SubscribeResp
.getDefaultInstance()));
ch.pipeline().addLast(new ProtobufVarint32LengthFieldPrepender());
ch.pipeline().addLast(new ProtobufEncoder());
ch.pipeline().addLast(new SubReqClientHandler());
}
}); // 发起异步连接操作
ChannelFuture f = b.connect(host, port).sync(); // 等待客户端链路关闭
f.channel().closeFuture().sync();
} finally {
// 优雅退出,释放NIO 线程组
group.shutdownGracefully();
}
} public static void main(String[] args) throws Exception {
int port = 8080;
if (args != null && args.length > 0) {
try {
port = Integer.valueOf(args[0]);
} catch (NumberFormatException e) {
// 采用默认值
}
}
new SubReqClient().connect(port, "127.0.0.1");
}
} import com.google.common.collect.Lists;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext; import java.util.List; public class SubReqClientHandler extends ChannelHandlerAdapter { public SubReqClientHandler() {
} @Override
public void channelActive(ChannelHandlerContext ctx) {
for (int i = 0; i < 10; i++) {
ctx.write(subReq(i));
}
ctx.flush();
} private SubscribeReqProto.SubscribeReq subReq(int i) {
SubscribeReqProto.SubscribeReq.Builder builder = SubscribeReqProto.SubscribeReq
.newBuilder();
builder.setSubReqID(i);
builder.setUserName("Lilinfeng");
builder.setProductName("Netty Book For Protobuf");
List<String> address = Lists.newArrayList();
address.add("NanJing YuHuaTai");
address.add("BeiJing LiuLiChang");
address.add("ShenZhen HongShuLin");
builder.addAllAddress(address);
return builder.build();
} @Override
public void channelRead(ChannelHandlerContext ctx, Object msg)
throws Exception {
System.out.println("Receive server response : [" + msg + "]");
} @Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.flush();
} @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}
运行结果:
服务端运行结果如下:
Service accept client subscribe req : [subReqID: 0
userName: "Lilinfeng"
productName: "Netty Book For Protobuf"
address: "NanJing YuHuaTai"
address: "BeiJing LiuLiChang"
address: "ShenZhen HongShuLin"
]
.....................................................................
Service accept client subscribe req : [subReqID: 9
userName: "Lilinfeng"
productName: "Netty Book For Protobuf"
address: "NanJing YuHuaTai"
address: "BeiJing LiuLiChang"
address: "ShenZhen HongShuLin"
]
客户端运行结果如下。
Receive server response : [subReqID: 0
respCode: 0
desc: "Netty book order succeed, 3 days later, sent to the designated address"
]
.....................................................................
Receive server response : [subReqID: 9
respCode: 0
desc: "Netty book order succeed, 3 days later, sent to the designated address"
]
利用Netty提供的Protobuf编解码能力,我们在不需要了解Protobuf实现和使用细节的情况下就能轻松支持Protobuf编解码,可以方便地实现跨语言的远程服务调用和与周边的异构系统进行通信对接。
Protobuf的使用注意事项
ProtobufDecoder仅仅负责解码,它不支持读半包。因此,在ProtobufDecoder前面,一定要有能够处理读半包的解码器,有三种方式可以选择。
- 使用Netty提供的ProtobufVarint32FrameDecoder,它可以处理半包消息;
- 继承Netty提供的通用半包解码器LengthFieldBasedFrameDecoder;
- 继承ByteToMessageDecoder类,自己处理半包消息。
如果你只使用ProtobufDecoder解码器而忽略对半包消息的处理,程序是不能正常工作的。
服务端注释掉ProtobufVarint32FramepDecoder:
编解码-protobuf的更多相关文章
- 【MINA】用protobuf做编解码协议
SOCKET协议 支持java serial 与 AMF3的混合协议,目前没有基于xml 与 json的实现. 协议说明: * 9个字节协议头+协议体. * * 协议头1-4字节表示协议长度 =协议体 ...
- (中级篇 NettyNIO编解码开发)第八章-Google Protobuf 编解码-2
8.1.2 Protobuf编解码开发 Protobuf的类库使用比较简单,下面我们就通过对SubscrjbeReqProto进行编解码来介绍Protobuf的使用. 8-1 Protob ...
- (中级篇 NettyNIO编解码开发)第八章-Google Protobuf 编解码-1
Google的Protobuf在业界非常流行,很多商业项目选择Protobuf作为编解码框架,这里一起回顾一下Protobuf 的优点.(1)在谷歌内部长期使用,产品成熟度高:(2)跨语言,支持 ...
- Netty--Google Protobuf编解码
Google Protobuf是一种轻便高效的结构化数据存储格式,可以用于结构化数据序列化.它很适合做数据存储或 RPC 数据交换格式.可用于通讯协议.数据存储等领域的语言无关.平台无关.可扩展的序列 ...
- Netty游戏服务器之四protobuf编解码和黏包处理
我们还没讲客户端怎么向服务器发送消息,服务器怎么接受消息. 在讲这个之前我们先要了解一点就是tcp底层存在粘包和拆包的机制,所以我们在进行消息传递的时候要考虑这个问题. 看了netty权威这里处理的办 ...
- Netty学习(七)-Netty编解码技术以及ProtoBuf和Thrift的介绍
在前几节我们学习过处理粘包和拆包的问题,用到了Netty提供的几个解码器对不同情况的问题进行处理.功能很是强大.我们有没有去想这么强大的功能是如何实现的呢?背后又用到了什么技术?这一节我们就来处理这个 ...
- (中级篇 NettyNIO编解码开发)第六章-编解码技术
基于Java提供的对象输入/输出流ObjectlnputStream和ObjectOutputStream,可以直接把Java对象作为可存储的字节数组写入文件,也可以传输到网络上.对程序员来说,基于J ...
- Netty 编解码技术 数据通信和心跳监控案例
Netty 编解码技术 数据通信和心跳监控案例 多台服务器之间在进行跨进程服务调用时,需要使用特定的编解码技术,对需要进行网络传输的对象做编码和解码操作,以便完成远程调用.Netty提供了完善,易扩展 ...
- Netty编解码技术
编解码技术,说白了就是java序列化技术,序列化目的就两个,第一进行网络传输,第二对象持久化. 虽然我们可以使用java进行对象序列化,netty去传输,但是java序列化的硬伤比较多,比如java序 ...
随机推荐
- 跨浏览器的事件对象-------EventUtil 中的方法及用法
什么是EventUti----封装好的事件对象 在JavaScript中,DOM0级.DOM2级与旧版本IE(8-)为对象添加事件的方法不同 为了以跨浏览器的方式处理事件,需要编写一段“通用代码”,即 ...
- ORACLE、MYSQL的JDBC配置
info.jdbc.driverClassName=oracle.jdbc.driver.OracleDriver info.jdbc.url=jdbc:oracle:thin:@192.168.18 ...
- [Android进阶]学习AccessibilityService实现微信抢红包插件
在你的手机更多设置或者高级设置中,我们会发现有个无障碍的功能,很多人不知道这个功能具体是干嘛的,其实这个功能是为了增强用户界面以帮助残障人士,或者可能暂时无法与设备充分交互的人们 它的具体实现是通过A ...
- 轻轻送送为你的App加点特效
前言 今天突然在一个应用中看到一个转场动画,蛮有意思的 退出动画 进入动画 ActivityOptionsCompat options = ActivityOptionsCompat.makeScal ...
- 【文件】读取一个文件夹下所有的jpg图片
今天做视频处理的时候,发现给的视频是用jpg图片的形式给出的,名字的命名规律性不是很强.就想找一种通用的遍历文件夹下图片的方法. 开始在网上找到了下面这份代码,发现只能读取所有的文件夹,文件都被跳过了 ...
- 无限轮播的N+2 策略
N张照片把contentsSize设置为N+2个图片的宽度,例子如下,两端填充如图,当处于一端时,且即将进入循环状态的时候,如第二张图,从状态1滑动到状态2,在滑动结束的时候,将当前的位置直接转到状态 ...
- 25个增强iOS应用程序性能的提示和技巧(初级篇)
25个增强iOS应用程序性能的提示和技巧(初级篇) 标签: ios内存管理性能优化 2013-12-13 10:53 916人阅读 评论(0) 收藏 举报 分类: IPhone开发高级系列(34) ...
- 假期(codevs 3622)
题目描述 Description 经过几个月辛勤的工作,FJ决定让奶牛放假.假期可以在1-N天内任意选择一段(需要连续),每一天都有一个享受指数W.但是奶牛的要求非常苛刻,假期不能短于P天,否则奶牛不 ...
- iOS - 直播相关文章
直播相关文章 直播RTMP可用于测试的服务器地址 FFmpeg avdumpformat输出的tbn.tbc.tbr.PAR.DAR的含义 FFmpeg 3.0 计算视频时长 HLS Streamin ...
- 昨天在公司加班,上午好像就是弄一个ftp的linux服务问题
在网上找了一些方法,可是其中有通过匿名方式登陆,但是在root的权限下才能存放文件,可是把匿名用户登陆取消之后又不能登陆,就是没有列出怎么来添加一个ftp的用户,今天打算直接装一个linux系统在虚拟 ...