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

芊寶寶最可愛發表於2019-12-02

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

本文整理自劉軍在 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 在跨語言和協議穿透性方向的探索:支援 HTTP/2 gRPC


眾所周知,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 協議幾乎被網路上的所有裝置所支援,具有很好的協議穿透性。


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


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

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

HTTP/2

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


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


  • 支援單條鏈路上的 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 設計目的特性來做簡單說明。

  • 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 工具,用來將語言中立的描述轉化為相應語言的具體描述。

它的一些特性包括:

  • 跨語言 跨平臺,語言中立的資料描述格式,預設提供了生成多種語言的 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 協議通訊。

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>

2. 生成 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);
}

3. 業務邏輯開發

繼承 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();
  }
}
  1. 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();
}
  1. 引用 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附:高階用法

一、非同步呼叫

再來看一遍 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());
  // ...
}

二、高階配置

由於當前實現方式是直接整合了 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);
}
  1. 傳遞附加引數
    DemoService 服務呼叫傳遞 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;
  }
}

三、雙向流式通訊
程式碼中還提供了一個支援 雙向流式通訊的示例,同時提供了攔截流式呼叫的 Interceptor 擴充套件示例實現。

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

四、TLS 配置

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

示例 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

  1. 生成 RPC stub
# 執行以下 maven 命令
$mvn clean compile

生成的 Java 類如下:

DemoServiceDubbo 為 Dubbo 定製的 stub

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 在協議互調上具備了基礎。我們只需要在服務發現模型上也能實現和這些不同體系的打通,就能解決不同技術棧互調和遷移的問題。關於這部分的具體應用場景以及工作模式,我們將在接下來的文章中來具體分析。

原文連結

本文為雲棲社群原創內容,未經允許不得轉載。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69949601/viewspace-2666493/,如需轉載,請註明出處,否則將追究法律責任。

相關文章