Netty Protobuf

23 篇文章 2 订阅

基本介绍:

  • Protobuf 是 Google 发布的开源项目,全程 Google Protocol Buffers, 是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化。 它很使用做数据存储或 RPC[远程过程调用 remote procedure call] 数据交换格式。
  • 参考文档:Language Guide (proto 2) | Protocol Buffers Documentation
  • Protobuf 是以 message 的方式来管理数据的
  • 支持跨平台、跨语言,即[客户端和服务器端可以是不同的语言编写的] (支持目前绝大多数语言, 例如 C++、 C#、Java、Python等)
  • 高性能,高可靠性
  • 使用 protobuf 编译器能自动生成代码, Protobuf 是将类的定义使用.proto文件进行描述。 idea 中编写 .proto文件时,会自动提示是否 下载 .protot 编写插件。可以让语法高亮
  • 然后通过protoc.exe 编译器根据 .proto 自动生成 .java 文件

protoc 下载安装:

下载地址:Releases · protocolbuffers/protobuf · GitHub, 然后配置环境变量

查看 protobuf 版本:protoc --version

引入POM:

<dependency>
    <groupId>com.google.protobuf</groupId>
    <artifactId>protobuf-java</artifactId>
    <version>3.14.0</version>
</dependency>

示例一:  单个POJO实体

  • Student.proto
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 --java_out=. .\Student.proto 生成 StudentPOJO.java

  • 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 InterruptedException {
        
        // 创建两个线程组 boosGroup 和 workerGroup
        // bossGroup 只处理连接请求, workerGroup 与客户端业务处理
        // 两者皆是无限循环
        // boosGroup 和 workerGroup 含有的子线程(NioEventLoop)的个数,默认实际 Cpu 核数 * 2
        EventLoopGroup boosGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            // 创建服务器端的启动对象,配置启动参数
            ServerBootstrap bootstrap = new ServerBootstrap();

            // 使用链式编程进行设置
            bootstrap.group(boosGroup, workerGroup) // 设置两个线程组
                    .channel(NioServerSocketChannel.class) // 使用NioServerSocketChannel 作为服务器通道实现
                    .option(ChannelOption.SO_BACKLOG, 128) // 设置线程队列等待连接的个数
                    .childOption(ChannelOption.SO_KEEPALIVE, true)  // 设置保持活动连接状态
                    .childHandler(new ChannelInitializer<SocketChannel>() { // 创建一个通道初始化对象(匿名对象)
                        // 给pipeline 设置处理器
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {

                            // 在 pipeline 加入 protoBufDecoder
                            ChannelPipeline pipeline = socketChannel.pipeline();
                            // 指定对哪种对象进行解码
                            pipeline.addLast("decoder", new ProtobufDecoder(StudentPOJO.Student.getDefaultInstance()));
                            pipeline.addLast(new NettyServerHandler());
                        }
                    });     // 给我们的workerGroup 的 EventLoop 对应的管道设置处理器

            System.out.println("...... 服务器 is ready ...");

            // 启动服务器,绑定一个端口并且同步,生成一个 channelFuture 对象
            ChannelFuture cf = bootstrap.bind(9527).sync();

            // 给cf 注册监听器,监听我们关心的事件
            cf.addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture channelFuture) throws Exception {
                    if(cf.isSuccess()) {
                        System.out.println("监听端口 9527 成功");
                    } else {
                        System.out.println("监听端口 9527 失败");
                    }
                }
            });

            // 对关闭通道进行监听
            cf.channel().closeFuture().sync();
        } finally {
            boosGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}
  • NettyServerHandler
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.util.CharsetUtil;


/**
 * 1. 自定义一个Handler 需要继承Netty 规定好的某个 HandlerAdapter, 这时才能称为一个Handler
 */
