Netty二、Netty编解码

我会带着你远行 2022-09-12 14:57 353阅读 0赞

1. 编解码技术

1.1 netty自身提供的编解码器

  1. StringDecoder StringEncoder 字符串解码编码器
  2. ObjectDecoder ObjectEncoder 对象解码编码器
  1. 底层都基于 JAVA 序列化,从 JDK 1.1 版本就提供,只需实现 java.io.Serializable并生成序列ID即可。
  2. JAVA序列化的目的,网络传输及持久化。
  3. Java序列化从JDK 1.1版本就己经提供,它不需要添加额外的类库,只需实现 java.io.Serializable并生成序列ID即可,但是在远程服务调用(RPC)时,很少宜接使用Java序列化进行消息的编解码和传输, 这又是什么原因呢?
  1. 无法跨语言,编码后其他语言无法正常解码
  2. 编码后的码流太大
  3. 性能差

1.2 业界主流的编解码框架

  1. MessagePack 编解码

MessagePack是一个高效的二进制序列化框架,它像JSON 一样支持不同语言间的数 据交换,但是它的性能更快,序列化之后的码流也更小。

  • 编解码高效,性能高;
  • 序列化之后的码流小;
  • 支持跨语言。
  1. Google Protobuf 编解码

Google的Protobuf在业界非常流行,很多商业项目选择Protobuf作为编解码框架。

  • 在谷歌内部长期使用,产品成熟度髙:
  • 跨语言、支持多种语言,包括C++、Java和Python:
  • 编码后的消息更小,更加冇利于存储和传输:
  • 编解码的性能非常高:
  • 支持不同协议版本的前向兼容:
  • 支持定义可选和必选字段。
  1. JBoss Marshalling 编解码

JBoss Marshalling是一个Java对象序列化包,对JDK默认的序列化框架进行了优化, 但又保持跟java.io.Serializablc接口的兼容,同时増加了 -些可调的参数和附加的特性, 这些参数和特性可通过工厂类进行配置。

2. Google Protobuf 编解码

在这里插入图片描述

客户端产生业务数据,经过 ProtobufEncoder 编码器编码,客户端接受后通过 ProtobufDecoder 解码。

2.1 编写.proto文件

  1. //版本
  2. syntax = "proto3";
  3. // 生成的外部类型名称
  4. option java_outer_classname = "StudentPOJO";
  5. // protobuf 使用 message 管理数据
  6. message Student{
  7. // 会在 StudentPOJO 外部类生成内部类, 真正发送的 pojo 对象
  8. // 1 标识属性序号,不代表值
  9. int32 id = 1;
  10. string name = 2;
  11. }
  12. // https://developers.google.com/protocol-buffers/docs/proto 文档

google protobuf 会根据此文件生成对应的 java类,此内部类对相应 message 定义的内容。

StudentPOJO 需要通过 protoc.exe 生成,protoc.exe —java_out=. Student.proto

  1. public final class StudentPOJO {
  2. private StudentPOJO() {
  3. }
  4. public static void registerAllExtensions(
  5. com.google.protobuf.ExtensionRegistryLite registry) {
  6. }
  7. public static void registerAllExtensions(
  8. com.google.protobuf.ExtensionRegistry registry) {
  9. registerAllExtensions(
  10. (com.google.protobuf.ExtensionRegistryLite) registry);
  11. }
  12. public interface StudentOrBuilder extends
  13. // @@protoc_insertion_point(interface_extends:Student)
  14. com.google.protobuf.MessageOrBuilder {
  15. /**
  16. * <pre>
  17. * 1 标识属性序号,不代表值
  18. * </pre>
  19. *
  20. * <code>int32 id = 1;</code>
  21. */
  22. int getId();
  23. /**
  24. * <code>string name = 2;</code>
  25. */
  26. java.lang.String getName();
  27. /**
  28. * <code>string name = 2;</code>
  29. */
  30. com.google.protobuf.ByteString
  31. getNameBytes();
  32. }
  33. public static final class Student extends com.google.protobuf.GeneratedMessageV3 implements StudentOrBuilder {
  34. private static final long serialVersionUID = 0L;
  35. // Use Student.newBuilder() to construct.
  36. private Student(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
  37. super(builder);
  38. }
  39. private Student() {
  40. id_ = 0;
  41. name_ = "";
  42. }
  43. ...
  44. }
  45. }

2.2 Netty 客户端服务端

