简介: Pixiu 是基于 Dubbogo 的云原生、高性能、可扩展的微服务 API 网关。作为一款网关产品,Pixiu 帮助用户轻松创建、发布、维护、监控和保护任意规模的 API ,接受和处理成千上万个并发 API 调用,包括流量管理、 CORS 支持、授权和访问控制、限制、监控,以及 API 版本管理。

Pixiu 是什么

在回答 Pixiu 是什么之前,我们简单解释一下 Dubbo 是什么。Dubbo 是一个开源的高性能 RPC 框架,有着丰富的服务治理能力以及优秀的扩展能力。Dubbo 更扩展出 Dubbo-go​,为用户提供了 Golang 的 Dubbo 解决方案,打通了两种语言之间的隔阂,使 Dubbo 更加贴近云原生。

Dubbo-go 作为 Golang 服务,实现与 Dubbo 服务之间的相互调用。然而,在日常使用场景中,用户往往有把 Dubbo 服务以 RESTful 风格向外暴露的需求同时也要兼顾内部 Dubbo 调用。为了解决这种场景,作为 Dubbo API 网关的 Pixiu​ (中文: 貔貅, 曾用名 dubbo-go-proxy) 便应运而生。之所以采用 Pixiu 这个名称,是因为 Java 同类产品 Zuul 的意象是一个西方怪兽,Pixiu 作为一个国产产品,就用了我们中国的一个类似的神兽貔貅作为项目名称。也同时表达了 Dubbo 社区希望扩展出一整套云原生生态链的决心。

目前 Dubbo 多语言生态,发展最好的自然是 Java,其次是 Golang,其他语言都差强人意。dubbo-go-pixiu 项目是一个基于 dubbo-go 发展起来的项目,目前接口协议层支持的是七层的 HTTP 请求调用,计划在未来的 0.5 版本中支持 gRPC 请求调用,其另外一个使命是作为一种新的 dubbo 多语言解决方案。

为什么使用 Pixiu

Pixiu 是基于 Dubbogo 的云原生、高性能、可扩展的微服务 API 网关。作为一款网关产品,Pixiu 帮助用户轻松创建、发布、维护、监控和保护任意规模的 API ,接受和处理成千上万个并发 API 调用,包括流量管理、 CORS 支持、授权和访问控制、限制、监控,以及 API 版本管理。除此以外,作为 Dubbo 的衍生产品,Pixiu可以帮助 Dubbo 用户进行协议转换,实现跨系统、跨协议的服务能力互通。

Pixiu 的整体设计遵守以下原则:

  1. High performance: 高吞吐量以及毫秒级的延时。
  2. 可扩展: 通过 go-plugin,用户可以根据自己的需求延展 Pixiu 的功能。
  3. 简单可用: 用户通过少量配置,即可上线。

Pixiu 的特性及核心功能

  • 为 RESTful API 和 Dubbo API 提供支持

非 RESTful 风格的 API 和 Dubbo 协议的服务往往需要修改才可以以 RESTful API 风格对外开放。Pixiu 提供协议转换功能,通过 Pixiu,开发者可以将自己的 HTTP API 或 Dubbo API 通过配置,以 RESTful API 风格对外开放。v0.2.1 版本已支持基于泛化调用的 HTTP 至 Dubbo 的协议转换以及 HTTP 协议的转发。在后续的版本,社区将会增加对 gRPC 和 http2 协议的支持。

  • 面向用户的配置方式

一般的网关的配置往往繁琐且复杂。Pixiu,目标作为一款易用的网关产品,在设计上拥有三层配置层级,Gateway 层全局配置, API resource 层配置以及 HTTP verbs 方法层配置。通过三个不同层级的配置,既可以实现深度的定制,亦支持统一的默认配置;同时,支持本地的配置文件,亦可使用统一配置服务器。另外,还提供控制台模块,通过控制台模块,支持配置的热更新。Pixiu 配套配套的控制台界面也在同步开发中。

  • 通用功能的集成

