46

使用gRPC构建实际的微服务

 5 years ago
source link: http://developer.51cto.com/art/201812/588011.htm?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

【51CTO.com快译】早期的微服务实现利用了代表性状态传输(REST)架构作为事实上的通信技术。然而,充分利用REST的服务常常适用于面向外部的服务,这些服务直接暴露给消费者。由于它们基于传统的基于文本的消息传递(JSON、XML和CVS over HTTP等)――针对人类进行了优化,因此这些不是内部服务间通信的理想选择。

相反,使用一种基于文本的消息传递协议,我们可以利用针对服务间通信进行优化的二进制协议。云原生计算基金会的gRPC(一种高性能的开源通用远程过程调用框架)是服务间通信的理想选择,因为它使用协议缓冲区(protocol buffer)作为服务间通信的二进制数据交换格式。

我们使用不同的技术和编程语言构建多个微服务时,有一种标准的方法来定义服务接口和底层的消息交换格式很重要。gRPC提供了一种简洁而强大的方法,可以使用协议缓冲区指定服务合约。因此,gRPC可能是最适合构建内部微服务间通信的解决方案。

我们在本文中将更深入地介绍为什么gRPC是构建微服务间通信的一种出色选择。

gRPC的基础知识

有了gRPC,客户可以对不同机器上的服务器应用程序直接调用方法,好像该机器就是本地对象。gRPC立足于传统的远程过程调用(RPC)技术的基础,但是实施在现代技术堆栈(比如HTTP2和协议缓冲区等)上,确保最大的互操作性。

gRPC本身支持这种功能:使用gRPC接口定义语言(IDL)来定义服务合约。因此,作为服务定义的一部分,你可以指定可以远程调用的方法以及参数和返回类型的数据结构。

图1表明了gRPC的使用,在线零售应用程序作为库存和产品搜索服务的一部分。 Inventory服务的合约使用gRPC IDL来定义,该IDL在inventory.proto文件中已有指定。因此,Inventory服务的开发人员应先使用该服务来定义所有业务功能,然后利用proto文件生成服务端框架代码。与之相仿,可以使用同样的proto文件生成客户端代码(存根,stub)。

7zEzimN.png!web

图1