public class NettyServerHandler extends ChannelInboundHandlerAdapter {
// 使用 simple
//public class NettyServerHandler extends SimpleChannelInboundHandler<StudentPOJO.Student> {
    /**
     * 读取数据事件(可以读取客户端发送的消息)
     * @param ctx 上下文对象, 含有 管道pipeline, 通道channel, 地址
     * @param msg 客户端发送的数据
     * @throws Exception
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        // 读取从客户端发送的StudentPOJO.student
        StudentPOJO.Student student = (StudentPOJO.Student) msg;
        System.out.println("客户端发送的数据 id=" + student.getId() + " 名字=" + student.getName());
    }
// 使用 simple
//    @Override
//    protected void channelRead0(ChannelHandlerContext channelHandlerContext, StudentPOJO.Student student) throws Exception {
//        System.out.println("客户端发送的数据 id=" + student.getId() + " 名字=" + student.getName());
//    }

    /**
     * 读取数据完毕
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {

        // write +  flush 将数据写入缓存,并刷新
        ctx.writeAndFlush(Unpooled.copiedBuffer("hello, Netty", CharsetUtil.UTF_8));
    }

    /**
     * 处理异常,一般是需要关闭通道
     * @param ctx
     * @param cause
     * @throws Exception
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}
  • 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 InterruptedException {

        // 客户端需要一个事件循环组
        EventLoopGroup group = new NioEventLoopGroup();

        // 客户端的启动对象,使用 BootStrap
        Bootstrap bootstrap = new Bootstrap();
        try {
            // 设置相关参数
            bootstrap.group(group) // 设置线程组
                    .channel(NioSocketChannel.class)  // 设置客户端通道的实现类(反射)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            // 在 pipeline 中加入 ProtoBufEncoder
                            ChannelPipeline pipeline = socketChannel.pipeline();
                            pipeline.addLast("encoder", new ProtobufEncoder());
                            pipeline.addLast(new NettyClientHandler()); // 加入自己的处理器
                        }
                    });

            System.out.println("客户端 ok..");

            // 启动客户端去连接服务器端, channelFuture 涉及到 netty的异步模型
            ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 9527).sync();

            // 给关闭通道进行监听
            channelFuture.channel().closeFuture().sync();
        }finally {
            group.shutdownGracefully();
        }
    }
}
  • NettyClientHandler
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;


public class NettyClientHandler extends ChannelInboundHandlerAdapter {
    /**
     * 当通道就绪时就会触发该方法
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        // 发送一个Student 对象到服务器
        StudentPOJO.Student student = StudentPOJO.Student.newBuilder().setId(9527).setName("doubily").build();
        ctx.writeAndFlush(student);
    }

    /**
     * 当通道有读取事件时,会触发
     * @param ctx
     * @param msg
     * @throws Exception
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf byteBuf = (ByteBuf) msg;

        System.out.println("服务器回复的消息:" + byteBuf.toString(CharsetUtil.UTF_8));

        System.out.println("服务器的地址:" + ctx.channel().remoteAddress());
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}

示例二:  多个POJO实体

  • Student.proto
syntax = "proto3"; //版本
option optimize_for = SPEED; // 加快解析
option java_package = "com.bxtech.netty.netty.codec2"; // 指定生成到哪个包下
option java_outer_classname = "DataInfo"; //外部类名
// protobuf 可以使用 message 管理其他的message
message MineMessage{
  // 定义一个枚举类型
  enum DataType{
    StudentType = 0; // 在 proto3 要求 enum的编号从0开始
    WorkerType = 1;
  }
  // 用 data_type  来标识传的是哪一个枚举类型
  DataType data_type = 1;
  // 表示每次枚举类型 最多只能出现其中的一个,节省空间
  oneof dataBody{
    Student student = 2;
    Worker worker = 3;
  }
}
message Student { // 会在 StudentPOJO 外部类生成一个内部类 Student, 他是 真正发送的 POJO 对象
  int32 id = 1;   //  Student 类中会有一个属性 名字为 id, 类型为 int32(protobuf类型) 1 表示属性序号,不是值
  string name = 2;
}

message Worker{
  string name = 1;
  int32 age = 2;
}

执行 protoc --java_out=. .\Student.proto 生成 DataInfo.java

  • 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 InterruptedException {
        
        // 创建两个线程组 boosGroup 和 workerGroup
        // bossGroup 只处理连接请求, workerGroup 与客户端业务处理
        // 两者皆是无限循环
        // boosGroup 和 workerGroup 含有的子线程(NioEventLoop)的个数,默认实际 Cpu 核数 * 2
        EventLoopGroup boosGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            // 创建服务器端的启动对象,配置启动参数
            ServerBootstrap bootstrap = new ServerBootstrap();

            // 使用链式编程进行设置
            bootstrap.group(boosGroup, workerGroup) // 设置两个线程组
                    .channel(NioServerSocketChannel.class) // 使用NioServerSocketChannel 作为服务器通道实现
                    .option(ChannelOption.SO_BACKLOG, 128) // 设置线程队列等待连接的个数
                    .childOption(ChannelOption.SO_KEEPALIVE, true)  // 设置保持活动连接状态
                    .childHandler(new ChannelInitializer<SocketChannel>() { // 创建一个通道初始化对象(匿名对象)
                        // 给pipeline 设置处理器
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {

                            // 在 pipeline 加入 protoBufDecoder
                            ChannelPipeline pipeline = socketChannel.pipeline();
                            // 指定对哪种对象进行解码
                            pipeline.addLast("decoder", new ProtobufDecoder(DataInfo.MineMessage.getDefaultInstance()));
                            pipeline.addLast(new NettyServerHandler());
                        }
                    });     // 给我们的workerGroup 的 EventLoop 对应的管道设置处理器

            System.out.println("...... 服务器 is ready ...");

            // 启动服务器,绑定一个端口并且同步,生成一个 channelFuture 对象
            ChannelFuture cf = bootstrap.bind(9527).sync();

            // 给cf 注册监听器,监听我们关心的事件
            cf.addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture channelFuture) throws Exception {
                    if(cf.isSuccess()) {
                        System.out.println("监听端口 9527 成功");
                    } else {
                        System.out.println("监听端口 9527 失败");
                    }
                }
            });

            // 对关闭通道进行监听
            cf.channel().closeFuture().sync();
        } finally {
            boosGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}
  • NettyServerHandler
mport io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.CharsetUtil;


/**
 * 1. 自定义一个Handler 需要继承Netty 规定好的某个 HandlerAdapter, 这时才能称为一个Handler
 */
public class NettyServerHandler extends SimpleChannelInboundHandler<DataInfo.MineMessage> {
// 使用 simple
    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, DataInfo.MineMessage msg) throws Exception {
        // 根据 dataType 来显示不同的信息
        DataInfo.MineMessage.DataType dataType = msg.getDataType();
        if (dataType == DataInfo.MineMessage.DataType.StudentType) {
            DataInfo.Student student = msg.getStudent();
            System.out.println("学生id=" + student.getId() + " 学生名字=" + student.getName());
        } else if (dataType == DataInfo.MineMessage.DataType.WorkerType){
            DataInfo.Worker worker = msg.getWorker();
            System.out.println("工人的名字=" + worker.getName() + " 年龄=" + worker.getAge());
        } else {
            System.out.println("传输的类型不正确");
        }
    }

