1
0
mirror of https://github.com/go-micro/go-micro.git synced 2024-11-24 08:02:32 +02:00

Merge pull request #1656 from micro/client-cache

Client Cache
This commit is contained in:
ben-toogood 2020-05-26 15:38:30 +01:00 committed by GitHub
commit be5a10a4d4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 1454 additions and 1179 deletions

View File

@ -5,7 +5,6 @@ import (
"fmt"
"sort"
"strings"
"sync"
"time"
"github.com/micro/go-micro/v2/auth"
@ -23,9 +22,6 @@ type svc struct {
auth pb.AuthService
rule pb.RulesService
jwt token.Provider
rules []*pb.Rule
sync.Mutex
}
func (s *svc) String() string {
@ -53,8 +49,6 @@ func (s *svc) Init(opts ...auth.Option) {
}
func (s *svc) Options() auth.Options {
s.Lock()
defer s.Unlock()
return s.options
}
@ -110,9 +104,6 @@ func (s *svc) Revoke(role string, res *auth.Resource) error {
// Verify an account has access to a resource
func (s *svc) Verify(acc *auth.Account, res *auth.Resource) error {
// load the rules if none are loaded
s.loadRulesIfEmpty()
// set the namespace on the resource
if len(res.Namespace) == 0 {
res.Namespace = s.Options().Namespace
@ -230,11 +221,14 @@ func accessForRule(rule *pb.Rule, acc *auth.Account, res *auth.Resource) pb.Acce
// listRules gets all the rules from the store which match the filters.
// filters are namespace, type, name and then endpoint.
func (s *svc) listRules(filters ...string) []*pb.Rule {
s.Lock()
defer s.Unlock()
// load rules using the client cache
allRules, err := s.loadRules()
if err != nil {
return []*pb.Rule{}
}
var rules []*pb.Rule
for _, r := range s.rules {
for _, r := range allRules {
if len(filters) > 0 && r.Resource.Namespace != filters[0] {
continue
}
@ -260,27 +254,15 @@ func (s *svc) listRules(filters ...string) []*pb.Rule {
}
// loadRules retrieves the rules from the auth service
func (s *svc) loadRules() {
rsp, err := s.rule.List(context.TODO(), &pb.ListRequest{})
s.Lock()
defer s.Unlock()
func (s *svc) loadRules() ([]*pb.Rule, error) {
rsp, err := s.rule.List(context.TODO(), &pb.ListRequest{}, client.WithCache(time.Minute))
if err != nil {
log.Errorf("Error listing rules: %v", err)
return
log.Debugf("Error listing rules: %v", err)
return nil, err
}
s.rules = rsp.Rules
}
func (s *svc) loadRulesIfEmpty() {
s.Lock()
rules := s.rules
s.Unlock()
if len(rules) == 0 {
s.loadRules()
}
return rsp.Rules, nil
}
func serializeToken(t *pb.Token) *auth.Token {

View File

@ -1,26 +0,0 @@
package service
import (
"testing"
pb "github.com/micro/go-micro/v2/auth/service/proto"
)
func TestListRulesSorting(t *testing.T) {
s := &svc{
rules: []*pb.Rule{
&pb.Rule{Priority: 1},
&pb.Rule{Priority: 3},
&pb.Rule{Priority: 2},
},
}
var priorities []int32
for _, r := range s.listRules() {
priorities = append(priorities, r.Priority)
}
if priorities[0] != 1 || priorities[1] != 2 || priorities[2] != 3 {
t.Errorf("Incorrect Rule Sequence")
}
}

66
client/cache.go Normal file
View File

@ -0,0 +1,66 @@
package client
import (
"context"
"encoding/json"
"fmt"
"hash/fnv"
"time"
"github.com/micro/go-micro/v2/metadata"
cache "github.com/patrickmn/go-cache"
)
// NewCache returns an initialised cache.
func NewCache() *Cache {
return &Cache{
cache: cache.New(cache.NoExpiration, 30*time.Second),
}
}
// Cache for responses
type Cache struct {
cache *cache.Cache
}
// Get a response from the cache
func (c *Cache) Get(ctx context.Context, req *Request) (interface{}, bool) {
return c.cache.Get(key(ctx, req))
}
// Set a response in the cache
func (c *Cache) Set(ctx context.Context, req *Request, rsp interface{}, expiry time.Duration) {
c.cache.Set(key(ctx, req), rsp, expiry)
}
// List the key value pairs in the cache
func (c *Cache) List() map[string]string {
items := c.cache.Items()
rsp := make(map[string]string, len(items))
for k, v := range items {
bytes, _ := json.Marshal(v.Object)
rsp[k] = string(bytes)
}
return rsp
}
// key returns a hash for the context and request
func key(ctx context.Context, req *Request) string {
md, _ := metadata.FromContext(ctx)
bytes, _ := json.Marshal(map[string]interface{}{
"metadata": md,
"request": map[string]interface{}{
"service": (*req).Service(),
"endpoint": (*req).Endpoint(),
"method": (*req).Method(),
"body": (*req).Body(),
},
})
h := fnv.New64()
h.Write(bytes)
return fmt.Sprintf("%x", h.Sum(nil))
}

76
client/cache_test.go Normal file
View File

@ -0,0 +1,76 @@
package client
import (
"context"
"testing"
"time"
"github.com/micro/go-micro/v2/metadata"
)
func TestCache(t *testing.T) {
ctx := context.TODO()
req := NewRequest("go.micro.service.foo", "Foo.Bar", nil)
t.Run("CacheMiss", func(t *testing.T) {
if _, ok := NewCache().Get(ctx, &req); ok {
t.Errorf("Expected to get no result from Get")
}
})
t.Run("CacheHit", func(t *testing.T) {
c := NewCache()
rsp := "theresponse"
c.Set(ctx, &req, rsp, time.Minute)
if res, ok := c.Get(ctx, &req); !ok {
t.Errorf("Expected a result, got nothing")
} else if res != rsp {
t.Errorf("Expected '%v' result, got '%v'", rsp, res)
}
})
}
func TestCacheKey(t *testing.T) {
ctx := context.TODO()
req1 := NewRequest("go.micro.service.foo", "Foo.Bar", nil)
req2 := NewRequest("go.micro.service.foo", "Foo.Baz", nil)
req3 := NewRequest("go.micro.service.foo", "Foo.Baz", "customquery")
t.Run("IdenticalRequests", func(t *testing.T) {
key1 := key(ctx, &req1)
key2 := key(ctx, &req1)
if key1 != key2 {
t.Errorf("Expected the keys to match for identical requests and context")
}
})
t.Run("DifferentRequestEndpoints", func(t *testing.T) {
key1 := key(ctx, &req1)
key2 := key(ctx, &req2)
if key1 == key2 {
t.Errorf("Expected the keys to differ for different request endpoints")
}
})
t.Run("DifferentRequestBody", func(t *testing.T) {
key1 := key(ctx, &req2)
key2 := key(ctx, &req3)
if key1 == key2 {
t.Errorf("Expected the keys to differ for different request bodies")
}
})
t.Run("DifferentMetadata", func(t *testing.T) {
mdCtx := metadata.Set(context.TODO(), "foo", "bar")
key1 := key(mdCtx, &req1)
key2 := key(ctx, &req1)
if key1 == key2 {
t.Errorf("Expected the keys to differ for different metadata")
}
})
}

View File

@ -29,6 +29,9 @@ type Options struct {
PoolSize int
PoolTTL time.Duration
// Response cache
Cache *Cache
// Middleware for client
Wrappers []Wrapper
@ -59,6 +62,8 @@ type CallOptions struct {
StreamTimeout time.Duration
// Use the services own auth token
ServiceToken bool
// Duration to cache the response for
CacheExpiry time.Duration
// Middleware for low level call func
CallWrappers []CallWrapper
@ -91,6 +96,7 @@ type RequestOptions struct {
func NewOptions(options ...Option) Options {
opts := Options{
Cache: NewCache(),
Context: context.Background(),
ContentType: DefaultContentType,
Codecs: make(map[string]codec.NewCodec),
@ -324,6 +330,14 @@ func WithServiceToken() CallOption {
}
}
// WithCache is a CallOption which sets the duration the response
// shoull be cached for
func WithCache(c time.Duration) CallOption {
return func(o *CallOptions) {
o.CacheExpiry = c
}
}
func WithMessageContentType(ct string) MessageOption {
return func(o *MessageOptions) {
o.ContentType = ct

View File

@ -471,9 +471,13 @@ func (c *cmd) Before(ctx *cli.Context) error {
var serverOpts []server.Option
var clientOpts []client.Option
// setup a client to use when calling the runtime
// setup a client to use when calling the runtime. It is important the auth client is wrapped
// after the cache client since the wrappers are applied in reverse order and the cache will use
// some of the headers set by the auth client.
authFn := func() auth.Auth { return *c.opts.Auth }
microClient := wrapper.AuthClient(authFn, grpc.NewClient())
cacheFn := func() *client.Cache { return (*c.opts.Client).Options().Cache }
microClient := wrapper.CacheClient(cacheFn, grpc.NewClient())
microClient = wrapper.AuthClient(authFn, microClient)
// Set the store
if name := ctx.String("store"); len(name) > 0 {

View File

@ -5,6 +5,7 @@ import (
"context"
"time"
"github.com/micro/go-micro/v2/client"
"github.com/micro/go-micro/v2/debug/log"
proto "github.com/micro/go-micro/v2/debug/service/proto"
"github.com/micro/go-micro/v2/debug/stats"
@ -13,11 +14,12 @@ import (
)
// NewHandler returns an instance of the Debug Handler
func NewHandler() *Debug {
func NewHandler(c client.Client) *Debug {
return &Debug{
log: log.DefaultLog,
stats: stats.DefaultStats,
trace: trace.DefaultTracer,
cache: c.Options().Cache,
}
}
@ -30,6 +32,8 @@ type Debug struct {
stats stats.Stats
// the tracer
trace trace.Tracer
// the cache
cache *client.Cache
}
func (d *Debug) Health(ctx context.Context, req *proto.HealthRequest, rsp *proto.HealthResponse) error {
@ -164,3 +168,9 @@ func (d *Debug) Log(ctx context.Context, stream server.Stream) error {
return nil
}
// Cache returns all the key value pairs in the client cache
func (d *Debug) Cache(ctx context.Context, req *proto.CacheRequest, rsp *proto.CacheResponse) error {
rsp.Values = d.cache.List()
return nil
}

View File

@ -582,6 +582,76 @@ func (m *Span) GetType() SpanType {
return SpanType_INBOUND
}
type CacheRequest struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *CacheRequest) Reset() { *m = CacheRequest{} }
func (m *CacheRequest) String() string { return proto.CompactTextString(m) }
func (*CacheRequest) ProtoMessage() {}
func (*CacheRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_df91f41a5db378e6, []int{9}
}
func (m *CacheRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_CacheRequest.Unmarshal(m, b)
}
func (m *CacheRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_CacheRequest.Marshal(b, m, deterministic)
}
func (m *CacheRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_CacheRequest.Merge(m, src)
}
func (m *CacheRequest) XXX_Size() int {
return xxx_messageInfo_CacheRequest.Size(m)
}
func (m *CacheRequest) XXX_DiscardUnknown() {
xxx_messageInfo_CacheRequest.DiscardUnknown(m)
}
var xxx_messageInfo_CacheRequest proto.InternalMessageInfo
type CacheResponse struct {
Values map[string]string `protobuf:"bytes,1,rep,name=values,proto3" json:"values,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *CacheResponse) Reset() { *m = CacheResponse{} }
func (m *CacheResponse) String() string { return proto.CompactTextString(m) }
func (*CacheResponse) ProtoMessage() {}
func (*CacheResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_df91f41a5db378e6, []int{10}
}
func (m *CacheResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_CacheResponse.Unmarshal(m, b)
}
func (m *CacheResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_CacheResponse.Marshal(b, m, deterministic)
}
func (m *CacheResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_CacheResponse.Merge(m, src)
}
func (m *CacheResponse) XXX_Size() int {
return xxx_messageInfo_CacheResponse.Size(m)
}
func (m *CacheResponse) XXX_DiscardUnknown() {
xxx_messageInfo_CacheResponse.DiscardUnknown(m)
}
var xxx_messageInfo_CacheResponse proto.InternalMessageInfo
func (m *CacheResponse) GetValues() map[string]string {
if m != nil {
return m.Values
}
return nil
}
func init() {
proto.RegisterEnum("SpanType", SpanType_name, SpanType_value)
proto.RegisterType((*HealthRequest)(nil), "HealthRequest")
@ -595,50 +665,56 @@ func init() {
proto.RegisterType((*TraceResponse)(nil), "TraceResponse")
proto.RegisterType((*Span)(nil), "Span")
proto.RegisterMapType((map[string]string)(nil), "Span.MetadataEntry")
proto.RegisterType((*CacheRequest)(nil), "CacheRequest")
proto.RegisterType((*CacheResponse)(nil), "CacheResponse")
proto.RegisterMapType((map[string]string)(nil), "CacheResponse.ValuesEntry")
}
func init() { proto.RegisterFile("debug/service/proto/debug.proto", fileDescriptor_df91f41a5db378e6) }
var fileDescriptor_df91f41a5db378e6 = []byte{
// 594 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x54, 0xdb, 0x6e, 0xd3, 0x40,
0x10, 0x8d, 0xed, 0x38, 0xb1, 0xa7, 0x8d, 0xa9, 0x96, 0x8b, 0x2c, 0x73, 0x69, 0x65, 0x09, 0x29,
0x5c, 0xe4, 0x40, 0x79, 0x41, 0xf0, 0x86, 0x8a, 0x04, 0x52, 0x69, 0xa5, 0x6d, 0xfb, 0x01, 0x5b,
0x7b, 0xe4, 0x1a, 0xea, 0x0b, 0xbb, 0xeb, 0x4a, 0xf9, 0x16, 0xbe, 0x80, 0x37, 0x7e, 0x86, 0xff,
0x41, 0x7b, 0x71, 0x1b, 0x0b, 0xa1, 0x3e, 0xf0, 0xb6, 0xe7, 0xec, 0xec, 0xc9, 0xcc, 0xc9, 0xf1,
0xc0, 0x6e, 0x81, 0xe7, 0x7d, 0xb9, 0x12, 0xc8, 0xaf, 0xaa, 0x1c, 0x57, 0x1d, 0x6f, 0x65, 0xbb,
0xd2, 0x5c, 0xa6, 0xcf, 0xe9, 0x33, 0x58, 0x7c, 0x42, 0x76, 0x29, 0x2f, 0x28, 0x7e, 0xef, 0x51,
0x48, 0x12, 0xc3, 0xdc, 0x56, 0xc7, 0xce, 0x9e, 0xb3, 0x0c, 0xe9, 0x00, 0xd3, 0x25, 0x44, 0x43,
0xa9, 0xe8, 0xda, 0x46, 0x20, 0x79, 0x00, 0x33, 0x21, 0x99, 0xec, 0x85, 0x2d, 0xb5, 0x28, 0x5d,
0xc2, 0xf6, 0x89, 0x64, 0x52, 0xdc, 0xae, 0xf9, 0xdb, 0x81, 0x85, 0x2d, 0xb5, 0x9a, 0x8f, 0x20,
0x94, 0x55, 0x8d, 0x42, 0xb2, 0xba, 0xd3, 0xd5, 0x53, 0x7a, 0x43, 0x68, 0x25, 0xc9, 0xb8, 0xc4,
0x22, 0x76, 0xf5, 0xdd, 0x00, 0x55, 0x2f, 0x7d, 0xa7, 0x0a, 0x63, 0x4f, 0x5f, 0x58, 0xa4, 0xf8,
0x1a, 0xeb, 0x96, 0xaf, 0xe3, 0xa9, 0xe1, 0x0d, 0x52, 0x4a, 0xf2, 0x82, 0x23, 0x2b, 0x44, 0xec,
0x1b, 0x25, 0x0b, 0x49, 0x04, 0x6e, 0x99, 0xc7, 0x33, 0x4d, 0xba, 0x65, 0x4e, 0x12, 0x08, 0xb8,
0x19, 0x44, 0xc4, 0x73, 0xcd, 0x5e, 0x63, 0xa5, 0x8e, 0x9c, 0xb7, 0x5c, 0xc4, 0x81, 0x51, 0x37,
0x28, 0xfd, 0x0a, 0x70, 0xd8, 0x96, 0xb7, 0xce, 0x6f, 0x1c, 0xe4, 0xc8, 0x6a, 0x3d, 0x4e, 0x40,
0x2d, 0x22, 0xf7, 0xc0, 0xcf, 0xdb, 0xbe, 0x91, 0x7a, 0x18, 0x8f, 0x1a, 0xa0, 0x58, 0x51, 0x35,
0x39, 0xea, 0x51, 0x3c, 0x6a, 0x40, 0xfa, 0xcb, 0x81, 0x19, 0xc5, 0xbc, 0xe5, 0xc5, 0xdf, 0xe6,
0x79, 0x9b, 0xe6, 0xbd, 0x86, 0xa0, 0x46, 0xc9, 0x0a, 0x26, 0x59, 0xec, 0xee, 0x79, 0xcb, 0xad,
0xfd, 0xfb, 0x99, 0x79, 0x98, 0x7d, 0xb1, 0xfc, 0xc7, 0x46, 0xf2, 0x35, 0xbd, 0x2e, 0x53, 0x9d,
0xd7, 0x28, 0x04, 0x2b, 0x8d, 0xad, 0x21, 0x1d, 0x60, 0xf2, 0x1e, 0x16, 0xa3, 0x47, 0x64, 0x07,
0xbc, 0x6f, 0xb8, 0xb6, 0x03, 0xaa, 0xa3, 0x6a, 0xf7, 0x8a, 0x5d, 0xf6, 0xa8, 0x67, 0x0b, 0xa9,
0x01, 0xef, 0xdc, 0xb7, 0x4e, 0xfa, 0x04, 0xb6, 0x4f, 0x39, 0xcb, 0x71, 0x30, 0x28, 0x02, 0xb7,
0x2a, 0xec, 0x53, 0xb7, 0x2a, 0xd2, 0x97, 0xb0, 0xb0, 0xf7, 0x36, 0x15, 0x0f, 0xc1, 0x17, 0x1d,
0x6b, 0x54, 0xd0, 0x54, 0xdf, 0x7e, 0x76, 0xd2, 0xb1, 0x86, 0x1a, 0x2e, 0xfd, 0xe1, 0xc2, 0x54,
0x61, 0xf5, 0x83, 0x52, 0x3d, 0xb3, 0x4a, 0x06, 0x58, 0x71, 0x77, 0x10, 0x57, 0x9e, 0x77, 0x8c,
0xa3, 0x35, 0x37, 0xa4, 0x16, 0x11, 0x02, 0xd3, 0x86, 0xd5, 0xc6, 0xdc, 0x90, 0xea, 0xf3, 0x66,
0xde, 0xfc, 0x71, 0xde, 0x12, 0x08, 0x8a, 0x9e, 0x33, 0x59, 0xb5, 0x8d, 0xcd, 0xca, 0x35, 0x26,
0xab, 0x0d, 0xa3, 0xe7, 0xba, 0xe1, 0xbb, 0xba, 0xe1, 0x7f, 0xda, 0xfc, 0x18, 0xa6, 0x72, 0xdd,
0xa1, 0x0e, 0x51, 0xb4, 0x1f, 0xea, 0xe2, 0xd3, 0x75, 0x87, 0x54, 0xd3, 0xff, 0xe5, 0xf5, 0xf3,
0xa7, 0x10, 0x0c, 0x72, 0x64, 0x0b, 0xe6, 0x9f, 0x8f, 0x3e, 0x1c, 0x9f, 0x1d, 0x1d, 0xec, 0x4c,
0xc8, 0x36, 0x04, 0xc7, 0x67, 0xa7, 0x06, 0x39, 0xfb, 0x3f, 0x1d, 0xf0, 0x0f, 0xd4, 0x62, 0x20,
0xbb, 0xe0, 0x1d, 0xb6, 0x25, 0xd9, 0xca, 0x6e, 0x12, 0x9c, 0xcc, 0x6d, 0x50, 0xd2, 0xc9, 0x2b,
0x87, 0xbc, 0x80, 0x99, 0x59, 0x04, 0x24, 0xca, 0x46, 0xcb, 0x23, 0xb9, 0x93, 0x8d, 0x37, 0x44,
0x3a, 0x21, 0x4b, 0xf0, 0xf5, 0x07, 0x4e, 0x16, 0xd9, 0xe6, 0x4e, 0x48, 0xa2, 0x6c, 0xf4, 0xdd,
0x9b, 0x4a, 0xfd, 0xa7, 0x93, 0x45, 0xb6, 0x19, 0x8e, 0x24, 0xca, 0x46, 0x59, 0x48, 0x27, 0xe7,
0x33, 0xbd, 0xbb, 0xde, 0xfc, 0x09, 0x00, 0x00, 0xff, 0xff, 0x6e, 0x42, 0x7f, 0x05, 0xde, 0x04,
0x00, 0x00,
// 646 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x54, 0xdb, 0x6e, 0xd3, 0x4a,
0x14, 0x8d, 0xed, 0x38, 0xb1, 0x77, 0x62, 0x9f, 0x6a, 0xce, 0x45, 0x96, 0x0f, 0xd0, 0xca, 0x12,
0x52, 0xb8, 0x68, 0x02, 0xe1, 0x85, 0xcb, 0x1b, 0x14, 0x09, 0xa4, 0xd2, 0x4a, 0xd3, 0x96, 0xf7,
0xa9, 0x3d, 0x4a, 0x03, 0xf5, 0x85, 0x99, 0x71, 0xa5, 0xbc, 0xf0, 0x23, 0xfc, 0x04, 0xff, 0x82,
0xf8, 0x1f, 0x34, 0x17, 0xb7, 0xb6, 0x10, 0xaa, 0x10, 0x6f, 0x5e, 0x6b, 0xaf, 0xd9, 0xd9, 0x7b,
0x69, 0x65, 0xc3, 0x6e, 0xc1, 0xce, 0xda, 0xf5, 0x52, 0x30, 0x7e, 0xb9, 0xc9, 0xd9, 0xb2, 0xe1,
0xb5, 0xac, 0x97, 0x9a, 0xc3, 0xfa, 0x3b, 0xbb, 0x07, 0xd1, 0x1b, 0x46, 0x2f, 0xe4, 0x39, 0x61,
0x9f, 0x5a, 0x26, 0x24, 0x4a, 0x60, 0x6a, 0xd5, 0x89, 0xb3, 0xe7, 0x2c, 0x42, 0xd2, 0xc1, 0x6c,
0x01, 0x71, 0x27, 0x15, 0x4d, 0x5d, 0x09, 0x86, 0xfe, 0x83, 0x89, 0x90, 0x54, 0xb6, 0xc2, 0x4a,
0x2d, 0xca, 0x16, 0x30, 0x3f, 0x96, 0x54, 0x8a, 0x9b, 0x7b, 0x7e, 0x77, 0x20, 0xb2, 0x52, 0xdb,
0xf3, 0x16, 0x84, 0x72, 0x53, 0x32, 0x21, 0x69, 0xd9, 0x68, 0xf5, 0x98, 0x5c, 0x13, 0xba, 0x93,
0xa4, 0x5c, 0xb2, 0x22, 0x71, 0x75, 0xad, 0x83, 0x6a, 0x96, 0xb6, 0x51, 0xc2, 0xc4, 0xd3, 0x05,
0x8b, 0x14, 0x5f, 0xb2, 0xb2, 0xe6, 0xdb, 0x64, 0x6c, 0x78, 0x83, 0x54, 0x27, 0x79, 0xce, 0x19,
0x2d, 0x44, 0xe2, 0x9b, 0x4e, 0x16, 0xa2, 0x18, 0xdc, 0x75, 0x9e, 0x4c, 0x34, 0xe9, 0xae, 0x73,
0x94, 0x42, 0xc0, 0xcd, 0x22, 0x22, 0x99, 0x6a, 0xf6, 0x0a, 0xab, 0xee, 0x8c, 0xf3, 0x9a, 0x8b,
0x24, 0x30, 0xdd, 0x0d, 0xca, 0x3e, 0x00, 0x1c, 0xd4, 0xeb, 0x1b, 0xf7, 0x37, 0x0e, 0x72, 0x46,
0x4b, 0xbd, 0x4e, 0x40, 0x2c, 0x42, 0xff, 0x80, 0x9f, 0xd7, 0x6d, 0x25, 0xf5, 0x32, 0x1e, 0x31,
0x40, 0xb1, 0x62, 0x53, 0xe5, 0x4c, 0xaf, 0xe2, 0x11, 0x03, 0xb2, 0xaf, 0x0e, 0x4c, 0x08, 0xcb,
0x6b, 0x5e, 0xfc, 0x6c, 0x9e, 0xd7, 0x37, 0xef, 0x31, 0x04, 0x25, 0x93, 0xb4, 0xa0, 0x92, 0x26,
0xee, 0x9e, 0xb7, 0x98, 0xad, 0xfe, 0xc5, 0xe6, 0x21, 0x7e, 0x67, 0xf9, 0xd7, 0x95, 0xe4, 0x5b,
0x72, 0x25, 0x53, 0x93, 0x97, 0x4c, 0x08, 0xba, 0x36, 0xb6, 0x86, 0xa4, 0x83, 0xe9, 0x0b, 0x88,
0x06, 0x8f, 0xd0, 0x0e, 0x78, 0x1f, 0xd9, 0xd6, 0x2e, 0xa8, 0x3e, 0xd5, 0xb8, 0x97, 0xf4, 0xa2,
0x65, 0x7a, 0xb7, 0x90, 0x18, 0xf0, 0xdc, 0x7d, 0xea, 0x64, 0x77, 0x60, 0x7e, 0xc2, 0x69, 0xce,
0x3a, 0x83, 0x62, 0x70, 0x37, 0x85, 0x7d, 0xea, 0x6e, 0x8a, 0xec, 0x21, 0x44, 0xb6, 0x6e, 0x53,
0xf1, 0x3f, 0xf8, 0xa2, 0xa1, 0x95, 0x0a, 0x9a, 0x9a, 0xdb, 0xc7, 0xc7, 0x0d, 0xad, 0x88, 0xe1,
0xb2, 0x2f, 0x2e, 0x8c, 0x15, 0x56, 0x3f, 0x28, 0xd5, 0x33, 0xdb, 0xc9, 0x00, 0xdb, 0xdc, 0xed,
0x9a, 0x2b, 0xcf, 0x1b, 0xca, 0x99, 0x35, 0x37, 0x24, 0x16, 0x21, 0x04, 0xe3, 0x8a, 0x96, 0xc6,
0xdc, 0x90, 0xe8, 0xef, 0x7e, 0xde, 0xfc, 0x61, 0xde, 0x52, 0x08, 0x8a, 0x96, 0x53, 0xb9, 0xa9,
0x2b, 0x9b, 0x95, 0x2b, 0x8c, 0x96, 0x3d, 0xa3, 0xa7, 0x7a, 0xe0, 0xbf, 0xf5, 0xc0, 0xbf, 0xb4,
0xf9, 0x36, 0x8c, 0xe5, 0xb6, 0x61, 0x3a, 0x44, 0xf1, 0x2a, 0xd4, 0xe2, 0x93, 0x6d, 0xc3, 0x88,
0xa6, 0xff, 0xcc, 0xeb, 0x18, 0xe6, 0xaf, 0x68, 0x7e, 0xde, 0x79, 0x9d, 0x7d, 0x86, 0xc8, 0x62,
0xeb, 0xed, 0x0a, 0x26, 0x5a, 0xdd, 0x99, 0x9b, 0xe2, 0x41, 0x1d, 0xbf, 0xd7, 0x45, 0x33, 0xb2,
0x55, 0xa6, 0xcf, 0x60, 0xd6, 0xa3, 0x7f, 0x67, 0x9e, 0xfb, 0x77, 0x21, 0xe8, 0xd6, 0x43, 0x33,
0x98, 0xbe, 0x3d, 0x7c, 0x79, 0x74, 0x7a, 0xb8, 0xbf, 0x33, 0x42, 0x73, 0x08, 0x8e, 0x4e, 0x4f,
0x0c, 0x72, 0x56, 0xdf, 0x1c, 0xf0, 0xf7, 0xd5, 0xa1, 0x42, 0xbb, 0xe0, 0x1d, 0xd4, 0x6b, 0x34,
0xc3, 0xd7, 0xff, 0xa8, 0x74, 0x6a, 0x83, 0x9b, 0x8d, 0x1e, 0x39, 0xe8, 0x01, 0x4c, 0xcc, 0x61,
0x42, 0x31, 0x1e, 0x1c, 0xb3, 0xf4, 0x2f, 0x3c, 0xbc, 0x58, 0xd9, 0x08, 0x2d, 0xc0, 0xd7, 0x07,
0x07, 0x45, 0xb8, 0x7f, 0xa3, 0xd2, 0x18, 0x0f, 0xee, 0x90, 0x51, 0xea, 0x10, 0xa2, 0x08, 0xf7,
0xc3, 0x9a, 0xc6, 0x78, 0x90, 0x4d, 0xa3, 0xd4, 0x96, 0xa1, 0x08, 0xf7, 0xad, 0x4e, 0xe3, 0xa1,
0x93, 0xd9, 0xe8, 0x6c, 0xa2, 0xaf, 0xee, 0x93, 0x1f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x22, 0x65,
0x99, 0x10, 0x98, 0x05, 0x00, 0x00,
}
// Reference imports to suppress errors if they are not otherwise used.
@ -657,6 +733,7 @@ type DebugClient interface {
Health(ctx context.Context, in *HealthRequest, opts ...grpc.CallOption) (*HealthResponse, error)
Stats(ctx context.Context, in *StatsRequest, opts ...grpc.CallOption) (*StatsResponse, error)
Trace(ctx context.Context, in *TraceRequest, opts ...grpc.CallOption) (*TraceResponse, error)
Cache(ctx context.Context, in *CacheRequest, opts ...grpc.CallOption) (*CacheResponse, error)
}
type debugClient struct {
@ -726,12 +803,22 @@ func (c *debugClient) Trace(ctx context.Context, in *TraceRequest, opts ...grpc.
return out, nil
}
func (c *debugClient) Cache(ctx context.Context, in *CacheRequest, opts ...grpc.CallOption) (*CacheResponse, error) {
out := new(CacheResponse)
err := c.cc.Invoke(ctx, "/Debug/Cache", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// DebugServer is the server API for Debug service.
type DebugServer interface {
Log(*LogRequest, Debug_LogServer) error
Health(context.Context, *HealthRequest) (*HealthResponse, error)
Stats(context.Context, *StatsRequest) (*StatsResponse, error)
Trace(context.Context, *TraceRequest) (*TraceResponse, error)
Cache(context.Context, *CacheRequest) (*CacheResponse, error)
}
// UnimplementedDebugServer can be embedded to have forward compatible implementations.
@ -750,6 +837,9 @@ func (*UnimplementedDebugServer) Stats(ctx context.Context, req *StatsRequest) (
func (*UnimplementedDebugServer) Trace(ctx context.Context, req *TraceRequest) (*TraceResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Trace not implemented")
}
func (*UnimplementedDebugServer) Cache(ctx context.Context, req *CacheRequest) (*CacheResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Cache not implemented")
}
func RegisterDebugServer(s *grpc.Server, srv DebugServer) {
s.RegisterService(&_Debug_serviceDesc, srv)
@ -830,6 +920,24 @@ func _Debug_Trace_Handler(srv interface{}, ctx context.Context, dec func(interfa
return interceptor(ctx, in, info, handler)
}
func _Debug_Cache_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(CacheRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(DebugServer).Cache(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/Debug/Cache",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(DebugServer).Cache(ctx, req.(*CacheRequest))
}
return interceptor(ctx, in, info, handler)
}
var _Debug_serviceDesc = grpc.ServiceDesc{
ServiceName: "Debug",
HandlerType: (*DebugServer)(nil),
@ -846,6 +954,10 @@ var _Debug_serviceDesc = grpc.ServiceDesc{
MethodName: "Trace",
Handler: _Debug_Trace_Handler,
},
{
MethodName: "Cache",
Handler: _Debug_Cache_Handler,
},
},
Streams: []grpc.StreamDesc{
{

View File

@ -46,6 +46,7 @@ type DebugService interface {
Health(ctx context.Context, in *HealthRequest, opts ...client.CallOption) (*HealthResponse, error)
Stats(ctx context.Context, in *StatsRequest, opts ...client.CallOption) (*StatsResponse, error)
Trace(ctx context.Context, in *TraceRequest, opts ...client.CallOption) (*TraceResponse, error)
Cache(ctx context.Context, in *CacheRequest, opts ...client.CallOption) (*CacheResponse, error)
}
type debugService struct {
@ -139,6 +140,16 @@ func (c *debugService) Trace(ctx context.Context, in *TraceRequest, opts ...clie
return out, nil
}
func (c *debugService) Cache(ctx context.Context, in *CacheRequest, opts ...client.CallOption) (*CacheResponse, error) {
req := c.c.NewRequest(c.name, "Debug.Cache", in)
out := new(CacheResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// Server API for Debug service
type DebugHandler interface {
@ -146,6 +157,7 @@ type DebugHandler interface {
Health(context.Context, *HealthRequest, *HealthResponse) error
Stats(context.Context, *StatsRequest, *StatsResponse) error
Trace(context.Context, *TraceRequest, *TraceResponse) error
Cache(context.Context, *CacheRequest, *CacheResponse) error
}
func RegisterDebugHandler(s server.Server, hdlr DebugHandler, opts ...server.HandlerOption) error {
@ -154,6 +166,7 @@ func RegisterDebugHandler(s server.Server, hdlr DebugHandler, opts ...server.Han
Health(ctx context.Context, in *HealthRequest, out *HealthResponse) error
Stats(ctx context.Context, in *StatsRequest, out *StatsResponse) error
Trace(ctx context.Context, in *TraceRequest, out *TraceResponse) error
Cache(ctx context.Context, in *CacheRequest, out *CacheResponse) error
}
type Debug struct {
debug
@ -217,3 +230,7 @@ func (h *debugHandler) Stats(ctx context.Context, in *StatsRequest, out *StatsRe
func (h *debugHandler) Trace(ctx context.Context, in *TraceRequest, out *TraceResponse) error {
return h.DebugHandler.Trace(ctx, in, out)
}
func (h *debugHandler) Cache(ctx context.Context, in *CacheRequest, out *CacheResponse) error {
return h.DebugHandler.Cache(ctx, in, out)
}

View File

@ -5,6 +5,7 @@ service Debug {
rpc Health(HealthRequest) returns (HealthResponse) {};
rpc Stats(StatsRequest) returns (StatsResponse) {};
rpc Trace(TraceRequest) returns (TraceResponse) {};
rpc Cache(CacheRequest) returns (CacheResponse) {};
}
message HealthRequest {
@ -97,3 +98,9 @@ message Span {
map<string,string> metadata = 7;
SpanType type = 8;
}
message CacheRequest {}
message CacheResponse {
map<string, string> values = 1;
}

View File

@ -1,350 +1,324 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.22.0
// protoc v3.6.1
// source: github.com/micro/go-micro/server/proto/server.proto
// source: server/proto/server.proto
package go_micro_server
import (
context "context"
fmt "fmt"
proto "github.com/golang/protobuf/proto"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
math "math"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion that a sufficiently up-to-date version
// of the legacy proto package is being used.
const _ = proto.ProtoPackageIsVersion4
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type HandleRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Service string `protobuf:"bytes,1,opt,name=service,proto3" json:"service,omitempty"`
Endpoint string `protobuf:"bytes,2,opt,name=endpoint,proto3" json:"endpoint,omitempty"`
Protocol string `protobuf:"bytes,3,opt,name=protocol,proto3" json:"protocol,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (x *HandleRequest) Reset() {
*x = HandleRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_github_com_micro_go_micro_server_proto_server_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *HandleRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (m *HandleRequest) Reset() { *m = HandleRequest{} }
func (m *HandleRequest) String() string { return proto.CompactTextString(m) }
func (*HandleRequest) ProtoMessage() {}
func (x *HandleRequest) ProtoReflect() protoreflect.Message {
mi := &file_github_com_micro_go_micro_server_proto_server_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use HandleRequest.ProtoReflect.Descriptor instead.
func (*HandleRequest) Descriptor() ([]byte, []int) {
return file_github_com_micro_go_micro_server_proto_server_proto_rawDescGZIP(), []int{0}
return fileDescriptor_1959cecd4d1121a1, []int{0}
}
func (x *HandleRequest) GetService() string {
if x != nil {
return x.Service
func (m *HandleRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_HandleRequest.Unmarshal(m, b)
}
func (m *HandleRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_HandleRequest.Marshal(b, m, deterministic)
}
func (m *HandleRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_HandleRequest.Merge(m, src)
}
func (m *HandleRequest) XXX_Size() int {
return xxx_messageInfo_HandleRequest.Size(m)
}
func (m *HandleRequest) XXX_DiscardUnknown() {
xxx_messageInfo_HandleRequest.DiscardUnknown(m)
}
var xxx_messageInfo_HandleRequest proto.InternalMessageInfo
func (m *HandleRequest) GetService() string {
if m != nil {
return m.Service
}
return ""
}
func (x *HandleRequest) GetEndpoint() string {
if x != nil {
return x.Endpoint
func (m *HandleRequest) GetEndpoint() string {
if m != nil {
return m.Endpoint
}
return ""
}
func (x *HandleRequest) GetProtocol() string {
if x != nil {
return x.Protocol
func (m *HandleRequest) GetProtocol() string {
if m != nil {
return m.Protocol
}
return ""
}
type HandleResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
}
func (x *HandleResponse) Reset() {
*x = HandleResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_github_com_micro_go_micro_server_proto_server_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *HandleResponse) String() string {
return protoimpl.X.MessageStringOf(x)
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *HandleResponse) Reset() { *m = HandleResponse{} }
func (m *HandleResponse) String() string { return proto.CompactTextString(m) }
func (*HandleResponse) ProtoMessage() {}
func (x *HandleResponse) ProtoReflect() protoreflect.Message {
mi := &file_github_com_micro_go_micro_server_proto_server_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use HandleResponse.ProtoReflect.Descriptor instead.
func (*HandleResponse) Descriptor() ([]byte, []int) {
return file_github_com_micro_go_micro_server_proto_server_proto_rawDescGZIP(), []int{1}
return fileDescriptor_1959cecd4d1121a1, []int{1}
}
func (m *HandleResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_HandleResponse.Unmarshal(m, b)
}
func (m *HandleResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_HandleResponse.Marshal(b, m, deterministic)
}
func (m *HandleResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_HandleResponse.Merge(m, src)
}
func (m *HandleResponse) XXX_Size() int {
return xxx_messageInfo_HandleResponse.Size(m)
}
func (m *HandleResponse) XXX_DiscardUnknown() {
xxx_messageInfo_HandleResponse.DiscardUnknown(m)
}
var xxx_messageInfo_HandleResponse proto.InternalMessageInfo
type SubscribeRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Topic string `protobuf:"bytes,1,opt,name=topic,proto3" json:"topic,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (x *SubscribeRequest) Reset() {
*x = SubscribeRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_github_com_micro_go_micro_server_proto_server_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *SubscribeRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (m *SubscribeRequest) Reset() { *m = SubscribeRequest{} }
func (m *SubscribeRequest) String() string { return proto.CompactTextString(m) }
func (*SubscribeRequest) ProtoMessage() {}
func (x *SubscribeRequest) ProtoReflect() protoreflect.Message {
mi := &file_github_com_micro_go_micro_server_proto_server_proto_msgTypes[2]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use SubscribeRequest.ProtoReflect.Descriptor instead.
func (*SubscribeRequest) Descriptor() ([]byte, []int) {
return file_github_com_micro_go_micro_server_proto_server_proto_rawDescGZIP(), []int{2}
return fileDescriptor_1959cecd4d1121a1, []int{2}
}
func (x *SubscribeRequest) GetTopic() string {
if x != nil {
return x.Topic
func (m *SubscribeRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_SubscribeRequest.Unmarshal(m, b)
}
func (m *SubscribeRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_SubscribeRequest.Marshal(b, m, deterministic)
}
func (m *SubscribeRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_SubscribeRequest.Merge(m, src)
}
func (m *SubscribeRequest) XXX_Size() int {
return xxx_messageInfo_SubscribeRequest.Size(m)
}
func (m *SubscribeRequest) XXX_DiscardUnknown() {
xxx_messageInfo_SubscribeRequest.DiscardUnknown(m)
}
var xxx_messageInfo_SubscribeRequest proto.InternalMessageInfo
func (m *SubscribeRequest) GetTopic() string {
if m != nil {
return m.Topic
}
return ""
}
type SubscribeResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
}
func (x *SubscribeResponse) Reset() {
*x = SubscribeResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_github_com_micro_go_micro_server_proto_server_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *SubscribeResponse) String() string {
return protoimpl.X.MessageStringOf(x)
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *SubscribeResponse) Reset() { *m = SubscribeResponse{} }
func (m *SubscribeResponse) String() string { return proto.CompactTextString(m) }
func (*SubscribeResponse) ProtoMessage() {}
func (x *SubscribeResponse) ProtoReflect() protoreflect.Message {
mi := &file_github_com_micro_go_micro_server_proto_server_proto_msgTypes[3]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use SubscribeResponse.ProtoReflect.Descriptor instead.
func (*SubscribeResponse) Descriptor() ([]byte, []int) {
return file_github_com_micro_go_micro_server_proto_server_proto_rawDescGZIP(), []int{3}
return fileDescriptor_1959cecd4d1121a1, []int{3}
}
var File_github_com_micro_go_micro_server_proto_server_proto protoreflect.FileDescriptor
var file_github_com_micro_go_micro_server_proto_server_proto_rawDesc = []byte{
0x0a, 0x33, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6d, 0x69, 0x63,
0x72, 0x6f, 0x2f, 0x67, 0x6f, 0x2d, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x2f, 0x73, 0x65, 0x72, 0x76,
0x65, 0x72, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x67, 0x6f, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x2e,
0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x22, 0x61, 0x0a, 0x0d, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69,
0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63,
0x65, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x02, 0x20,
0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x1a, 0x0a,
0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52,
0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x22, 0x10, 0x0a, 0x0e, 0x48, 0x61, 0x6e,
0x64, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x28, 0x0a, 0x10, 0x53,
0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,
0x14, 0x0a, 0x05, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05,
0x74, 0x6f, 0x70, 0x69, 0x63, 0x22, 0x13, 0x0a, 0x11, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69,
0x62, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xab, 0x01, 0x0a, 0x06, 0x53,
0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x4b, 0x0a, 0x06, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x12,
0x1e, 0x2e, 0x67, 0x6f, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65,
0x72, 0x2e, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
0x1f, 0x2e, 0x67, 0x6f, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65,
0x72, 0x2e, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x22, 0x00, 0x12, 0x54, 0x0a, 0x09, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x12,
0x21, 0x2e, 0x67, 0x6f, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65,
0x72, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x1a, 0x22, 0x2e, 0x67, 0x6f, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x2e, 0x73, 0x65,
0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
func (m *SubscribeResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_SubscribeResponse.Unmarshal(m, b)
}
func (m *SubscribeResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_SubscribeResponse.Marshal(b, m, deterministic)
}
func (m *SubscribeResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_SubscribeResponse.Merge(m, src)
}
func (m *SubscribeResponse) XXX_Size() int {
return xxx_messageInfo_SubscribeResponse.Size(m)
}
func (m *SubscribeResponse) XXX_DiscardUnknown() {
xxx_messageInfo_SubscribeResponse.DiscardUnknown(m)
}
var (
file_github_com_micro_go_micro_server_proto_server_proto_rawDescOnce sync.Once
file_github_com_micro_go_micro_server_proto_server_proto_rawDescData = file_github_com_micro_go_micro_server_proto_server_proto_rawDesc
)
var xxx_messageInfo_SubscribeResponse proto.InternalMessageInfo
func file_github_com_micro_go_micro_server_proto_server_proto_rawDescGZIP() []byte {
file_github_com_micro_go_micro_server_proto_server_proto_rawDescOnce.Do(func() {
file_github_com_micro_go_micro_server_proto_server_proto_rawDescData = protoimpl.X.CompressGZIP(file_github_com_micro_go_micro_server_proto_server_proto_rawDescData)
})
return file_github_com_micro_go_micro_server_proto_server_proto_rawDescData
func init() {
proto.RegisterType((*HandleRequest)(nil), "go.micro.server.HandleRequest")
proto.RegisterType((*HandleResponse)(nil), "go.micro.server.HandleResponse")
proto.RegisterType((*SubscribeRequest)(nil), "go.micro.server.SubscribeRequest")
proto.RegisterType((*SubscribeResponse)(nil), "go.micro.server.SubscribeResponse")
}
var file_github_com_micro_go_micro_server_proto_server_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
var file_github_com_micro_go_micro_server_proto_server_proto_goTypes = []interface{}{
(*HandleRequest)(nil), // 0: go.micro.server.HandleRequest
(*HandleResponse)(nil), // 1: go.micro.server.HandleResponse
(*SubscribeRequest)(nil), // 2: go.micro.server.SubscribeRequest
(*SubscribeResponse)(nil), // 3: go.micro.server.SubscribeResponse
}
var file_github_com_micro_go_micro_server_proto_server_proto_depIdxs = []int32{
0, // 0: go.micro.server.Server.Handle:input_type -> go.micro.server.HandleRequest
2, // 1: go.micro.server.Server.Subscribe:input_type -> go.micro.server.SubscribeRequest
1, // 2: go.micro.server.Server.Handle:output_type -> go.micro.server.HandleResponse
3, // 3: go.micro.server.Server.Subscribe:output_type -> go.micro.server.SubscribeResponse
2, // [2:4] is the sub-list for method output_type
0, // [0:2] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
func init() { proto.RegisterFile("server/proto/server.proto", fileDescriptor_1959cecd4d1121a1) }
var fileDescriptor_1959cecd4d1121a1 = []byte{
// 223 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x2c, 0x4e, 0x2d, 0x2a,
0x4b, 0x2d, 0xd2, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0xd7, 0x87, 0x70, 0xf4, 0xc0, 0x1c, 0x21, 0xfe,
0xf4, 0x7c, 0xbd, 0xdc, 0xcc, 0xe4, 0xa2, 0x7c, 0x3d, 0x88, 0xb0, 0x52, 0x22, 0x17, 0xaf, 0x47,
0x62, 0x5e, 0x4a, 0x4e, 0x6a, 0x50, 0x6a, 0x61, 0x69, 0x6a, 0x71, 0x89, 0x90, 0x04, 0x17, 0x3b,
0x48, 0x2a, 0x33, 0x39, 0x55, 0x82, 0x51, 0x81, 0x51, 0x83, 0x33, 0x08, 0xc6, 0x15, 0x92, 0xe2,
0xe2, 0x48, 0xcd, 0x4b, 0x29, 0xc8, 0xcf, 0xcc, 0x2b, 0x91, 0x60, 0x02, 0x4b, 0xc1, 0xf9, 0x20,
0x39, 0xb0, 0x05, 0xc9, 0xf9, 0x39, 0x12, 0xcc, 0x10, 0x39, 0x18, 0x5f, 0x49, 0x80, 0x8b, 0x0f,
0x66, 0x45, 0x71, 0x41, 0x7e, 0x5e, 0x71, 0xaa, 0x92, 0x06, 0x97, 0x40, 0x70, 0x69, 0x52, 0x71,
0x72, 0x51, 0x66, 0x12, 0xdc, 0x5e, 0x11, 0x2e, 0xd6, 0x92, 0xfc, 0x82, 0xcc, 0x64, 0xa8, 0xad,
0x10, 0x8e, 0x92, 0x30, 0x97, 0x20, 0x92, 0x4a, 0x88, 0x76, 0xa3, 0xd5, 0x8c, 0x5c, 0x6c, 0xc1,
0x60, 0xe7, 0x0b, 0x79, 0x73, 0xb1, 0x41, 0xcc, 0x16, 0x92, 0xd3, 0x43, 0xf3, 0x9a, 0x1e, 0x8a,
0xbf, 0xa4, 0xe4, 0x71, 0xca, 0x43, 0x1d, 0xc5, 0x20, 0x14, 0xc2, 0xc5, 0x09, 0xb7, 0x4c, 0x48,
0x11, 0x43, 0x3d, 0xba, 0x93, 0xa5, 0x94, 0xf0, 0x29, 0x81, 0x99, 0x9a, 0xc4, 0x06, 0x0e, 0x08,
0x63, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0xa4, 0x3f, 0x79, 0x80, 0x96, 0x01, 0x00, 0x00,
}
func init() { file_github_com_micro_go_micro_server_proto_server_proto_init() }
func file_github_com_micro_go_micro_server_proto_server_proto_init() {
if File_github_com_micro_go_micro_server_proto_server_proto != nil {
return
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConn
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion4
// ServerClient is the client API for Server service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type ServerClient interface {
Handle(ctx context.Context, in *HandleRequest, opts ...grpc.CallOption) (*HandleResponse, error)
Subscribe(ctx context.Context, in *SubscribeRequest, opts ...grpc.CallOption) (*SubscribeResponse, error)
}
if !protoimpl.UnsafeEnabled {
file_github_com_micro_go_micro_server_proto_server_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*HandleRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
type serverClient struct {
cc *grpc.ClientConn
}
func NewServerClient(cc *grpc.ClientConn) ServerClient {
return &serverClient{cc}
}
file_github_com_micro_go_micro_server_proto_server_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*HandleResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
func (c *serverClient) Handle(ctx context.Context, in *HandleRequest, opts ...grpc.CallOption) (*HandleResponse, error) {
out := new(HandleResponse)
err := c.cc.Invoke(ctx, "/go.micro.server.Server/Handle", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
file_github_com_micro_go_micro_server_proto_server_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*SubscribeRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
func (c *serverClient) Subscribe(ctx context.Context, in *SubscribeRequest, opts ...grpc.CallOption) (*SubscribeResponse, error) {
out := new(SubscribeResponse)
err := c.cc.Invoke(ctx, "/go.micro.server.Server/Subscribe", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
file_github_com_micro_go_micro_server_proto_server_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*SubscribeResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
// ServerServer is the server API for Server service.
type ServerServer interface {
Handle(context.Context, *HandleRequest) (*HandleResponse, error)
Subscribe(context.Context, *SubscribeRequest) (*SubscribeResponse, error)
}
// UnimplementedServerServer can be embedded to have forward compatible implementations.
type UnimplementedServerServer struct {
}
func (*UnimplementedServerServer) Handle(ctx context.Context, req *HandleRequest) (*HandleResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Handle not implemented")
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_github_com_micro_go_micro_server_proto_server_proto_rawDesc,
NumEnums: 0,
NumMessages: 4,
NumExtensions: 0,
NumServices: 1,
func (*UnimplementedServerServer) Subscribe(ctx context.Context, req *SubscribeRequest) (*SubscribeResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Subscribe not implemented")
}
func RegisterServerServer(s *grpc.Server, srv ServerServer) {
s.RegisterService(&_Server_serviceDesc, srv)
}
func _Server_Handle_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(HandleRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ServerServer).Handle(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/go.micro.server.Server/Handle",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ServerServer).Handle(ctx, req.(*HandleRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Server_Subscribe_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(SubscribeRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ServerServer).Subscribe(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/go.micro.server.Server/Subscribe",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ServerServer).Subscribe(ctx, req.(*SubscribeRequest))
}
return interceptor(ctx, in, info, handler)
}
var _Server_serviceDesc = grpc.ServiceDesc{
ServiceName: "go.micro.server.Server",
HandlerType: (*ServerServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Handle",
Handler: _Server_Handle_Handler,
},
GoTypes: file_github_com_micro_go_micro_server_proto_server_proto_goTypes,
DependencyIndexes: file_github_com_micro_go_micro_server_proto_server_proto_depIdxs,
MessageInfos: file_github_com_micro_go_micro_server_proto_server_proto_msgTypes,
}.Build()
File_github_com_micro_go_micro_server_proto_server_proto = out.File
file_github_com_micro_go_micro_server_proto_server_proto_rawDesc = nil
file_github_com_micro_go_micro_server_proto_server_proto_goTypes = nil
file_github_com_micro_go_micro_server_proto_server_proto_depIdxs = nil
{
MethodName: "Subscribe",
Handler: _Server_Subscribe_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "server/proto/server.proto",
}

View File

@ -1,5 +1,5 @@
// Code generated by protoc-gen-micro. DO NOT EDIT.
// source: github.com/micro/go-micro/server/proto/server.proto
// source: server/proto/server.proto
package go_micro_server

View File

@ -35,13 +35,14 @@ func newService(opts ...Option) Service {
// service name
serviceName := options.Server.Options().Name
// authFn returns the auth, we pass as a function since auth
// has not yet been set at this point.
// we pass functions to the wrappers since the values can change during initialisation
authFn := func() auth.Auth { return options.Server.Options().Auth }
cacheFn := func() *client.Cache { return options.Client.Options().Cache }
// wrap client to inject From-Service header on any calls
options.Client = wrapper.FromService(serviceName, options.Client)
options.Client = wrapper.TraceCall(serviceName, trace.DefaultTracer, options.Client)
options.Client = wrapper.CacheClient(cacheFn, options.Client)
options.Client = wrapper.AuthClient(authFn, options.Client)
// wrap the server to provide handler stats
@ -183,7 +184,7 @@ func (s *service) Run() error {
// register the debug handler
s.opts.Server.Handle(
s.opts.Server.NewHandler(
handler.NewHandler(),
handler.NewHandler(s.opts.Client),
server.InternalHandler(true),
),
)

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
// Code generated by protoc-gen-micro. DO NOT EDIT.
// source: micro/go-micro/util/file/proto/file.proto
// source: util/file/proto/file.proto
package go_micro_server

View File

@ -5,6 +5,7 @@ import (
"context"
"sync"
"github.com/micro/go-micro/v2/client"
"github.com/micro/go-micro/v2/debug/service/handler"
"github.com/micro/go-micro/v2/proxy"
"github.com/micro/go-micro/v2/server"
@ -42,7 +43,7 @@ func New(name string, p proxy.Proxy) *Server {
server.DefaultRouter.Handle(
// inject the debug handler
server.DefaultRouter.NewHandler(
handler.NewHandler(),
handler.NewHandler(client.DefaultClient),
server.InternalHandler(true),
),
)

View File

@ -2,6 +2,7 @@ package wrapper
import (
"context"
"reflect"
"strings"
"time"
@ -227,3 +228,55 @@ func AuthHandler(fn func() auth.Auth) server.HandlerWrapper {
}
}
}
type cacheWrapper struct {
cacheFn func() *client.Cache
client.Client
}
// Call executes the request. If the CacheExpiry option was set, the response will be cached using
// a hash of the metadata and request as the key.
func (c *cacheWrapper) Call(ctx context.Context, req client.Request, rsp interface{}, opts ...client.CallOption) error {
// parse the options
var options client.CallOptions
for _, o := range opts {
o(&options)
}
// if the client doesn't have a cacbe setup don't continue
cache := c.cacheFn()
if cache == nil {
return c.Client.Call(ctx, req, rsp, opts...)
}
// if the cache expiry is not set, execute the call without the cache
if options.CacheExpiry == 0 {
return c.Client.Call(ctx, req, rsp, opts...)
}
// if the response is nil don't call the cache since we can't assign the response
if rsp == nil {
return c.Client.Call(ctx, req, rsp, opts...)
}
// check to see if there is a response cached, if there is assign it
if r, ok := cache.Get(ctx, &req); ok {
val := reflect.ValueOf(rsp).Elem()
val.Set(reflect.ValueOf(r).Elem())
return nil
}
// don't cache the result if there was an error
if err := c.Client.Call(ctx, req, rsp, opts...); err != nil {
return err
}
// set the result in the cache
cache.Set(ctx, &req, rsp, options.CacheExpiry)
return nil
}
// CacheClient wraps requests with the cache wrapper
func CacheClient(cacheFn func() *client.Cache, c client.Client) client.Client {
return &cacheWrapper{cacheFn, c}
}

View File

@ -2,8 +2,11 @@ package wrapper
import (
"context"
"reflect"
"testing"
"time"
"github.com/micro/go-micro/v2/client"
"github.com/micro/go-micro/v2/metadata"
)
@ -49,5 +52,100 @@ func TestWrapper(t *testing.T) {
}
}
}
}
type testClient struct {
callCount int
callRsp interface{}
client.Client
}
func (c *testClient) Call(ctx context.Context, req client.Request, rsp interface{}, opts ...client.CallOption) error {
c.callCount++
if c.callRsp != nil {
val := reflect.ValueOf(rsp).Elem()
val.Set(reflect.ValueOf(c.callRsp).Elem())
}
return nil
}
type testRsp struct {
value string
}
func TestCacheWrapper(t *testing.T) {
req := client.NewRequest("go.micro.service.foo", "Foo.Bar", nil)
t.Run("NilCache", func(t *testing.T) {
cli := new(testClient)
w := CacheClient(func() *client.Cache {
return nil
}, cli)
// perfroming two requests should increment the call count by two indicating the cache wasn't
// used even though the WithCache option was passed.
w.Call(context.TODO(), req, nil, client.WithCache(time.Minute))
w.Call(context.TODO(), req, nil, client.WithCache(time.Minute))
if cli.callCount != 2 {
t.Errorf("Expected the client to have been called twice")
}
})
t.Run("OptionNotSet", func(t *testing.T) {
cli := new(testClient)
cache := client.NewCache()
w := CacheClient(func() *client.Cache {
return cache
}, cli)
// perfroming two requests should increment the call count by two since we didn't pass the WithCache
// option to Call.
w.Call(context.TODO(), req, nil)
w.Call(context.TODO(), req, nil)
if cli.callCount != 2 {
t.Errorf("Expected the client to have been called twice")
}
})
t.Run("OptionSet", func(t *testing.T) {
val := "foo"
cli := &testClient{callRsp: &testRsp{value: val}}
cache := client.NewCache()
w := CacheClient(func() *client.Cache {
return cache
}, cli)
// perfroming two requests should increment the call count by once since the second request should
// have used the cache. The correct value should be set on both responses and no errors should
// be returned.
rsp1 := &testRsp{}
rsp2 := &testRsp{}
err1 := w.Call(context.TODO(), req, rsp1, client.WithCache(time.Minute))
err2 := w.Call(context.TODO(), req, rsp2, client.WithCache(time.Minute))
if err1 != nil {
t.Errorf("Expected nil error, got %v", err1)
}
if err2 != nil {
t.Errorf("Expected nil error, got %v", err2)
}
if rsp1.value != val {
t.Errorf("Expected %v to be assigned to the value, got %v", val, rsp1.value)
}
if rsp2.value != val {
t.Errorf("Expected %v to be assigned to the value, got %v", val, rsp2.value)
}
if cli.callCount != 1 {
t.Errorf("Expected the client to be called 1 time, was actually called %v time(s)", cli.callCount)
}
})
}