core.go
// @Title aop流程core
// @Description 参考gin库实现aop的core
package flow
import ctx "context"
type WrapF struct {
f func(ctx ctx.Context) error
ctx *Context
}
func WrapFunc(ctx ctx.Context, f func(ctx ctx.Context) error) *WrapF {
return &WrapF{
f: f,
ctx: newContext(ctx),
}
}
func (wf *WrapF) UseBefore(f func(c *Context) error) {
wf.ctx.addBeforeHandler(f)
}
func (wf *WrapF) UseBehind(f func(c *Context) error) {
wf.ctx.addBehindHandler(f)
}
func (wf *WrapF) Handle() error {
var err error
wf.ctx.handlers = append(wf.ctx.handlers, wf.ctx.beforeHandlers...)
wf.ctx.handlers = append(wf.ctx.handlers, func(c *Context) error {
err := wf.f(wf.ctx)
return err
})
wf.ctx.handlers = append(wf.ctx.handlers, wf.ctx.behindHandlers...)
if len(wf.ctx.handlers) > 0 {
err = wf.ctx.Next()
}
wf.ctx.Reset()
return err
}
context.go
// @Title aop流程context
// @Description 参考gin库实现aop的context
package flow
import (
ctx "context"
"math"
)
const abort = math.MaxInt32 - 10000
const PAYLOADKEY = "payload"
type Context struct {
ctx.Context
offset int
beforeHandlers []func(ctx *Context) error
behindHandlers []func(ctx *Context) error
handlers []func(ctx *Context) error
}
func newContext(ctx ctx.Context) *Context {
return &Context{
Context: ctx,
offset: -1,
beforeHandlers: make([]func(*Context) error, 0, 10),
behindHandlers: make([]func(*Context) error, 0, 10),
handlers: make([]func(*Context) error, 0, 20),
}
}
func (ctx *Context) Next() error {
ctx.offset++
s := len(ctx.handlers)
for ; ctx.offset < s; ctx.offset++ {
if !ctx.isAbort() {
err := ctx.handlers[ctx.offset](ctx)
if err != nil {
return err
}
} else {
return nil
}
}
return nil
}
func (ctx *Context) Reset() {
//ctx.PerRequestContext = &sync.Map{}
ctx.offset = -1
ctx.behindHandlers = ctx.handlers[:0]
ctx.beforeHandlers = ctx.handlers[:0]
ctx.handlers = ctx.handlers[:0]
}
func (ctx *Context) Abort() {
ctx.offset = math.MaxInt32 - 10000
}
func (ctx *Context) isAbort() bool {
if ctx.offset >= abort {
return true
}
return false
}
func (ctx *Context) addBeforeHandler(f func(ctx *Context) error) {
ctx.beforeHandlers = append(ctx.beforeHandlers, f)
}
func (ctx *Context) addBehindHandler(f func(ctx *Context) error) {
ctx.behindHandlers = append(ctx.behindHandlers, f)
}
测试main.go
package main
import (
"context"
"errors"
"fmt"
"main/flow/flow"
)
type Payload struct {
a int
b int
}
type I interface {
AlertTimeout()
}
func Fl() {
payload := Payload{
a: 1,
b: 2,
}
a := 5
c := context.WithValue(context.Background(), flow.PAYLOADKEY, &payload)
wrapF := flow.WrapFunc(c, func(c context.Context) error {
handle1(c, a)
//pay := c.Value(flow.PAYLOADKEY).(*Payload)
fmt.Println("000000000000000")
return nil
})
wrapF.UseBefore(AlertTimeout)
wrapF.UseBehind(Sentinel)
err := wrapF.Handle()
fmt.Println(err)
}
func main() {
Fl()
//wrapF2 := flow.WrapFunc(a, func() {
// handle2(a)
//})
//wrapF2.Use(AlertTimeout)
//wrapF2.Use(Sentinel)
//wrapF2.Handle()
}
func AlertTimeout(c *flow.Context) error {
pay := c.Value(flow.PAYLOADKEY).(*Payload)
pay.b = 1000
fmt.Println("before")
return nil
}
func Sentinel(c *flow.Context) error {
fmt.Println("behind")
return errors.New("ppppppppp")
}
func handle1(c context.Context, a int) int {
fmt.Println("main1")
//fmt.Println(c.Value(flow.PAYLOADKEY).(Payload).b)
return 67
}
func handle2(a interface{}) {
fmt.Println("main2")
}
请仔细阅读并实验该aop方案,代码参照gin、k8s中visitor、,包含对错误处理机制的调研
golang的错误处理机制
- 接口定义,直观返回error
package errs
import "github.com/pkg/errors"
type A interface {
Enter() error
Visit() error
Leave() error
}
func B(a A, i int, j int) error {
if err := a.Enter(); err != nil {
return errors.WithMessage(err, "Enter failed")
}
if err := a.Visit(); err != nil {
return errors.WithMessage(err, "visit failed")
}
if err := a.Leave(); err != nil {
return errors.WithMessage(err, "leave failed")
}
return nil
}
分步骤处理,可以针对具体返回结果进行处理
2.屏蔽过程中的error处理
package errs
import "github.com/pkg/errors"
type A interface {
Enter()
Visit()
Leave()
Err() error
}
func B(a A) error {
a.Enter()
a.Visit()
a.Leave()
if err := a.Err(); err != nil {
return errors.WithMessage(err, "failed")
}
return nil
}
type C struct {
err error
}
func (l *C) Visit() {
if l.err != nil {
return
}
}
将error保存稻草对象内部,处理逻辑交给每个方法,本质上还是顺序执行
aop我第一版实现就是用这种方式处理错误
3.上面aop为第三种err处理,是目前最为优雅的,利用函数式编程的yan'c