REST协议改造gRPC实战
gRPC 是由 Google 主导开发的 RPC 框架,使用 HTTP/2 协议并用 ProtoBuf 作为序列化工具。
gRPC官方对REST的声音是:
- 和REST一样遵循HTTP协议(明确的说是HTTP/2),但是gRPC提供了全双工流
- 和传统的REST不同的是gRPC使用了静态路径,从而提高性能
- 用一些格式化的错误码代替了HTTP的状态码更好的标示错误
背景
regist 是在微服务架构上再抽出的一层,用于服务注册,心跳机制,服务注销。
regist 和 微服务架构 之间使用的是 REST 协议进行通信,现需要改造成 gRPC
-
RPC 框架: gRPC
-
协议:HTTP/2
-
序列化工具: ProtoBuf
-
服务端: regist (golang)
-
客户端 :微服务架构(java)
一、服务端改造(golang)
1. protocal buffer安装
去官网下载 protoc 压缩包,解压后,将 protoc.exe
放在 GOPATH\bin
目录下面
2. 安装GoLang protoc 插件
gRPC-go可以通过golang 的get命令直接安装,非常方便。
go get -a github.com/golang/protobuf/protoc-gen-go
3. 定义register.proto
文件
syntax = "proto3";
package proto;
service Regist {
rpc Register (ResponseService) returns (RegisterReply) {}
rpc Deregister (ResponseService) returns (DeregisterReply) {}
}
message RegisterReply {
string message = 1;
}
message DeregisterReply {
string message = 1;
}
message ResponseService {
string id = 1;
string name = 2;
string version = 3;
string address = 4;
int32 port = 5;
map<string, string> metadata = 6;
}
-
syntax = "proto3"
,声明protobuf版本为3,默认为2。 - 定义了一个服务 Regist,其中有两个API
Register
和Register
。 - 定义3个message为接受和返回的参数。
4. 生成 register.pb.go
文件
在register.proto
文件所在根目录下,执行:
protoc --go_out=plugins=grpc:. register.proto
则会在同目录下生成对应的 register.pb.go
文件,相应的服务器端和客户端的GoLang代码。生成的代码中包含了客户端能够进行RPC的方法以及服务器端需要进行实现的接口。
5.服务端代码改造
-
register.pb.go
文件中提供的服务接口
type RegistClient interface {
Register(ctx context.Context, in *ResponseService, opts ...grpc.CallOption) (*RegisterReply, error)
Deregister(ctx context.Context, in *ResponseService, opts ...grpc.CallOption) (*DeregisterReply, error)
}
- 服务端代码实现上面的接口
func (s *server) Register(ctx context.Context, in *pb.ResponseService) (*pb.RegisterReply, error) {
// 定义注册的服务,组装 gRPC 传过来的参数
service := &rp.Service{
Name: in.GetName(),
Version: in.GetVersion(),
Metadata: in.GetMetadata(),
NodeName: nodeName,
Nodes: []*rp.Node{
&rp.Node{
ID: in.GetId(),
Address: in.GetAddress(),
Port: int(in.GetPort()),
},
},
Endpoints: []*rp.Endpoint{
&rp.Endpoint{
Name: in.GetName(),
Request: &rp.Value{},
Response: &rp.Value{},
Metadata: in.GetMetadata(),
},
},
}
// 调用 consul 注册接口
return &pb.RegisterReply{}, registry.Register(service, rp.RegisterTTL(time.Duration(ttl)*time.Second))
}
二、客户端改造(java)
1. pom.xml文件中添加 maven 依赖
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-netty</artifactId>
<version>1.7.0</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-protobuf</artifactId>
<version>1.7.0</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-stub</artifactId>
<version>1.7.0</version>
</dependency>
2. pom.xml文件中 配置protobuf maven插件
<build>
<extensions>
<extension>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>1.5.0.Final</version>
</extension>
</extensions>
<plugins>
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>0.5.0</version>
<configuration>
<protocArtifact>com.google.protobuf:protoc:3.4.0:exe:${os.detected.classifier}</protocArtifact>
<pluginId>grpc-java</pluginId>
<pluginArtifact>io.grpc:protoc-gen-grpc-java:1.7.0:exe:${os.detected.classifier}</pluginArtifact>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>compile-custom</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
3. 定义regist.proto
文件
syntax = "proto3";
package proto;
option java_multiple_files = true;
option java_package = "com.fiberhome.smartms.grpc";
option java_outer_classname = "RegistProto";
service Regist {
rpc Register (ResponseService) returns (RegisterReply) {}
rpc Deregister (ResponseService) returns (DeregisterReply) {}
}
message RegisterReply {
string message = 1;
}
message DeregisterReply {
string message = 1;
}
message ResponseService {
string id = 1;
string name = 2;
string version = 3;
string address = 4;
int32 port = 5;
map<string, string> metadata = 6;
}
4. 自动生成java接口代码
在pom.xml文件根目录中,执行mvn compile
(也可试下mvn install
),根据.proto文件自动生成一系列的接口文件。
- 若生成不成功,多试几次
5.客户端代码改造
public class ServiceRegistryGrpc implements ServiceRegistry<Registration> {
private static final Logger logger = Logger.getLogger(ServiceRegistryGrpc.class.getName());
private final ManagedChannel channel;
private final RegistGrpc.RegistBlockingStub blockingStub;
public ServiceRegistryGrpc(String host, int port) {
channel = ManagedChannelBuilder.forAddress(host, port).usePlaintext(true).build();
blockingStub = RegistGrpc.newBlockingStub(channel);
}
public void shutdown() throws InterruptedException {
channel.shutdown().awaitTermination(5, TimeUnit.SECONDS);
}
@Override
public void register(Registration reg) {
Service service = reg.getService();
ServiceRegistryGrpc client = new ServiceRegistryGrpc("mos", 9999);
ResponseService request = ResponseService.newBuilder().setId(service.getId()).setName(service.getName())
.setVersion(service.getVersion()).setAddress(service.getAddress()).setPort(service.getPort()).build();
RegisterReply response;
try {
response = blockingStub.register(request);
} catch (StatusRuntimeException e) {
logger.log(Level.WARNING, "RPC failed: {0}", e.getStatus());
return;
}
logger.info("Registering service success ");
}
}
-
public ServiceRegistryGrpc(String host, int port) {}
,这个函数是构造客户端 连接 服务 -
response = blockingStub.register(request);
,调用服务接口
三、验证
-
启动服务端
服务端 -
启动客户端
客户端 -
数据通信成功
服务端获取客户端 传来的数据
-
consul注册成功
attachment服务