dsfdsfdsfdsf1223 2017-02-22 09:51
浏览 59

去1.8插件使用自定义界面

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

Definition of 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) {

}

This pkg is in my go app project.

load plugin file

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()
}

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

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

I found errors:

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

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

  1. filter.Filter is in pkg/filter package.
  2. load plugin file in proxy/factory.go
  3. plugin go file is in another project.
  • 写回答

1条回答 默认 最新

  • dtzjvj3915 2017-02-22 10:59
    关注

    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.

    1. With a common package 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.

    Define it in package filter:

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

    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
    ", f, f)
        return
    }
    

    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
    ", filter, filter, err)
        fmt.Println("\tName:", filter.Name())
        fmt.Println("\tAge:", filter.Age())
    }
    

    Output:

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

    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.

    No filter package this time.

    The plugin is in package 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
    ", f, f)
        return
    }
    

    And the main app:

    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
    ", filterIface, filterIface, err)
        myfilter := filterIface.(MyFilter)
        fmt.Println("\tName:", myfilter.Name())
        fmt.Println("\tAge:", myfilter.Age())
    }
    
    type MyFilter interface {
        Name() string
        Age() int
    }
    

    Output:

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

    Also see related question: How do Go plugin dependencies work?

    评论

报告相同问题?

悬赏问题

  • ¥15 微信小程序协议怎么写
  • ¥15 c语言怎么用printf(“\b \b”)与getch()实现黑框里写入与删除?
  • ¥20 怎么用dlib库的算法识别小麦病虫害
  • ¥15 华为ensp模拟器中S5700交换机在配置过程中老是反复重启
  • ¥15 java写代码遇到问题,求帮助
  • ¥15 uniapp uview http 如何实现统一的请求异常信息提示?
  • ¥15 有了解d3和topogram.js库的吗?有偿请教
  • ¥100 任意维数的K均值聚类
  • ¥15 stamps做sbas-insar,时序沉降图怎么画
  • ¥15 买了个传感器,根据商家发的代码和步骤使用但是代码报错了不会改,有没有人可以看看