去1.8插件使用自定义界面 [英] go 1.8 plugin use custom interface

查看:79
本文介绍了去1.8插件使用自定义界面的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想使用基于go插件的自定义界面,但是我发现它不支持.

I want to use custom interface based on go plugin, but I found it's not support.

package filter

import (
    "net/http"

    "github.com/valyala/fasthttp"
)

// Context filter context
type Context interface {
    SetStartAt(startAt int64)
    SetEndAt(endAt int64)
    GetStartAt() int64
    GetEndAt() int64

    GetProxyServerAddr() string
    GetProxyOuterRequest() *fasthttp.Request
    GetProxyResponse() *fasthttp.Response
    NeedMerge() bool

    GetOriginRequestCtx() *fasthttp.RequestCtx

    GetMaxQPS() int

    ValidateProxyOuterRequest() bool

    InBlacklist(ip string) bool
    InWhitelist(ip string) bool

    IsCircuitOpen() bool
    IsCircuitHalf() bool

    GetOpenToCloseFailureRate() int
    GetHalfTrafficRate() int
    GetHalfToOpenSucceedRate() int
    GetOpenToCloseCollectSeconds() int

    ChangeCircuitStatusToClose()
    ChangeCircuitStatusToOpen()

    RecordMetricsForRequest()
    RecordMetricsForResponse()
    RecordMetricsForFailure()
    RecordMetricsForReject()

    GetRecentlyRequestSuccessedCount(sec int) int
    GetRecentlyRequestCount(sec int) int
    GetRecentlyRequestFailureCount(sec int) int
}

// Filter filter interface
type Filter interface {
    Name() string

    Pre(c Context) (statusCode int, err error)
    Post(c Context) (statusCode int, err error)
    PostErr(c Context)
}

// BaseFilter base filter support default implemention
type BaseFilter struct{}

// Pre execute before proxy
func (f BaseFilter) Pre(c Context) (statusCode int, err error) {
    return http.StatusOK, nil
}

// Post execute after proxy
func (f BaseFilter) Post(c Context) (statusCode int, err error) {
    return http.StatusOK, nil
}

// PostErr execute proxy has errors
func (f BaseFilter) PostErr(c Context) {

}

此pkg在我的go应用程序项目中.

This pkg is in my go app project.

package proxy

import (
    "errors"
    "plugin"
    "strings"

    "github.com/fagongzi/gateway/pkg/conf"
    "github.com/fagongzi/gateway/pkg/filter"
)

var (
    // ErrKnownFilter known filter error
    ErrKnownFilter = errors.New("unknow filter")
)

const (
    // FilterHTTPAccess access log filter
    FilterHTTPAccess = "HTTP-ACCESS"
    // FilterHeader header filter
    FilterHeader = "HEAD" // process header fiter
    // FilterXForward xforward fiter
    FilterXForward = "XFORWARD"
    // FilterBlackList blacklist filter
    FilterBlackList = "BLACKLIST"
    // FilterWhiteList whitelist filter
    FilterWhiteList = "WHITELIST"
    // FilterAnalysis analysis filter
    FilterAnalysis = "ANALYSIS"
    // FilterRateLimiting limit filter
    FilterRateLimiting = "RATE-LIMITING"
    // FilterCircuitBreake circuit breake filter
    FilterCircuitBreake = "CIRCUIT-BREAKE"
    // FilterValidation validation request filter
    FilterValidation = "VALIDATION"
)

func newFilter(filterSpec *conf.FilterSpec) (filter.Filter, error) {
    if filterSpec.External {
        return newExternalFilter(filterSpec)
    }

    input := strings.ToUpper(filterSpec.Name)

    switch input {
    case FilterHTTPAccess:
        return newAccessFilter(), nil
    case FilterHeader:
        return newHeadersFilter(), nil
    case FilterXForward:
        return newXForwardForFilter(), nil
    case FilterAnalysis:
        return newAnalysisFilter(), nil
    case FilterBlackList:
        return newBlackListFilter(), nil
    case FilterWhiteList:
        return newWhiteListFilter(), nil
    case FilterRateLimiting:
        return newRateLimitingFilter(), nil
    case FilterCircuitBreake:
        return newCircuitBreakeFilter(), nil
    case FilterValidation:
        return newValidationFilter(), nil
    default:
        return nil, ErrKnownFilter
    }
}

func newExternalFilter(filterSpec *conf.FilterSpec) (filter.Filter, error) {
    p, err := plugin.Open(filterSpec.ExternalPluginFile)
    if err != nil {
        return nil, err
    }

    s, err := p.Lookup("NewExternalFilter")
    if err != nil {
        return nil, err
    }

    sf := s.(func() (filter.Filter, error))
    return sf()
}

这是我的go应用程序项目中的加载插件的代码

This is the code of load plugin in my go app project

package main

import (
    "C"
    "strings"
    "time"

    "github.com/CodisLabs/codis/pkg/utils/log"
    "github.com/fagongzi/gateway/pkg/filter"
    "github.com/valyala/fasthttp"
)

// AccessFilter record the http access log
// log format: $remoteip "$method $path" $code "$agent" $svr $cost
type AccessFilter struct {
}

// NewExternalFilter create a External filter
func NewExternalFilter() (filter.Filter, error) {
    return &AccessFilter{}, nil
}

// Name return name of this filter
func (f *AccessFilter) Name() string {
    return "HTTP-ACCESS"
}

// Pre pre process
func (f *AccessFilter) Pre(c filter.Context) (statusCode int, err error) {
    return 200, nil
}

