protocol buffers 序列化

什么是RPC

远程过程调用(英语:Remote Procedure Call,缩写为 RPC)是一个计算机通信协议。
该协议允许运行于一台计算机的程序调用另一台计算机的子程序,而程序员无需额外地为这个交互作用编程。
  • 1,为了允许不同的客户端均能访问服务器,许多标准化的RPC系统应运而生了。其中大部分采用接口描述语言(Interface Description Language,IDL),方便跨平台的远程过程调用。

  • 2,底层通过socket 传递数据

  • 3,序列化与反序列化也叫做: 编码与解码。

  • 4,RPC 远程过程调用,很多RPC框架是跨语言的。

  • 5,RPC 内网之间相互调用(服务于服务之间)

RPC 的开发流程

  • 1,定义一个接口说明文件:描述了对象(结构体),对象成员,接口方法等一系列信息。

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

  • 3,在客户端与服务器端分别引入RPC编译器所生成的文件,即可像调用本地方法一样调用远程方法。

RPC 框架的效率

编解码的压缩比例 ,网络传输的速度

Protocol buffers

Protocol buffers是一个灵活的、高效的、自动化的机制用于序列化结构化数据。可以编译成各种源代码,比如 Java, PHP,python,nodejs,ruby ...

  • 1,proto 文件又称为idl.

  • 2,字段名称 不用驼峰命名 用下划线

示例

syntax = "proto2";

package com.lihao.netty.nettyprotobuf;

option optimize_for = SPEED;
option java_package = "com.lihao.netty.nettyprotobuf";
option java_outer_classname = "MyDataInfo";


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

}


  • 1,syntax 表示语法 如 proto2 proto3

  • 2,package 表示包名

  • 3,optimize_for SPEED, CODE_SIZE, or LITE_RUNTIME 表示c++ java 生成代码的生成器。

  • 4,java_package 指定java 的包名,如果设置了java_package package将不起作用,但是还是设置。

  • 5, java_outer_classname 表示生成的类名

  • 6, message 表示消息体

  • 7,required 字段描述 表示此参数必须要有

  • 8,optional 字段描述 表示可选。

  • 9,repeated 字段描述 字段重复,是集合的含义,比如 list

protoc 安装 和java 引入 java包

1, 下载编译器 protoc protoc-3.4.0-osx-x86_64.zip
https://github.com/google/protobuf/releases

2,设置环境变量

 vi ~/.bash_profile
 export PATH=$PATH:/Users/lixueqin/common/protoc-3.4.0/bin

3,gradle 引入文件

compile 'com.google.protobuf:protobuf-java:3.4.0'
compile 'com.google.protobuf:protobuf-java-util:3.4.0'

查看帮助

 protoc -h

.proto 文件存放位置 在源代码目录也就是java 目录

Srudent.proto

syntax = "proto2";

package com.lihao.netty.protobuf;

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


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

}


通过Srudent.proto 生成对应的java类

protoc --java_out=src/main/java src/protobuf/Student.proto

生成的 DataInfo.java 不要修改它,把他看成一个只读文件就好了.

protocol buffers 序列化测试

public class ProtoBufTest {
    public static void main(String ...arg) throws Exception {

        DataInfo.Student student = DataInfo.Student.newBuilder().setName("张三").setAge(28).setAddress("北京").build();

        System.out.println(student);

        //转换成字节可以在网络上传输
        byte[] stdent2ByteArray = student.toByteArray();

        //转换成java对象
        DataInfo.Student student2 = DataInfo.Student.parseFrom(stdent2ByteArray);

        System.out.println(student2);

    }

}

netty 对 protocol buffers 的支持

示例一:

Patients.proto

syntax = "proto3";

package com.lihao.netty.api;

option optimize_for = SPEED;
option java_package = "com.lihao.netty.api";
option java_outer_classname = "Patients";


message Patient {
    string id = 1;
    string name = 2;
    int32 age = 3;

}

//返回列表
message PatientListResponse {
    repeated Patient patientList = 1;
}

message PatientListRequest {
    string uid = 1;
}


message PatientDetailRequest {
    string uid = 1;
    string pid = 2;
}

message PatientDetailResponse {
    Patient patient = 1;
}