由于gRPC与编程语言无关,你可以使用异构语言来构建服务和客户端。在这个例子中,我们使用Ballerina(ballerina.io)生成了Inventory服务代码,使用Java生成了客户端代码。你可以使用GitHub上的这个源代码(https://github.com/kasun04/grpc-microservices)来试试该示例。

库存(inventory.proto)的服务合约如下所示:

syntax = "proto3"; 
package grpc_service; 
import "google/protobuf/wrappers.proto"; 
service InventoryService { 
   rpc getItemByName(google.protobuf.StringValue) returns (Items); 
   rpc getItemByID(google.protobuf.StringValue) returns (Item); 
   rpc addItem(Item) returns (google.protobuf.BoolValue); 
} 
message Items { 
   string itemDesc = 1; 
   repeated Item items = 2; 
} 
message Item { 
    string id = 1; 
    string name = 2; 
    string description = 3; 
}  

服务合约易于理解,可以在客户端和服务之间共享。如果服务合约有任何变化,服务代码和客户端代码都要重新生成。

比如说,以下代码片段显示了为Ballerina生成的gRPC服务的代码。 对于我们在gRPC服务定义中的每个操作,都会生成相应的Ballerina代码。(Ballerina提供了开箱即用的功能,使用“ballerina grpc –input inventory.proto –output service-skeleton –mode service”或“ballerina grpc –input inventory.proto –output bal-client –mode client”,生成服务代码或客户端代码)。

import ballerina/grpc; 
import ballerina/io; 
endpoint grpc:Listener listener { 
   host:"localhost", 
   port:9000 
}; 
@grpc:ServiceConfig 
service InventoryService bind listener { 
   getItemByName(endpoint caller, string value) { 
       // Implementation goes here. 
       // You should return a Items 
   } 
   getItemByID(endpoint caller, string value) { 
       // Creating a dummy inventory item 
       Item requested_item; 
       requested_item.id = value; 
       requested_item.name = "Sample Item " + value ; 
       requested_item.description = "Sample Item Desc for " + value; 
       _ = caller->send(requested_item); 
       _ = caller->complete(); 
   } 
   addItem(endpoint caller, Item value) { 
       // Implementation goes here. 
       // You should return a boolean 
   } 
}  

至于客户端,再次用Inventory服务的gRPC服务定义来生成产品搜索服务,这是一个Java(Spring Boot)服务。你可以使用maven插件为Spring Boot/Java服务生成客户端存根(客户端代码嵌入在Spring Boot服务中)。调用生成的客户端存根的客户端代码如下所示:

package mfe.ch03.grpc; 
import com.google.protobuf.StringValue; 
import io.grpc.ManagedChannel; 
import io.grpc.ManagedChannelBuilder; 
public class InventoryClient { 
   public static void main(String[] args) { 
       ManagedChannel channel = ManagedChannelBuilder.forAddress("127.0.0.1", 9000) 
               .usePlaintext() 
               .build(); 
       InventoryServiceGrpc.InventoryServiceBlockingStub stub 
               = InventoryServiceGrpc.newBlockingStub(channel); 
       Inventory.Item item = stub.getItemByID( 
StringValue.newBuilder().setValue("123").build()); 
       System.out.println("Response : " + item.getDescription()); 
   } 
}  

底层的通信

客户端调用服务时,客户端gRPC库使用协议缓冲区,并编组(marshal)远程过程调用,该调用随后通过HTTP2来发送。在服务器端,请求解组(un-marshalled),使用协议缓冲区执行相应的过程调用。响应遵循从服务器到客户端的类似的执行流程。

使用gRPC开发服务和客户端的主要优点是,你的服务代码或客户端代码不需要为解析JSON或类似的基于文本的消息格式(在代码内或隐含在Jackson等底层库中,对服务代码而言隐藏起来)操心。二进制格式解组、转换成对象。此外,我们要处理多个微服务并确保和维护互操作性时,对通过IDL定义服务接口给予一流支持是强大的功能。

用gRPC构建微服务的实例

基于微服务的应用程序由多个服务组成,并使用众多编程语言构建。基于业务使用场景,你可以选择最合适的技术来构建服务。gRPC在这种多语言架构中起到非常重要的作用。如图2所示,产品搜索服务与另外多个服务进行通信,这些服务是使用gRPC作为通信协议构建的。因此,我们可以为每个服务定义服务合约:库存、电子品类和服装品类等。现在,如果你想要打造一种多语言架构,可以使用不同的实现技术来生成服务框架。

图2显示了用Ballerina lang编写的库存服务、用Golang编写的电子服务和用Vert.x(Java)编写的服装服务。客户端还可以为这每个服务合约生成存根。

6R7F7r2.png!web

图2

仔细研究图2中的微服务通信风格,可以看出gRPC用于所有内部通信,而面向外部的通信可以基于REST或GraphQL。我们将REST用于面向外部的通信时,大多数外部客户端可以将服务用作API(利用Open API等API定义技术),因为大多数外部客户端知道如何与充分利用REST的HTTP服务进行通信。此外,我们可以使用GraphQL之类的技术,让消费者可以根据特定的客户需求来查询服务,这是无法用gRPC提供便利的。

因此作为一般实践,我们可以将gRPC用于内部微服务之间的所有同步通信。其他同步消息传递技术(比如充分利用REST的服务和GraphQL)更适合面向外部的服务。

作者简介:WSO2架构团队负责该公司集成平台的开发工作,Kasun Indrasiri是该团队的重要成员。之前,他作为产品主管参与开发了WSO2企业服务总线(ESB)。他撰有《WSO2 ESB入门》一书,并与人合著了《企业级微服务》。他是Apache软件基金会的当选成员,还是Apache Synapse开源ESB项目的项目管理委员会成员和提交者。

原文标题:Build Real-World Microservices with gRPC,作者:Kasun Indrasiri

【51CTO译稿,合作站点转载请注明原文译者和出处为51CTO.com】

【责任编辑:庞桂玉 TEL:(010)68476606】


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK