01.14 微服務:使用gRPC-Gateway快速構建微服務

編者注

原文地址:http://dockone.io/article/2836

微服務:獨立的,去中心化的架構模式

獨立的,去中心化的,圍繞業務組織服務和管理數據,並且使用輕量級通訊機制

按照業務領域組織服務並且提供Restful接口,服務與服務之間通過輕量級通訊方式(Restful)進行數據交換和調用,對外使用輕量級網關簡化客戶端訪問複雜度。基於服務發現和註冊中心,完成服務之間的相互發現以及實現服務自身的橫向擴展。

微服務:使用gRPC-Gateway快速構建微服務

gRPC: 通用的,高性能的RPC框架

微服務:使用gRPC-Gateway快速構建微服務

Google開發的基於HTTP/2標準設計的一個通用的,高性能的RPC框架

  • 基於HTTP/2協議提供了更好的強的應用性能(節省帶寬,減少TCP請求連接數)
  • 基於ProtoBuf定義服務,面向接口對服務進行頂層設計
syntax = "proto3";
package example;
message StringMessage {
string value = 1;
}
service YourService {
rpc Echo(StringMessage) returns (StringMessage) {}
}
  • 支持主流的編程語言,C ,Java,Python,Go,Ruby,Node.js,PHP等, 基於ProtoBuf生成相應的服務端和客戶端代碼。

相比在使用Restful方式完成服務之間的相互訪問,GRPC能提供更好的性能,更低的延遲,並且生來適合與分佈式系統。

同時基於標準化的IDL(ProtoBuf)來生成服務器端和客戶端代碼, ProtoBuf服務定義可以作為服務契約,因此可以更好的支持團隊與團隊之間的接口設計,開發,測試,協作等等。

因此在很多對於應用性能有較高要求的情況下,對外使用Restful提供API接口以支持不同的客戶端渠道(Web, Mobile)而服務與服務之間則採用RPC方式進行交互。

擴展你的gRPC定義

使用gRPC基於Protobuf可以實現服務間的標準化定義,同時可以能夠提供更好的應用性能,而在某些情況下我們依然希望我們的服務接口是能夠支持Restful API的,比如在第一個圖中,我們需要對外支持不同的渠道。因此我們可以在原有的Protobuf服務定義文件中添加更多的擴展,來講Protobuf在定義服務的同時定義相應的Restful接口即可:

syntax = "proto3";
package example;
import "google/api/annotations.proto";
message StringMessage {
string value = 1;
}
service YourService {
- rpc Echo(StringMessage) returns (StringMessage) {}
rpc Echo(StringMessage) returns (StringMessage) {
option (google.api.http) = {
post: "/v1/example/echo"
body: "*"
};
}
}

其中google/api/annotations.proto出自https://github.com/googleapis/googleapis,是Google提供的用於定義REST和gRPC的標準接口定義

gRPC-Gateway: 從gRPC到HTTP

通過google提供的標準接口google/api/annotations.proto我們可以有效的對Protobuf服務描述其相應的HTTP接口形式。而gRPC-Gateway則提供了基於.proto文件中的服務接口定義生成Http的反向代理的能力。因為對於同一個標準的Grpc服務定義,除了基本的grpc client以外還能生成相應的HTTP JSON的接口實現。

微服務:使用gRPC-Gateway快速構建微服務

示例:使用gRPC-Gateway創建微服務

1, 定義服務echo_service.proto

定義服務EchoService,並且Echo方法,該方法接收一個StringMessage結構的數據,並且返回StringMessage。 同時聲明該方法對外提供Rest API /v1/example/echo/{value}

syntax = "proto3";
package echo;
import "google/api/annotations.proto";
message StringMessage {
string value = 1;
}
service EchoService {
rpc Echo(StringMessage) returns (StringMessage) {
option (google.api.http) = {
post: "/v1/example/echo/{value}"
body: "*"
};
}
}

2, 生成Server端代碼

這裡使用gradle進行構建 build.gradle,調用構建命令基於proto文件生成服務端代碼

|- ProjectRoot
|- build.gradle
|- src
|- main
|- proto
|- api
|- annotations.proto
|- http.proto
|- echo_service.proto

build.gradle

apply plugin: 'java'
apply plugin: 'maven'
apply plugin: 'com.google.protobuf'
buildscript {
repositories {

mavenCentral()
}
dependencies {
// ASSUMES GRADLE 2.12 OR HIGHER. Use plugin version 0.7.5 with earlier
// gradle versions
classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.1'
}
}
repositories {
mavenCentral()
mavenLocal()
}
group = 'com.demo.grpc'
version = '1.0-SNAPSHOT'
description = """echo-service"""
sourceCompatibility = 1.5
targetCompatibility = 1.5
def grpcVersion = '1.6.1' // CURRENT_GRPC_VERSION
dependencies {
compile "com.google.api.grpc:proto-google-common-protos:0.1.9"
compile "io.grpc:grpc-netty:${grpcVersion}"
compile "io.grpc:grpc-protobuf:${grpcVersion}"
compile "io.grpc:grpc-stub:${grpcVersion}"
testCompile "io.grpc:grpc-testing:${grpcVersion}"
testCompile "junit:junit:4.11"
testCompile "org.mockito:mockito-core:1.9.5"
}
protobuf {
protoc {
artifact = 'com.google.protobuf:protoc:3.3.0'
}
plugins {
grpc {
artifact = "io.grpc:protoc-gen-grpc-java:${grpcVersion}"
}
}
generateProtoTasks {
all()*.plugins {
grpc {
// To generate deprecated interfaces and static bindService method,
// turn the enable_deprecated option to true below:
option 'enable_deprecated=false'
}
}
}
generatedFilesBaseDir = new File("$projectDir", "src")
}
// Inform IntelliJ projects about the generated code.
apply plugin: 'maven'
// Provide convenience executables for trying out the examples.

