gpt4 book ai didi

go 1.8 插件使用自定义接口(interface)

转载 作者:IT王子 更新时间:2023-10-29 01:23:51 25 4
gpt4 key购买 nike

我想使用基于 go 插件的自定义接口(interface),但我发现它不支持。

filter.Filter的定义

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 app 项目中。

加载插件文件

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 app项目中加载插件的代码

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]
}

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

我发现错误:

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

你可以在这个项目中找到代码https://github.com/fagongzi/gateway/tree/go18-plugin-support。

  1. filter.Filter 在 pkg/filter 包中。
  2. 加载插件文件 in proxy/factory.go
  3. plugin go file 在另一个项目中。

最佳答案

自定义界面工作得很好。

但是一件重要的事情:你只能type assert从在插件的外部 定义的插件查找的值中的类型(您不能引用插件中定义的类型)。这也适用于“复合类型”的每个组件,例如,您只能键入断言其参数和结果类型也在插件外部定义的函数类型。

1。在插件之外使用通用包

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

filter包中定义:

package filter

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

插件在pq包中,导入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(),调用它并使用返回的 过滤器:

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{} 的值。您的主应用可以定义它期望的接口(interface),并且可以对插件返回的 interface{} 值使用类型断言。

这次没有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

另见相关问题:How do Go plugin dependencies work?

关于go 1.8 插件使用自定义接口(interface),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42388090/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com