重试、熔断、流量控制、访问控制等通用功能不再需要在每个后端服务上重复实现。使用 Pixiu,通过配置 filter ,开发者可以进行全局的控制,亦可以根据 API 配置各自的规则。因此开发者可以专注于业务逻辑和服务,而不是将时间用在维护基础设施上。

  • 可扩展

不同的使用场景有着各自独特的需求。为满足不同用户的定制化需求,Pixiu 使用了插件模式。开发者可以通过编写 go plugin,将自身特有的业务逻辑以 filter 形式内嵌至 Pixiu 网关中,实现诸如企业登录鉴权等功能。

图 1: Pixiu 核心功能列表

Pixiu 的架构设计

图 2: Pixiu 架构

貔貅: 即 dubbo-go-pixiu,由四个主要模块:Listener、Router、Filters 和 Clients 组成;
Dubbo Cluster: Dubbo 服务所在集群,包含一个或多个 Dubbo Services;
Other Cluster: Dubbo 以外的服务所在集群,现支持 HTTP 服务,未来将拓展支持 gRPC 等其他服务;
Registry Center: 注册中心,维护每个业务服务的调用地址信息;
Metadata Center: 元数据中心,维护每个业务服务的配置信息以及存储 Pixiu 本身的配置信息。

作为 Dubbo 所衍生的 API 网关,Pixiu 使用 Golang 搭建,主要因为: 1. Golang 的 G-M-P,net poller 等特性使 Golang 非常适合构建IO密集型应用;2. 使用 Golang 可以直接引入 Dubbo-go 中的一些组建,简化开发。

整个Pixiu大致可以拆分为四个主要模块:Listener、Router、Filters 和 Client

1、Listener

在 Pixiu 中,Listener 代表外部可以访问Pixiu的方式。通过配置指定协议类型,地址,端口等属性,暴露 Gateway。现阶段暂支持 HTTP 协议,未来将会加入 gRPC。

listeners:
- name: "net/http"
address:
socket_address:
protocol_type: "HTTP"
address: "0.0.0.0"
port: 8888
config:
idle_timeout: 5s
read_timeout: 5s
write_timeout: 5s

2、Router

Router 是 Pixiu 的路由组件。根据配置文件,Pixiu 将对外暴露的 URLs 以树的形势存储于内存中,当请求到了 router 组件时,即会根据 URL 及 HTTP 方法查找到对应的后端服务及其 API 配置,并将信息封装于请求中,为后续 filter,及 client 的调用提供足够的内容。

现阶段,Router 提供以下功能:

  • 支持请求一对一转发路由配置或 wildcard 路由配置。
  • 支持 HTTP 请求的转发到后端 HTTP 服务。
  • 支持 HTTP 请求转化为 dubbo 泛化调用请求。

3、Filters

Filter 是 Pixiu 实现额外功能及其扩展性的主要组件。其实现类似于 Dubbo-go 中的 filter,根据配置中 filter 的指定,生成调用链,从而在调用后端服务前,将各 filter 中的逻辑运行一遍,实现节流,日志等功能。

用户如果需要客制化的 filter,可通过编写 go-plugin 实现。在配置中,可通过类似如下配置,加载 .so文件,并在 API config 中指定使用的 plugin group,plugin name 实现。


pluginFilePath: ""
pluginsGroup:
- groupName: "group1"
plugins:
- name: "rate limit"
version: "0.0.1"
priority: 1000
externalLookupName: "ExternalPluginRateLimit"
- name: "access"
version: "0.0.1"
priority: 1000
externalLookupName: "ExternalPluginAccess"
- groupName: "group2"
plugins:
- name: "blacklist"
version: "0.0.1"
priority: 1000
externalLookupName: "ExternalPluginBlackList"

4、Client

Client 负责调用具体服务。现阶段,Pixiu 支持 HTTP 与 Dubbo 的后端服务。社区将逐渐增加 gRPC 等其他 Client 以满足不同的协议。

HTTP client 的实现相对简单,根据 Router 中获取的后端服务信息,通过 Golang 官方包 net/http 生成请求并调用。