message Api {
    enum ApiType {
        PatientListResponseType = 0;
        PatientListRequestType = 1;
        PatientDetailRequestType = 2;
        PatientDetailResponseType = 3;

    }

    ApiType api_type = 1;

    // 一下同一时间只能访问一个
    oneof data_type {
        PatientListResponse patientListResponse = 2;

        PatientListRequest patientListRequest = 3;

        PatientDetailRequest patientDetailRequest = 4;

        PatientDetailResponse patientDetailResponse = 5;

    }


}




生成对应的java 文件

protoc --java_out=src/main/java src/protobuf/Patients.proto

server
public class ServerBuf {

    public static void main(String... arg) throws Exception {

        NioEventLoopGroup bossGroup = new NioEventLoopGroup();
        NioEventLoopGroup workGroup = new NioEventLoopGroup();

        try {

            ServerBootstrap serverBootstrap = new ServerBootstrap();

            serverBootstrap.group(bossGroup, workGroup).handler(new LoggingHandler(LogLevel.INFO))
                    .channel(NioServerSocketChannel.class).childHandler(new BufInitialzer());


            ChannelFuture channelFuture = serverBootstrap.bind(8888).sync();
            channelFuture.channel().closeFuture().sync();

        } finally {
            bossGroup.shutdownGracefully();
            workGroup.shutdownGracefully();
        }

    }

}

Initializer
public class BufInitialzer extends ChannelInitializer<SocketChannel> {
    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();


        pipeline.addLast(new ProtobufVarint32LengthFieldPrepender());
        pipeline.addLast(new ProtobufVarint32FrameDecoder());
        pipeline.addLast(new ProtobufDecoder(Users.Api.getDefaultInstance()));
        pipeline.addLast(new ProtobufEncoder());

        pipeline.addLast(new BufHandler());



    }
}

handler
public class BufHandler extends SimpleChannelInboundHandler<Users.Api> {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Users.Api msg) throws Exception {
        System.out.println("服务器接收到:");
        System.out.println(msg);

        if (msg.hasLoginRequest()) { //登陆请求

            System.out.println("------登陆请求------");

            Users.Api.Builder apiBuild = Users.Api.newBuilder();

            Users.LoginResult loginResult = Users.LoginResult.newBuilder().setUid("10001")
                    .setUsername("xiaowang").setAge(18).setAvatar("www.leyueq00.com").setToken("ewr=234sdf").build();
            apiBuild.setLoginResult(loginResult);

            Users.Api back = apiBuild.build();

            ctx.channel().writeAndFlush(back);
        }


    }
}

客户端代码

client
public class ClientBuf {
    public static void main(String... arg) throws Exception {
        NioEventLoopGroup eventLoopGroup = new NioEventLoopGroup();

        try {

            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(eventLoopGroup).channel(NioSocketChannel.class).handler(new ClientInitializer());

            ChannelFuture channelFuture = bootstrap.connect("localhost", 8888).sync();
            channelFuture.channel().closeFuture().sync();


        } finally {
            eventLoopGroup.shutdownGracefully();
        }

    }
}

Initializer
public class ClientInitializer extends ChannelInitializer<SocketChannel> {
    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();


        pipeline.addLast(new ProtobufVarint32LengthFieldPrepender());
        pipeline.addLast(new ProtobufVarint32FrameDecoder());
        pipeline.addLast(new ProtobufDecoder(Users.Api.getDefaultInstance()));
        pipeline.addLast(new ProtobufEncoder());

        pipeline.addLast(new ClientHandler());


    }
}

handler
public class ClientHandler extends SimpleChannelInboundHandler<Users.Api> {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Users.Api msg) throws Exception {
        System.out.println("客户端收到:");
        System.out.println(msg);

    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        Users.Api.Builder apiBuilder = Users.Api.newBuilder();

        Users.LoginRequest loginRequest = Users.LoginRequest.newBuilder().setUsername("lihao").setPassword("123").build();
        apiBuilder.setLoginRequest(loginRequest);

        Users.Api api = apiBuilder.build();

        ctx.channel().writeAndFlush(api);
    }
}


示例二 使用oneOf:
Patients.proto

syntax = "proto3";

package com.lihao.netty.api;

option optimize_for = SPEED;
option java_package = "com.lihao.netty.api";
option java_outer_classname = "Patients";


