1. Client 调用以本地调用方式调用服务;
2. Client Stub 接收到调用后负责将方法、参数等组装成能够进行网络传输的消息体(桩代码是未实现的接口,此处使用动态代理);
3. Client Stub 找到服务地址,并将消息发送到服务端;
4. Server Stub 收到消息后进行解码;
5. Server Stub 根据解码结果调用本地的服务;
6. 本地服务执行并将结果返回给 Server Stub;
7. Server Stub 将返回结果打包成消息并发送至消费方;
8. Client Stub 接收到消息,并进行解码;
9. Client 得到最终结果
- Stub (桩代码)指满足形式要求但没有实现实际功能的占位或代理代码
- RPC 中的桩代码负责远程请求或响应
RPC 和 HTTP 的关系
基于 HTTP 的远程调用方案和 RPC 的相同点、不同点
service
定义服务,使用 message
定义服务使用的类
service
定义的每个 rpc 方法,根据输入和返回是否为 stream
修饰,分为四种情况syntax = "proto3" // 语法版本
option java_package = "com.hit.demo"; // 生成桩代码的包,位于target的子目录
...
// 定义服务
service PhoneLookup {
rpc QueryPhoneByAddress (Request) returns (Response) {}
}
message Requset {
required string id = 1; // required:必须传入1个参数
optional string name = 2; // optional:可以传入0或1个参数,如果不传入则为默认0值
repeated string address = 3; // repeated:可以传入0或多个参数
}
message Response {
string id = 1;
ResultCode result = 2 [default = SUCCESS]; // 声明默认值
repeated int64 number = 3;
repeated bool active = 4;
enum ResultCode {
SUCCESS = 0;
FAIL = 1;
UNKNOWN = 3;
}
}
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<properties>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<grpc.version>1.51.1grpc.version>
<protobuf.version>3.21.7protobuf.version>
<protoc.version>3.21.7protoc.version>
<maven.compiler.source>1.7maven.compiler.source>
<maven.compiler.target>1.7maven.compiler.target>
properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.grpcgroupId>
<artifactId>grpc-bomartifactId>
<version>${grpc.version}version>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
<dependencies>
<dependency>
<groupId>io.grpcgroupId>
<artifactId>grpc-netty-shadedartifactId>
dependency>
<dependency>
<groupId>io.grpcgroupId>
<artifactId>grpc-protobufartifactId>
dependency>
<dependency>
<groupId>io.grpcgroupId>
<artifactId>grpc-stubartifactId>
dependency>
<dependency>
<groupId>com.google.protobufgroupId>
<artifactId>protobuf-java-utilartifactId>
<version>${protobuf.version}version>
dependency>
<dependency>
<groupId>com.google.code.gsongroupId>
<artifactId>gsonartifactId>
<version>2.9.0version>
dependency>
<dependency>
<groupId>org.apache.tomcatgroupId>
<artifactId>annotations-apiartifactId>
<version>6.0.53version>
<scope>providedscope>
dependency>
<dependency>
<groupId>io.grpcgroupId>
<artifactId>grpc-testingartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
<scope>testscope>
dependency>
<dependency>
<groupId>org.mockitogroupId>
<artifactId>mockito-coreartifactId>
<version>3.4.0version>
<scope>testscope>
dependency>
dependencies>
<build>
<extensions>
<extension>
<groupId>kr.motd.mavengroupId>
<artifactId>os-maven-pluginartifactId>
<version>1.6.2version>
extension>
extensions>
<plugins>
<plugin>
<groupId>org.xolstice.maven.pluginsgroupId>
<artifactId>protobuf-maven-pluginartifactId>
<version>0.6.1version>
<configuration>
<protocArtifact>com.google.protobuf:protoc:${protoc.version}:exe:${os.detected.classifier}protocArtifact>
<pluginId>grpc-javapluginId>
<pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}pluginArtifact>
configuration>
<executions>
<execution>
<goals>
<goal>compilegoal>
<goal>compile-customgoal>
goals>
execution>
executions>
plugin>
plugins>
build>
project>
syntax="proto3";
option java_package = "com.xuecheng.fastdfs.target.stub"; // 指定桩代码在target目录下的包
option java_generic_services = true;
service HelloService {
rpc sayHello(HelloRequest) returns (HelloResponse) {}
}
message HelloRequest {
int32 requestId = 1;
string name = 2;
}
message HelloResponse {
int32 responseId = 1;
bool responseState = 2;
string greeting = 3;
}
Maven lifecycle compile
编译工程protobuf compile(生成message实体类)
和 protobuf compile-customer(生成service的实现基类)
- PB 配置的
option java_package="com.xuecheng.fastdfs.target.stub"
指定了生成的桩代码在编译结果target
目录下的位置(蓝框所在的包),不要和复制的目标位置(红框)处于相同的包- 如果相同,即设置
option java_package="com.xuecheng.fastdfs.stub"
,会在后续执行maven compile
时出现类重复的异常(因为HelloService.proto
和源代码都想向同一个包下写入同名class
文件)
package com.xuecheng.fastdfs.demo;
import com.xuecheng.fastdfs.stub.HelloServiceGrpc;
import com.xuecheng.fastdfs.stub.HelloServiceOuterClass;
import io.grpc.Server;
import io.grpc.netty.shaded.io.grpc.netty.NettyServerBuilder;
import io.grpc.stub.StreamObserver;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
public class MyHelloServer extends HelloServiceGrpc.HelloServiceImplBase {
@Override
public void sayHello(HelloServiceOuterClass.HelloRequest request, StreamObserver<HelloServiceOuterClass.HelloResponse> responseObserver) {
int requestId = request.getRequestId();
String requestName = request.getName();
HelloServiceOuterClass.HelloResponse response = HelloServiceOuterClass.HelloResponse.newBuilder()
.setResponseId(requestId)
.setResponseState(true)
.setGreeting("Hello " + requestName + "! This greeting is from server!")
.build();
responseObserver.onNext(response);
responseObserver.onCompleted();
}
public static void main(String[] args) {
// 设置服务端口号
int port = 8899;
Server server = NettyServerBuilder.forPort(port)
.addService(new MyHelloServer())
.build();
try {
// 启动服务
server.start();
System.out.println("gRPC server started, on port: " + port);
// 设置60秒后关闭
server.awaitTermination(60, TimeUnit.SECONDS);
System.out.println("gRPC server terminated.");
} catch (IOException e) {
System.out.println("Start server exception: " + e.getMessage());
e.printStackTrace();
} catch (InterruptedException e) {
System.out.println("Server interrupted: " + e.getMessage());
e.printStackTrace();
}
}
}
package com.xuecheng.fastdfs.demo;
import com.xuecheng.fastdfs.stub.HelloServiceGrpc;
import com.xuecheng.fastdfs.stub.HelloServiceOuterClass;
import io.grpc.ManagedChannel;
import io.grpc.netty.shaded.io.grpc.netty.NegotiationType;
import io.grpc.netty.shaded.io.grpc.netty.NettyChannelBuilder;
public class MyHelloClient {
public static void main(String[] args) {
// 指定服务的ip和端口号
String host = "192.168.0.105";
int port = 8899;
// 创建客户端
ManagedChannel channel = NettyChannelBuilder.forAddress(host, port)
.negotiationType(NegotiationType.PLAINTEXT)
.build();
HelloServiceGrpc.HelloServiceBlockingStub blockingStub = HelloServiceGrpc.newBlockingStub(channel);
// 构造请求
HelloServiceOuterClass.HelloRequest request = HelloServiceOuterClass.HelloRequest.newBuilder()
.setRequestId(1)
.setName("zy")
.build();
// 请求服务
HelloServiceOuterClass.HelloResponse response = blockingStub.sayHello(request);
// 解析结果
System.out.println("Greeting: " + response.getGreeting());
}
}