Dubbo client 的实现对比 HTTP client 会稍微复杂,其基础为 Dubbo 服务的泛化调用。泛化调用技术是 Dubbo 提供的一个很基础的功能只需要知道调用的方法名、参数类型和返回值类型,即可发起服务调用。客户端对服务端的泛化调用既可以通过注册中心发现服务,也可以直连服务端,实现对服务的动态调用。

如下面代码所示,Pixiu 通过动态配置 referenceConfig,然后通过 GetRPCService 生成 Dubbo 的 Generic Client(泛化调用客户端)进行下一步的调用。​

 referenceConfig := dg.NewReferenceConfig(irequest.Interface, context.TODO())
referenceConfig.InterfaceName = irequest.Interface
referenceConfig.Cluster = constant.DEFAULT_CLUSTER
var registers []string
for k := range dgCfg.Registries {
registers = append(registers, k)
}
referenceConfig.Registry = strings.Join(registers, ",") if len(irequest.DubboBackendConfig.Protocol) == 0 {
referenceConfig.Protocol = dubbo.DUBBO
} else {
referenceConfig.Protocol = irequest.DubboBackendConfig.Protocol
} referenceConfig.Version = irequest.DubboBackendConfig.Version
referenceConfig.Group = irequest.Group
referenceConfig.Generic = true
if len(irequest.DubboBackendConfig.Retries) == 0 {
referenceConfig.Retries = "3"
} else {
referenceConfig.Retries = irequest.DubboBackendConfig.Retries
}
dc.lock.Lock()
defer dc.lock.Unlock()
referenceConfig.GenericLoad(key)
clientService := referenceConfig.GetRPCService().(*dg.GenericService)

​实际上,在泛化调用的客户端中,实际执行泛化调用的关键步骤是 Dubbo-go 中的 generic_filter (如下代码片段)。在调用 generic_filter 的 Invoke 时,约定 invocation 参数列表第一个为方法名,第二个为参数类型列表,第三个为参数值列表。generic_filter 将用户请求的参数值列表转化为统一格式的 map(代码中的 struct2MapAll ),将类( golang 中为 struct )的正反序列化操作变成 map 的正反序列化操作。这使得无需 POJO 描述通过硬编码注入 hessain 库,从而完成 Dubbo 服务的泛化调用。

func (ef *GenericFilter) Invoke(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
if invocation.MethodName() == constant.GENERIC && len(invocation.Arguments()) == 3 {
oldArguments := invocation.Arguments()
if oldParams, ok := oldArguments[2].([]interface{}); ok {
newParams := make([]hessian.Object, 0, len(oldParams))
for i := range oldParams {
newParams = append(newParams, hessian.Object(struct2MapAll(oldParams[i])))
}
newArguments := []interface{}{
oldArguments[0],
oldArguments[1],
newParams,
}
newInvocation := invocation2.NewRPCInvocation(invocation.MethodName(), newArguments, invocation.Attachments())
newInvocation.SetReply(invocation.Reply())
return invoker.Invoke(ctx, newInvocation)
}
}
return invoker.Invoke(ctx, invocation)
}

总结

通过上面的四个模块以及注册中心的简单介绍不难发现,当请求通过 listener 被 Pixiu 接收后,请求被传入 router 中。router 根据接口的配置,从原请求中找到目标后端服务连同相关API配置下发到 filter 组件。filter 组件根据原请求、 API 配置等信息顺序执行,最终请求到达 client, 通过 client 调用后端服务。

Pixiu的未来

图 3: Pixiu 迭代里程碑

Pixiu 作为网关产品外,其衍生项目也会在我们的未来计划中,主要目的是提供更好的可用性。例如,由于 Golang 语言缺乏原生的注解, 因此 Dubbo-go 需要通过配置文件方式生成服务的元数据写入注册中心。开课啦教育公司相关同学写了一个扫描代码的工具 _https://github.com/jack15083/dubbo-go-proxy-tool_,在每个 RPC 服务方法前加上对应的注释,从而在服务启动前通过扫描注释生成元数据。Pixiu 也计划在未来的版本上通过提供 package,允许服务通过注释借助 _https://github.com/MarcGrol/golangAnnotations _生成 API 配置并注册到 Pixiu 上。