// Post execute after proxy
func (f *AccessFilter) Post(c filter.Context) (statusCode int, err error) {
    cost := (c.GetStartAt() - c.GetEndAt())

    log.Infof("%s %s \"%s\" %d \"%s\" %s %s",
        GetRealClientIP(c.GetOriginRequestCtx()),
        c.GetOriginRequestCtx().Method(),
        c.GetProxyOuterRequest().RequestURI(),
        c.GetProxyResponse().StatusCode(),
        c.GetOriginRequestCtx().UserAgent(),
        c.GetProxyServerAddr(),
        time.Duration(cost))

    return 200, nil
}

// PostErr post error process
func (f *AccessFilter) PostErr(c filter.Context) {

}

// GetRealClientIP get read client ip
func GetRealClientIP(ctx *fasthttp.RequestCtx) string {
    xforward := ctx.Request.Header.Peek("X-Forwarded-For")
    if nil == xforward {
        return strings.SplitN(ctx.RemoteAddr().String(), ":", 2)[0]
    }

    return strings.SplitN(string(xforward), ",", 2)[0]
}

这是插件的定义,它在我的插件项目中.插件项目和go app项目是不同的项目.

This is the definition of plugin, it's in my plugin project. The plugin project and go app project are different projects.

panic: interface conversion: plugin.Symbol is func() (filter.Filter, error), not func() (filter.Filter, error)

您可以在此项目中找到代码https://github.com/fagongzi/gateway/tree/go18-plugin-support.

You can find code in this project https://github.com/fagongzi/gateway/tree/go18-plugin-support.

  1. filter.Filter在pkg/filter程序包中.
  2. proxy/factory.go中的
  3. load plugin file
  4. plugin go file在另一个项目中.
  1. filter.Filter is in pkg/filter package.
  2. load plugin file in proxy/factory.go
  3. plugin go file is in another project.

推荐答案

自定义界面可以正常工作.

Custom interfaces work just fine.

但是重要的一件事:您只能根据从插件中查找的值中的类型断言类型是在插件的外部中定义的(您不能引用插件中定义的类型).这也适用于复合类型"的每个组件,例如,您只能键入断言一个函数类型,该函数类型的参数和结果类型也在插件之外定义.

But one important thing: you can only type assert types from values looked up from plugins that are defined outside of the plugin (you can't refer types defined in plugins). This also applies to each component of "composite types", for example you can only type assert a function type whose parameter and result types are also defined outside of the plugin.

一种解决方案是在插件外部的包中定义接口,并且插件和您的应用程序都可以导入并引用它.

One solution is to define the interface in a package outside of the plugin, and both the plugin and your app can import it and refer to it.

在程序包filter中定义它:

package filter

type Filter interface {
    Name() string
    Age() int
}

该插件位于软件包pq中,并导入软件包filter:

The plugin is in package pq and imports package filter:

package main

import (
    "fmt"
    "filter"
)

type plgFilter struct{}

func (plgFilter) Name() string { return "Bob" }
func (plgFilter) Age() int     { return 23 }

func GetFilter() (f filter.Filter, err error) {
    f = plgFilter{}
    fmt.Printf("[plugin GetFilter] Returning filter: %T %v\n", f, f)
    return
}

并且还导入(相同)包filter的主应用程序,加载插件,查找GetFilter(),调用它,还使用返回的Filter:

And the main app that also imports (the same) package filter, loads the plugin, looks up GetFilter(), calls it and also uses the returned Filter:

package main

import (
    "fmt"
    "filter"
    "plugin"
)

func main() {
    p, err := plugin.Open("pg/pg.so")
    if err != nil {
        panic(err)
    }

    GetFilter, err := p.Lookup("GetFilter")
    if err != nil {
        panic(err)
    }
    filter, err := GetFilter.(func() (filter.Filter, error))()
    fmt.Printf("GetFilter result: %T %v %v\n", filter, filter, err)
    fmt.Println("\tName:", filter.Name())
    fmt.Println("\tAge:", filter.Age())
}

输出:

[plugin GetFilter] Returning filter: main.plgFilter {}
GetFilter result: main.plgFilter {} <nil>
        Name: Bob
        Age: 23

2.插件返回interface{},并且在主应用中定义了界面

另一种解决方案是让插件函数返回类型为interface{}的值.您的主应用程序可以定义所需的接口,并且可以在插件返回的interface{}值上使用类型断言.

2. With plugin returning interface{}, and interface defined in main app

Another solution is to have the plugin function return a value of type interface{}. Your main app can define the interface it expects, and it can use type assertion on the interface{} value returned by the plugin.

这次没有filter软件包.

该插件位于软件包pq中:

package main

import (
    "fmt"
)

type plgFilter struct{}

func (plgFilter) Name() string { return "Bob" }
func (plgFilter) Age() int     { return 23 }

func GetFilterIface() (f interface{}, err error) {
    f = plgFilter{}
    fmt.Printf("[plugin GetFilterIface] Returning filter: %T %v\n", f, f)
    return
}

主应用程序:

package main

import (
    "fmt"
    "plugin"
)

func main() {
    p, err := plugin.Open("pg/pg.so")
    if err != nil {
        panic(err)
    }

    GetFilterIface, err := p.Lookup("GetFilterIface")
    if err != nil {
        panic(err)
    }
    filterIface, err := GetFilterIface.(func() (interface{}, error))()
    fmt.Printf("GetFilterIface result: %T %v %v\n", filterIface, filterIface, err)
    myfilter := filterIface.(MyFilter)
    fmt.Println("\tName:", myfilter.Name())
    fmt.Println("\tAge:", myfilter.Age())
}

type MyFilter interface {
    Name() string
    Age() int
}

输出:

[plugin GetFilterIface] Returning filter: main.plgFilter {}
GetFilterIface result: main.plgFilter {} <nil>
        Name: Bob
        Age: 23

另请参阅相关问题: Go插件依赖项如何工作?

这篇关于去1.8插件使用自定义界面的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