Dubbo 在跨語言和協議穿透性方向的探索:支持 HTTP/2 gRPC

本文整理自劉軍在 Dubbo meetup 成都站分享的《Dubbo 在多語言和協議穿透性方向上的探索》。

本文總體上可分爲基礎產品簡介、Dubbo 對 gRPC (HTTP/2) 和 Protobuf 的支持及示例演示三部分,在簡介部分介紹了 Dubbo、HTTP/2、gRPC、Protobuf 的基本概念和特點;第二部分介紹了 Dubbo 爲何要支持 gRPC (HTTP/2) 和 Protobuf,以及這種支持爲 gRPC 和 Dubbo 開發帶來的好處與不同;第三部分通過兩個實例分別演示了 Dubbo gRPC 和 Dubbo Protobuf 的使用方式。

基本介紹

Dubbo 協議

從協議層面展開,以下是當前 2.7 版本支持的 Dubbo 協議:

衆所周知,Dubbo 協議是直接定義在 TCP 傳輸層協議之上,由於 TCP 高可靠全雙工的特點,爲 Dubbo 協議的定義提供了最大的靈活性,但同時也正是因爲這樣的靈活性,RPC 協議普遍都是定製化的私有協議,Dubbo 同樣也面臨這個問題。在這裏我們着重講一下 Dubbo 在協議通用性方面值得改進的地方,關於協議詳細解析請參見官網博客。

  • Dubbo 協議體 Body 中有一個可擴展的 attachments 部分,這給 RPC 方法之外額外傳遞附加屬性提供了可能,是一個很好的設計。但是類似的 Header 部分,卻缺少類似的可擴展 attachments,這點可參考 HTTP 定義的 Ascii Header 設計,將 Body Attachments 和 Header Attachments 做職責劃分。
  • Body 協議體中的一些 RPC 請求定位符如 Service Name、Method Name、Version 等,可以提到 Header 中,和具體的序列化協議解耦,以更好的被網絡基礎設施識別或用於流量管控。
  • 擴展性不夠好,欠缺協議升級方面的設計,如 Header 頭中沒有預留的狀態標識位,或者像 HTTP 有專爲協議升級或協商設計的特殊 packet。
  • 在 Java 版本的代碼實現上,不夠精簡和通用。如在鏈路傳輸中,存在一些語言綁定的內容;消息體中存在冗餘內容,如 Service Name 在 Body 和 Attachments 中都存在。

HTTP/1

相比於直接構建與 TPC 傳輸層的私有 RPC 協議,構建於 HTTP 之上的遠程調用解決方案會有更好的通用性,如WebServices 或 REST 架構,使用 HTTP + JSON 可以說是一個事實標準的解決方案。

之所有選擇構建在 HTTP 之上,我認爲有兩個最大的優勢:

  1. HTTP 的語義和可擴展性能很好的滿足 RPC 調用需求。
  2. 通用性,HTTP 協議幾乎被網絡上的所有設備所支持,具有很好的協議穿透性。

具體來說,HTTP/1 的優勢和限制是:

  • 典型的 Request – Response 模型,一個鏈路上一次只能有一個等待的 Request 請求
  • HTTP/1 支持 Keep-Alive 鏈接,避免了鏈接重複創建開銷
  • Human Readable Headers,使用更通用、更易於人類閱讀的頭部傳輸格式
  • 無直接 Server Push 支持,需要使用 Polling Long-Polling 等變通模式

HTTP/2

HTTP/2 保留了 HTTP/1 的所有語義,在保持兼容的同時,在通信模型和傳輸效率上做了很大的改進。

  • 支持單條鏈路上的 Multiplexing,相比於 Request - Response 獨佔鏈路,基於 Frame 實現更高效利用鏈路
  • Request - Stream 語義,原生支持 Server Push 和 Stream 數據傳輸
  • Flow Control,單條 Stream 粒度的和整個鏈路粒度的流量控制
  • 頭部壓縮 HPACK
  • Binary Frame
  • 原生 TLS 支持

gRPC

上面提到了在 HTTP 及 TCP 協議之上構建 RPC 協議各自的優缺點,相比於 Dubbo 構建於 TPC 傳輸層之上,Google 選擇將 gRPC 直接定義在 HTTP/2 協議之上,關於 gRPC 的 基本介紹和 設計願景 請參考以上兩篇文章,我這裏僅摘取設計願景中幾個能反映 gRPC 設計目的特性來做簡單說明。

