6、张龙netty学习 protobuf

protobuf

RPC简介:

rmi: remote method invocation 只针对java序列化与反序列化 也叫做:编码与解码

RPC:Remote procedure Call 远程过程调用,

很多RPC框架是跨语言的

1、定义一个接口的说明文档,描述了对象(结构体),对象成员、接口方法等一系列语言

2、通过RPC框架所提供的编译器,将接口说明文件编译成具体语言文件

3、在客户端和服务器端各引入RPC编译器所编译的文件,即可像调用本地方法一样调用远程方法

这个是一个netty实现的server和client通过protobuf传输的程序, 其中ProtobufServerInitialiszer和protobufClientInitializer这两个类里面 addLast的handler里面pipeline.addLast(new ProtobufDecoder(DataInfo.Student.getDefaultInstance())); 这个handler是用来解码的,就是把字节数组转成对象的。现在这个程序的问题是 如果我们要传输的对象很多,不仅仅是DataInfo.Student这一个对象,我们应该怎么做呢。 由两种办法: 1、就是自定义解码器,因为socket底层传输的都是字节流,我们可以通过在定义 协议的时候通过魔数来分开,什么意思呢,就是比如AA后面跟一个对象, BB后面跟一个对象,这样在自定义解码器的时候就通过可以通过判断来解码了。 2、protobuf协议里面的关键字oneof定义。通过在消息体里面定义要传输的对象们的 枚举类来指定要传输哪个对象,然后再定义oneof数据体。比如:

 message Person{
    enum DataType{
        StudentType = 1;
        TeacherType = 2;
    }
    required DataType data_type = 1;
    oneof dataBody {
        Student student = 2;
        Teacher teacher = 3;
    }
}

message Student{
    optional string name = 1;
    optional int32 age = 2;
}
message Teacher{
    optional string name =1;
    optional string age = 2;
}

通过这种方式定义协议,解码器解码的就是Person,通过DataType区分到底传输的是什么对象

protobuf生成Java代码的命令 protoc --java_out=src/main/java src/main/java/com/fjh/netty/protobuf/Student.proto

一个protobuf使用的最佳实践: 因为我们在平时的开发中,server端和client端往往是不在一个仓库里面的,那么用protobuf工具生成的java类怎么 在两个仓库中同步呢。 往往我们把protobuf的java文件也放在一个单独的git仓库中。 现在我们由三个仓库分别是: serverproject protobufproject clientproject 使用git作为版本控制工具的时候,git submodule命令意思是git仓库中的子仓库 我们可以把protobufprject作为serverproject和clientproject的子仓库 ,这样如果protobufproject由更新的话,我们无论是在server还是client,在pull自己项目的时候就能拉到protobuf的更新了。

还可以用git subtree,但是上面的方法会好点

homebrew mac上面的包管理器

Student.proto

syntax = "proto2";
package com.fjh.netty.protobuf;

option optimize_for = SPEED;
option java_package = "com.fjh.netty.protobuf";
option java_outer_classname = "DataInfo";

message Student{
    required string name = 1;
    optional int32 age = 2;
    optional string address = 3;
}

public class ProtobufTest {
    public static void main(String[] args) throws Exception{
        DataInfo.Student student = DataInfo.Student.newBuilder().setName("张三").setAge(30).setAddress("北京").build();
        byte[] bytes = student.toByteArray();

        DataInfo.Student student1 = DataInfo.Student.parseFrom(bytes);
        System.out.println(student1);
        System.out.println(student1.getName());
        System.out.println(student1.getAge());
        System.out.println(student1.getAddress());
    }
}

服务器端:

public class ProtobufServer {
    public static void main(String[] args) throws Exception{
        EventLoopGroup boosGrop = new NioEventLoopGroup();
        EventLoopGroup workGrop = new NioEventLoopGroup();

        try {
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(boosGrop,workGrop).channel(NioServerSocketChannel.class)
                    .handler(new LoggingHandler(LogLevel.INFO)).childHandler(new ProtobufServerInitializer());
            ChannelFuture channelFuture = serverBootstrap.bind(8899).sync();
            channelFuture.channel().closeFuture().sync();
        } finally {
            boosGrop.shutdownGracefully();
            workGrop.shutdownGracefully();
        }
    }
}
public class ProtobufServerInitializer extends ChannelInitializer<SocketChannel> {
    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        pipeline.addLast(new ProtobufVarint32FrameDecoder());
        pipeline.addLast(new ProtobufDecoder(DataInfo.Student.getDefaultInstance()));
        pipeline.addLast(new ProtobufVarint32LengthFieldPrepender());
        pipeline.addLast(new ProtobufEncoder());

        pipeline.addLast(new ProtobufServerHandler());
    }
}
public class ProtobufServerHandler extends SimpleChannelInboundHandler<DataInfo.Student> {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, DataInfo.Student msg) throws Exception {
        System.out.println(msg.getAddress());
        System.out.println(msg.getName());
        System.out.println(msg.getAddress());

        DataInfo.Student student = DataInfo.Student.newBuilder().setName("李四")
                .setAge(20).setAddress("上海").build();
        ctx.channel().writeAndFlush(student);
    }
}

客户端

public class ProtobufClient {
    public static void main(String[] args)throws Exception {
        EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(eventLoopGroup).channel(NioSocketChannel.class)
                    .handler(new ProtobufClientInitializer());
            ChannelFuture channelFuture = bootstrap.connect("localhost", 8899).sync();
            channelFuture.channel().closeFuture().sync();

        } finally {
            eventLoopGroup.shutdownGracefully();
        }
    }
}
public class ProtobufClientInitializer extends ChannelInitializer<SocketChannel> {
    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        pipeline.addLast(new ProtobufVarint32FrameDecoder());
        pipeline.addLast(new ProtobufDecoder(DataInfo.Student.getDefaultInstance()));
        pipeline.addLast(new ProtobufVarint32LengthFieldPrepender());
        pipeline.addLast(new ProtobufEncoder());

        pipeline.addLast(new ProtobufClientHandler());
    }
}
public class ProtobufClientHandler extends SimpleChannelInboundHandler<DataInfo.Student> {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, DataInfo.Student msg) throws Exception {
        System.out.println(msg.getName());
        System.out.println(msg.getAge());
        System.out.println(msg.getAddress());
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        DataInfo.Student student = DataInfo.Student.newBuilder().setName("张三")
                .setAge(30).setAddress("北京").build();

        ctx.writeAndFlush(student);

    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值