本文介绍如何使用Grpc来否件和发布RPC服务,本文在最后附上github地址。
1.下载&安装 java版本的protoc,下载地址
我这里使用的是3.3.0版本,如图所示
选择一个属于你的平台protoc来安装,安装很简单,只需要将下载好的protoc的zip包解压到指定目录即可。
2.创建maven工程
选择一款你喜欢的编译器创建一个maven工程,maven工程创建好之后,在其工程的根目录下的pom文件中添加如下内容:
pom.xml文件内容:
1.6.1
1.8
3.3.0
1.5.0.Final
io.grpc
grpc-netty
${grpc.version}
io.grpc
grpc-protobuf
${grpc.version}
io.grpc
grpc-stub
${grpc.version}
kr.motd.maven
os-maven-plugin
${kr.motd.version}
org.xolstice.maven.plugins
protobuf-maven-plugin
0.5.0
com.google.protobuf:protoc:${protoc.version}:exe:${os.detected.classifier}
grpc-java
io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}
src/main/proto
/Volumes/NETAC/soft/dev/protoc/bin/protoc
compile
compile-custom
org.apache.maven.plugins
maven-compiler-plugin
${jdk.version}
3.创建proto规则文件
在工程目录下的src/main下创建proto规则文件目录,目录结构如图所示:3.1.创建规则文件
在这里我的proto规则文件中分别定义了Account和Greeter两个服务接口。
Greeter服务接口中定义了SayHello一个方法。
在Greeter服务中的方法只是简单的打招呼方法。Account服务接口中定义了addAccount和getAccoutByName两个方法。
在Account服务中的这两个方法功能是模拟添加和查询账户。
3.1.1.定义服务
在创建好的proto目录下创建hello_account.proto规则文件,用于定义rpc服务,内容如下:
//使用proto3语法
syntax = "proto3";
//指定proto文件包名
package org.cooze.grpc.service;
//指定 java 包名
option java_package = "org.cooze.grpc.service";
//指定proto文件生成java文件后的类名
option java_outer_classname = "ServiceProto";
//开启多文件
option java_multiple_files = true;
//倒入指定的.proto文件
import "entity/req_res.proto";
//定义rpc服务接口
service Greeter {
//服务端接口方法
rpc SayHello (org.cooze.grpc.entity.HelloRequest) returns (org.cooze.grpc.entity.HelloReply);
}
//定义rpc服务接口
service Account {
rpc addAccount(org.cooze.grpc.entity.Account) returns (org.cooze.grpc.entity.AccountResponse);
rpc getAccoutByName(org.cooze.grpc.entity.Account) returns (org.cooze.grpc.entity.AccountResponse);
}
3.1.2.定义消息
在proto目录下的子目录entity中创建req_res.proto文件,用于定义rpc的消息类型,内容如下:
//使用proto3语法
syntax = "proto3";
//指定proto文件包名
package org.cooze.grpc.entity;
//指定 java 包名
option java_package = "org.cooze.grpc.entity";
//指定proto文件生成java文件后的类名
option java_outer_classname = "EntityProto";
//开启多文件
option java_multiple_files = true;
//请求参数
message HelloRequest {
string name = 1;
}
//响应参数
message HelloReply {
string message = 1;
}
message Account {
string name = 1;
string sex = 2;
int32 age = 3;
}
message AccountResponse {
string msg = 1;
int32 code = 2;
repeated Account results = 3;
}
proto3定义服务和消息,请参看我翻译的《gRPC之proto语法》,翻译难免带有个人主观色彩,请见谅.
3.2.编译proto文件
在创建好proto文件,并定义好消息类型和服务之后,结下来就是编译proto规则文件生成对应的java代码。
打开命令终端并切换到工程根目录下
我这里使用的编译器是idea编译器,所以打开终端很简单-_-!,终端打开后键入命令mvn compile
编译即可,如图 :
在执行完比那一命令之后,在工程根目录下会生成的target,而target目录的结构以及java类文件,如下图所示:
其中,生成的服务定义接口类为:AccountGrpc和GreeterGrpc
,消息类型类为:Account、AccountResponse、HelloReply、HelloRequest
。
4.Grpc服务和客户端实现
完成编辑proto规则文件和生成对应待grpc代码之后,就可以开始实现Grpc的服务端代码和客户端代码了。
创建java包结构和文件,如图所示:
4.1.Grpc服务端业务实现代码
不多说啥了,代码里有注释,所以上代码!
GreeterImpl.java
代码如下:
public class GreeterImpl extends GreeterGrpc.GreeterImplBase {
@Override
public void sayHello(HelloRequest req, StreamObserver responseObserver) {
HelloReply reply = HelloReply.newBuilder().setMessage("Hello " + req.getName()).build();
//处理接收到的消息
String msg = reply.getMessage();
System.out.println("服务端收到消息:" + msg);
//响应消息
HelloReply response = reply.toBuilder().setMessage("世界你好!").build();
responseObserver.onNext(response);
responseObserver.onCompleted();
}
}
AccountImpl.java
代码如下:
public class AccountImpl extends AccountGrpc.AccountImplBase {
@Override
public void addAccount(Account request, StreamObserver responseObserver) {
//处理请求参数
System.out.println(StringFormatter.format("新增用户:%s\n性别:%s\n年龄:%d岁", request.getName(), request.getSex(), request.getAge()).get());
//处理响应参数
AccountResponse response = AccountResponse.getDefaultInstance().toBuilder()
.setCode(10000)
.setMsg("success!").build();
responseObserver.onNext(response);
responseObserver.onCompleted();
}
@Override
public void getAccoutByName(Account request, StreamObserver responseObserver) {
//处理请求参数
System.out.println(StringFormatter.format("请求查询用户名:%s", request.getName()).get());
//处理响应参数
List list = new ArrayList<>();
Account account1 = Account.getDefaultInstance().toBuilder()
.setName("张三")
.setAge(20)
.setSex("男").build();
list.add(account1);
Account account2 = Account.getDefaultInstance().toBuilder()
.setAge(30)
.setSex("男")
.setName("李四").build();
list.add(account2);
AccountResponse response = AccountResponse.getDefaultInstance().toBuilder()
.setCode(10000)
.setMsg("success!")
.addAllResults(list)
.build();
responseObserver.onNext(response);
responseObserver.onCompleted();
}
}
4.2.Grpc客户端代码
不多说啥了,代码里有注释,所以上代码!
BaseClient.java
代码如下:
public class BaseClient {
private final ManagedChannel channel;
private final GreeterGrpc.GreeterBlockingStub greeterBlockingStub;
private final AccountGrpc.AccountBlockingStub accountBlockingStub;
private BaseClient(ManagedChannel channel) {
this.channel = channel;
this.greeterBlockingStub = GreeterGrpc.newBlockingStub(channel);
this.accountBlockingStub = AccountGrpc.newBlockingStub(channel);
}
/**
* 构造客户端与Greeter 服务端连接 {@code host:port}
*
* @param host 主机地址
* @param port 端口
*/
public BaseClient(String host, int port) {
this(ManagedChannelBuilder.forAddress(host, port)
// Channels are secure by default (via SSL/TLS). For the example we disable TLS to avoid
// needing certificates.
.usePlaintext(true)
.build());
}
/**
* 关闭函数
* @throws InterruptedException
*/
public void shutdown() throws InterruptedException {
channel.shutdown().awaitTermination(5, TimeUnit.SECONDS);
}
public GreeterGrpc.GreeterBlockingStub getGreeterBlockingStub() {
return greeterBlockingStub;
}
public AccountGrpc.AccountBlockingStub getAccountBlockingStub() {
return accountBlockingStub;
}
}
GreeterClient.java
代码如下:
public class GreeterClient {
private final GreeterGrpc.GreeterBlockingStub blockingStub;
public GreeterClient(BaseClient client) {
blockingStub = client.getGreeterBlockingStub();
}
public void greet(String name) {
HelloRequest request = HelloRequest.newBuilder().setName(name).build();
HelloReply response;
try {
response = blockingStub.sayHello(request);
String msg = response.getMessage();
//接收到服务端返回的消息
System.out.println("客户端收到消息:" + msg);
} catch (StatusRuntimeException e) {
return;
}
}
}
AccountClient.java
代码如下:
public class AccountClient {
private final AccountGrpc.AccountBlockingStub accountBlockingStub;
private final BaseClient client;
public AccountClient(BaseClient client) {
this.client = client;
this.accountBlockingStub = client.getAccountBlockingStub();
}
public void addAccount(String name, String sex, int age) {
Account account = Account.getDefaultInstance().toBuilder()
.setName(name)
.setSex(sex)
.setAge(age)
.build();
AccountResponse response = this.accountBlockingStub.addAccount(account);
System.out.println(StringFormatter.format("返回消息:%s\n状态:%d", response.getMsg(), response.getCode()).get());
}
public void queryAccout(String name) {
Account account = Account.getDefaultInstance().toBuilder()
.setName(name).build();
AccountResponse response = this.accountBlockingStub.getAccoutByName(account);
System.out.println(StringFormatter.format("返回消息:%s\n状态:%d", response.getMsg(), response.getCode()).getValue());
System.out.println("查询结果:");
List list = response.getResultsList();
for (Account acc : list) {
System.out.println(StringFormatter.format("姓名:%s,性别:%s,年龄:%d", acc.getName(), acc.getSex(), acc.getAge()).get());
}
}
}
4.3.Grpc服务端启动引导类代码
不多说啥了,代码里有注释,所以上代码!
rpc引导类BootStrap.java
代码如下:
public class BootStrap {
private Server server;
/**
* 服务启动类
*
* @param port 端口
* @throws IOException
*/
private void start(int port) throws IOException {
server = ServerBuilder.forPort(port)
//注册服务
.addService(new GreeterImpl())
.addService(new AccountImpl())
.build()
.start();
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
System.err.println("*** JVM 关闭,导致gRPC服务关闭!");
BootStrap.this.stop();
System.err.println("*** 服务关闭");
}
});
}
/**
* RPC 服务关闭
*/
private void stop() {
if (server != null) {
server.shutdown();
}
}
/**
* 设置守护进程
*/
private void blockUntilShutdown() throws InterruptedException {
if (server != null) {
server.awaitTermination();
}
}
/**
* RPC服务启动main函数
*/
public static void main(String[] args) throws IOException, InterruptedException {
final BootStrap server = new BootStrap();
server.start(50051);
server.blockUntilShutdown();
}
}
4.3.Grpc测试
测试类代码如下:
public class Test {
public static void main(String[] args) throws Exception {
BaseClient client = new BaseClient("localhost", 50051);
try {
System.out.println("===============GreeterClient============");
GreeterClient greeterClient = new GreeterClient(client);
greeterClient.greet("Hello");
System.out.println("===============AccountClient============");
AccountClient accountClient = new AccountClient(client);
System.out.println("===============新增============");
accountClient.addAccount("张飞", "男", 45);
System.out.println("===============查找============");
accountClient.queryAccout("测试");
} finally {
client.shutdown();
}
}
}
好了,所有的前序工作都做完了,开始测试了!
启动rpc引导类BootStrap.java
运行客户端测试类
服务端效果图如下:
客户端效果图如下:
打完收工,项目代码地址