gRPC 的基本介紹:

https://platformlab.stanford.edu/Seminar%2Talks/gRPC.pdf

設計願景:

https://grpc.io/blog/principles/?spm=ata.13261165.0.0.2be55017XbUhs8

  • Coverage & Simplicity,協議設計和框架實現要足夠通用和簡單,能運行在任何設備之上,甚至一些資源首先的如 IoT、Mobile 等設備。
  • Interoperability & Reach,要構建在更通用的協議之上,協議本身要能被網絡上幾乎所有的基礎設施所支持。
  • General Purpose & Performant,要在場景和性能間做好平衡,首先協議本身要是適用於各種場景的,同時也要儘量有高的性能。
  • Payload Agnostic,協議上傳輸的負載要保持語言和平臺中立。
  • Streaming,要支持 Request - Response、Request - Stream、Bi-Steam 等通信模型。
  • Flow Control,協議自身具備流量感知和限制的能力。
  • Metadata Exchange,在 RPC 服務定義之外,提供額外附加數據傳輸的能力。

總的來說,在這樣的設計理念指導下,gRPC 最終被設計爲一個跨語言、跨平臺的、通用的、高性能的、基於 HTTP/2 的 RPC 協議和框架。

Protobuf

Protocol buffers (Protobuf) 是 Google 推出的一個跨平臺、語言中立的結構化數據描述和序列化的產品,它定義了一套結構化數據定義的協議,同時也提供了相應的 Compiler 工具,用來將語言中立的描述轉化爲相應語言的具體描述。

Protocol buffers (Protobuf) 詳情參考:

https://developers.google.com/protocol-buffers/docs/overview

Compiler 詳情參考:

https://github.com/protocolbuffers/protobuf/releases/tag/v3.10.0

它的一些特性包括:

  • 跨語言 跨平臺,語言中立的數據描述格式,默認提供了生成多種語言的 Compiler 工具。
  • 安全性,由於反序列化的範圍和輸出內容格式都是 Compiler 在編譯時預生成的,因此繞過了類似 Java Deserialization Vulnarability 的問題。
  • 二進制 高性能
  • 強類型
  • 字段變更向後兼容
message Person {
      required string name = 1;
      required int32 id = 2;
      optional string email = 3;

      enum PhoneType {
        MOBILE = 0;
        HOME = 1;
        WORK = 2;
      }

      message PhoneNumber {
        required string number = 1;
        optional PhoneType type = 2 [default = HOME];
      }

      repeated PhoneNumber phone = 4;
    }

除了結構化數據描述之外,Protobuf 還支持定義 RPC 服務,它允許我們定義一個 .proto 的服務描述文件,進而利用 Protobuf Compiler 工具生成特定語言和 RPC 框架的接口和 stub。後續將要具體講到的 gRPC + Protobuf、Dubbo-gRPC + Protobuf 以及 Dubbo + Protobuf 都是通過定製 Compiler 類實現的。

service SearchService {
    rpc Search (SearchRequest) returns (SearchResponse);
}

Dubbo 所做的支持

跨語言的服務開發涉及到多個方面,從服務定義、RPC 協議到序列化協議都要做到語言中立,同時還針對每種語言有對應的 SDK 實現。雖然得益於社區的貢獻,現在 Dubbo 在多語言 SDK 實現上逐步有了起色,已經提供了包括 Java, Go, PHP, C#, Python, NodeJs, C 等版本的客戶端或全量實現版本,但在以上提到的跨語言友好型方面,以上三點還是有很多可改進之處。

  • 協議,上面我們已經分析過 Dubbo 協議既有的缺點,如果能在 HTTP/2 之上構建應用層協議,則無疑能避免這些弊端,同時最大可能的提高協議的穿透性,避免網關等協議轉換組件的存在,更有利於鏈路上的流量管控。考慮到 gRPC 是構建在 HTTP/2 之上,並且已經是雲原生領域推薦的通信協議,Dubbo 在第一階段選擇了直接支持 gRPC 協議作爲當前的 HTTP/2 解決方案。我們也知道 gRPC 框架自身的弊端在於易用性不足以及服務治理能力欠缺(這也是目前絕大多數廠商不會直接裸用 gRPC 框架的原因),通過將其集成進 Dubbo 框架,用戶可以方便的使用 Dubbo 編程模型 + Dubbo 服務治理 + gRPC 協議通信的組合。
  • 服務定義,當前 Dubbo 的服務定義和具體的編程語言綁定,沒有提供一種語言中立的服務描述格式,比如 Java 就是定義 Interface 接口,到了其他語言又得重新以另外的格式定義一遍。因此 Dubbo 通過支持 Protobuf 實現了語言中立的服務定義。
  • 序列化,Dubbo 當前支持的序列化包括 Json、Hessian2、Kryo、FST、Java 等,而這其中支持跨語言的只有 Json、Hessian2,通用的 Json 有固有的性能問題,而 Hessian2 無論在效率還是多語言 SDK 方面都有所欠缺。爲此,Dubbo 通過支持 Protobuf 序列化來提供更高效、易用的跨語言序列化方案。

