Быстро создавайте микросервисы с помощью gRPC-Gateway

Go HTTP gRPC

«Рыба и медведь ладони могут быть получены», все еще запутаны за HTTP и GRPC? Попробуйте GRPC-шлюз

Микросервисы: независимые, децентрализованные архитектурные шаблоны

Независимая, децентрализованная, организует услуги и управляет данными по всему бизнесу, а также использует легкие механизмы связи.

Службы организованы в соответствии с бизнес-областями и предоставляются интерфейсы Restful.Обмен данными и вызовы выполняются между службами через упрощенный метод связи (Restful), а упрощенный шлюз используется извне для упрощения сложности доступа клиентов. Основываясь на центре обнаружения и регистрации служб, выполните взаимное обнаружение между службами и осуществите горизонтальное расширение самих служб.

http://7pn5d3.com1.z0.glb.clouddn.com/api_gateway.png

gRPC: универсальная высокопроизводительная среда RPC.

http://7xj61w.com1.z0.glb.clouddn.com/grpc-logo.png

Универсальная высокопроизводительная платформа RPC, разработанная Google на основе стандарта HTTP/2.

  • Основанный на протоколе 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.protoGitHub.com/API Google/…

gRPC-Gateway: от gRPC к HTTP

С помощью стандартного интерфейса google/api/annotations.proto, предоставляемого Google, мы можем эффективно описать соответствующую форму интерфейса HTTP службы Protobuf. А gRPC-Gateway предоставляет возможность генерировать обратный прокси-сервер Http на основе определения интерфейса службы в файле .proto. Поскольку для того же стандартного определения службы Grpc, в дополнение к базовому клиенту grpc, также может быть создана соответствующая реализация интерфейса HTTP+JSON.

https://camo.githubusercontent.com/e75a8b46b078a3c1df0ed9966a16c24add9ccb83/68747470733a2f2f646f63732e676f6f676c652e636f6d2f64726177696e67732f642f3132687034435071724e5046686174744c5f63496f4a707446766c41716d35774c513067677149356d6b43672f7075623f773d37343926683d333730

Пример: создание поддержки для gRPC

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. Сгенерируйте код на стороне сервера

Здесь gradle используется для сборки build.gradle, а команда сборки вызывается для генерации кода на стороне сервера на основе прото-файла.

|- 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();
    }
}

Создать стартовый класс

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

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 proxy

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-example

резюме

Пока возьмем в качестве примера развертывание под Kubernetes:

  • Создание развертывания для каждой микрослужбы состоит из двух контейнеров, реализации gRPC-сервера службы и соответствующего обратного прокси-сервера. И используйте это как единицу для масштабирования (контейнеры в одном поде совместно используют общедоступную сеть и ресурсы хранилища, вы можете напрямую использовать 127.0.0.1 для доступа).
  • Создайте службу и прокси-сервер Http-порта и RPC-порта развертывания (внутренне открыты как Http-, так и RPC-службы).
  • Для служб без сохранения состояния внутренние службы системы используют Службу в качестве DNS для реализации удаленных вызовов RPC.
  • Для служб с отслеживанием состояния необходимо добавить дополнительные средства обнаружения служб и реестры, такие как Consul или Eureka. Реализовать вызовы «точка-точка».
  • Предоставляет Rest API внешним клиентам (браузер, H5) на основе API Gateway.

http://7pn5d3.com1.z0.glb.clouddn.com/http-with-grpc.png

В среде микрослужб, такой как Spring Cloud, каждая служба по умолчанию предоставляет Restful API на основе протокола HTTP, чтобы предоставлять возможности службы снаружи и внутри. В некоторых сценариях нам нужно не только сохранить простоту Restful, но и полностью улучшить внутреннюю производительность и надежность приложения.Использование gRPC может помочь нам в достижении этой цели, а с помощью таких инструментов, как gRPC-Gateway, мы можем легко Основываясь на определении прототипа интерфейса, он предоставляет Restful внешнему миру при использовании RPC и реализует пошаговую оптимизацию архитектуры программного обеспечения и повышение производительности приложений.