message Patient {
    string id = 1;
    string name = 2;
    int32 age = 3;

}

//返回列表
message PatientListResponse {
    repeated Patient patientList = 1;
}

message PatientListRequest {
    string uid = 1;
}


message PatientDetailRequest {
    string uid = 1;
    string pid = 2;
}

message PatientDetailResponse {
    Patient patient = 1;
}

message Api {
    enum ApiType {
        PatientListResponseType = 0;
        PatientListRequestType = 1;
        PatientDetailRequestType = 2;
        PatientDetailResponseType = 3;

    }

    ApiType api_type = 1;

    // 一下同一时间只能访问一个
    oneof data_type {
        PatientListResponse patientListResponse = 2;

        PatientListRequest patientListRequest = 3;

        PatientDetailRequest patientDetailRequest = 4;

        PatientDetailResponse patientDetailResponse = 5;

    }


}


server handler
public class BufHandlerOneof extends SimpleChannelInboundHandler<Patients.Api> {

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Patients.Api msg) throws Exception {

        System.out.println("服务器收到:"+msg);

        Channel channel = ctx.channel();

        switch (msg.getApiType()){
            case PatientListRequestType:

                Patients.PatientListResponse.Builder listresponseBuild = Patients.PatientListResponse.newBuilder();

                listresponseBuild.addPatientList(Patients.Patient.newBuilder().setId("10001").setName("wang xi ya").setAge(20));
                listresponseBuild.addPatientList(Patients.Patient.newBuilder().setId("10002").setName("xiao wang").setAge(24));
                listresponseBuild.addPatientList(Patients.Patient.newBuilder().setId("10003").setName("li zhi min").setAge(29));

                Patients.Api.Builder builder = Patients.Api.newBuilder().setApiType(Patients.Api.ApiType.PatientListResponseType)
                        .setPatientListResponse(listresponseBuild.build());

                channel.writeAndFlush(builder.build());

                break;

            case PatientDetailRequestType:
                Patients.PatientDetailResponse.Builder detailBuild = Patients.PatientDetailResponse.newBuilder().setPatient(Patients.Patient.newBuilder().setId("10001").setName("wang xi ya").setAge(20));
               builder = Patients.Api.newBuilder().setApiType(Patients.Api.ApiType.PatientDetailResponseType)
                        .setPatientDetailResponse(detailBuild);

                channel.writeAndFlush(builder.build());
                break;

        }
    }
}

client handler
public class ClientHandlerOneof extends SimpleChannelInboundHandler<Patients.Api> {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Patients.Api msg) throws Exception {
        System.out.println("客户端收到:");
        System.out.println(msg);

    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {

        Random random = new Random();
        for (int i = 0; i < 5; i++) {


            int value = random.nextInt(3) <= 1 ? 1 : 2;

            System.out.println("random : " + value);

            if (value == 1) {
                Patients.Api requestlist = Patients.Api.newBuilder().setApiType(Patients.Api.ApiType.PatientListRequestType)
                        .setPatientListRequest(Patients.PatientListRequest.newBuilder().setUid("1001")).build();

                ctx.channel().writeAndFlush(requestlist);

            } else if (value == 2) {

                Patients.Api detailReuset = Patients.Api.newBuilder().setApiType(Patients.Api.ApiType.PatientDetailRequestType)
                        .setPatientDetailRequest(Patients.PatientDetailRequest.newBuilder().setPid("2001").setUid("1001")).build();

                ctx.channel().writeAndFlush(detailReuset);

            } else {
                Patients.Api requestlist = Patients.Api.newBuilder().setApiType(Patients.Api.ApiType.PatientListRequestType)
                        .setPatientListRequest(Patients.PatientListRequest.newBuilder().setUid("10012")).build();

                ctx.channel().writeAndFlush(requestlist);
            }


            System.out.println("------------------");

        }


    }
}

参照文档

https://github.com/google/protobuf/tree/master/java
https://developers.google.com/protocol-buffers/docs/proto

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,905评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,140评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,791评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,483评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,476评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,516评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,905评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,560评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,778评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,557评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,635评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,338评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,925评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,898评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,142评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,818评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,347评论 2 342

推荐阅读更多精彩内容