2.2.1 服务端及处理器

  1. package cn.painnote.netty.codec.protocolBuf;
  2. import io.netty.bootstrap.ServerBootstrap;
  3. import io.netty.channel.ChannelFuture;
  4. import io.netty.channel.ChannelInitializer;
  5. import io.netty.channel.ChannelOption;
  6. import io.netty.channel.nio.NioEventLoopGroup;
  7. import io.netty.channel.socket.SocketChannel;
  8. import io.netty.channel.socket.nio.NioServerSocketChannel;
  9. import io.netty.handler.codec.protobuf.ProtobufDecoder;
  10. public class NettyServer {
  11. /**
  12. * Description netty 服务端
  13. *
  14. * @author Qian YuHua
  15. */
  16. public static void main(String[] args) throws Exception {
  17. // Boss group 处理链接请求 NioEventLoopGroup 默认构造函数线程数为 0,CPU * 2
  18. NioEventLoopGroup bossGroup = new NioEventLoopGroup();
  19. // Worker group 处理数据客户端业务
  20. NioEventLoopGroup workerGroup = new NioEventLoopGroup();
  21. try {
  22. // 服务器端启动对象
  23. ServerBootstrap bootstrap = new ServerBootstrap();
  24. // 设置线程组
  25. bootstrap.group(bossGroup, workerGroup)
  26. // 使用 NioServerSocketChannel 作为服务器通道实现类
  27. .channel(NioServerSocketChannel.class)
  28. /*
  29. * ChannelOption.SO_BACKLOG对应的是tcp/ip协议listen函数中的backlog参数,
  30. * 函数listen(int socketfd,int backlog)用来初始化服务端可连接队列,
  31. * 服务端处理客户端连接请求是顺序处理的,所以同一时间只能处理一个客户端连接,
  32. * 多个客户端来的时候,服务端将不能处理的客户端连接请求放在队列中等待处理,
  33. * backlog参数指定了队列的大小
  34. */
  35. .option(ChannelOption.SO_BACKLOG, 128)
  36. .childHandler(new ChannelInitializer<SocketChannel>() {
  37. //创建一个通道初始化对象(匿名对象)
  38. //给pipeline 设置处理器
  39. @Override
  40. protected void initChannel(SocketChannel ch) throws Exception {
  41. // 制定对哪种对象进行解码
  42. ch.pipeline().addLast("decoder", new ProtobufDecoder(StudentPOJO.Student.getDefaultInstance()));
  43. ch.pipeline().addLast(new NettyServerHandler());
  44. }
  45. }); // 给我们的workerGroup 的 EventLoop 对应的管道设置处理器
  46. System.out.println(".....server is ready...");
  47. //绑定一个端口并且同步, 生成了一个 ChannelFuture 对象
  48. //启动服务器(并绑定端口)
  49. ChannelFuture channelFuture = bootstrap.bind(6666).sync();
  50. //对关闭通道进行监听
  51. channelFuture.channel().closeFuture().sync();
  52. } catch (Exception e) {
  53. System.out.println(e);
  54. } finally {
  55. bossGroup.shutdownGracefully();
  56. workerGroup.shutdownGracefully();
  57. }
  58. }
  59. }
  60. package cn.painnote.netty.codec.protocolBuf;
  61. import io.netty.buffer.ByteBuf;
  62. import io.netty.buffer.Unpooled;
  63. import io.netty.channel.ChannelHandlerContext;
  64. import io.netty.channel.ChannelInboundHandlerAdapter;
  65. import io.netty.util.CharsetUtil;
  66. import java.util.concurrent.TimeUnit;
  67. public class NettyServerHandler extends ChannelInboundHandlerAdapter {
  68. @Override
  69. public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
  70. StudentPOJO.Student student = (StudentPOJO.Student) msg;
  71. System.out.println("客户端发送消息是:" + " id " + student.getId() + " name " + student.getName());
  72. }
  73. }

2.2.1 客户端及处理器

  1. package cn.painnote.netty.codec.protocolBuf;
  2. import io.netty.bootstrap.Bootstrap;
  3. import io.netty.channel.ChannelFuture;
  4. import io.netty.channel.ChannelInitializer;
  5. import io.netty.channel.nio.NioEventLoopGroup;
  6. import io.netty.channel.socket.SocketChannel;
  7. import io.netty.channel.socket.nio.NioSocketChannel;
  8. import io.netty.handler.codec.protobuf.ProtobufEncoder;
  9. public class NettyClient {
  10. public static void main(String[] args) throws Exception {
  11. NioEventLoopGroup eventExecutors = new NioEventLoopGroup();
  12. Bootstrap bootstrap = new Bootstrap();
  13. bootstrap.group(eventExecutors)
  14. .channel(NioSocketChannel.class)
  15. .handler(new ChannelInitializer<SocketChannel>() {
  16. @Override
  17. protected void initChannel(SocketChannel ch) throws Exception {
  18. // 加入 protobufEncoder
  19. ch.pipeline().addLast("encoder", new ProtobufEncoder());
  20. ch.pipeline().addLast(new NettyClientHandler()); //加入自己的处理器
  21. }
  22. });
  23. System.out.println("client ok..");
  24. ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 6666).sync();
  25. //给关闭通道进行监听
  26. channelFuture.channel().closeFuture().sync();
  27. }
  28. }
  29. package cn.painnote.netty.codec.protocolBuf;
  30. import io.netty.buffer.ByteBuf;
  31. import io.netty.buffer.Unpooled;
  32. import io.netty.channel.ChannelHandlerContext;
  33. import io.netty.channel.ChannelInboundHandlerAdapter;
  34. import io.netty.util.CharsetUtil;
  35. /**
  36. *
  37. * <p>Title: </p>
  38. * <p>Description: Function Description </p>
  39. * @author qyh
  40. */
  41. public class NettyClientHandler extends ChannelInboundHandlerAdapter {
  42. @Override
  43. public void channelActive(ChannelHandlerContext ctx) throws Exception {
  44. System.out.println("client " + ctx);
  45. // 发送 Student 到服务器
  46. StudentPOJO.Student student = StudentPOJO.Student.newBuilder().setId(111).setName("钱育华").build();
  47. ctx.writeAndFlush(student);
  48. }
  49. }

3.xmind

请添加图片描述

发表评论

表情:
评论列表 (有 0 条评论,353人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Netty4之解码

    本文是基于Netty4.1.x,一般在使用Netty作为网络框架进行开发时,编解码框架是我们应该注意的一个重要部分。应用从网络层接收数据需要经过解码(Decode),将二进制的

    相关 Netty解码及protostuff

    Netty编解码是什么? 要想了解编解码,首先要知客户端和服务端是怎么处理信息的,当通过Netty发送或者接收信息的时候,首先要将接收到的消息尽心decode()方法进行