示例

示例 1,使用 Dubbo 開發 gRPC 服務

gRPC 是 Google 開源的構建在 HTTP/2 之上的一個 PRC 通信協議。Dubbo 依賴其靈活的協議擴展機制,增加了對 gRPC (HTTP/2) 協議的支持。

目前的支持限定在 Dubbo Java 語言版本,後續 Go 語言或其他語言版本將會以類似方式提供支持。下面,通過一個簡單的示例來演示如何在 Dubbo 中使用 gRPC 協議通信,詳情參考:https://github.com/apache/dubbo-samples/tree/master/dubbo-samples-grpc

1. 定義服務 IDL

首先,通過標準的 Protobuf 協議定義服務如下:

syntax = "proto3";

   option java_multiple_files = true;
   option java_package = "io.grpc.examples.helloworld";
   option java_outer_classname = "HelloWorldProto";
   option objc_class_prefix = "HLW";

   package helloworld;

   // The greeting service definition.
   service Greeter {
     // Sends a greeting
     rpc SayHello (HelloRequest) returns (HelloReply) {}
   }

   // The request message containing the user's name.
   message HelloRequest {
     string name = 1;
   }

   // The response message containing the greetings
   message HelloReply {
     string message = 1;
   }

在此,我們定義了一個只有一個方法 sayHello 的 Greeter 服務,同時定義了方法的入參和出參,

2. Protobuf Compiler 生成 Stub

  1. 定義 Maven Protobuf Compiler 插件工具。這裏我們擴展了 Protobuf 的 Compiler 工具,以用來生成 Dubbo 特有的 RPC stub,此當前以 Maven 插件的形式發佈。
<plugin>
  <groupId>org.xolstice.maven.plugins</groupId>
  <artifactId>protobuf-maven-plugin</artifactId>
  <version>0.5.1</version>
  <configuration>
    <protocArtifact>com.google.protobuf:protoc:3.7.1:exe:${os.detected.classifier}  
    </protocArtifact>
    <pluginId>dubbo-grpc-java</pluginId>
    <pluginArtifact>org.apache.dubbo:protoc-gen-dubbo-java:1.19.0-SNAPSHOT:exe:${os.detected.classifier}</pluginArtifact>
    <outputDirectory>build/generated/source/proto/main/java</outputDirectory>
    <clearOutputDirectory>false</clearOutputDirectory>
    <pluginParameter>grpc</pluginParameter>
  </configuration>
  <executions>
    <execution>
      <goals>
        <goal>compile</goal>
        <goal>compile-custom</goal>
      </goals>
    </execution>
  </executions>
</plugin>

其中,pluginArtifact 指定了 Dubbo 定製版本的 Java Protobuf Compiler 插件,通過這個插件來在編譯過程中生成 Dubbo 定製版本的 gRPC stub。

<pluginArtifact>org.apache.dubbo:protoc-gen-dubbo-java:1.19.0-SNAPSHOT:exe:${os.detected.classifier}</pluginArtifact>

由於 protoc-gen-dubbo-java 支持 gRPC 和 Dubbo 兩種協議,可生成的 stub 類型,默認值是 gRPC,關於 dubbo 協議的使用可參見 使用 Protobuf 開發 Dubbo 服務。

<pluginParameter>grpc</pluginParameter>