apply plugin: 'application'
startScripts.enabled = false
task echoServer(type: CreateStartScripts) {
mainClassName = 'com.wise2c.grpc.App'
applicationName = 'echo-server'
outputDir = new File(project.buildDir, 'tmp')
classpath = jar.outputs.files project.configurations.runtime
}
applicationDistribution.into('bin') {
from(echoServer)
fileMode = 0755
}
/gradlew build

3, 實現Echo接口,並且啟動服務

接口獲取請求內容,並且生成相應內容

public class EchoImpl extends EchoServiceImplBase {
@Override
public void echo(StringMessage request, StreamObserver<stringmessage> responseObserver) {
StringMessage reply = StringMessage.newBuilder().setValue("Hello " request.getValue()).build();
responseObserver.onNext(reply);
responseObserver.onCompleted();
}
}
/<stringmessage>

創建啟動類

public class EchoServer
{
private static final Logger logger = Logger.getLogger(EchoServer.class.getName());
private Server server;
private void start() throws IOException {
int port = 9090;
server = ServerBuilder.forPort(port)
.addService(new EchoImpl())
.build()
.start();
logger.info("Server started, listening on " port);
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {

// Use stderr here since the logger may have been reset by its JVM shutdown hook.
System.err.println("*** shutting down gRPC server since JVM is shutting down");
EchoServer.this.stop();
System.err.println("*** server shut down");
}
});
}
private void stop() {
if (server != null) {
server.shutdown();
}
}
private void blockUntilShutdown() throws InterruptedException {
if (server != null) {
server.awaitTermination();
}
}
public static void main(String[] args) throws IOException, InterruptedException {
final EchoServer server = new EchoServer();
server.start();
server.blockUntilShutdown();
}
}

運行gRPC Server實例

Oct 24, 2017 4:05:00 PM com.wise2c.grpc.EchoServer start
信息: Server started, listening on 9090

4, 生成Restful反向代理(Go)代碼

目前gRPC-Gateway只支持生成Go的Restful反向代理

protoc -I/usr/local/include -I. -I$GOPATH/src -I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis --grpc-gateway_out=logtostderr=true:. echo/echo_service.proto

5, 創建Restful代理啟動類

package main 

import (
"flag"
"net/http"
gw "git.wise2c.com/grpc-gateway-example/echo"
"github.com/golang/glog"
"github.com/grpc-ecosystem/grpc-gateway/runtime"
"golang.org/x/net/context"
"google.golang.org/grpc"
)
var (
echoEndpoint = flag.String("echo_endpoint", "localhost:9090", "endpoint of YourService")
)
func run() error {
ctx := context.Background()
ctx, cancel := context.WithCancel(ctx)
defer cancel()
mux := runtime.NewServeMux()
opts := []grpc.DialOption{grpc.WithInsecure()}
err := gw.RegisterEchoServiceHandlerFromEndpoint(ctx, mux, *echoEndpoint, opts)
if err != nil {
return err
}
return http.ListenAndServe(":8080", mux)
}
func main() {
flag.Parse()
defer glog.Flush()
if err := run(); err != nil {
glog.Fatal(err)
}
}

啟動Restful接口

bee run
微服務:使用gRPC-Gateway快速構建微服務

小結

至此,以Kubernetes下部署為例:

  • 對於每個微服務創建Deployment包含兩個容器,該服務的gRPCServer實現以及其對應的反向代理。並且以此為單位進行伸縮(同一Pod內容器共享公網,存儲資源,直接使用127.0.0.1訪問即可)。
  • 創建Service並且代理Deployment的Http端口以及RPC端口(對內同時暴露Http和RPC服務)。
  • 對於無狀態服務而言,系統內部服務之間以Service作為DNS,實現RPC的遠程調用。
  • 對於有狀態服務,需要添加額外的服務發現和註冊中心如Consul或Eureka。實現點對點調用。 對外基於API
  • Gateway對外部客戶端(瀏覽器,H5)提供Rest API。
微服務:使用gRPC-Gateway快速構建微服務

在諸如Spring Cloud這樣的微服務框架當中,每一個服務默認基於HTTP協議對外提供Restful API,從而對外對內的提供服務能力。 而在某些場景下,我們既需要保持Restful的簡單性,又想充分提升應用內部的性能以及可靠性,採用gRPC可以幫助我們實現是這樣的目的,而使用gRPC-Gateway這樣的工具我們可以很快速的基於proto接口定義,在使用RPC的同時對外提供Restful,實現軟件架構的小步優化以及應用性能的提升。


分享到:


相關文章: