Protocol Buffer整合netty
1-1 前言
我们是在学习netty的背景下,学些protocol Buffer的,所以我们在我们的实践文档,也是在这样的背景下整理出来的。
1-2 编辑简单的Proco文件
1-2-1 Student .proto
syntax = "proto2";
package com.zt.proto;
option optimize_for = SPEED;
option java_package = "com.zt.proto";
option java_outer_classname = "MyDataInfo";
message Person {
required string name =1;
optional int32 age=2;
optional string address=3;
}
-
syntax :指定使用的proto的版本 我们这里使用的是proto2
-
package :指定生成的java的类的包路径,如果 java_package不指定,就是这个目录
-
optimize_for:文件级别的选项,Protocol Buffer定义三种优化级别SPEED/CODE_SIZE/LITE_RUNTIME。缺省情况下是SPEED
-
SPEED:表示生成的代码运行效率高,但是由此生成的代码编译后会占用更多的空间。
-
CODE_SIZE: 和SPEED恰恰相反,代码运行效率较低,但是由此生成的代码编译后会占用更少的空间,通常用于资源有限的平台,如Mobile。
-
LITE_RUNTIME: 生成的代码执行效率高,同时生成代码编译后的所占用的空间也是非常少。这是以牺牲Protocol Buffer提供的反射功能为代价的。因此我们在C++中链接Protocol Buffer库时仅需链接libprotobuf-lite,而非libprotobuf。在Java中仅需包含protobuf-java-2.4.1-lite.jar,而非protobuf-java-2.4.1.jar。
-
java_package :表示生成的java类的包路径,官方建议,我们在编辑proto文件到额时候,最好还是指定。
-
java_outer_classname :表示生成的Java类的类名
-
message :指定一个消息协议
-
required:表示在传输数据的的时候这个字段为必须传输的数据。
-
optional:对应required,可选输出数据。
-
string 和int32 :还有其他的类型,指定的数据的类型
可以在程序调试阶段使用 SPEED模式,而上线以后使用提升性能使用 LITE_RUNTIME 模式优化。
1-2-2 生成Java类和小测试
- 生成的命令
protoc --java_out=src\main\java src\protobuf\Student.proto
-
生成的java类
生成的Java类有点大 这里就不贴出来。
-
测试小程序
package com.zt.proto;
import com.google.protobuf.InvalidProtocolBufferException;
/**
* @Description:
* @Date: 2020/12/15 10:37
* @author: zt
*/
public class ProtoBufTest {
public static void main(String[] args) throws InvalidProtocolBufferException {
MyDataInfo.Person person = MyDataInfo.Person.newBuilder()
.setName("张山")
.setAge(1111)
.setAddress("成都")
.build();
byte[] bytes = person.toByteArray();
// 字节数组可以网络传输 实现不同机子的远程过程调用
MyDataInfo.Person person1 = MyDataInfo.Person.parseFrom(bytes);
System.out.println(person1.getAddress());
System.out.println(person1.getAge());
System.out.println(person1.getName());
}
}
- 测试小程序的运行结果
成都
1111
张山
Process finished with exit code 0
1-3 整合netty和proco
1-3-1 proco文件
syntax = "proto2";
package com.zt.proto;
option optimize_for = SPEED;
option java_package = "com.zt.proto";
option java_outer_classname = "MyDataInfo";
message Person {
required string name =1;
optional int32 age=2;
optional string address=3;
}
1-3-2 netty相关
1-3-2-1 TestServer
package com.zt.proto;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
/**
* @Description:
* @Date: 2020/12/15 10:53
* @author: zt
*/
public class TestServer {
public static void main(String[] args) {
NioEventLoopGroup bossGroup = new NioEventLoopGroup();
NioEventLoopGroup workGroup = new NioEventLoopGroup();
try {
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup,workGroup).channel(NioServerSocketChannel.class)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new TestInitializer());
ChannelFuture channelFuture = serverBootstrap.bind(8899).sync();
channelFuture.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
bossGroup.shutdownGracefully();
workGroup.shutdownGracefully();
}
}
}
1-3-2-2 TestInitializer
package com.zt.proto;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
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;
/**
* @Description:
* @Date: 2020/12/15 10:59
* @author: zt
*/
public class TestInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new ProtobufVarint32FrameDecoder());
pipeline.addLast(new ProtobufDecoder(MyDataInfo.Person.getDefaultInstance()));
pipeline.addLast(new ProtobufVarint32LengthFieldPrepender());
// 第一次没出现Bf shu'xua是ProtobufEncoder
pipeline.addLast(new ProtobufEncoder());
pipeline.addLast(new TestServerHandler());
}
}
1-3-2-3 TestServerHandler
package com.zt.proto;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
/**
* @Description:
* @Date: 2020/12/15 14:27
* @author: zt
*/
public class TestServerHandler extends SimpleChannelInboundHandler<MyDataInfo.Person> {
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, MyDataInfo.Person person) throws Exception {
System.out.println(person.getAddress());
System.out.println(person.getAge());
System.out.println(person.getName());
}
}
1-3-2-4 TestClient
package com.zt.proto;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
/**
* @Description:
* @Date: 2020/12/15 14:28
* @author: zt
*/
public class TestClient {
public static void main(String[] args) {
EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
try {
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(eventLoopGroup).channel(NioSocketChannel.class)
.handler(new TestClientInitializer());
ChannelFuture channelFuture = bootstrap.connect("localhost", 8899).sync();
channelFuture.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
eventLoopGroup.shutdownGracefully();
}
}
}
1-3-2-5 TestClientInitializer
package com.zt.proto;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
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;
/**
* @Description:
* @Date: 2020/12/15 14:31
* @author: zt
*/
public class TestClientInitializerextends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new ProtobufVarint32FrameDecoder());
pipeline.addLast(new ProtobufDecoder(MyDataInfo.Person.getDefaultInstance()));
pipeline.addLast(new ProtobufVarint32LengthFieldPrepender());
pipeline.addLast(new ProtobufEncoder());
pipeline.addLast(new TestClientHandler());
}
}
1-3-2-6 TestClientHandler
package com.zt.proto;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
/**
* @Description:
* @Date: 2020/12/15 14:37
* @author: zt
*/
public class TestClientHandler extends SimpleChannelInboundHandler<MyDataInfo.Person> {
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, MyDataInfo.Person person) throws Exception {
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
MyDataInfo.Person person = MyDataInfo.Person.newBuilder()
.setName("张山")
.setAge(1111)
.setAddress("成都")
.build();
ctx.channel().writeAndFlush(person);
System.out.println("TestClientHandler:channelActive");
}
}
1-3-2-7 pom文件补充
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.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">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>ProtocolDemo2</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<!--设置java的编译的jdk版本-->
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.source>1.8</maven.compiler.source>
<protobufJavaVersion>3.11.0</protobufJavaVersion>
<protobufJavaUtilVersion>3.11.0</protobufJavaUtilVersion>
<nettyAllVersion>4.1.40.Final</nettyAllVersion>
</properties>
<dependencies>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>${protobufJavaVersion}</version>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java-util</artifactId>
<version>${protobufJavaUtilVersion}</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>${nettyAllVersion}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
1-3-2-8 简单说明下
netty相关的点,我这里就不赘述,前面的文档都有相关的说明。
1-3-3 测试和总结
1-3-3-1 server的日志
十二月 17, 2020 10:33:53 上午 io.netty.handler.logging.LoggingHandler channelRegistered
信息: [id: 0x916b2ebf] REGISTERED
十二月 17, 2020 10:33:53 上午 io.netty.handler.logging.LoggingHandler bind
信息: [id: 0x916b2ebf] BIND: 0.0.0.0/0.0.0.0:8899
十二月 17, 2020 10:33:53 上午 io.netty.handler.logging.LoggingHandler channelActive
信息: [id: 0x916b2ebf, L:/0:0:0:0:0:0:0:0:8899] ACTIVE
十二月 17, 2020 10:34:04 上午 io.netty.handler.logging.LoggingHandler channelRead
信息: [id: 0x916b2ebf, L:/0:0:0:0:0:0:0:0:8899] READ: [id: 0x07922e1b, L:/127.0.0.1:8899 - R:/127.0.0.1:59693]
十二月 17, 2020 10:34:04 上午 io.netty.handler.logging.LoggingHandler channelReadComplete
信息: [id: 0x916b2ebf, L:/0:0:0:0:0:0:0:0:8899] READ COMPLETE
成都
1111
张山
1-3-3-2 client的日志
TestClientHandler:channelActive
1-3-3-3 总结
-
基本上程序是成功的运行了,我这里简单的说明下,在1-2小章节中我们写了小程序测试下,哪里我
们是自己编码和解码,我们这个整合netty的时候,发现我们没有解码和编码,其实交给了netty帮我
们 处理了。 - 整合netty的大部分还是netty的部分,proco的部分编码还是没几行代码。
- 我们这里是最的数据通信协议,要是我们存在多种数据通信协议,我们怎末处理?我会在后面整理出来。