Pixiu 目前的定位是一个七层协议网关,其最初版本是被定义成一个 Dubbo 的服务网关。作为云时代的产品,Pixiu 的发展方向必然是面向云原生的。现在的版本为0.2.1, 已经实现基本的 Dubbo/Http 服务代理和部分的网关通用功能。目前正在开发中的 0.4 及其后续版本支持 gRPC 和 Spring Cloud 服务调用, 后续还将提供 MQ 服务支持。另外,社区将继续优化配置方式,降低用户的使用难度,继续优化官方的 filter,使 Pixiu 可以在官方层面实现更多的网关通用功能。

在未来的一年内,社区计划支持 xDS API,将 Pixiu 演化为 Dubbo mesh 的 sidecar。其最终目的就是:在现有的 dubbo mesh 形态中演化出 Proxy Service Mesh 形态。基于这个形态,Js、Python、PHP、Ruby 和 Perl 等脚本语言程序除了收获 dubbo mesh 原有的技术红利之外,大概率还能收获性能上的提升。

Pixiu 在 Dubbo Mesh 中的终极目的是:把东西向和南北向数据面流量逐步统一 Pixiu 中的同时,让它逐步具备 Application Runtime 的能力,作为 Dubbo 多语言生态的关键解决方案。

冯振宇,Apache Dubbo Committer,目前负责管理香港一家消费品公司的IT部门整个团队。2020 年夏天 偶然看到了介绍 dubbogo 的文章后加入了 dubbogo 社区,目前在主导 Pixiu 0.4.0 版本的开发。

原文链接

本文为阿里云原创内容,未经允许不得转载。

