什么是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