initial import
This commit is contained in:
29
core/rpc/clientinterceptors/breakerinterceptor.go
Normal file
29
core/rpc/clientinterceptors/breakerinterceptor.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package clientinterceptors
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path"
|
||||
|
||||
"zero/core/breaker"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
func acceptable(err error) bool {
|
||||
switch status.Code(err) {
|
||||
case codes.DeadlineExceeded, codes.Internal, codes.Unavailable, codes.DataLoss:
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
func BreakerInterceptor(ctx context.Context, method string, req, reply interface{},
|
||||
cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
|
||||
breakerName := path.Join(cc.Target(), method)
|
||||
return breaker.DoWithAcceptable(breakerName, func() error {
|
||||
return invoker(ctx, method, req, reply, cc, opts...)
|
||||
}, acceptable)
|
||||
}
|
||||
51
core/rpc/clientinterceptors/breakerinterceptor_test.go
Normal file
51
core/rpc/clientinterceptors/breakerinterceptor_test.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package clientinterceptors
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"zero/core/breaker"
|
||||
"zero/core/stat"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
func init() {
|
||||
stat.SetReporter(nil)
|
||||
}
|
||||
|
||||
type mockError struct {
|
||||
st *status.Status
|
||||
}
|
||||
|
||||
func (m mockError) GRPCStatus() *status.Status {
|
||||
return m.st
|
||||
}
|
||||
|
||||
func (m mockError) Error() string {
|
||||
return "mocked error"
|
||||
}
|
||||
|
||||
func TestBreakerInterceptorNotFound(t *testing.T) {
|
||||
err := mockError{st: status.New(codes.NotFound, "any")}
|
||||
for i := 0; i < 1000; i++ {
|
||||
assert.Equal(t, err, breaker.DoWithAcceptable("call", func() error {
|
||||
return err
|
||||
}, acceptable))
|
||||
}
|
||||
}
|
||||
|
||||
func TestBreakerInterceptorDeadlineExceeded(t *testing.T) {
|
||||
err := mockError{st: status.New(codes.DeadlineExceeded, "any")}
|
||||
errs := make(map[error]int)
|
||||
for i := 0; i < 1000; i++ {
|
||||
e := breaker.DoWithAcceptable("call", func() error {
|
||||
return err
|
||||
}, acceptable)
|
||||
errs[e]++
|
||||
}
|
||||
assert.Equal(t, 2, len(errs))
|
||||
assert.True(t, errs[err] > 0)
|
||||
assert.True(t, errs[breaker.ErrServiceUnavailable] > 0)
|
||||
}
|
||||
31
core/rpc/clientinterceptors/durationinterceptor.go
Normal file
31
core/rpc/clientinterceptors/durationinterceptor.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package clientinterceptors
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"zero/core/logx"
|
||||
"zero/core/timex"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
const slowThreshold = time.Millisecond * 500
|
||||
|
||||
func DurationInterceptor(ctx context.Context, method string, req, reply interface{},
|
||||
cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
|
||||
serverName := path.Join(cc.Target(), method)
|
||||
start := timex.Now()
|
||||
err := invoker(ctx, method, req, reply, cc, opts...)
|
||||
if err != nil {
|
||||
logx.WithDuration(timex.Since(start)).Infof("fail - %s - %v - %s", serverName, req, err.Error())
|
||||
} else {
|
||||
elapsed := timex.Since(start)
|
||||
if elapsed > slowThreshold {
|
||||
logx.WithDuration(elapsed).Slowf("[RPC] ok - slowcall - %s - %v - %v", serverName, req, reply)
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
43
core/rpc/clientinterceptors/prommetricinterceptor.go
Normal file
43
core/rpc/clientinterceptors/prommetricinterceptor.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package clientinterceptors
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"zero/core/metric"
|
||||
"zero/core/timex"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
const clientNamespace = "rpc_client"
|
||||
|
||||
var (
|
||||
metricClientReqDur = metric.NewHistogramVec(&metric.HistogramVecOpts{
|
||||
Namespace: clientNamespace,
|
||||
Subsystem: "requests",
|
||||
Name: "duration_ms",
|
||||
Help: "rpc client requests duration(ms).",
|
||||
Labels: []string{"method"},
|
||||
Buckets: []float64{5, 10, 25, 50, 100, 250, 500, 1000},
|
||||
})
|
||||
|
||||
metricClientReqCodeTotal = metric.NewCounterVec(&metric.CounterVecOpts{
|
||||
Namespace: clientNamespace,
|
||||
Subsystem: "requests",
|
||||
Name: "code_total",
|
||||
Help: "rpc client requests code count.",
|
||||
Labels: []string{"method", "code"},
|
||||
})
|
||||
)
|
||||
|
||||
func PromMetricInterceptor(ctx context.Context, method string, req, reply interface{},
|
||||
cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
|
||||
startTime := timex.Now()
|
||||
err := invoker(ctx, method, req, reply, cc, opts...)
|
||||
metricClientReqDur.Observe(int64(timex.Since(startTime)/time.Millisecond), method)
|
||||
metricClientReqCodeTotal.Inc(method, strconv.Itoa(int(status.Code(err))))
|
||||
return err
|
||||
}
|
||||
25
core/rpc/clientinterceptors/timeoutinterceptor.go
Normal file
25
core/rpc/clientinterceptors/timeoutinterceptor.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package clientinterceptors
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"zero/core/contextx"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
const defaultTimeout = time.Second * 2
|
||||
|
||||
func TimeoutInterceptor(timeout time.Duration) grpc.UnaryClientInterceptor {
|
||||
if timeout <= 0 {
|
||||
timeout = defaultTimeout
|
||||
}
|
||||
|
||||
return func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn,
|
||||
invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
|
||||
ctx, cancel := contextx.ShrinkDeadline(ctx, timeout)
|
||||
defer cancel()
|
||||
return invoker(ctx, method, req, reply, cc, opts...)
|
||||
}
|
||||
}
|
||||
25
core/rpc/clientinterceptors/tracinginterceptor.go
Normal file
25
core/rpc/clientinterceptors/tracinginterceptor.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package clientinterceptors
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"zero/core/trace"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/metadata"
|
||||
)
|
||||
|
||||
func TracingInterceptor(ctx context.Context, method string, req, reply interface{},
|
||||
cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
|
||||
ctx, span := trace.StartClientSpan(ctx, cc.Target(), method)
|
||||
defer span.Finish()
|
||||
|
||||
var pairs []string
|
||||
span.Visit(func(key, val string) bool {
|
||||
pairs = append(pairs, key, val)
|
||||
return true
|
||||
})
|
||||
ctx = metadata.AppendToOutgoingContext(ctx, pairs...)
|
||||
|
||||
return invoker(ctx, method, req, reply, cc, opts...)
|
||||
}
|
||||
Reference in New Issue
Block a user