编解码-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序 ...
随机推荐
- eclipse修改jdk后版本冲突问题
将安装的jdk1.8改为1.7之后出现了很淡疼的问题 修改工程下.setting/ org.eclipse.jdt.core.prefs eclipse.preferences.version=1or ...
- 51nod 1449 砝码称重(贪心算法)
题目:传送门. 题意:中文题. 题解:左物右码,w进制.m%w==0||m%w==1||m%w==w-1都是可以的,否则是NO. #include <iostream> #include ...
- Java异常题库
一.填空题 __异常处理__机制是一种非常有用的辅助性程序设计方法.采用这种方法可以使得在程序设计时将程序的正常流程与错误处理分开,有利于代码的编写和维护. 在Java异常处理中可以使用多个catch ...
- ASP.NET SignalR 与 LayIM2.0 配合轻松实现Web聊天室(九) 之 用 Redis 实现用户在线离线状态消息处理(一)
前言 上一篇中简单讲解了用Redis缓存在线用户逻辑.篇幅也比较小,本篇将详细实现用户的上线下线通知.图片效果转换功能.而且,代码和开发思路都会详细介绍. 效果展示 目前有三个用户,user1,use ...
- Linux(Ubuntu)之设定开机自启动
分两种:对自建脚本,对已安装服务 对已安装服务, 只需在/etc/rc.local中添加相应的启动命令,如: 重启后,即可自启动服务. 对自建脚本自启动: 创建脚本test,修改其权限为755,如: ...
- 苹果应用 Windows 申请 普通证书 和Push 证书 Hbuilder 个推
最近使用Hbuilder 进行了HTML5开发,因为 HTML5 可以放在android 机器上,也可以放到 IOS机器上,所以很感兴趣,于是开发了一个小应用, 不过问题接着来了: 图1 如图所示:当 ...
- 无根树转有根树(dfs,tree)
#include <bits/stdc++.h> #include <iostream> #include <queue> #include <stdio.h ...
- JavaScript基础——实现循环
循环是多次执行同一段代码的一种手段.当你需要在一个数组或对象集上重复执行相同的任务时,这是非常有用的. JavaScript提供执行for和while循环的功能. 1.while循环 JavaScri ...
- Asp.Net - 7.AJAX
AJAX简介 没有AJAX会怎么样?普通的ASP.Net每次执行服务端方法的时候都要刷新当前页面,比如实现显示服务器的时间.每次都要刷新面的坏处:页面刷新打断用户操作.速度慢.增加服务器的流量压力.如 ...
- 攻城狮在路上(贰) Spring(三)--- Spring 资源访问利器Resource接口
Spring为了更好的满足各种底层资源的访问需求.设计了一个Resource接口,提供了更强的访问底层资源的能力.Spring框架使用Resource装载各种资源,包括配置文件资源.国际化属性文件资源 ...