    /**
     * 读取数据完毕
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {

        // write +  flush 将数据写入缓存,并刷新
        ctx.writeAndFlush(Unpooled.copiedBuffer("hello, Netty", CharsetUtil.UTF_8));
    }

    /**
     * 处理异常,一般是需要关闭通道
     * @param ctx
     * @param cause
     * @throws Exception
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}
  • 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 InterruptedException {

        // 客户端需要一个事件循环组
        EventLoopGroup group = new NioEventLoopGroup();

        // 客户端的启动对象,使用 BootStrap
        Bootstrap bootstrap = new Bootstrap();
        try {
            // 设置相关参数
            bootstrap.group(group) // 设置线程组
                    .channel(NioSocketChannel.class)  // 设置客户端通道的实现类(反射)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            // 在 pipeline 中加入 ProtoBufEncoder
                            ChannelPipeline pipeline = socketChannel.pipeline();
                            pipeline.addLast("encoder", new ProtobufEncoder());
                            pipeline.addLast(new NettyClientHandler()); // 加入自己的处理器
                        }
                    });

            System.out.println("客户端 ok..");

            // 启动客户端去连接服务器端, channelFuture 涉及到 netty的异步模型
            ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 9527).sync();

            // 给关闭通道进行监听
            channelFuture.channel().closeFuture().sync();
        }finally {
            group.shutdownGracefully();
        }
    }
}
  • NettyClientHandler
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;

import java.util.Random;


public class NettyClientHandler extends ChannelInboundHandlerAdapter {
    /**
     * 当通道就绪时就会触发该方法
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        // 随机发送 student 或者 worker对象
        int random = new Random().nextInt(3);
        DataInfo.MineMessage mineMessage = null;
        if (0 == random){   // 发送 student 对象
            mineMessage = DataInfo.MineMessage.newBuilder()
                    .setDataType(DataInfo.MineMessage.DataType.StudentType)
                    .setStudent(DataInfo.Student.newBuilder()
                            .setId(9527)
                            .setName("doubily")
                            .build())
                    .build();
        } else {    // 发送一个 worker对象
            mineMessage = DataInfo.MineMessage.newBuilder()
                    .setDataType(DataInfo.MineMessage.DataType.WorkerType)
                    .setWorker(DataInfo.Worker.newBuilder()
                            .setAge(8848)
                            .setName("doubily2")
                            .build())
                    .build();
        }
        ctx.writeAndFlush(mineMessage);
    }

    /**
     * 当通道有读取事件时,会触发
     * @param ctx
     * @param msg
     * @throws Exception
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf byteBuf = (ByteBuf) msg;

        System.out.println("服务器回复的消息:" + byteBuf.toString(CharsetUtil.UTF_8));

        System.out.println("服务器的地址:" + ctx.channel().remoteAddress());
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值