3. 生成 Java Bean 和 Dubbo-gRPC stub

# 運行以下 maven 命令
$ mvn clean compile

生成的 Stub 和消息類 如下:

重點關注 GreeterGrpc ,包含了所有 gRPC 標準的 stub 類/方法,同時增加了 Dubbo 特定的接口,之後 Provider 端的服務暴露和 Consumer 端的服務調用都將依賴這個接口。


/**
* Code generated for Dubbo
*/
public interface IGreeter {
  default public io.grpc.examples.helloworld.HelloReply     sayHello(io.grpc.examples.helloworld.HelloRequest request) {
    throw new UnsupportedOperationException("No need to override this method, extend XxxImplBase and override all methods it allows.");
  }
  default public com.google.common.util.concurrent.ListenableFuture<io.grpc.examples.helloworld.HelloReply> sayHelloAsync(
    io.grpc.examples.helloworld.HelloRequest request) {
    throw new UnsupportedOperationException("No need to override this method, extend XxxImplBase and override all methods it allows.");
  }
  public void sayHello(io.grpc.examples.helloworld.HelloRequest request,
                       io.grpc.stub.StreamObserver<io.grpc.examples.helloworld.HelloReply> responseObserver);
}

4.業務邏輯開發

繼承 GreeterGrpc.GreeterImplBase (來自第 2 步),編寫業務邏輯,這點和原生 gRPC 是一致的。

package org.apache.dubbo.samples.basic.impl;

import io.grpc.examples.helloworld.GreeterGrpc;
import io.grpc.examples.helloworld.HelloReply;
import io.grpc.examples.helloworld.HelloRequest;
import io.grpc.stub.StreamObserver;

public class GrpcGreeterImpl extends GreeterGrpc.GreeterImplBase {
  @Override
  public void sayHello(HelloRequest request, StreamObserver<HelloReply> responseObserver)       {
    System.out.println("Received request from client.");
    System.out.println("Executing thread is " + Thread.currentThread().getName());
    HelloReply reply = HelloReply.newBuilder()
      .setMessage("Hello " +    request.getName()).build();
    responseObserver.onNext(reply);
    responseObserver.onCompleted();
  }
}

5.Provider 端暴露 Dubbo 服務

以 Spring XML 爲例:

<dubbo:application name="demo-provider"/>

<!-- 指定服務暴露協議爲 gRPC -->
<dubbo:protocol id="grpc" name="grpc"/>

<dubbo:registry address="zookeeper://${zookeeper.address:127.0.0.1}:2181"/>

<bean id="greeter" class="org.apache.dubbo.samples.basic.impl.GrpcGreeterImpl"/>

<!-- 指定 protoc-gen-dubbo-java 生成的接口 -->
<dubbo:service interface="io.grpc.examples.helloworld.GreeterGrpc$IGreeter" ref="greeter" protocol="grpc"/>
public static void main(String[] args) throws Exception {
  ClassPathXmlApplicationContext context =
    new ClassPathXmlApplicationContext("spring/dubbo-demo-provider.xml");
  context.start();

  System.out.println("dubbo service started");
  new CountDownLatch(1).await();
}

6.引用 Dubbo 服務

<dubbo:application name="demo-consumer"/>

<dubbo:registry address="zookeeper://${zookeeper.address:127.0.0.1}:2181"/>

<!-- 指定 protoc-gen-dubbo-java 生成的接口 -->
<dubbo:reference id="greeter" interface="io.grpc.examples.helloworld.GreeterGrpc$IGreeter" protocol="grpc"/>
public static void main(String[] args) throws IOException {
  ClassPathXmlApplicationContext context =
    new ClassPathXmlApplicationContext("spring/dubbo-demo-consumer.xml");
  context.start();

  GreeterGrpc.IGreeter greeter = (GreeterGrpc.IGreeter) context.getBean("greeter");

  HelloReply reply = greeter.sayHello(HelloRequest.newBuilder().setName("world!").build());
  System.out.println("Result: " + reply.getMessage());

  System.in.read();
}

示例1附:高級用法

1、異步調用

再來看一遍 protoc-gen-dubbo-java 生成的接口:

/**
* Code generated for Dubbo
*/
public interface IGreeter {
  default public HelloReply sayHello(HelloRequest request) {
    // ......
  }
  default public ListenableFuture<HelloReply> sayHelloAsync(HelloRequest request) {
    // ......
  }
  public void sayHello(HelloRequest request, StreamObserver<HelloReply> responseObserver);
}

這裏爲 sayHello 方法生成了三種類型的重載方法,分別用於同步調用、異步調用和流式調用,如果消費端要進行異步調用,直接調用 sayHelloAsync() 即可:

public static void main(String[] args) throws IOException {
  // ...
  GreeterGrpc.IGreeter greeter = (GreeterGrpc.IGreeter) context.getBean("greeter");
  ListenableFuture<HelloReply> future =   
    greeter.sayHAsyncello(HelloRequest.newBuilder().setName("world!").build());
  // ...
}

2、高級配置

由於當前實現方式是直接集成了 gRPC-java SDK,因此很多配置還沒有和 Dubbo 側對齊,或者還沒有以 Dubbo 的配置形式開放,因此,爲了提供最大的靈活性,我們直接把 gRPC-java 的配置接口暴露了出來。

絕大多數場景下,你可能並不會用到以下擴展,因爲它們更多的是對 gRPC 協議的攔截或者 HTTP/2 層面的配置。同時使用這些擴展點可能需要對 HTTP/2 或 gRPC 有基本的瞭解。

擴展點

目前支持的擴展點如下:

  • org.apache.dubbo.rpc.protocol.grpc.interceptors.ClientInterceptor
  • org.apache.dubbo.rpc.protocol.grpc.interceptors.GrpcConfigurator
  • org.apache.dubbo.rpc.protocol.grpc.interceptors.ServerInterceptor
  • org.apache.dubbo.rpc.protocol.grpc.interceptors.ServerTransportFilter

GrpcConfigurator 是最通用的擴展點,我們以此爲例來說明一下,其基本定義如下:

public interface GrpcConfigurator {
  // 用來定製 gRPC NettyServerBuilder
  default NettyServerBuilder configureServerBuilder(NettyServerBuilder builder, URL url) {
    return builder;
  }
  // 用來定製 gRPC NettyChannelBuilder
  default NettyChannelBuilder configureChannelBuilder(NettyChannelBuilder builder, URL url) {
    return builder;
  }
  // 用來定製 gRPC CallOptions, 定義某個服務在每次請求間傳遞數據
  default CallOptions configureCallOptions(CallOptions options, URL url) {
    return options;
  }
}

以下是一個示例擴展實現:

public class MyGrpcConfigurator implements GrpcConfigurator {
  private final ExecutorService executor = Executors
    .newFixedThreadPool(200, new NamedThreadFactory("Customized-grpc", true));

  @Override
  public NettyServerBuilder configureServerBuilder(NettyServerBuilder builder, URL url) {
    return builder.executor(executor);
  }

  @Override
  public NettyChannelBuilder configureChannelBuilder(NettyChannelBuilder builder, URL url)
{
    return builder.flowControlWindow(10);
  }

  @Override
  public CallOptions configureCallOptions(CallOptions options, URL url) {
    return options.withOption(CallOptions.Key.create("key"), "value");
  }
}

