protocol buffers 序列化

什么是RPC

遠(yuǎn)程過程調(diào)用(英語:Remote Procedure Call,縮寫為 RPC)是一個(gè)計(jì)算機(jī)通信協(xié)議。
該協(xié)議允許運(yùn)行于一臺計(jì)算機(jī)的程序調(diào)用另一臺計(jì)算機(jī)的子程序阀圾,而程序員無需額外地為這個(gè)交互作用編程。
  • 1,為了允許不同的客戶端均能訪問服務(wù)器,許多標(biāo)準(zhǔn)化的RPC系統(tǒng)應(yīng)運(yùn)而生了。其中大部分采用接口描述語言(Interface Description Language酝豪,IDL),方便跨平臺的遠(yuǎn)程過程調(diào)用精堕。

  • 2,底層通過socket 傳遞數(shù)據(jù)

  • 3,序列化與反序列化也叫做: 編碼與解碼孵淘。

  • 4,RPC 遠(yuǎn)程過程調(diào)用,很多RPC框架是跨語言的歹篓。

  • 5瘫证,RPC 內(nèi)網(wǎng)之間相互調(diào)用(服務(wù)于服務(wù)之間)

RPC 的開發(fā)流程

  • 1,定義一個(gè)接口說明文件:描述了對象(結(jié)構(gòu)體)滋捶,對象成員痛悯,接口方法等一系列信息余黎。

  • 2重窟,通過RPC框架所提供的編譯器,將接口說明文件編譯成具體語言文件惧财。

  • 3巡扇,在客戶端與服務(wù)器端分別引入RPC編譯器所生成的文件扭仁,即可像調(diào)用本地方法一樣調(diào)用遠(yuǎn)程方法。

RPC 框架的效率

編解碼的壓縮比例 厅翔,網(wǎng)絡(luò)傳輸?shù)乃俣?

Protocol buffers

Protocol buffers是一個(gè)靈活的乖坠、高效的、自動(dòng)化的機(jī)制用于序列化結(jié)構(gòu)化數(shù)據(jù)刀闷⌒鼙茫可以編譯成各種源代碼,比如 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 的包名缸沃,如果設(shè)置了java_package package將不起作用,但是還是設(shè)置修械。

  • 5趾牧, java_outer_classname 表示生成的類名

  • 6, message 表示消息體

  • 7,required 字段描述 表示此參數(shù)必須要有

  • 8祠肥,optional 字段描述 表示可選武氓。

  • 9,repeated 字段描述 字段重復(fù)仇箱,是集合的含義县恕,比如 list

protoc 安裝 和java 引入 java包

1, 下載編譯器 protoc protoc-3.4.0-osx-x86_64.zip
https://github.com/google/protobuf/releases

2,設(shè)置環(huán)境變量

 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 生成對應(yīng)的java類

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

生成的 DataInfo.java 不要修改它剂桥,把他看成一個(gè)只讀文件就好了.

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);

        //轉(zhuǎn)換成字節(jié)可以在網(wǎng)絡(luò)上傳輸
        byte[] stdent2ByteArray = student.toByteArray();

        //轉(zhuǎn)換成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;

    // 一下同一時(shí)間只能訪問一個(gè)
    oneof data_type {
        PatientListResponse patientListResponse = 2;

        PatientListRequest patientListRequest = 3;

        PatientDetailRequest patientDetailRequest = 4;

        PatientDetailResponse patientDetailResponse = 5;

    }


}




生成對應(yīng)的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("服務(wù)器接收到:");
        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;

    // 一下同一時(shí)間只能訪問一個(gè)
    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("服務(wù)器收到:"+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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末忠烛,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子权逗,更是在濱河造成了極大的恐慌美尸,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件斟薇,死亡現(xiàn)場離奇詭異师坎,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)堪滨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進(jìn)店門胯陋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事遏乔∫迕” “怎么了?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵盟萨,是天一觀的道長凉翻。 經(jīng)常有香客問我,道長捻激,這世上最難降的妖魔是什么制轰? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮胞谭,結(jié)果婚禮上艇挨,老公的妹妹穿的比我還像新娘。我一直安慰自己韭赘,他們只是感情好缩滨,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著泉瞻,像睡著了一般脉漏。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上袖牙,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天侧巨,我揣著相機(jī)與錄音,去河邊找鬼鞭达。 笑死司忱,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的畴蹭。 我是一名探鬼主播坦仍,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼叨襟!你這毒婦竟也來了繁扎?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤糊闽,失蹤者是張志新(化名)和其女友劉穎梳玫,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體右犹,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡提澎,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了念链。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片盼忌。...
    茶點(diǎn)故事閱讀 38,137評論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡莉炉,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出碴犬,到底是詐尸還是另有隱情,我是刑警寧澤梆暮,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布服协,位于F島的核電站,受9級特大地震影響啦粹,放射性物質(zhì)發(fā)生泄漏偿荷。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一唠椭、第九天 我趴在偏房一處隱蔽的房頂上張望跳纳。 院中可真熱鬧,春花似錦贪嫂、人聲如沸寺庄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽斗塘。三九已至,卻和暖如春亮靴,著一層夾襖步出監(jiān)牢的瞬間馍盟,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工茧吊, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留贞岭,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓搓侄,卻偏偏與公主長得像瞄桨,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子讶踪,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內(nèi)容