Dubbo 跨语言调用神兽:dubbo-go-pixiu的更多相关文章

  1. Atitit java c# php c++ js跨语言调用matlab实现边缘检测等功能attilax总结

    Atitit java c# php c++ js跨语言调用matlab实现边缘检测等功能attilax总结 1.1. 边缘检测的基本方法Canny最常用了1 1.2. 编写matlab边缘检测代码, ...

  2. Golang通过Thrift框架完美实现跨语言调用

    每种语言都有自己最擅长的领域,Golang 最适合的领域就是服务器端程序. 做为服务器端程序,需要考虑性能同时也要考虑与各种语言之间方便的通讯.采用http协议简单,但性能不高.采用TCP通讯,则需要 ...

  3. Golang、Php、Python、Java基于Thrift0.9.1实现跨语言调用

    目录: 一.什么是Thrift? 1) Thrift内部框架一瞥 2) 支持的数据传输格式.数据传输方式和服务模型 3) Thrift IDL 二.Thrift的官方网站在哪里? 三.在哪里下载?需要 ...

  4. 跨语言调用Hangfire定时作业服务

    跨语言调用Hangfire定时作业服务 背景 Hangfire允许您以非常简单但可靠的方式执行后台定时任务的工作.内置对任务的可视化操作.非常方便. 但令人遗憾的是普遍都是业务代码和hagnfire服 ...

  5. C++ 跨语言调用 Java

    C++ 跨语言调用 Java Java JDK 提供了 JNI 接口供 C/C++ 程序调用 Java 编译后的类与方法,主要依赖于头文件(jni.h) 和 动态库(jvm.so/jvm.dll),由 ...

  6. 使用thrift进行跨语言调用(php c# java)

    使用thrift进行跨语言调用(php c# java)   1:前言 实际上本文说的是跨进程的异构语言调用,举个简单的例子就是利用PHP写的代码去调C#或是java写的服务端.其实除了本文提供的办法 ...

  7. vs2019 Com组件初探-简单的COM编写以及实现跨语言调用

    前提条件 1.掌握C++基础语法 2.平台安装 vs2019 3.本地平台为 windows 10 1909 X64 4.了解vbs基础语法 本次目标 1.掌握Com组件的概念及原理 2.编写一个简单 ...

  8. Java跨语言调用,使用JNA访问Java外部接口

    1. JNA简单介绍 先说JNI(Java Native Interface)吧,有过不同语言间通信经历的一般都知道,它允许Java代码和其他语言(尤其C/C++)写的代码进行交互,只要遵守调用约定即 ...

  9. CLS(公共语言规范)的CLSCompliant(跨语言调用)

    .net的一个很重要的特性就是跨语言的编程,用C#写的dll可以在VB.net里调用,例如:用C#写的一个类,编译到dll中,然后在VB.net中调用: using System;namespace  ...

  10. 跨语言调用C#代码的新方式-DllExport

    简介 上一篇文章使用C#编写一个.NET分析器文章发布以后,很多小伙伴都对最新的NativeAOT函数导出比较感兴趣,今天故写一篇短文来介绍一下如何使用它. 在以前,如果有其他语言需要调用C#编写的库 ...

随机推荐

  1. verilog勘误系列之-->算术运算符运算失败

    描述 在verilog代码设计时使用算术运算符与乘法搭配使用出现计算错误 原因 由于数据位宽设置不当导致 错误案例 wire signed [13:0] w01; wire signed [23:0] ...

  2. [剑指 Offer II 114. 外星文字典] 拓扑排序

    import java.util.*; class Solution { public static void main(String[] args) { Solution solution = ne ...

  3. 【leetcode 1799 N次操作后的最大分数和】状态压缩

    回溯会超时,状态压缩 class Solution { public int maxScore(int[] nums) { int len = nums.length; int size = 1 &l ...

  4. JS(数组)

    一 数组的概念 问:之前学习的数据类型,只能存储一个值.如果我们想存储班级中所有学生的姓名,那么该如何存储呢?答:可以使用数组(Array).数组可以把一组相关的数据一起存放,并提供方便的访问(获取) ...

  5. JavaScript知识总结 终结篇--面向对象,垃圾回收与内存泄漏

    这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 一.面向对象 一般使用字面量的形式直接创建对象,但是这种创建方式对于创建大量相似对象的时候,会产生大量的重复代码.但 js和一般的面向对象 ...

  6. QT 智能指针 QPointer QScopedPointer QSharedPointer QWeakPointer QSharedDataPointer 隐式共享 显示共享

    QPointer QPointer 使一种受保护的指针,当其引用的对象被销毁时,它会被自动清除(但是,销毁引用对象还是必须手动delete).QPointer所指向的对象必须是QObject或其派生类 ...

  7. 大模型落地实战指南:从选择到训练,深度解析显卡选型、模型训练技、模型选择巧及AI未来展望—打造AI应用新篇章

    大模型落地实战指南:从选择到训练,深度解析显卡选型.模型训练技.模型选择巧及AI未来展望---打造AI应用新篇章 0.前言大模型发展史 早期阶段(1950s~1980s) 在1950年代初期,人们开始 ...

  8. KingbaseES V8R6集群运维案例之---同一主机节点部署多个集群

    案例说明: 在同一主机环境,由于生产需要,需要部署两个集群:本案例详细描述了两个集群的部署过程. 注意:同一主机部署多个集群需要先部署securecmdd服务,节点之间通过securecmdd服务通讯 ...

  9. KingbaseES数据库批量加载数据的最佳方法

    前言 本文讨论在KingbaseES数据库服务器中批量加载数据的方法,以及空数据库中的初始数据加载和增量数据加载的最佳做法. 批量加载方法 以下数据加载方法按照从最耗时到最不耗时的顺序排列: 1.运行 ...

  10. UE4_C++实现TimeLine

    主要实现蓝图节点中时间轴的功能. 目前UE提供了两种实现方式,一个是使用FTimeLine其是一个时间轴的结构体:另一种方式是使用UTimeLineComponent,其是一个时间轴组件类.两者内部定 ...