配置爲 Dubbo SPI,`resources/META-INF/services 增加配置文件。

default=org.apache.dubbo.samples.basic.comtomize.MyGrpcConfigurator
  1. 指定 Provider 端線程池

默認用的是 Dubbo 的線程池,有 fixed (默認)、cached、direct 等類型。以下演示了切換爲業務自定義線程池。

private final ExecutorService executor = Executors
              .newFixedThreadPool(200, new NamedThreadFactory("Customized-grpc", true));

public NettyServerBuilder configureServerBuilder(NettyServerBuilder builder, URL url)
{
return builder.executor(executor);
}
  1. 指定 Consumer 端限流值

設置 Consumer 限流值爲 10。

@Override
public NettyChannelBuilder configureChannelBuilder(NettyChannelBuilder builder, URL url)
{
  return builder.flowControlWindow(10);
}

3.傳遞附加參數D

emoService 服務調用傳遞 key。

@Override  
public CallOptions configureCallOptions(CallOptions options, URL url) {
  if (url.getServiceInterface().equals("xxx.DemoService")) {
    return options.withOption(CallOptions.Key.create("key"), "value");
  } else {
    return options;
  }
}

3、雙向流式通信

代碼中還提供了一個支持雙向流式通信的示例,同時提供了攔截流式調用的 Interceptor 擴展示例實現。

  • MyClientStreamInterceptor,工作在 client 端,攔截髮出的請求流和接收的響應流。
  • MyServerStreamInterceptor,工作在 server 端,攔截收到的請求流和發出的響應流。

雙向流式通信的示例,詳情點擊:

https://github.com/apache/dubbo-samples/tree/master/dubbo-samples-grpc/src/main/java/org/apache/dubbo/samples/basic/impl/routeguide

4、TLS 配置

配置方式和 Dubbo 提供的通用的 TLS 支持一致,具體請參見文檔

示例 2, 使用 Protobuf 開發 Dubbo 服務

下面,我們以一個具體的示例來看一下基於 Protobuf 的 Dubbo 服務開發流程。

1. 定義服務

通過標準 Protobuf 定義服務。

syntax = "proto3";

    option java_multiple_files = true;
    option java_package = "org.apache.dubbo.demo";
    option java_outer_classname = "DemoServiceProto";
    option objc_class_prefix = "DEMOSRV";

    package demoservice;

    // The demo service definition.
    service DemoService {
      rpc SayHello (HelloRequest) returns (HelloReply) {}
    }

    // The request message containing the user's name.
    message HelloRequest {
      string name = 1;
    }

    // The response message containing the greetings
    message HelloReply {
      string message = 1;
    } 

這裏定義了一個 DemoService 服務,服務只包含一個 sayHello 方法,同時定義了方法的入參和出參。

2. Compiler 編譯服務

  1. 引入 Protobuf Compiler Maven 插件,同時指定 protoc-gen-dubbo-java RPC 擴展。
<plugin>
  <groupId>org.xolstice.maven.plugins</groupId>
  <artifactId>protobuf-maven-plugin</artifactId>
  <version>0.5.1</version>
  <configuration>
  <protocArtifact>com.google.protobuf:protoc:3.7.1:exe:${os.detected.classifier}  
  </protocArtifact>
  <pluginId>dubbo-grpc-java</pluginId>
  <pluginArtifact>org.apache.dubbo:protoc-gen-dubbo-java:1.19.0-SNAPSHOT:exe:${os.detected.classifier}</pluginArtifact>
  <outputDirectory>build/generated/source/proto/main/java</outputDirectory>
  <clearOutputDirectory>false</clearOutputDirectory>
  <pluginParameter>dubbo</pluginParameter>
  </configuration>
  <executions>
  <execution>
    <goals>
    <goal>compile</goal>
    <goal>compile-custom</goal>
    </goals>
  </execution>
  </executions>
</plugin>

注意,這裏與 Dubbo 對 gRPC 支持部分的區別在於:dubbo

2、生成 RPC stub

# 運行以下 maven 命令
$mvn clean compile

生成的 Java 類如下:

public final class DemoServiceDubbo {

private static final AtomicBoolean registered = new AtomicBoolean();

private static Class<?> init() {
 Class<?> clazz = null;
 try {
   clazz = Class.forName(DemoServiceDubbo.class.getName());
   if (registered.compareAndSet(false, true)) {
    org.apache.dubbo.common.serialize.protobuf.support.ProtobufUtils.marshaller(
      org.apache.dubbo.demo.HelloRequest.getDefaultInstance());
    org.apache.dubbo.common.serialize.protobuf.support.ProtobufUtils.marshaller(
      org.apache.dubbo.demo.HelloReply.getDefaultInstance());
   }
 } catch (ClassNotFoundException e) {
   // ignore
 }
 return clazz;
}

private DemoServiceDubbo() {}

public static final String SERVICE_NAME = "demoservice.DemoService";

/**
       * Code generated for Dubbo
       */
public interface IDemoService {

 static Class<?> clazz = init();
 org.apache.dubbo.demo.HelloReply sayHello(org.apache.dubbo.demo.HelloRequest request);

 java.util.concurrent.CompletableFuture<org.apache.dubbo.demo.HelloReply> sayHelloAsync(
   org.apache.dubbo.demo.HelloRequest request);

}

}

最值得注意的是 IDemoService 接口,它會作爲 Dubbo 服務定義基礎接口。

3、開發業務邏輯

從這一步開始,所有開發流程就和直接定義 Java 接口一樣了。實現接口定義業務邏輯。

public class DemoServiceImpl implements DemoServiceDubbo.IDemoService {
  private static final Logger logger = LoggerFactory.getLogger(DemoServiceImpl.class);

  @Override
  public HelloReply sayHello(HelloRequest request) {
    logger.info("Hello " + request.getName() + ", request from consumer: " + RpcContext.getContext().getRemoteAddress());
    return HelloReply.newBuilder()
      .setMessage("Hello " + request.getName() + ", response from provider: "
                  + RpcContext.getContext().getLocalAddress())
      .build();
  }

  @Override
  public CompletableFuture<HelloReply> sayHelloAsync(HelloRequest request) {
    return CompletableFuture.completedFuture(sayHello(request));
  }
}

4、配置 Provider

暴露 Dubbo 服務。

<dubbo:application name="demo-provider"/>

<dubbo:registry address="zookeeper://127.0.0.1:2181"/>

<dubbo:protocol name="dubbo"/>

<bean id="demoService" class="org.apache.dubbo.demo.provider.DemoServiceImpl"/>

<dubbo:service interface="org.apache.dubbo.demo.DemoServiceDubbo$IDemoService" ref="demoService"/>
public static void main(String[] args) throws Exception {
  ClassPathXmlApplicationContext context = 
    new ClassPathXmlApplicationContext("spring/dubbo-provider.xml");
  context.start();
  System.in.read();
}

5、配置 Consumer

引用 Dubbo 服務。

<dubbo:application name="demo-consumer"/>

<dubbo:registry address="zookeeper://127.0.0.1:2181"/>

<dubbo:reference id="demoService" check="false" interface="org.apache.dubbo.demo.DemoServiceDubbo$IDemoService"/>
public static void main(String[] args) throws Exception {
  ClassPathXmlApplicationContext context = 
    new ClassPathXmlApplicationContext("spring/dubbo-consumer.xml");
  context.start();
  IDemoService demoService = context.getBean("demoService", IDemoService.class);
  HelloRequest request = HelloRequest.newBuilder().setName("Hello").build();
  HelloReply reply = demoService.sayHello(request);
  System.out.println("result: " + reply.getMessage());
  System.in.read();
}

展望

RPC 協議是實現微服務體系互通的核心組件,通常採用不同的微服務通信框架則意味着綁定某一個特定的協議,如 Spring Cloud 基於 HTTP、gRPC 提供 gRPC over HTTP/2、Thrift Hessian 等都是自定義私有協議。

Dubbo 自身同樣提供了私有的 Dubbo 協議,這樣你也能基於 Dubbo 協議構建微服務。但除了單一協議之外,和以上所有框架不同的,Dubbo 最大的優勢在於它能同時支持多協議的暴露和消費,再配合 Dubbo 多註冊訂閱的模型,可以讓 Dubbo 成爲橋接多種不同協議的微服務體系的開發框架,輕鬆的實現不同微服務體系的互調互通或技術棧遷移。

這篇文章詳細講解了 Dubbo 對 gRPC 協議的支持,再加上 Dubbo 之前已具備的對 REST、Hessian、Thrift 等的支持,使 Dubbo 在協議互調上具備了基礎。我們只需要在服務發現模型上也能實現和這些不同體系的打通,就能解決不同技術棧互調和遷移的問題。關於這部分的具體應用場景以及工作模式,我們將在接下來的文章中來具體分析。

作者介紹

劉軍,Github賬號Chickenlj,Apache Dubbo PMC,項目核心維護者,見證了Dubbo從重啓開源到Apache畢業的整個流程。現任職阿里巴巴中間件團隊,參與服務框架、微服務相關工作,目前主要在推動Dubbo開源的雲原生化。

本文轉載自公衆號阿里巴巴中間件(ID:Aliware_2018)。

原文鏈接

https://mp.weixin.qq.com/s?__biz=MzU4NzU0MDIzOQ==&mid=2247488245&idx=2&sn=b59960420b4e7ae65bb813d4215601a2&chksm=fdeb2095ca9ca983a8da5379b29191c7cba256aaf287966d419d25a9606187805249c8988d63&scene=27#wechat_redirect

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章