Apinto 网关进阶教程,插件开发入门指南

Apinto 是基于Go语言,由 Eolink 自主研发的一款高性能、可扩展、易维护的云原生 API 网关。Apinto 能够帮助用户简单、快速、低成本、低风险地实现:系统微服务化、系统集成、向合作伙伴、开发者开放功能和数据。

通过 Apinto,企业能够专注于自身业务的发展,并且让企业间能互相借力,实现共赢。

借助 Apinto 强大的插件拓展能力,用户可像乐高积木一样根据需要自行拓展Apinto 的插件,丰富 Apinto 的能力。

本篇文章将给大家介绍如何开发 Apinto插件,自行拓展 Apinto 网关能力

前置知识

插件执行流程

插件系统定义了 Access 、Proxy 两个插件执行周期,其执行阶段定义如下:

· Access:客户端请求 Apinto,将请求转发给上游服务前执行;

· Proxy:将响应返回给客户端前执行;

请求从客户端发出,到达路由进行转发前,按顺序从上到下进行每个插件的 Access 执行阶段,转发获取响应后,从下到上执行每个插件的 Proxy 执行阶段。

插件执行流程如下图,请求处理即为转发前执行的操作,响应处理即为转发获得响应后的操作。

插件实现 UML 图

插件实现采用工厂设计模式,系统初始化时,自动将插件工厂注册到插件管理器中,当有新增插件/修改插件需求时,插件工厂实例会创建插件执行器实例。

开发插件

为了更加直观介绍自定义开发插件,本文将以开发 响应重写 插件为例,完整代码可参考 [Apinto 项目响应插件]

定义插件配置

开发插件前,先确定插件的功能和界限,根据需求定义插件配置格式,此处以 响应重写 插件为例。

{
    "status_code":0,
    "body":"",
    "body_base64":true,
    "headers":{

    },
    "match":{
        "code":[

        ]
    }
}

根据上述配置,定义插件配置结构体。

type Config struct {
 StatusCode int               `json:"status_code" label:"响应状态码" minimum:"100" description:"最小值:100"`
 Body       string            `json:"body" label:"响应内容"`
 BodyBase64 bool              `json:"body_base64" label:"是否base64加密"`
 Headers    map[string]string `json:"headers" label:"响应头部"`
 Match      *MatchConf        `json:"match" label:"匹配状态码列表"`
}

type MatchConf struct {
 Code []int `json:"code" label:"状态码" minimum:"100" description:"最小值:100"`
}

实现插件执行器

1)定义执行器结构

type ResponseRewrite struct {
 drivers.WorkerBase
 statusCode int
 body       string
 headers    map[string]string
 match      *MatchConf
}

2)实现 eosc.IWorker 接口

type IWorker interface {
   Id() string
   Start() error
   Reset(conf interface{}, workers map[RequireId]IWorker) error
   Stop() error
   CheckSkill(skill string) bool
}

当插件配置被修改时,将会调用 Reset 方法,重置执行器内置数据,当插件被删除时,将会调用 Stop 方法。

3)实现 eosc.IFilter 接口

type IFilter interface {
   DoFilter(ctx EoContext, next IChain) (err error)
   Destroy()
}

该接口在转发流程中被调用,其中DoFilter方法执行插件主流程,Destory方法则在插件被删除时,销毁实例。

由于插件有顺序,在实际调用时,会将插件编排成调用链,并通过next.DoChain(ctx)操作调用下一个插件,您可以自定义调用插件前的操作,即 Access阶段 ,也可以自定义调用插件后的操作,即 Proxy阶段 。代码示例如下:

func (r *ResponseRewrite) DoFilter(ctx eocontext.EoContext, next eocontext.IChain) (err error) {
 return http_service.DoHttpFilter(r, ctx, next)
}

func (r *ResponseRewrite) DoHttpFilter(ctx http_service.IHttpContext, next eocontext.IChain) error {
 if next != nil {
  err := next.DoChain(ctx)
  if err != nil {
   log.Error(err)
  }
 }

 return r.rewrite(ctx)
}

func (r *ResponseRewrite) Destroy() {
 r.statusCode = 0
 r.body = ""
 r.headers = nil
 r.match = nil
}

定义工厂创建方法

func NewFactory() eosc.IExtenderDriverFactory {
 return drivers.NewFactory[Config](Create, Check)
}

func Create(id, name string, conf *Config, workers map[eosc.RequireId]eosc.IWorker) (eosc.IWorker, error) {
 err := conf.doCheck()
 if err != nil {
  return nil, err
 }

 //若body非空且需要base64转码
 if conf.Body != "" && conf.BodyBase64 {
  conf.Body, err = utils.B64DecodeString(conf.Body)
  if err != nil {
   return nil, err
  }
 }

 r := &ResponseRewrite{
  WorkerBase: drivers.Worker(id, name),
  statusCode: conf.StatusCode,
  body:       conf.Body,
  headers:    conf.Headers,
  match:      conf.Match,
 }

 return r, nil
}

结合上文代码,当插件新建时,插件工厂会调用 Create 方法和 Check 方法,Create 方法负责初始化插件执行器实例,Check 方法一般用来校验插件配置的合法性,Check 方法用户可以按需实现,但 Create 方法必须实现。

定义工厂注册方法

const (
 Name = "response_rewrite"
)

func Register(register eosc.IExtenderDriverRegister) {
 register.RegisterExtenderDriver(Name, NewFactory())
}

将插件注册到拓展驱动管理器中

文件名:app/apinto/worker.go

使用插件

开发完成后,新插件就会被同步到节点插件中。我们可以在控制台界面 基础设施 > 节点插件新建插件。

新建插件时选择eolinker.com:apinto:{插件名},本次演示的插件名为response_rewrite,因此选择插件eolinker.com:apinto:response_rewrite

若想了解插件的详细使用教程,可点击链接

写在最后

本文简单介绍了插件入门相关细节,使用者可以根据业务需要,自行开发、安装/卸载、开启/关闭、编排插件,丰富网关能力,强化网关功能。

未来我们计划支持多语言插件,引入插件市场,个人开发者/企业可以将开发的插件贡献到插件市场中,与 Apinto 社区的使用者共享,合作共赢。