Netty Google Protobuf
2021-03-12 20:31
标签:ble interface 关于 complete 监控 geo sign builder inbound 1.编码和解码的基本介绍 : 2.Netty 本身的编码解码的机制和问题分析 1) Netty 自身提供了一些 codec(编解码器) 而 Java 序列化技术本身效率就不高, 存在如下问题无法跨语言序列化后的体积太大, 是二进制编码的 5 倍多。序列化性能太低 3.Protobuf
2) Protobuf 是 Google 发布的开源项目, 全称 Google Protocol Buffers, 是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化, 或者说序列化。 它很适合做数据存储或 RPC[远程过程调用 remote procedure call ] 数据交换格式 。目前很多公司 http+json tcp+protobuf 3) 参考文档 : https://developers.google.com/protocol-buffers/docs/proto 语言指南 4 Protobuf 快速入门实例 编写程序, 使用 Protobuf 完成如下功能 Student.proto 编译
1) 编写网络应用程序时, 因为数据在网络中传输的都是二进制字节码数据, 在发送数据时就需要编码, 接收数据时就需要解码 [示意图]
2) codec(编解码器) 的组成部分有两个: decoder(解码器)和 encoder(编码器)。 encoder 负责把业务数据转换成字节码数据, decoder 负责把字节码数据转换成业务数据
2) Netty 提供的编码器
StringEncoder, 对字符串数据进行编码
ObjectEncoder, 对 Java 对象进行编码
...
3) Netty 提供的解码器
StringDecoder, 对字符串数据进行解码
ObjectDecoder, 对 Java 对象进行解码
...
4) Netty 本身自带的 ObjectDecoder 和 ObjectEncoder 可以用来实现 POJO 对象或各种业务对象的编码和解码, 底层使用的仍是 Java 序列化技术 ,
5) => 引出 新的解决方案 [Google 的 Protobuf]
1) Protobuf 基本介绍和使用示意图
4) Protobuf 是以 message 的方式来管理数据的.
5) 支持跨平台、 跨语言, 即[客户端和服务器端可以是不同的语言编写的] (支持目前绝大多数语言, 例如 C++、C#、 Java、 python 等)
6) 高性能, 高可靠性
7) 使用 protobuf 编译器能自动生成代码, Protobuf 是将类的定义使用.proto 文件进行描述。 说明, 在 idea 中编写 .proto 文件时, 会自动提示是否下载 .ptotot 编写插件. 可以让语法高亮。
8) 然后通过 protoc.exe 编译器根据.proto 自动生成.java 文件
1) 客户端可以发送一个 Student PoJo 对象到服务器 (通过 Protobuf 编码)
2) 服务端能接收 Student PoJo 对象, 并显示信息(通过 Protobuf 解码)
syntax = "proto3"; //版本
option java_outer_classname = "StudentPOJO";//生成的外部类名, 同时也是文件名
//protobuf 使用 message 管理数据
message Student { //会在 StudentPOJO 外部类生成一个内部类 Student, 他是真正发送的 POJO 对象
int32 id = 1; // Student 类中有 一个属性 名字为 id 类型为 int32(protobuf 类型) 1 表示属性序号, 不是值
string name = 2;
}
protoc.exe --java_out=. Student.proto
将生成的 StudentPOJO 放入到项目使用
NettyClient
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.protobuf.ProtobufEncoder;
public class NettyClient {
public static void main(String[] args) throws Exception {
//客户端需要一个事件循环组
EventLoopGroup group = new NioEventLoopGroup();
try {
//创建客户端启动对象
//注意客户端使用的不是 ServerBootstrap 而是 Bootstrap
Bootstrap bootstrap = new Bootstrap();
//设置相关参数
bootstrap.group(group) //设置线程组
.channel(NioSocketChannel.class) // 设置客户端通道的实现类(反射)
.handler(new ChannelInitializer
NettyClientHandler
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;
public class NettyClientHandler extends ChannelInboundHandlerAdapter {
//当通道就绪就会触发该方法
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
//发生一个Student 对象到服务器
StudentPOJO.Student student = StudentPOJO.Student.newBuilder().setId(4).setName("智多星 吴用").build();
//Teacher , Member ,Message
ctx.writeAndFlush(student);
}
//当通道有读取事件时,会触发
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf buf = (ByteBuf) msg;
System.out.println("服务器回复的消息:" + buf.toString(CharsetUtil.UTF_8));
System.out.println("服务器的地址: "+ ctx.channel().remoteAddress());
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
NettyServer
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.protobuf.ProtobufDecoder;
public class NettyServer {
public static void main(String[] args) throws Exception {
//创建BossGroup 和 WorkerGroup
//说明
//1. 创建两个线程组 bossGroup 和 workerGroup
//2. bossGroup 只是处理连接请求 , 真正的和客户端业务处理,会交给 workerGroup完成
//3. 两个都是无限循环
//4. bossGroup 和 workerGroup 含有的子线程(NioEventLoop)的个数
// 默认实际 cpu核数 * 2
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup(); //8
try {
//创建服务器端的启动对象,配置参数
ServerBootstrap bootstrap = new ServerBootstrap();
//使用链式编程来进行设置
bootstrap.group(bossGroup, workerGroup) //设置两个线程组
.channel(NioServerSocketChannel.class) //使用NioSocketChannel 作为服务器的通道实现
.option(ChannelOption.SO_BACKLOG, 128) // 设置线程队列得到连接个数
.childOption(ChannelOption.SO_KEEPALIVE, true) //设置保持活动连接状态
// .handler(null) // 该 handler对应 bossGroup , childHandler 对应 workerGroup
.childHandler(new ChannelInitializer
NettyServerHandler
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.util.CharsetUtil;
/*
说明
1. 我们自定义一个Handler 需要继续netty 规定好的某个HandlerAdapter(规范)
2. 这时我们自定义一个Handler , 才能称为一个handler
*/
//public class NettyServerHandler extends ChannelInboundHandlerAdapter {
public class NettyServerHandler extends SimpleChannelInboundHandler
生成的 StudentPOJO
public final class StudentPOJO {
private StudentPOJO() {}
public static void registerAllExtensions(
com.google.protobuf.ExtensionRegistryLite registry) {
}
public static void registerAllExtensions(
com.google.protobuf.ExtensionRegistry registry) {
registerAllExtensions(
(com.google.protobuf.ExtensionRegistryLite) registry);
}
public interface StudentOrBuilder extends
// @@protoc_insertion_point(interface_extends:Student)
com.google.protobuf.MessageOrBuilder {
/**
*
* Student 类中有 一个属性 名字为 id 类型为int32(protobuf类型) 1表示属性序号,不是值
*
*
* int32 id = 1;
*/
int getId();
/**
* string name = 2;
*/
String getName();
/**
* string name = 2;
*/
com.google.protobuf.ByteString
getNameBytes();
}
/**
*
*protobuf 使用message 管理数据
*
*
* Protobuf type {@code Student}
*/
public static final class Student extends
com.google.protobuf.GeneratedMessageV3 implements
// @@protoc_insertion_point(message_implements:Student)
StudentOrBuilder {
private static final long serialVersionUID = 0L;
// Use Student.newBuilder() to construct.
private Student(com.google.protobuf.GeneratedMessageV3.Builder> builder) {
super(builder);
}
private Student() {
id_ = 0;
name_ = "";
}
@Override
public final com.google.protobuf.UnknownFieldSet
getUnknownFields() {
return this.unknownFields;
}
private Student(
com.google.protobuf.CodedInputStream input,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws com.google.protobuf.InvalidProtocolBufferException {
this();
if (extensionRegistry == null) {
throw new NullPointerException();
}
int mutable_bitField0_ = 0;
com.google.protobuf.UnknownFieldSet.Builder unknownFields =
com.google.protobuf.UnknownFieldSet.newBuilder();
try {
boolean done = false;
while (!done) {
int tag = input.readTag();
switch (tag) {
case 0:
done = true;
break;
case 8: {
id_ = input.readInt32();