Netty结合Protobuf进行编解码的方法

网友投稿 777 2023-01-04

Netty结合Protobuf进行编解码的方法

Netty结合Protobuf进行编解码的方法

一般在使用netty时,数据传输的时候都会选择对传输的数据进行编解码,编码后的数据变小, 有利于在有限的带宽下传输更多的数据。

由于java本身序列化的缺点较多(无法跨语言,序列化后的码流太大,序列化的性能太低等),业界主流的编解码框架主要有如下三个:

Google的Protobuf

Facebook的Thrift

JBoss的Marshalling

今天我们简单介绍一下Netty结合google的Protobuf框架进行数据的编解码。

1. 什么是Protobuf?

Protobuf全称是Google Protocol Buffers, 它是谷歌公司开源的一个序列化框架。

它将数据结构以.proto文件进行描述,通过代码生成工具可以生成对应数据结构的POJO对象和Protobuf相关的方法和属性。

它的特点如下:

结构化数据存储格式

高效的编解码性能

语言无关、平台无关、扩展性好

官方支持多个语言(java,c++,python,c#等)

2. -安装

Protobuf已经托管在github上,可以在release页面-,本案例使用的是v3.6(要-后缀为-win32.zip的)。

-后,将压缩包进行解压。这里主要用到bin目录下的protoc.exe。

3. 定义好proto文件

SubscribeReq.proto

// 区分不同的protobuf版本,必须有

syntax = "proto2";

package netty;

// 生成的目标类的包路径

option java_package = "cn.ddlover-tystudy.protobuf";

// 生成的目标类的名字

option java_outer_classname = "SubscribeReqProto";

message SubscribeReq{

required int32 subReqID = 1;

required string userName = 2;

required string productName = 3;

repeated string address = 4;

}

SubscribeResp.proto

syntax = "proto2";

package netty;

option java_package = "cn.ddlover-tystudy.protobuf";

option java_outer_classname = "SubscribeRespProto";

message SubscribeResp{

required int32 subReqID = 1;

required int32 respCode = 2;

required string desc = 3;

}

此时项目结构如下

重点关注一下两个proto文件的位置,是位于项目的根路径下

4. 分别执行以下命令

D:\xhb\protoc-3.6.1-win32\bin\protoc.exe --java_out=.\src\main\java SubscribeResp.proto

D:\xhb\protoc-3.6.1-win32\bin\protoc.exe --java_out=.\src\main\java SubscribeReq.proto

这里需要把protoc.exe的位置换成自己的, --java_out命令设置的是proto文件中java_package指令的父目录。

5. 此时项目结构如下

下面开始代码方面的开发,

1. 先贴一波maven的配置

xmlns:xsi="http://w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

4.0.0

cn.ddlover

nettystudy

1.0-SNAPSHOT

1.8

io-ty

netty-all

4.1.33.Final

com.google.protobuf

protobuf-java

3.6.1

org.projectlombok

lombok

1.18.4

xmlns:xsi="http://w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

4.0.0

cn.ddlover

nettystudy

1.0-SNAPSHOT

1.8

io-ty

netty-all

4.1.33.Final

com.google.protobuf

protobuf-java

3.6.1

org.projectlombok

lombok

1.18.4

2. 这里贴上完整代码的项目结构

3. SubReqServer.java

import cn.ddlover-tystudy.handler.SubReqServerHandler;

import cn.ddlover-tystudy.protobuf.SubscribeReqProto;

import io-ty.bootstrap.ServerBootstrap;

import io-ty.channel.ChannelFuture;

import io-ty.channel.ChannelInitializer;

import io-ty.channel.ChannelOption;

import io-ty.channel.EventLoopGroup;

import io-ty.channel.nio.NioEventLoopGroup;

import io-ty.channel.socket.SocketChannel;

import io-ty.channel.socket.nio.NioServerSocketChannel;

import io-ty.handler.codec.protobuf.ProtobufDecoder;

import io-ty.handler.codec.protobuf.ProtobufEncoder;

import io-ty.handler.codec.protobuf.ProtobufVarint32FrameDecoder;

import io-ty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender;

import io-ty.handler.logging.LogLevel;

import io-ty.handler.logging.LoggingHandler;

/**

* Protobuf版本图书订购代码

*/

public class SubReqServer {

private static final int PORT = 8080;

public static void main(String[] args) {

bind(PORT);

}

private static void bind(int port) {

EventLoopGroup bossGroup = new NvvSKGioEventLoopGroup();

EventLoopGroup workerGroup = new NioEventLoopGroup();

ServerBootstrap b = new ServerBootstrap();

try {

b.group(bossGroup, workerGroup)

.channel(NioServerSocketChannel.class)

.option(ChannelOption.SO_BACKLOG, 100)

.handler(new LoggingHandler(LogLevel.INFO))

.childHandler(new ChannelInitializer() {

@Override

protected void initChannel(SocketChannel socketChannel) throws Exception {

// 半包处理

socketChannel.pipeline().addLast(new ProtobufVarint32FrameDecoder());

socketChannel.pipeline().addLast(new ProtobufDecoder(SubscribeReqProto.SubscribeReq.getDefaultInstance()));

socketChannel.pipeline().addLast(new ProtobufVarint32LengthFieldPrepender());

socketChannel.pipeline().addLast(new ProtobufEncoder());

socketChannel.pipeline().addLast(new SubReqServerHandler());

}

});

ChannelFuture future = b.bind(port).sync();

future.channel().closeFuture().sync();

} catch (InterruptedException e) {

e.printStackTrace();

}finally {

bossGroup.shutdownGracefully();

workerGroup.shutdownGracefully();

}

}

}

4. SubReqServerHandler.java

import cn.ddlover-tystudy.protobuf.SubscribeReqProto;

import cn.ddlover-tystudy.protobuf.SubscribeRespProto;

import io-ty.channel.ChannelHandlerContext;

import io-ty.channel.ChannelInboundHandlerAdapter;

public class SubReqServerHandler extends ChannelInboundHandlerAdapter {

@Override

public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {

SubscribeReqProto.SubscribeReq req = (SubscribeReqProto.SubscribeReq)msg;

if ("张三".equals(req.getUserName())) {

System.out.println("Server accept clietn subscribe req : ["+req.toString()+"]");

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书籍下单成功,3天后将会送到你的住处");

return builder. build();

}

@Override

public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {

cause.printStackTrace();

ctx.close();

}

}

5. SubReqClient.java

import cn.ddlover-tystudy.handler.SubReqClientHandler;

import cn.ddlover-tystudy.protobuf.SubscribeRespProto;

import io-ty.bootstrap.Bootstrap;

import io-ty.channel.ChannelFuture;

import io-ty.channel.ChannelInitializer;

import io-ty.channel.ChannelOption;

import io-ty.channel.EventLoopGroup;

import io-ty.channel.nio.NioEventLoopGroup;

import io-ty.channel.socket.SocketChannel;

import io-ty.channel.socket.nio.NioSocketChannel;

import io-ty.handler.codec.protobuf.ProtobufDecoder;

import io-ty.handler.codec.protobuf.ProtobufEncoder;

import io-ty.handler.codec.protobuf.ProtobufVarint32FrameDecoder;

import io-ty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender;

public class SubReqClient {

private static final String HOST = "localhost";

private static final int PORT = 8080;

public static void main(String[] args) {

connect(HOST, PORT);

}

private static void connect(String host, int port) {

EventLoopGroup group = new NioEventLoopGroup();

Bootstrap b = new Bootstrap();

try {

b.group(group).channel(NioSocketChannel.class)

.option(ChannelOption.TCP_NODELAY, true)

.handler(new ChannelInitializer() {

@Override

protected void initChannel(SocketChannel socketChannel) throws Exception {

// 半包处理

socketChannel.pipeline().addLast(new ProtobufVarint32FrameDecoder());

socketChannel.pipeline().addLast(new ProtobufDecoder(SubscribeRespProto.SubscribeResp.getDefaultInstance()));

socketChannel.pipeline().addLast(new ProtobufVarint32LengthFieldPrepender());

socketChannel.pipeline().addLast(new ProtobufEncoder());

socketChannel.pipeline().addLast(new SubReqClientHandler());

}

});

ChannelFuture f = b.connect(host, port).sync();

f.channel().closeFuture().sync();

} catch (InterruptedException e) {

e.printStackTrace();

}finally {

group.shutdownGracefully();

}

}

}

6. SubReqClientHandler.java

import cn.ddlover-tystudy.protobuf.SubscribeReqProto;

import io-ty.channel.ChannelHandlerContext;

import io-ty.channel.ChannelInboundHandlerAdapter;

import java.util.ArrayList;

import java.util.List;

public class SubReqClientHandler extends ChannelInboundHandlerAdapter {

@Override

public void channelActive(ChannelHandlerContext ctx) throws Exception {

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("张三");

builder.setProductName("Netty Book");

List address = new ArrayList<>();

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 exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {

cause.printStackTrace();

ctx.close();

}

}

代码部分到此就结束了,然后分别运行SubReqServer和SubReqClient, 就可以发现运行成功了。

这里要注意的事,调用toString的时候,中文在控制台打印的是字节,而调用对应属性的getter方法的时候,是可以做中文的判断的,显然这里protobuf的toString没有被修改好呀。

当然,我们也可以发现,netty本身已经封装好了对谷歌的protobuf的支持。Netty还是很强大的。

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:招商银行小程序开放平台(招商银行的微信小程序)
下一篇:车载软件(车载软件测试)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~