Skip to content

Commit 3926bca

Browse files
authored
Add adapter for go-zero (#458)
1 parent c5f1f1d commit 3926bca

File tree

9 files changed

+1412
-0
lines changed

9 files changed

+1412
-0
lines changed

pkg/adapters/go-zero/doc.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
This package provides Sentinel integration for go-zero.
3+
4+
go-zero provides both zrpc and rest modules.
5+
6+
For go-zero/zrpc, user can call `AddUnaryInterceptors` and `AddStreamInterceptors`
7+
on zrpc.RpcServer or `WithUnaryClientInterceptor` on zrpc.ClientOption to add grpc interceptors.
8+
9+
For go-zero/rest, there are two kinds of middlewares.
10+
The first one is the global middleware, which can be applied to rest.Server via:
11+
12+
import (
13+
sgz "github.com/sentinel-go/pkg/adapters/go-zero"
14+
)
15+
server := rest.MustNewServer(c.RestConf)
16+
server.Use(sgz.SentinelMiddleware())
17+
18+
The plugin extracts service FullMethod as the resource name by default.
19+
Users may provide customized resource name extractor when creating new
20+
Sentinel interceptors (via options).
21+
22+
Fallback logic: the plugin will return the BlockError by default
23+
if current request is blocked by Sentinel rules. Users may also
24+
provide customized fallback logic via WithXxxBlockFallback(handler) options.
25+
26+
The second one is the routing middleware,
27+
which is registered to specific routes via by go-zero automatically.
28+
Therefore, it is recomended to create routing middleware based on the generated templates.
29+
An example with Sentinel based on go-zero template is provided in `routing_middleware`.
30+
31+
The calling order is first global ones, then routing ones.
32+
33+
*/
34+
package go_zero

pkg/adapters/go-zero/example

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
package go_zero
2+
3+
import (
4+
"fmt"
5+
"io"
6+
"net/http"
7+
"time"
8+
9+
sentinel "github.com/alibaba/sentinel-golang/api"
10+
"github.com/alibaba/sentinel-golang/core/base"
11+
"github.com/alibaba/sentinel-golang/core/flow"
12+
13+
"github.com/zeromicro/go-zero/core/conf"
14+
"github.com/zeromicro/go-zero/rest"
15+
"github.com/zeromicro/go-zero/rest/httpx"
16+
)
17+
18+
func createSentinel() {
19+
err := sentinel.InitDefault()
20+
if err != nil {
21+
fmt.Printf("Unexpected error: %+v", err)
22+
}
23+
24+
_, err = flow.LoadRules([]*flow.Rule{
25+
{
26+
Resource: "GET:/ping",
27+
Threshold: 0.0,
28+
TokenCalculateStrategy: flow.Direct,
29+
ControlBehavior: flow.Reject,
30+
StatIntervalInMs: 1000,
31+
},
32+
{
33+
Resource: "GET:/",
34+
Threshold: 1.0,
35+
TokenCalculateStrategy: flow.Direct,
36+
ControlBehavior: flow.Reject,
37+
StatIntervalInMs: 1000,
38+
},
39+
{
40+
Resource: "/from/me",
41+
Threshold: 0.0,
42+
TokenCalculateStrategy: flow.Direct,
43+
ControlBehavior: flow.Reject,
44+
StatIntervalInMs: 1000,
45+
},
46+
{
47+
Resource: "GET:/from/you",
48+
Threshold: 0.0,
49+
TokenCalculateStrategy: flow.Direct,
50+
ControlBehavior: flow.Reject,
51+
StatIntervalInMs: 1000,
52+
},
53+
})
54+
if err != nil {
55+
fmt.Printf("Unexpected error: %+v", err)
56+
return
57+
}
58+
}
59+
60+
func main() {
61+
type args struct {
62+
method string
63+
path string
64+
reqPath string
65+
handler http.HandlerFunc
66+
body io.Reader
67+
}
68+
type want struct {
69+
code int
70+
}
71+
var (
72+
tests = []struct {
73+
name string
74+
args args
75+
want want
76+
}{
77+
{
78+
name: "default get",
79+
args: args{
80+
method: http.MethodGet,
81+
path: "/",
82+
reqPath: "http://localhost:8888/",
83+
handler: func(w http.ResponseWriter, r *http.Request) {
84+
resp := "index page"
85+
httpx.OkJson(w, &resp)
86+
},
87+
body: nil,
88+
},
89+
want: want{
90+
code: http.StatusOK,
91+
},
92+
},
93+
}
94+
)
95+
createSentinel()
96+
for _, tt := range tests {
97+
var c rest.RestConf
98+
conf.MustLoad("./test.yml", &c)
99+
server := rest.MustNewServer(c)
100+
// global middleware
101+
server.Use(SentinelMiddleware())
102+
server.AddRoutes(
103+
[]rest.Route{
104+
{
105+
Method: tt.args.method,
106+
Path: tt.args.path,
107+
Handler: tt.args.handler,
108+
},
109+
},
110+
)
111+
defer server.Stop()
112+
go server.Start()
113+
time.Sleep(time.Duration(2) * time.Second)
114+
r, err := http.Get(tt.args.reqPath)
115+
116+
fmt.Printf("%+v\n", r)
117+
fmt.Printf("%d\n", r.StatusCode)
118+
fmt.Printf("%+v\n", err)
119+
for {
120+
121+
}
122+
}
123+
}
124+
125+
func SentinelMiddleware() rest.Middleware {
126+
fmt.Printf("created\n")
127+
return func(next http.HandlerFunc) http.HandlerFunc {
128+
return func(w http.ResponseWriter, r *http.Request) {
129+
resourceName := r.Method + ":" + r.URL.Path
130+
fmt.Printf("%s\n", resourceName)
131+
fmt.Printf("%s\n", resourceName)
132+
entry, blockErr := sentinel.Entry(
133+
resourceName,
134+
sentinel.WithResourceType(base.ResTypeWeb),
135+
sentinel.WithTrafficType(base.Inbound),
136+
)
137+
if blockErr != nil {
138+
// default error response
139+
http.Error(w, "Blocked by Sentinel", http.StatusTooManyRequests)
140+
return
141+
}
142+
defer entry.Exit()
143+
144+
next(w, r)
145+
}
146+
}
147+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package go_zero
2+
3+
import (
4+
"net/http"
5+
6+
sentinel "github.com/alibaba/sentinel-golang/api"
7+
"github.com/alibaba/sentinel-golang/core/base"
8+
"github.com/zeromicro/go-zero/rest"
9+
)
10+
11+
// SentinelMiddleware returns new echo.HandlerFunc.
12+
// Default resource name pattern is {httpMethod}:{apiPath}, such as "GET:/api/:id".
13+
// Default block fallback is to return 429 (Too Many Requests) response.
14+
//
15+
// You may customize your own resource extractor and block handler by setting options.
16+
func SentinelMiddleware(opts ...Option) rest.Middleware {
17+
options := evaluateOptions(opts)
18+
return func(next http.HandlerFunc) http.HandlerFunc {
19+
return func(w http.ResponseWriter, r *http.Request) {
20+
resourceName := r.Method + ":" + r.URL.Path
21+
if options.resourceExtract != nil {
22+
resourceName = options.resourceExtract(r)
23+
}
24+
entry, blockErr := sentinel.Entry(
25+
resourceName,
26+
sentinel.WithResourceType(base.ResTypeWeb),
27+
sentinel.WithTrafficType(base.Inbound),
28+
)
29+
if blockErr != nil {
30+
if options.blockFallback != nil {
31+
status, msg := options.blockFallback(r)
32+
http.Error(w, msg, status)
33+
} else {
34+
// default error response
35+
http.Error(w, "Blocked by Sentinel", http.StatusTooManyRequests)
36+
}
37+
return
38+
}
39+
defer entry.Exit()
40+
41+
next(w, r)
42+
}
43+
}
44+
}

pkg/adapters/go-zero/go.mod

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
module github.com/alibaba/sentinel-golang/pkg/adapters/go-zero
2+
3+
go 1.17
4+
5+
require (
6+
github.com/alibaba/sentinel-golang v1.0.2
7+
github.com/stretchr/testify v1.7.0
8+
github.com/zeromicro/go-zero v1.3.0
9+
)
10+
11+
require (
12+
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect
13+
github.com/beorn7/perks v1.0.1 // indirect
14+
github.com/cespare/xxhash/v2 v2.1.1 // indirect
15+
github.com/davecgh/go-spew v1.1.1 // indirect
16+
github.com/go-logr/logr v1.2.2 // indirect
17+
github.com/go-logr/stdr v1.2.2 // indirect
18+
github.com/go-ole/go-ole v1.2.5 // indirect
19+
github.com/golang-jwt/jwt/v4 v4.2.0 // indirect
20+
github.com/golang/protobuf v1.5.2 // indirect
21+
github.com/google/uuid v1.3.0 // indirect
22+
github.com/justinas/alice v1.2.0 // indirect
23+
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
24+
github.com/openzipkin/zipkin-go v0.4.0 // indirect
25+
github.com/pkg/errors v0.9.1 // indirect
26+
github.com/pmezard/go-difflib v1.0.0 // indirect
27+
github.com/prometheus/client_golang v1.11.0 // indirect
28+
github.com/prometheus/client_model v0.2.0 // indirect
29+
github.com/prometheus/common v0.26.0 // indirect
30+
github.com/prometheus/procfs v0.6.0 // indirect
31+
github.com/shirou/gopsutil v3.20.11+incompatible // indirect
32+
github.com/spaolacci/murmur3 v1.1.0 // indirect
33+
go.opentelemetry.io/otel v1.3.0 // indirect
34+
go.opentelemetry.io/otel/exporters/jaeger v1.3.0 // indirect
35+
go.opentelemetry.io/otel/exporters/zipkin v1.3.0 // indirect
36+
go.opentelemetry.io/otel/sdk v1.3.0 // indirect
37+
go.opentelemetry.io/otel/trace v1.3.0 // indirect
38+
go.uber.org/automaxprocs v1.4.0 // indirect
39+
golang.org/x/sys v0.0.0-20220111092808-5a964db01320 // indirect
40+
google.golang.org/grpc v1.43.0 // indirect
41+
google.golang.org/protobuf v1.27.1 // indirect
42+
gopkg.in/yaml.v2 v2.4.0 // indirect
43+
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
44+
)

0 commit comments

Comments
 (0)