├── .gitignore ├── README.md ├── aop ├── aop.go ├── auth.go ├── cost.go ├── exception.go ├── logger.go ├── req.go └── trace.go ├── auto └── auto.go ├── balancer ├── balancer.go └── defaultBalancer.go ├── clients.go ├── common.go ├── common ├── blockingWait.go ├── define.go ├── manager.go ├── service.go └── strconv.go ├── config ├── config.go └── toml.go ├── context └── err.go ├── demo ├── auth_client.go ├── auth_server.go ├── client.go ├── exthttp_client.go ├── exthttp_server.go ├── extpb │ ├── exthttp.pb.go │ └── exthttp.proto ├── gls_log.go ├── pb │ ├── echo.pb.go │ └── echo.proto └── server.go ├── driver ├── driver.go ├── etcd.go └── etcd │ └── etcd_mgr.go ├── extends ├── .test ├── log │ ├── log.go │ └── real_log │ │ └── log.go ├── ploy │ ├── demo │ │ ├── main.go │ │ └── test │ │ │ ├── test.pb.go │ │ │ └── test.proto │ ├── fake_interface │ │ ├── fake_interface.go │ │ └── fake_interface_test.go │ ├── flow.go │ ├── server.go │ └── set_interface.go └── sql │ └── mysql.go ├── ketty.go ├── log.go ├── log ├── filelog.go ├── log.go ├── logformat.go ├── logoption.go ├── ringbuf.go ├── stdlog.go └── toggle.go ├── option └── option.go ├── protocol ├── client.go ├── grpc │ ├── client.go │ ├── grpc.go │ ├── meta.go │ ├── option.go │ └── server.go ├── http │ ├── client.go │ ├── http.go │ ├── httpTransportManager.go │ ├── http_context.go │ ├── multipart.go │ ├── option.go │ ├── proto.go │ ├── protoIf.go │ ├── querystring.go │ ├── router.go │ ├── server.go │ └── transport.go ├── marshal.go ├── protocol.go └── server.go ├── test ├── auth_test.go ├── cert.pem ├── client_bench.go ├── create_pem.sh ├── jsonhyaline │ ├── echo.pb.go │ ├── echo.proto │ └── main.go ├── ketty_test.go ├── key.pem ├── log_test.go ├── server_bench.go └── test_pb │ ├── echo.pb.go │ └── echo.proto └── url └── url.go /.gitignore: -------------------------------------------------------------------------------- 1 | test/logdir 2 | test/jsonhyaline/jsonhyaline 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ketty 2 | 3 | ### INSTALL 4 | 5 | ``` 6 | $ go get github.com/yyzybb537/{ketty,kettygen} 7 | ``` 8 | 9 | ### Generate Code 10 | 11 | ``` 12 | $ kettygen xxx.proto 13 | ``` 14 | 15 | ### Example 16 | 17 | #### Proto 18 | ``` 19 | syntax = "proto3"; 20 | 21 | package test_pb; 22 | 23 | message Req { 24 | int64 val = 1; 25 | } 26 | 27 | message Rsp { 28 | int64 val = 1; 29 | } 30 | 31 | service EchoService { 32 | rpc Echo(Req) returns(Rsp) {} 33 | } 34 | ``` 35 | 36 | #### Server 37 | ``` 38 | package main 39 | 40 | import ( 41 | echo "github.com/yyzybb537/ketty/demo/pb" 42 | "github.com/yyzybb537/ketty" 43 | "golang.org/x/net/context" 44 | ) 45 | 46 | type EchoServer struct { 47 | } 48 | 49 | func (this *EchoServer) Echo(ctx context.Context, req *echo.Req) (*echo.Rsp, error) { 50 | return &echo.Rsp{Val:req.Val}, nil 51 | } 52 | 53 | func main() { 54 | server, err := ketty.Listen("grpc://127.0.0.1:8090", "") 55 | if err != nil { 56 | ketty.GetLog().Errorf("Listen error:%s", err.Error()) 57 | } 58 | 59 | server.RegisterMethod(echo.EchoServiceHandle, &EchoServer{}) 60 | 61 | err = server.Serve() 62 | if err != nil { 63 | ketty.GetLog().Errorf("Serve error:%s", err.Error()) 64 | } 65 | 66 | q := make(chan int) 67 | <-q 68 | } 69 | ``` 70 | 71 | #### Client 72 | ``` 73 | package main 74 | 75 | import ( 76 | echo "github.com/yyzybb537/ketty/demo/pb" 77 | "github.com/yyzybb537/ketty" 78 | "golang.org/x/net/context" 79 | ) 80 | 81 | func main() { 82 | client, err := ketty.Dial("grpc://127.0.0.1:8090", "") 83 | if err != nil { 84 | ketty.GetLog().Errorf("Dial error:%s", err.Error()) 85 | return 86 | } 87 | defer client.Close() 88 | 89 | req := &echo.Req{ Val : 123 } 90 | stub := echo.NewKettyEchoServiceClient(client) 91 | rsp, err := stub.Echo(context.Background(), req) 92 | if err != nil { 93 | ketty.GetLog().Errorf("Invoke error:%+v", err) 94 | return 95 | } 96 | 97 | ketty.GetLog().Infof("Rsp: %d", rsp.Val) 98 | } 99 | ``` 100 | 101 | ### URL 102 | 103 | 104 | ``` 105 | // @sUrl: protocol://ip[:port][,ip[:port]]/path 106 | // E.g: 107 | // http://127.0.0.1:8030/path 108 | // https://127.0.0.1:8030 109 | // grpc://127.0.0.1:8030,127.0.0.1:8031 110 | // 111 | // @sBalanceUrl: driver://ip[:port][,ip[:port]]/path 112 | // etcd://127.0.0.1:2379/path 113 | ``` 114 | -------------------------------------------------------------------------------- /aop/aop.go: -------------------------------------------------------------------------------- 1 | package aop 2 | 3 | import ( 4 | "golang.org/x/net/context" 5 | ) 6 | 7 | // client 8 | type BeforeClientInvokeAop interface { 9 | BeforeClientInvoke(ctx context.Context, req interface{}) context.Context 10 | } 11 | 12 | type AfterClientInvokeAop interface { 13 | AfterClientInvoke(pCtx *context.Context, req, rsp interface{}) 14 | } 15 | 16 | type ClientInvokeCleanupAop interface { 17 | ClientCleanup(ctx context.Context) 18 | } 19 | 20 | // server 21 | type BeforeServerInvokeAop interface { 22 | BeforeServerInvoke(ctx context.Context, req interface{}) context.Context 23 | } 24 | 25 | type AfterServerInvokeAop interface { 26 | AfterServerInvoke(pCtx *context.Context, req, rsp interface{}) 27 | } 28 | 29 | type ServerInvokeCleanupAop interface { 30 | ServerCleanup(ctx context.Context) 31 | } 32 | 33 | // transport metadata 34 | type ClientTransportMetaDataAop interface { 35 | ClientSendMetaData(ctx context.Context, metadata map[string]string) context.Context 36 | } 37 | 38 | type ServerTransportMetaDataAop interface { 39 | ServerRecvMetaData(ctx context.Context, metadata map[string]string) context.Context 40 | } 41 | 42 | // list 43 | type AopListI interface { 44 | AddAop(aop ... interface{}) 45 | 46 | GetAop() []interface{} 47 | } 48 | 49 | type AopList struct { 50 | aopList []interface{} 51 | } 52 | 53 | func (this *AopList) AddAop(aop ... interface{}) { 54 | this.aopList = append(this.aopList, aop...) 55 | } 56 | 57 | func (this *AopList) GetAop() []interface{} { 58 | return this.aopList 59 | } 60 | 61 | var gAopList *AopList = new(AopList) 62 | 63 | func DefaultAop() *AopList { 64 | return gAopList 65 | } 66 | 67 | func init() { 68 | DefaultAop().AddAop(new(ExceptionAop)) 69 | DefaultAop().AddAop(new(ReqRspAop)) 70 | DefaultAop().AddAop(new(CostAop)) 71 | DefaultAop().AddAop(new(TraceAop)) 72 | DefaultAop().AddAop(new(LoggerAop)) 73 | } 74 | 75 | func GetAop(ctx context.Context) []interface{} { 76 | aopList, ok := ctx.Value("aop").([]interface{}) 77 | if !ok { 78 | return nil 79 | } 80 | 81 | return aopList 82 | } 83 | 84 | func SetAop(ctx context.Context, aopList []interface{}) context.Context { 85 | return context.WithValue(ctx, "aop", aopList) 86 | } 87 | 88 | -------------------------------------------------------------------------------- /aop/auth.go: -------------------------------------------------------------------------------- 1 | package aop 2 | 3 | import ( 4 | "golang.org/x/net/context" 5 | C "github.com/yyzybb537/ketty/context" 6 | COM "github.com/yyzybb537/ketty/common" 7 | ) 8 | 9 | type AuthorizeClient interface { 10 | CreateAuthorization(ctx context.Context) string 11 | } 12 | 13 | type AuthorizeServer interface { 14 | CheckAuthorization(ctx context.Context, authorization string) error 15 | } 16 | 17 | type AuthAop struct { 18 | c AuthorizeClient 19 | s AuthorizeServer 20 | } 21 | 22 | func NewAuthAop(c AuthorizeClient, s AuthorizeServer) *AuthAop { 23 | return &AuthAop{c, s} 24 | } 25 | 26 | func (this *AuthAop) ClientSendMetaData(ctx context.Context, metadata map[string]string) context.Context { 27 | if this.c != nil { 28 | authorization := this.c.CreateAuthorization(ctx) 29 | if authorization != "" { 30 | metadata[COM.AuthorizationMetaKey] = authorization 31 | } 32 | } 33 | return ctx 34 | } 35 | 36 | func (this *AuthAop) ServerRecvMetaData(ctx context.Context, metadata map[string]string) context.Context { 37 | authorization, _ := metadata[COM.AuthorizationMetaKey] 38 | if this.s != nil { 39 | err := this.s.CheckAuthorization(ctx, authorization) 40 | if err != nil { 41 | return C.WithError(ctx, err) 42 | } 43 | } 44 | return ctx 45 | } 46 | 47 | -------------------------------------------------------------------------------- /aop/cost.go: -------------------------------------------------------------------------------- 1 | package aop 2 | 3 | import ( 4 | "golang.org/x/net/context" 5 | "time" 6 | ) 7 | 8 | type CostAop struct { 9 | } 10 | 11 | // client 12 | func (this *CostAop) BeforeClientInvoke(ctx context.Context, req interface{}) context.Context { 13 | return context.WithValue(ctx, "begin", time.Now()) 14 | } 15 | 16 | func (this *CostAop) AfterClientInvoke(pCtx *context.Context, req, rsp interface{}) { 17 | ctx := *pCtx 18 | begin := ctx.Value("begin").(time.Time) 19 | cost := time.Since(begin) 20 | *pCtx = context.WithValue(ctx, "cost", cost) 21 | } 22 | 23 | // server 24 | func (this *CostAop) BeforeServerInvoke(ctx context.Context, req interface{}) context.Context { 25 | return context.WithValue(ctx, "begin", time.Now()) 26 | } 27 | 28 | func (this *CostAop) AfterServerInvoke(pCtx *context.Context, req, rsp interface{}) { 29 | ctx := *pCtx 30 | begin := ctx.Value("begin").(time.Time) 31 | cost := time.Since(begin) 32 | *pCtx = context.WithValue(ctx, "cost", cost) 33 | } 34 | 35 | func getCost(ctx context.Context) time.Duration { 36 | dur, ok := ctx.Value("cost").(time.Duration) 37 | if !ok { 38 | return time.Duration(0) 39 | } 40 | return dur 41 | } 42 | 43 | func getCostString(ctx context.Context) string { 44 | return getCost(ctx).String() 45 | } 46 | -------------------------------------------------------------------------------- /aop/exception.go: -------------------------------------------------------------------------------- 1 | package aop 2 | 3 | import ( 4 | "golang.org/x/net/context" 5 | "github.com/pkg/errors" 6 | C "github.com/yyzybb537/ketty/context" 7 | "github.com/yyzybb537/ketty/log" 8 | "runtime/debug" 9 | ) 10 | 11 | type ExceptionAop struct { 12 | } 13 | 14 | func (this *ExceptionAop) AfterServerInvoke(pCtx *context.Context, req, rsp interface{}) { 15 | iErr := recover() 16 | if iErr == nil { 17 | return 18 | } 19 | 20 | log.GetLog().Errorf("Ketty.Exception: %+v\nStack: %s", iErr, string(debug.Stack())) 21 | 22 | err, ok := iErr.(error) 23 | if !ok { 24 | err = errors.Errorf("%v", iErr) 25 | } 26 | 27 | err = errors.WithStack(err) 28 | *pCtx = C.WithError(*pCtx, err) 29 | } 30 | 31 | -------------------------------------------------------------------------------- /aop/logger.go: -------------------------------------------------------------------------------- 1 | package aop 2 | 3 | import ( 4 | "golang.org/x/net/context" 5 | "github.com/yyzybb537/ketty/log" 6 | ) 7 | 8 | type LoggerAop struct { 9 | } 10 | 11 | // client 12 | func (this *LoggerAop) BeforeClientInvoke(ctx context.Context, req interface{}) context.Context { 13 | method := ctx.Value("method") 14 | remote := ctx.Value("remote") 15 | log.GetLog().Debugf("C-To %s Invoke (%s) TraceID:%s Req %s", remote, method, GetTraceID(), log.LogFormat(req)) 16 | return ctx 17 | } 18 | 19 | func (this *LoggerAop) AfterClientInvoke(pCtx *context.Context, req, rsp interface{}) { 20 | ctx := *pCtx 21 | method := ctx.Value("method") 22 | remote := ctx.Value("remote") 23 | err := ctx.Err() 24 | if err != nil { 25 | log.GetLog().Errorf("C-From %s Reply (%s) TraceID:%s Cost:%s Error:%s", remote, method, GetTraceID(), getCostString(ctx), err.Error()) 26 | } else { 27 | log.GetLog().Debugf("C-From %s Reply (%s) TraceID:%s Rsp:%s Cost:%s", remote, method, GetTraceID(), log.LogFormat(rsp), getCostString(ctx)) 28 | } 29 | } 30 | 31 | // server 32 | func (this *LoggerAop) BeforeServerInvoke(ctx context.Context, req interface{}) context.Context { 33 | method := ctx.Value("method") 34 | remote := ctx.Value("remote") 35 | log.GetLog().Debugf("From %s Invoke (%s) TraceID:%s Req:%s", remote, method, GetTraceID(), log.LogFormat(req)) 36 | return ctx 37 | } 38 | 39 | func (this *LoggerAop) AfterServerInvoke(pCtx *context.Context, req, rsp interface{}) { 40 | //println("logger.AfterServerInvoke", rsp) 41 | ctx := *pCtx 42 | method := ctx.Value("method") 43 | remote := ctx.Value("remote") 44 | err := ctx.Err() 45 | if err != nil { 46 | log.GetLog().Errorf("To %s Reply (%s) TraceID:%s Error:%s", remote, method, GetTraceID(), err.Error()) 47 | } else { 48 | log.GetLog().Debugf("To %s Reply (%s) TraceID:%s Rsp:%s Cost:%s", remote, method, GetTraceID(), log.LogFormat(rsp), getCostString(ctx)) 49 | } 50 | } 51 | 52 | -------------------------------------------------------------------------------- /aop/req.go: -------------------------------------------------------------------------------- 1 | package aop 2 | 3 | import ( 4 | "golang.org/x/net/context" 5 | ) 6 | 7 | type ReqRspAop struct { 8 | } 9 | 10 | type requestKey struct {} 11 | type responseKey struct {} 12 | 13 | // client 14 | func (this *ReqRspAop) BeforeClientInvoke(ctx context.Context, req interface{}) context.Context { 15 | return context.WithValue(ctx, requestKey{}, req) 16 | } 17 | 18 | func (this *ReqRspAop) AfterClientInvoke(pCtx *context.Context, req, rsp interface{}) { 19 | *pCtx = context.WithValue(*pCtx, responseKey{}, rsp) 20 | } 21 | 22 | // server 23 | func (this *ReqRspAop) BeforeServerInvoke(ctx context.Context, req interface{}) context.Context { 24 | return context.WithValue(ctx, requestKey{}, req) 25 | } 26 | 27 | func (this *ReqRspAop) AfterServerInvoke(pCtx *context.Context, req, rsp interface{}) { 28 | *pCtx = context.WithValue(*pCtx, responseKey{}, rsp) 29 | } 30 | 31 | func GetRequest(ctx context.Context) interface{} { 32 | return ctx.Value(requestKey{}) 33 | } 34 | 35 | func GetResponse(ctx context.Context) interface{} { 36 | return ctx.Value(responseKey{}) 37 | } 38 | 39 | -------------------------------------------------------------------------------- /aop/trace.go: -------------------------------------------------------------------------------- 1 | package aop 2 | 3 | import ( 4 | "golang.org/x/net/context" 5 | uuid "github.com/satori/go.uuid" 6 | "github.com/yyzybb537/gls" 7 | "github.com/yyzybb537/ketty/log" 8 | ) 9 | 10 | type TraceAop struct { 11 | } 12 | 13 | type createdTraceIdKey struct {} 14 | 15 | // client 16 | func (this *TraceAop) BeforeClientInvoke(ctx context.Context, req interface{}) context.Context { 17 | traceId, created := genTraceID() 18 | if created { 19 | ctx = context.WithValue(ctx, createdTraceIdKey{}, traceId) 20 | } 21 | return ctx 22 | } 23 | 24 | // server 25 | func (this *TraceAop) BeforeServerInvoke(ctx context.Context, req interface{}) context.Context { 26 | traceId, created := genTraceID() 27 | if created { 28 | ctx = context.WithValue(ctx, createdTraceIdKey{}, traceId) 29 | } 30 | return ctx 31 | } 32 | 33 | func (this *TraceAop) ClientCleanup(ctx context.Context) { 34 | cleanupTraceID(ctx) 35 | } 36 | 37 | func (this *TraceAop) ServerCleanup(ctx context.Context) { 38 | cleanupTraceID(ctx) 39 | } 40 | 41 | func (this *TraceAop) ClientSendMetaData(ctx context.Context, metadata map[string]string) context.Context { 42 | traceId, created := genTraceID() 43 | if created { 44 | ctx = context.WithValue(ctx, createdTraceIdKey{}, traceId) 45 | } 46 | metadata["traceid"] = traceId 47 | return ctx 48 | } 49 | 50 | func (this *TraceAop) ServerRecvMetaData(ctx context.Context, metadata map[string]string) context.Context { 51 | traceId, exists := metadata["traceid"] 52 | if exists { 53 | // log.GetLog().Debugf("set trace id:%s", traceId) 54 | gls.Set(traceIdKey{}, traceId) 55 | ctx = context.WithValue(ctx, createdTraceIdKey{}, traceId) 56 | } 57 | return ctx 58 | } 59 | 60 | type traceIdKey struct {} 61 | 62 | func genTraceID() (string, bool) { 63 | traceId, ok := gls.Get(traceIdKey{}).(string) 64 | if !ok || traceId == "" { 65 | uuid, _ := uuid.NewV4() 66 | traceId = uuid.String() 67 | gls.Set(traceIdKey{}, traceId) 68 | log.GetLog().Debugf("create trace id:%s", traceId) 69 | return traceId, true 70 | } 71 | return traceId, false 72 | } 73 | 74 | func GetTraceID() string { 75 | s, ok := gls.Get(traceIdKey{}).(string) 76 | if !ok { 77 | return "" 78 | } 79 | return s 80 | } 81 | 82 | func cleanupTraceID(ctx context.Context) { 83 | ctxTraceId, exists := ctx.Value(createdTraceIdKey{}).(string) 84 | if exists { 85 | traceId, exists := gls.Get(traceIdKey{}).(string) 86 | if exists && ctxTraceId == traceId { 87 | gls.Del(traceIdKey{}) 88 | } 89 | } 90 | } 91 | 92 | -------------------------------------------------------------------------------- /auto/auto.go: -------------------------------------------------------------------------------- 1 | package auto 2 | 3 | import ( 4 | "flag" 5 | ) 6 | 7 | var isAuto bool 8 | 9 | func init() { 10 | flag.BoolVar(&isAuto, "auto", false, "Set ketty.auto.IsAuto flag") 11 | flag.Parse() 12 | } 13 | 14 | func IsAuto() bool { 15 | return isAuto 16 | } 17 | -------------------------------------------------------------------------------- /balancer/balancer.go: -------------------------------------------------------------------------------- 1 | package ketty 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | "golang.org/x/net/context" 7 | U "github.com/yyzybb537/ketty/url" 8 | ) 9 | 10 | // @looks like grpc.Balancer 11 | type Balancer interface { 12 | Filte(in []U.Url) (out []U.Url) 13 | 14 | Up(addr U.Url) (down func()) 15 | 16 | Get(ctx context.Context) (addr U.Url, put func(), err error) 17 | 18 | Clone() Balancer 19 | } 20 | 21 | var balancers = make(map[string]Balancer) 22 | 23 | func GetBalancer(sBalancer string) (Balancer, error) { 24 | balancer, exists := balancers[strings.ToLower(sBalancer)] 25 | if !exists { 26 | return nil, fmt.Errorf("Unkown balancer:%s", sBalancer) 27 | } 28 | return balancer.Clone(), nil 29 | } 30 | 31 | func RegBalancer(sBalancer string, balancer Balancer) { 32 | balancers[strings.ToLower(sBalancer)] = balancer 33 | } 34 | -------------------------------------------------------------------------------- /balancer/defaultBalancer.go: -------------------------------------------------------------------------------- 1 | package ketty 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "sync/atomic" 7 | "golang.org/x/net/context" 8 | U "github.com/yyzybb537/ketty/url" 9 | ) 10 | 11 | type RobinBalancer struct { 12 | addrs []U.Url 13 | chooseIndex uint32 14 | mtx sync.RWMutex 15 | } 16 | 17 | func init() { 18 | RegBalancer("", new(RobinBalancer)) 19 | RegBalancer("default", new(RobinBalancer)) 20 | } 21 | 22 | func (this *RobinBalancer) Filte(in []U.Url) (out []U.Url) { 23 | out = in 24 | return 25 | } 26 | 27 | func (this *RobinBalancer) Up(addr U.Url) (down func()) { 28 | this.mtx.Lock() 29 | defer this.mtx.Unlock() 30 | this.addrs = append(this.addrs, addr) 31 | return func() { 32 | this.mtx.Lock() 33 | defer this.mtx.Unlock() 34 | for i, v := range this.addrs { 35 | if v == addr { 36 | copy(this.addrs[i:], this.addrs[i+1:]) 37 | this.addrs = this.addrs[:len(this.addrs)-1] 38 | return 39 | } 40 | } 41 | } 42 | } 43 | 44 | func (this *RobinBalancer) Get(ctx context.Context) (addr U.Url, put func(), err error) { 45 | this.mtx.RLock() 46 | defer this.mtx.RUnlock() 47 | if len(this.addrs) == 0 { 48 | return U.Url{}, nil, fmt.Errorf("No estab connection") 49 | } 50 | 51 | index := atomic.AddUint32(&this.chooseIndex, 1) 52 | index = index % uint32(len(this.addrs)) 53 | return this.addrs[index], nil, nil 54 | } 55 | 56 | func (this *RobinBalancer) Clone() Balancer { 57 | return new(RobinBalancer) 58 | } 59 | 60 | -------------------------------------------------------------------------------- /clients.go: -------------------------------------------------------------------------------- 1 | package ketty 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "time" 7 | "golang.org/x/net/context" 8 | "github.com/yyzybb537/ketty/common" 9 | U "github.com/yyzybb537/ketty/url" 10 | P "github.com/yyzybb537/ketty/protocol" 11 | O "github.com/yyzybb537/ketty/option" 12 | B "github.com/yyzybb537/ketty/balancer" 13 | D "github.com/yyzybb537/ketty/driver" 14 | A "github.com/yyzybb537/ketty/aop" 15 | COM "github.com/yyzybb537/ketty/common" 16 | "github.com/yyzybb537/gls" 17 | ) 18 | 19 | // Implement Client interface, and manage multiply clients. 20 | type Clients struct { 21 | A.AopList 22 | 23 | url U.Url 24 | balancer B.Balancer 25 | opt O.OptionI 26 | 27 | // manage all of address 28 | addrMtx sync.Mutex 29 | addrs map[string]U.Url 30 | 31 | // retry 32 | q chan interface{} 33 | 34 | // close 35 | closeMtx sync.Mutex 36 | onClose []func() 37 | closed bool 38 | 39 | // blocking wait queue 40 | blockingWait *common.BlockingWait 41 | 42 | // root reference 43 | root *Clients 44 | } 45 | 46 | func newClients(url U.Url, balancer B.Balancer, root *Clients) *Clients { 47 | url.MetaData = nil 48 | c := &Clients{ 49 | url : url, 50 | balancer : balancer, 51 | addrs : map[string]U.Url{}, 52 | q : make(chan interface{}), 53 | closed : false, 54 | blockingWait : common.NewBlockingWait(), 55 | root : root, 56 | } 57 | if c.root == nil { 58 | c.root = c 59 | } 60 | c.onClose = append(c.onClose, func(){ close(c.q) }) 61 | return c 62 | } 63 | 64 | func (this *Clients) dial() error { 65 | //GetLog().Debugf("dial(%s)", this.url.ToString()) 66 | proto, err := P.GetProtocol(this.url.GetMainProtocol()) 67 | if err == nil { 68 | return this.dialProtocol(proto) 69 | } 70 | 71 | driver, err := D.GetDriver(this.url.Protocol) 72 | if err == nil { 73 | return this.dialDriver(driver) 74 | } 75 | 76 | return fmt.Errorf("Error url, unkown protocol. url:%s", this.url.ToString()) 77 | } 78 | 79 | func (this *Clients) dialProtocol(proto P.Protocol) error { 80 | client, err := proto.Dial(this.url) 81 | if err != nil { 82 | return err 83 | } 84 | if this.opt != nil { 85 | client.SetOption(this.opt) 86 | } 87 | 88 | this.url.MetaData = client 89 | this.balancer.Up(this.url) 90 | return nil 91 | } 92 | 93 | func (this *Clients) dialDriver(driver D.Driver) error { 94 | upC, downC, stop, err := driver.Watch(this.url) 95 | if err != nil { 96 | return err 97 | } 98 | this.onClose = append(this.onClose, stop) 99 | 100 | gls.Go(func() { 101 | for { 102 | up := []U.Url{} 103 | down := []U.Url{} 104 | readOk := true 105 | select { 106 | case up, readOk = <-upC: 107 | break 108 | case down, readOk = <-downC: 109 | break 110 | } 111 | if !readOk { 112 | break 113 | } 114 | 115 | // down 116 | for _, url := range down { 117 | key := url.ToString() 118 | this.addrMtx.Lock() 119 | var exists bool 120 | if url, exists = this.addrs[key]; !exists { 121 | this.addrMtx.Unlock() 122 | continue 123 | } 124 | 125 | // 清除 126 | delete(this.addrs, key) 127 | this.addrMtx.Unlock() 128 | 129 | // Close 130 | url.MetaData.(P.Client).Close() 131 | } 132 | 133 | // up 134 | up = this.balancer.Filte(up) 135 | for _, url := range up { 136 | key := url.ToString() 137 | this.addrMtx.Lock() 138 | var exists bool 139 | if _, exists = this.addrs[key]; exists { 140 | this.addrMtx.Unlock() 141 | continue 142 | } 143 | client := newClients(url, this.balancer, this.root) 144 | if this.opt != nil { 145 | client.SetOption(this.opt) 146 | } 147 | url.MetaData = client 148 | this.addrs[key] = url 149 | this.addrMtx.Unlock() 150 | 151 | // connect 152 | err = client.dial() 153 | if err == nil { 154 | down := this.balancer.Up(url) 155 | client.onClose = append(client.onClose, down) 156 | this.blockingWait.Notify() 157 | continue 158 | } 159 | 160 | // 连接失败, 转入后台重试 161 | gls.Go(func() { 162 | err := client.retryDial() 163 | if err != nil { 164 | down := this.balancer.Up(url) 165 | client.onClose = append(client.onClose, down) 166 | this.blockingWait.Notify() 167 | } 168 | }) 169 | } 170 | } 171 | }) 172 | 173 | return nil 174 | } 175 | 176 | func (this *Clients) retryDial() error { 177 | for { 178 | select { 179 | case <-this.q: 180 | return fmt.Errorf("Stop retry dial") 181 | case <-time.After(time.Second * 3): 182 | } 183 | 184 | err := this.dial() 185 | if err != nil { 186 | continue 187 | } 188 | 189 | this.closeMtx.Lock() 190 | if this.closed { 191 | this.closeMtx.Unlock() 192 | this.Close() 193 | return fmt.Errorf("Client is closed") 194 | } 195 | this.closeMtx.Unlock() 196 | return nil 197 | } 198 | } 199 | 200 | func (this *Clients) Close() { 201 | this.closeMtx.Lock() 202 | onClose := this.onClose 203 | this.onClose = nil 204 | this.closed = true 205 | this.closeMtx.Unlock() 206 | for _, f := range onClose { 207 | f() 208 | } 209 | } 210 | 211 | func (this *Clients) SetOption(opt O.OptionI) error { 212 | this.opt = opt 213 | this.addrMtx.Lock() 214 | defer this.addrMtx.Unlock() 215 | for _, url := range this.addrs { 216 | url.MetaData.(P.Client).SetOption(this.opt) 217 | } 218 | if c, ok := this.url.MetaData.(P.Client);ok { 219 | c.SetOption(this.opt) 220 | } 221 | return nil 222 | } 223 | 224 | func (this *Clients) Invoke(ctx context.Context, handle COM.ServiceHandle, method string, req, rsp interface{}) error { 225 | url, put, err := this.balancer.Get(ctx) 226 | if err != nil { 227 | if this.opt != nil { 228 | this.blockingWait.TimedWait(time.Millisecond * time.Duration(this.opt.(*O.Option).TimeoutMilliseconds)) 229 | } else { 230 | this.blockingWait.Wait() 231 | } 232 | url, put, err = this.balancer.Get(ctx) 233 | if err != nil { 234 | return err 235 | } 236 | } 237 | if put != nil { 238 | defer put() 239 | } 240 | client := url.MetaData.(P.Client) 241 | return client.Invoke(A.SetAop(ctx, this.root.GetAop()), handle, method, req, rsp) 242 | } 243 | 244 | -------------------------------------------------------------------------------- /common.go: -------------------------------------------------------------------------------- 1 | package ketty 2 | 3 | import ( 4 | "time" 5 | "fmt" 6 | "golang.org/x/net/context" 7 | C "github.com/yyzybb537/ketty/context" 8 | COM "github.com/yyzybb537/ketty/common" 9 | A "github.com/yyzybb537/ketty/aop" 10 | ) 11 | 12 | type ServiceHandle COM.ServiceHandle 13 | 14 | func Assert(err error) { 15 | if err != nil { 16 | panic(fmt.Sprintf("%s\n ---- \n%+v\n ---- \n", err.Error(), err)) 17 | } 18 | } 19 | 20 | func Hung() { 21 | time.Sleep(time.Second) 22 | GetLog().Infof("ketty service startup") 23 | for { 24 | time.Sleep(time.Second * 3600) 25 | } 26 | } 27 | 28 | func WithError(ctx context.Context, err error) context.Context { 29 | return C.WithError(ctx, err) 30 | } 31 | 32 | func GetTraceID() string { 33 | return A.GetTraceID() 34 | } 35 | -------------------------------------------------------------------------------- /common/blockingWait.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "sync" 5 | "time" 6 | "github.com/pkg/errors" 7 | ) 8 | 9 | type BlockingWait struct { 10 | mtx sync.Mutex 11 | queue chan interface{} 12 | count int64 13 | waitIdx int64 14 | wakeup bool 15 | } 16 | 17 | func NewBlockingWait() *BlockingWait { 18 | return &BlockingWait{ 19 | queue : make(chan interface{}, 1024), 20 | } 21 | } 22 | 23 | func (this *BlockingWait) Wait() { 24 | this.mtx.Lock() 25 | if this.wakeup { 26 | this.mtx.Unlock() 27 | return 28 | } 29 | 30 | this.count++ 31 | this.mtx.Unlock() 32 | <-this.queue 33 | } 34 | 35 | func (this *BlockingWait) TimedWait(dur time.Duration) error { 36 | if dur == 0 { 37 | this.Wait() 38 | return nil 39 | } 40 | 41 | this.mtx.Lock() 42 | if this.wakeup { 43 | this.mtx.Unlock() 44 | return nil 45 | } 46 | 47 | idx := this.waitIdx 48 | this.count++ 49 | this.mtx.Unlock() 50 | select { 51 | case <-this.queue: 52 | return nil 53 | case <-time.NewTimer(dur).C: 54 | this.mtx.Lock() 55 | defer this.mtx.Unlock() 56 | if idx == this.waitIdx && this.count > 0 { 57 | this.count-- 58 | } 59 | return errors.Errorf("wait timeout") 60 | } 61 | } 62 | 63 | func (this *BlockingWait) Notify() { 64 | if this.wakeup { 65 | return 66 | } 67 | 68 | this.mtx.Lock() 69 | defer this.mtx.Unlock() 70 | if this.wakeup { 71 | return 72 | } 73 | 74 | this.wakeup = true 75 | this.waitIdx++ 76 | var i int64 77 | for ; i < this.count; i++ { 78 | this.queue <- nil 79 | } 80 | this.count = 0 81 | } 82 | 83 | -------------------------------------------------------------------------------- /common/define.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | var AuthorizationMetaKey string = "ketty_authorization" 4 | -------------------------------------------------------------------------------- /common/manager.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | ) 7 | 8 | type Manager struct { 9 | m map[string]interface{} 10 | handlerType interface{} 11 | } 12 | 13 | func NewManager(handlerType interface{}) *Manager { 14 | return &Manager { 15 | m : make(map[string]interface{}), 16 | handlerType : handlerType, 17 | } 18 | } 19 | 20 | func (this *Manager) Register(name string, obj interface{}) { 21 | ht := reflect.TypeOf(this.handlerType).Elem() 22 | st := reflect.TypeOf(obj) 23 | if !st.Implements(ht) { 24 | panic(fmt.Errorf("register %s. obj is not instance %s", name, ht.Name())) 25 | } 26 | this.register(name, obj) 27 | } 28 | 29 | func (this *Manager) register(name string, obj interface{}) { 30 | this.m[name] = obj 31 | } 32 | 33 | func (this *Manager) Get(name string) interface{} { 34 | obj, _ := this.m[name] 35 | return obj 36 | } 37 | -------------------------------------------------------------------------------- /common/service.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | type ServiceHandle interface { 4 | Implement() interface{} 5 | 6 | ServiceName() string 7 | } 8 | -------------------------------------------------------------------------------- /common/strconv.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "reflect" 5 | "fmt" 6 | "strconv" 7 | "github.com/pkg/errors" 8 | ) 9 | 10 | func V2String(v reflect.Value) (s string, err error) { 11 | switch v.Kind() { 12 | case reflect.Bool: 13 | s = strconv.FormatBool(v.Bool()) 14 | 15 | case reflect.Int: 16 | fallthrough 17 | case reflect.Int8: 18 | fallthrough 19 | case reflect.Int16: 20 | fallthrough 21 | case reflect.Int32: 22 | fallthrough 23 | case reflect.Int64: 24 | s = strconv.FormatInt(v.Int(), 10) 25 | 26 | case reflect.Uint: 27 | fallthrough 28 | case reflect.Uint8: 29 | fallthrough 30 | case reflect.Uint16: 31 | fallthrough 32 | case reflect.Uint32: 33 | fallthrough 34 | case reflect.Uint64: 35 | s = strconv.FormatUint(v.Uint(), 10) 36 | 37 | case reflect.Float32: 38 | fallthrough 39 | case reflect.Float64: 40 | s = fmt.Sprintf("%v", v.Float()) 41 | 42 | case reflect.String: 43 | s = v.String() 44 | 45 | case reflect.Uintptr: 46 | fallthrough 47 | case reflect.Complex64: 48 | fallthrough 49 | case reflect.Complex128: 50 | fallthrough 51 | case reflect.Array: 52 | fallthrough 53 | case reflect.Chan: 54 | fallthrough 55 | case reflect.Func: 56 | fallthrough 57 | case reflect.Interface: 58 | fallthrough 59 | case reflect.Map: 60 | fallthrough 61 | case reflect.Ptr: 62 | fallthrough 63 | case reflect.Slice: 64 | fallthrough 65 | case reflect.Struct: 66 | fallthrough 67 | case reflect.UnsafePointer: 68 | err = errors.Errorf("V2String unsupport kind: %s", v.Kind().String()) 69 | } 70 | return 71 | } 72 | 73 | func String2V(s string, v reflect.Value) (err error) { 74 | if v.Kind() == reflect.Ptr { 75 | if v.IsNil() { 76 | v.Set(reflect.New(v.Elem().Type())) 77 | } 78 | v = v.Elem() 79 | } 80 | 81 | switch v.Kind() { 82 | case reflect.Bool: 83 | var b bool 84 | b, err = strconv.ParseBool(s) 85 | if err == nil { 86 | v.SetBool(b) 87 | } 88 | 89 | case reflect.Int: 90 | fallthrough 91 | case reflect.Int8: 92 | fallthrough 93 | case reflect.Int16: 94 | fallthrough 95 | case reflect.Int32: 96 | fallthrough 97 | case reflect.Int64: 98 | var i64 int64 99 | i64, err = strconv.ParseInt(s, 10, 64) 100 | if err == nil { 101 | v.SetInt(i64) 102 | } 103 | 104 | case reflect.Uint: 105 | fallthrough 106 | case reflect.Uint8: 107 | fallthrough 108 | case reflect.Uint16: 109 | fallthrough 110 | case reflect.Uint32: 111 | fallthrough 112 | case reflect.Uint64: 113 | var u64 uint64 114 | u64, err = strconv.ParseUint(s, 10, 64) 115 | if err == nil { 116 | v.SetUint(u64) 117 | } 118 | 119 | case reflect.Float32: 120 | fallthrough 121 | case reflect.Float64: 122 | var f64 float64 123 | f64, err = strconv.ParseFloat(s, 64) 124 | if err == nil { 125 | v.SetFloat(f64) 126 | } 127 | 128 | case reflect.String: 129 | v.SetString(s) 130 | 131 | case reflect.Uintptr: 132 | fallthrough 133 | case reflect.Complex64: 134 | fallthrough 135 | case reflect.Complex128: 136 | fallthrough 137 | case reflect.Array: 138 | fallthrough 139 | case reflect.Chan: 140 | fallthrough 141 | case reflect.Func: 142 | fallthrough 143 | case reflect.Interface: 144 | fallthrough 145 | case reflect.Map: 146 | fallthrough 147 | case reflect.Ptr: 148 | fallthrough 149 | case reflect.Slice: 150 | fallthrough 151 | case reflect.Struct: 152 | fallthrough 153 | case reflect.UnsafePointer: 154 | err = errors.Errorf("Unsupport kind: %s", v.Kind().String()) 155 | } 156 | return 157 | } 158 | 159 | -------------------------------------------------------------------------------- /config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "strings" 5 | "fmt" 6 | ) 7 | 8 | func Read(cfg interface{}, file string) error { 9 | ext := getFileExtend(file) 10 | config, err := GetConfig(ext) 11 | if err != nil { 12 | return err 13 | } 14 | 15 | return config.Read(cfg, file) 16 | } 17 | 18 | func Write(cfg interface{}, file string) error { 19 | ext := getFileExtend(file) 20 | config, err := GetConfig(ext) 21 | if err != nil { 22 | return err 23 | } 24 | 25 | return config.Write(cfg, file) 26 | } 27 | 28 | // ------------------------- internal 29 | type Config interface { 30 | Read(cfg interface{}, file string) error 31 | 32 | Write(cfg interface{}, file string) error 33 | } 34 | 35 | var configs = make(map[string]Config) 36 | 37 | func GetConfig(sConfig string) (Config, error) { 38 | config, exists := configs[strings.ToLower(sConfig)] 39 | if !exists { 40 | return nil, fmt.Errorf("Unkown config file type:%s", sConfig) 41 | } 42 | return config, nil 43 | } 44 | 45 | func RegConfig(sConfig string, config Config) { 46 | configs[strings.ToLower(sConfig)] = config 47 | } 48 | 49 | func getFileExtend(file string) string { 50 | ss := strings.Split(file, ".") 51 | return ss[len(ss)-1] 52 | } 53 | -------------------------------------------------------------------------------- /config/toml.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "github.com/BurntSushi/toml" 5 | "os" 6 | "reflect" 7 | "fmt" 8 | "path/filepath" 9 | ) 10 | 11 | type TomlConfig struct {} 12 | 13 | func init() { 14 | RegConfig("toml", new(TomlConfig)) 15 | } 16 | 17 | func (this *TomlConfig) Read(cfg interface{}, file string) (err error) { 18 | err = this.check(cfg) 19 | if err != nil { 20 | return 21 | } 22 | 23 | _, err = toml.DecodeFile(file, cfg) 24 | return 25 | } 26 | 27 | func (this *TomlConfig) Write(cfg interface{}, file string) (err error) { 28 | err = this.check(cfg) 29 | if err != nil { 30 | return 31 | } 32 | 33 | err = os.MkdirAll(filepath.Dir(file), 0775) 34 | if os.IsExist(err) { 35 | err = nil 36 | } 37 | if err != nil { 38 | return 39 | } 40 | 41 | f, err := os.Create(file) 42 | if err != nil { 43 | return 44 | } 45 | defer f.Close() 46 | err = toml.NewEncoder(f).Encode(cfg) 47 | return 48 | } 49 | 50 | func (this *TomlConfig) check(cfg interface{}) (err error) { 51 | v := reflect.ValueOf(cfg) 52 | if v.Kind() != reflect.Ptr { 53 | err = fmt.Errorf("ConfigInit parameter must be a pointer. cfg.Name=%s cfg.Kind=%s", v.Type().Name(), v.Kind().String()) 54 | return 55 | } 56 | 57 | v = v.Elem() 58 | if v.Kind() != reflect.Struct { 59 | err = fmt.Errorf("ConfigInit parameter must be a pointer to struct. cfg.Name=%s cfg.Kind=%s", v.Type().Name(), v.Kind().String()) 60 | return 61 | } 62 | 63 | return 64 | } 65 | -------------------------------------------------------------------------------- /context/err.go: -------------------------------------------------------------------------------- 1 | package context 2 | 3 | import ( 4 | "golang.org/x/net/context" 5 | ) 6 | 7 | type errorCtx struct { 8 | context.Context 9 | err error 10 | } 11 | 12 | func WithError(ctx context.Context, err error) context.Context { 13 | return &errorCtx{ ctx, err } 14 | } 15 | 16 | func (this *errorCtx) Err() error { 17 | return this.err 18 | } 19 | -------------------------------------------------------------------------------- /demo/auth_client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | echo "github.com/yyzybb537/ketty/demo/pb" 5 | "github.com/yyzybb537/ketty" 6 | "github.com/yyzybb537/ketty/aop" 7 | "golang.org/x/net/context" 8 | ) 9 | 10 | type Auth struct {} 11 | func (this *Auth) CreateAuthorization(ctx context.Context) string { 12 | return "MyAuthInfo" 13 | } 14 | 15 | func main() { 16 | client, err := ketty.Dial("grpc://127.0.0.1:8090", "") 17 | if err != nil { 18 | ketty.GetLog().Errorf("Dial error:%s", err.Error()) 19 | return 20 | } 21 | defer client.Close() 22 | 23 | client.AddAop(aop.NewAuthAop(new(Auth), nil)) 24 | 25 | req := &echo.Req{ Val : 123 } 26 | stub := echo.NewKettyEchoServiceClient(client) 27 | rsp, err := stub.Echo(context.Background(), req) 28 | if err != nil { 29 | ketty.GetLog().Errorf("Invoke error:%+v", err) 30 | return 31 | } 32 | 33 | ketty.GetLog().Infof("Rsp: %d", rsp.Val) 34 | } 35 | 36 | -------------------------------------------------------------------------------- /demo/auth_server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | echo "github.com/yyzybb537/ketty/demo/pb" 5 | "github.com/yyzybb537/ketty" 6 | "github.com/yyzybb537/ketty/aop" 7 | "golang.org/x/net/context" 8 | "fmt" 9 | ) 10 | 11 | type EchoServer struct {} 12 | func (this *EchoServer) Echo(ctx context.Context, req *echo.Req) (*echo.Rsp, error) { 13 | return &echo.Rsp{Val:req.Val}, nil 14 | } 15 | 16 | type Auth struct {} 17 | func (this *Auth) CheckAuthorization(ctx context.Context, authorization string) error { 18 | if authorization != "MyAuthInfo" { 19 | return fmt.Errorf("AuthError") 20 | } 21 | return nil 22 | } 23 | 24 | func main() { 25 | server, err := ketty.Listen("grpc://127.0.0.1:8090", "") 26 | if err != nil { 27 | ketty.GetLog().Errorf("Listen error:%s", err.Error()) 28 | } 29 | 30 | server.AddAop(aop.NewAuthAop(nil, new(Auth))) 31 | server.RegisterMethod(echo.EchoServiceHandle, &EchoServer{}) 32 | 33 | err = server.Serve() 34 | if err != nil { 35 | ketty.GetLog().Errorf("Serve error:%s", err.Error()) 36 | } 37 | 38 | q := make(chan int) 39 | <-q 40 | } 41 | 42 | -------------------------------------------------------------------------------- /demo/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | echo "github.com/yyzybb537/ketty/demo/pb" 5 | "github.com/yyzybb537/ketty" 6 | grpc "github.com/yyzybb537/ketty/protocol/grpc" 7 | "golang.org/x/net/context" 8 | ) 9 | 10 | func main() { 11 | client, err := ketty.Dial("grpc://127.0.0.1:8090", "") 12 | if err != nil { 13 | ketty.GetLog().Errorf("Dial error:%s", err.Error()) 14 | return 15 | } 16 | 17 | defer client.Close() 18 | 19 | opt := &grpc.GrpcOption{} 20 | opt.Option.TimeoutMilliseconds = 1000 21 | 22 | err = client.SetOption(opt) 23 | ketty.Assert(err) 24 | 25 | req := &echo.Req{ Val : 123 } 26 | stub := echo.NewKettyEchoServiceClient(client) 27 | rsp, err := stub.Echo(context.Background(), req) 28 | if err != nil { 29 | ketty.GetLog().Errorf("Invoke error:%+v", err) 30 | return 31 | } 32 | 33 | ketty.GetLog().Infof("Rsp: %d", rsp.Val) 34 | } 35 | 36 | -------------------------------------------------------------------------------- /demo/exthttp_client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | ext_pb "github.com/yyzybb537/ketty/demo/extpb" 5 | "github.com/yyzybb537/ketty" 6 | http "github.com/yyzybb537/ketty/protocol/http" 7 | "golang.org/x/net/context" 8 | ) 9 | 10 | func main() { 11 | client, err := ketty.Dial("http.json://127.0.0.1:8091", "") 12 | if err != nil { 13 | ketty.GetLog().Errorf("Dial error:%s", err.Error()) 14 | return 15 | } 16 | defer client.Close() 17 | 18 | opt := &http.HttpOption{} 19 | //opt.ConnectTimeoutMillseconds = 100 20 | opt.TimeoutMilliseconds = 1000 21 | //opt.ResponseHeaderTimeoutMillseconds = 100 22 | 23 | err = client.SetOption(opt) 24 | ketty.Assert(err) 25 | 26 | req := &ext_pb.Req{ 27 | Qr : &ext_pb.QueryReq{ QVal : 123 }, 28 | Jr : &ext_pb.JsonReq{ JVal : 321 }, 29 | } 30 | stub := ext_pb.NewKettyEchoServiceClient(client) 31 | rsp, err := stub.Echo(context.Background(), req) 32 | if err != nil { 33 | ketty.GetLog().Errorf("Invoke error:%+v", err) 34 | return 35 | } 36 | 37 | ketty.GetLog().Infof("Rsp: %d", rsp.Val) 38 | } 39 | 40 | -------------------------------------------------------------------------------- /demo/exthttp_server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | ext_pb "github.com/yyzybb537/ketty/demo/extpb" 5 | "github.com/yyzybb537/ketty" 6 | http "github.com/yyzybb537/ketty/protocol/http" 7 | "golang.org/x/net/context" 8 | ) 9 | 10 | type EchoServer struct { 11 | } 12 | 13 | func (this *EchoServer) Echo(ctx context.Context, req *ext_pb.Req) (*ext_pb.Rsp, error) { 14 | return &ext_pb.Rsp{Val:req.Qr.QVal}, nil 15 | } 16 | 17 | func main() { 18 | server, err := ketty.Listen("http.json://127.0.0.1:8091", "") 19 | if err != nil { 20 | ketty.GetLog().Errorf("Listen error:%+v", err) 21 | } 22 | 23 | opt := &http.HttpOption{} 24 | opt.TimeoutMilliseconds = 100 25 | 26 | err = server.SetOption(opt) 27 | ketty.Assert(err) 28 | 29 | server.RegisterMethod(ext_pb.EchoServiceHandle, &EchoServer{}) 30 | 31 | err = server.Serve() 32 | if err != nil { 33 | ketty.GetLog().Errorf("Serve error:%s", err.Error()) 34 | } 35 | 36 | q := make(chan int) 37 | <-q 38 | } 39 | 40 | -------------------------------------------------------------------------------- /demo/extpb/exthttp.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. DO NOT EDIT. 2 | // source: exthttp.proto 3 | 4 | /* 5 | Package ext_pb is a generated protocol buffer package. 6 | 7 | It is generated from these files: 8 | exthttp.proto 9 | 10 | It has these top-level messages: 11 | QueryReq 12 | JsonReq 13 | Req 14 | Rsp 15 | */ 16 | package ext_pb 17 | 18 | import proto "github.com/golang/protobuf/proto" 19 | import fmt "fmt" 20 | import math "math" 21 | 22 | import ( 23 | context "golang.org/x/net/context" 24 | grpc "google.golang.org/grpc" 25 | ) 26 | 27 | import ( 28 | ketty "github.com/yyzybb537/ketty" 29 | ) 30 | 31 | // Reference imports to suppress errors if they are not otherwise used. 32 | var _ = proto.Marshal 33 | var _ = fmt.Errorf 34 | var _ = math.Inf 35 | 36 | // This is a compile-time assertion to ensure that this generated file 37 | // is compatible with the proto package it is being compiled against. 38 | // A compilation error at this line likely means your copy of the 39 | // proto package needs to be updated. 40 | const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package 41 | 42 | type QueryReq struct { 43 | QVal int64 `protobuf:"varint,1,opt,name=qVal" json:"qVal,omitempty"` 44 | } 45 | 46 | func (m *QueryReq) Reset() { *m = QueryReq{} } 47 | func (m *QueryReq) String() string { return proto.CompactTextString(m) } 48 | func (*QueryReq) ProtoMessage() {} 49 | func (*QueryReq) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } 50 | 51 | func (m *QueryReq) GetQVal() int64 { 52 | if m != nil { 53 | return m.QVal 54 | } 55 | return 0 56 | } 57 | 58 | type JsonReq struct { 59 | JVal int64 `protobuf:"varint,1,opt,name=JVal" json:"JVal,omitempty"` 60 | } 61 | 62 | func (m *JsonReq) Reset() { *m = JsonReq{} } 63 | func (m *JsonReq) String() string { return proto.CompactTextString(m) } 64 | func (*JsonReq) ProtoMessage() {} 65 | func (*JsonReq) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } 66 | 67 | func (m *JsonReq) GetJVal() int64 { 68 | if m != nil { 69 | return m.JVal 70 | } 71 | return 0 72 | } 73 | 74 | type Req struct { 75 | Qr *QueryReq `protobuf:"bytes,1,opt,name=qr" json:"qr,omitempty"` 76 | Jr *JsonReq `protobuf:"bytes,2,opt,name=jr" json:"jr,omitempty"` 77 | } 78 | 79 | func (m *Req) Reset() { *m = Req{} } 80 | func (m *Req) String() string { return proto.CompactTextString(m) } 81 | func (*Req) ProtoMessage() {} 82 | func (*Req) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } 83 | 84 | func (m *Req) GetQr() *QueryReq { 85 | if m != nil { 86 | return m.Qr 87 | } 88 | return nil 89 | } 90 | 91 | func (m *Req) GetJr() *JsonReq { 92 | if m != nil { 93 | return m.Jr 94 | } 95 | return nil 96 | } 97 | 98 | type Rsp struct { 99 | Val int64 `protobuf:"varint,1,opt,name=val" json:"val,omitempty"` 100 | } 101 | 102 | func (m *Rsp) Reset() { *m = Rsp{} } 103 | func (m *Rsp) String() string { return proto.CompactTextString(m) } 104 | func (*Rsp) ProtoMessage() {} 105 | func (*Rsp) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } 106 | 107 | func (m *Rsp) GetVal() int64 { 108 | if m != nil { 109 | return m.Val 110 | } 111 | return 0 112 | } 113 | 114 | func init() { 115 | proto.RegisterType((*QueryReq)(nil), "ext_pb.QueryReq") 116 | proto.RegisterType((*JsonReq)(nil), "ext_pb.JsonReq") 117 | proto.RegisterType((*Req)(nil), "ext_pb.Req") 118 | proto.RegisterType((*Rsp)(nil), "ext_pb.Rsp") 119 | } 120 | 121 | // Reference imports to suppress errors if they are not otherwise used. 122 | var _ context.Context 123 | var _ grpc.ClientConn 124 | 125 | // This is a compile-time assertion to ensure that this generated file 126 | // is compatible with the grpc package it is being compiled against. 127 | const _ = grpc.SupportPackageIsVersion4 128 | 129 | // Client API for EchoService service 130 | 131 | type EchoServiceClient interface { 132 | Echo(ctx context.Context, in *Req, opts ...grpc.CallOption) (*Rsp, error) 133 | } 134 | 135 | type echoServiceClient struct { 136 | cc *grpc.ClientConn 137 | } 138 | 139 | func NewEchoServiceClient(cc *grpc.ClientConn) EchoServiceClient { 140 | return &echoServiceClient{cc} 141 | } 142 | 143 | func (c *echoServiceClient) Echo(ctx context.Context, in *Req, opts ...grpc.CallOption) (*Rsp, error) { 144 | out := new(Rsp) 145 | err := grpc.Invoke(ctx, "/ext_pb.EchoService/Echo", in, out, c.cc, opts...) 146 | if err != nil { 147 | return nil, err 148 | } 149 | return out, nil 150 | } 151 | 152 | // Server API for EchoService service 153 | 154 | type EchoServiceServer interface { 155 | Echo(context.Context, *Req) (*Rsp, error) 156 | } 157 | 158 | func RegisterEchoServiceServer(s *grpc.Server, srv EchoServiceServer) { 159 | s.RegisterService(&_EchoService_serviceDesc, srv) 160 | } 161 | 162 | func _EchoService_Echo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 163 | in := new(Req) 164 | if err := dec(in); err != nil { 165 | return nil, err 166 | } 167 | if interceptor == nil { 168 | return srv.(EchoServiceServer).Echo(ctx, in) 169 | } 170 | info := &grpc.UnaryServerInfo{ 171 | Server: srv, 172 | FullMethod: "/ext_pb.EchoService/Echo", 173 | } 174 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 175 | return srv.(EchoServiceServer).Echo(ctx, req.(*Req)) 176 | } 177 | return interceptor(ctx, in, info, handler) 178 | } 179 | 180 | var _EchoService_serviceDesc = grpc.ServiceDesc{ 181 | ServiceName: "ext_pb.EchoService", 182 | HandlerType: (*EchoServiceServer)(nil), 183 | Methods: []grpc.MethodDesc{ 184 | { 185 | MethodName: "Echo", 186 | Handler: _EchoService_Echo_Handler, 187 | }, 188 | }, 189 | Streams: []grpc.StreamDesc{}, 190 | Metadata: "exthttp.proto", 191 | } 192 | 193 | // Reference imports to suppress errors if they are not otherwise used. 194 | var _ ketty.Dummy 195 | 196 | // This is a compile-time assertion to ensure that this generated file 197 | // is compatible with the ketty package it is being compiled against. 198 | 199 | type EchoServiceHandleT struct { 200 | desc *grpc.ServiceDesc 201 | } 202 | 203 | func (h *EchoServiceHandleT) Implement() interface{} { 204 | return h.desc 205 | } 206 | 207 | func (h *EchoServiceHandleT) ServiceName() string { 208 | return h.desc.ServiceName 209 | } 210 | 211 | var EchoServiceHandle = &EchoServiceHandleT{desc: &_EchoService_serviceDesc} 212 | 213 | type KettyEchoServiceClient struct { 214 | client ketty.Client 215 | } 216 | 217 | func NewKettyEchoServiceClient(client ketty.Client) *KettyEchoServiceClient { 218 | return &KettyEchoServiceClient{client} 219 | } 220 | 221 | func (this *KettyEchoServiceClient) Echo(ctx context.Context, in *Req) (*Rsp, error) { 222 | out := new(Rsp) 223 | err := this.client.Invoke(ctx, EchoServiceHandle, "Echo", in, out) 224 | if err != nil { 225 | return nil, err 226 | } 227 | return out, nil 228 | } 229 | 230 | func (*QueryReq) KettyTransport() string { 231 | return "query" 232 | } 233 | 234 | func (*JsonReq) KettyMarshal() string { 235 | return "json" 236 | } 237 | 238 | func (*JsonReq) KettyTransport() string { 239 | return "body" 240 | } 241 | 242 | func (*Req) KettyHttpExtendMessage() {} 243 | 244 | func init() { proto.RegisterFile("exthttp.proto", fileDescriptor0) } 245 | 246 | var fileDescriptor0 = []byte{ 247 | // 234 bytes of a gzipped FileDescriptorProto 248 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x4c, 0xcf, 0xbd, 0x4e, 0xc3, 0x30, 249 | 0x14, 0x05, 0x60, 0xe2, 0x5c, 0x0a, 0x5c, 0x0b, 0x11, 0x79, 0xa1, 0xea, 0x42, 0xe5, 0x09, 0x96, 250 | 0x20, 0xca, 0x96, 0x9d, 0xa5, 0x03, 0x12, 0x46, 0x62, 0x45, 0xa4, 0x5c, 0xa9, 0x04, 0x54, 0xff, 251 | 0x52, 0x35, 0x6f, 0xc0, 0xcc, 0xd8, 0xb7, 0xf4, 0x1b, 0x20, 0xbb, 0x0d, 0xb0, 0x9d, 0x63, 0x7d, 252 | 0xf2, 0xb1, 0xf1, 0x94, 0x36, 0x61, 0x19, 0x82, 0xa9, 0x8d, 0xd3, 0x41, 0x8b, 0x11, 0x6d, 0xc2, 253 | 0xb3, 0x69, 0x27, 0xfc, 0x9d, 0x42, 0xe8, 0x77, 0x87, 0xf2, 0x0a, 0x8f, 0x1f, 0x3e, 0xc9, 0xf5, 254 | 0x8a, 0xac, 0x10, 0x08, 0xf6, 0xe9, 0xe5, 0x63, 0x5c, 0x4c, 0x8b, 0xcb, 0x52, 0xe5, 0xdc, 0x9c, 255 | 0x6c, 0x23, 0x1c, 0xda, 0x24, 0xe4, 0x35, 0x1e, 0xcd, 0xbd, 0x5e, 0xed, 0xe5, 0xfc, 0x9f, 0x4c, 256 | 0xb9, 0xa9, 0xbe, 0x23, 0x40, 0xe7, 0xf5, 0x6a, 0x1b, 0x01, 0x5a, 0xfd, 0xda, 0xcb, 0x7b, 0x2c, 257 | 0x13, 0x9e, 0x22, 0xb3, 0x2e, 0x53, 0x3e, 0xab, 0xea, 0xdd, 0x23, 0xea, 0x61, 0x54, 0x31, 0xeb, 258 | 0xc4, 0x05, 0xb2, 0xce, 0x8d, 0x59, 0x16, 0x67, 0x83, 0xd8, 0x6f, 0x29, 0xd6, 0xb9, 0x06, 0xbe, 259 | 0x22, 0x14, 0xf2, 0x1c, 0x4b, 0xe5, 0x8d, 0xa8, 0xb0, 0x5c, 0xff, 0x6e, 0xa7, 0x38, 0xbb, 0x41, 260 | 0x7e, 0xb7, 0x58, 0xea, 0x47, 0x72, 0xeb, 0xb7, 0x05, 0x09, 0x89, 0x90, 0xaa, 0xe0, 0xc3, 0x55, 261 | 0x8a, 0xec, 0xe4, 0xaf, 0x78, 0x23, 0x0f, 0xda, 0x51, 0xfe, 0xfe, 0xed, 0x4f, 0x00, 0x00, 0x00, 262 | 0xff, 0xff, 0xf1, 0xd8, 0xf3, 0xeb, 0x24, 0x01, 0x00, 0x00, 263 | } 264 | -------------------------------------------------------------------------------- /demo/extpb/exthttp.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package ext_pb; 4 | 5 | import "ketty.proto"; 6 | 7 | message QueryReq { 8 | option (transport) = "query"; 9 | 10 | int64 qVal = 1; 11 | } 12 | 13 | message JsonReq { 14 | option (transport) = "body"; 15 | option (marshal) = "json"; 16 | 17 | int64 JVal = 1; 18 | } 19 | 20 | message Req { 21 | option (use_ketty_http_extend) = true; 22 | // or 23 | // option (mulit_tranport) = true; 24 | 25 | QueryReq qr = 1; 26 | 27 | JsonReq jr = 2; 28 | } 29 | 30 | message Rsp { 31 | int64 val = 1; 32 | } 33 | 34 | service EchoService { 35 | rpc Echo(Req) returns(Rsp) {} 36 | } 37 | 38 | -------------------------------------------------------------------------------- /demo/gls_log.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | echo "github.com/yyzybb537/ketty/demo/pb" 5 | "github.com/yyzybb537/ketty" 6 | "github.com/yyzybb537/ketty/log" 7 | "golang.org/x/net/context" 8 | "github.com/yyzybb537/gls" 9 | ) 10 | 11 | var _ = gls.Get 12 | 13 | type EchoServer struct {} 14 | func (this *EchoServer) Echo(ctx context.Context, req *echo.Req) (*echo.Rsp, error) { 15 | ketty.GetLog().Debugf("Test Gls Log = %v", log.GetGlsDefaultKey()) 16 | return &echo.Rsp{Val:req.Val}, nil 17 | } 18 | 19 | type logKey struct{} 20 | 21 | func main() { 22 | log.SetGlsDefaultKey(logKey{}) 23 | defer log.CleanupGlsDefaultKey(logKey{}) 24 | 25 | opt := log.DefaultLogOption() 26 | opt.LogCategory = "file" 27 | opt.OutputFile = "/dev/null" 28 | lg, err := log.MakeLogger(opt) 29 | ketty.Assert(err) 30 | log.SetLog(lg) 31 | 32 | opt = log.DefaultLogOption() 33 | opt.LogCategory = "std" 34 | log.BindOption(logKey{}, opt) 35 | 36 | server, err := ketty.Listen("grpc://127.0.0.1:8090", "") 37 | if err != nil { 38 | ketty.GetLog().Errorf("Listen error:%s", err.Error()) 39 | } 40 | 41 | server.RegisterMethod(echo.EchoServiceHandle, &EchoServer{}) 42 | 43 | err = server.Serve() 44 | if err != nil { 45 | ketty.GetLog().Errorf("Serve error:%s", err.Error()) 46 | } 47 | 48 | ketty.Hung() 49 | } 50 | -------------------------------------------------------------------------------- /demo/pb/echo.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. DO NOT EDIT. 2 | // source: echo.proto 3 | 4 | /* 5 | Package test_pb is a generated protocol buffer package. 6 | 7 | It is generated from these files: 8 | echo.proto 9 | 10 | It has these top-level messages: 11 | Req 12 | Rsp 13 | */ 14 | package test_pb 15 | 16 | import proto "github.com/golang/protobuf/proto" 17 | import fmt "fmt" 18 | import math "math" 19 | 20 | import ( 21 | context "golang.org/x/net/context" 22 | grpc "google.golang.org/grpc" 23 | ) 24 | 25 | import ( 26 | ketty "github.com/yyzybb537/ketty" 27 | ) 28 | 29 | // Reference imports to suppress errors if they are not otherwise used. 30 | var _ = proto.Marshal 31 | var _ = fmt.Errorf 32 | var _ = math.Inf 33 | 34 | // This is a compile-time assertion to ensure that this generated file 35 | // is compatible with the proto package it is being compiled against. 36 | // A compilation error at this line likely means your copy of the 37 | // proto package needs to be updated. 38 | const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package 39 | 40 | type Req struct { 41 | Val int64 `protobuf:"varint,1,opt,name=val" json:"val,omitempty"` 42 | } 43 | 44 | func (m *Req) Reset() { *m = Req{} } 45 | func (m *Req) String() string { return proto.CompactTextString(m) } 46 | func (*Req) ProtoMessage() {} 47 | func (*Req) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } 48 | 49 | func (m *Req) GetVal() int64 { 50 | if m != nil { 51 | return m.Val 52 | } 53 | return 0 54 | } 55 | 56 | type Rsp struct { 57 | Val int64 `protobuf:"varint,1,opt,name=val" json:"val,omitempty"` 58 | } 59 | 60 | func (m *Rsp) Reset() { *m = Rsp{} } 61 | func (m *Rsp) String() string { return proto.CompactTextString(m) } 62 | func (*Rsp) ProtoMessage() {} 63 | func (*Rsp) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } 64 | 65 | func (m *Rsp) GetVal() int64 { 66 | if m != nil { 67 | return m.Val 68 | } 69 | return 0 70 | } 71 | 72 | func init() { 73 | proto.RegisterType((*Req)(nil), "test_pb.Req") 74 | proto.RegisterType((*Rsp)(nil), "test_pb.Rsp") 75 | } 76 | 77 | // Reference imports to suppress errors if they are not otherwise used. 78 | var _ context.Context 79 | var _ grpc.ClientConn 80 | 81 | // This is a compile-time assertion to ensure that this generated file 82 | // is compatible with the grpc package it is being compiled against. 83 | const _ = grpc.SupportPackageIsVersion4 84 | 85 | // Client API for EchoService service 86 | 87 | type EchoServiceClient interface { 88 | Echo(ctx context.Context, in *Req, opts ...grpc.CallOption) (*Rsp, error) 89 | } 90 | 91 | type echoServiceClient struct { 92 | cc *grpc.ClientConn 93 | } 94 | 95 | func NewEchoServiceClient(cc *grpc.ClientConn) EchoServiceClient { 96 | return &echoServiceClient{cc} 97 | } 98 | 99 | func (c *echoServiceClient) Echo(ctx context.Context, in *Req, opts ...grpc.CallOption) (*Rsp, error) { 100 | out := new(Rsp) 101 | err := grpc.Invoke(ctx, "/test_pb.EchoService/Echo", in, out, c.cc, opts...) 102 | if err != nil { 103 | return nil, err 104 | } 105 | return out, nil 106 | } 107 | 108 | // Server API for EchoService service 109 | 110 | type EchoServiceServer interface { 111 | Echo(context.Context, *Req) (*Rsp, error) 112 | } 113 | 114 | func RegisterEchoServiceServer(s *grpc.Server, srv EchoServiceServer) { 115 | s.RegisterService(&_EchoService_serviceDesc, srv) 116 | } 117 | 118 | func _EchoService_Echo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 119 | in := new(Req) 120 | if err := dec(in); err != nil { 121 | return nil, err 122 | } 123 | if interceptor == nil { 124 | return srv.(EchoServiceServer).Echo(ctx, in) 125 | } 126 | info := &grpc.UnaryServerInfo{ 127 | Server: srv, 128 | FullMethod: "/test_pb.EchoService/Echo", 129 | } 130 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 131 | return srv.(EchoServiceServer).Echo(ctx, req.(*Req)) 132 | } 133 | return interceptor(ctx, in, info, handler) 134 | } 135 | 136 | var _EchoService_serviceDesc = grpc.ServiceDesc{ 137 | ServiceName: "test_pb.EchoService", 138 | HandlerType: (*EchoServiceServer)(nil), 139 | Methods: []grpc.MethodDesc{ 140 | { 141 | MethodName: "Echo", 142 | Handler: _EchoService_Echo_Handler, 143 | }, 144 | }, 145 | Streams: []grpc.StreamDesc{}, 146 | Metadata: "echo.proto", 147 | } 148 | 149 | // Reference imports to suppress errors if they are not otherwise used. 150 | var _ ketty.Dummy 151 | 152 | // This is a compile-time assertion to ensure that this generated file 153 | // is compatible with the ketty package it is being compiled against. 154 | 155 | type EchoServiceHandleT struct { 156 | desc *grpc.ServiceDesc 157 | } 158 | 159 | func (h *EchoServiceHandleT) Implement() interface{} { 160 | return h.desc 161 | } 162 | 163 | func (h *EchoServiceHandleT) ServiceName() string { 164 | return h.desc.ServiceName 165 | } 166 | 167 | var EchoServiceHandle = &EchoServiceHandleT{desc: &_EchoService_serviceDesc} 168 | 169 | type KettyEchoServiceClient struct { 170 | client ketty.Client 171 | } 172 | 173 | func NewKettyEchoServiceClient(client ketty.Client) *KettyEchoServiceClient { 174 | return &KettyEchoServiceClient{client} 175 | } 176 | 177 | func (this *KettyEchoServiceClient) Echo(ctx context.Context, in *Req) (*Rsp, error) { 178 | out := new(Rsp) 179 | err := this.client.Invoke(ctx, EchoServiceHandle, "Echo", in, out) 180 | if err != nil { 181 | return nil, err 182 | } 183 | return out, nil 184 | } 185 | 186 | func init() { proto.RegisterFile("echo.proto", fileDescriptor0) } 187 | 188 | var fileDescriptor0 = []byte{ 189 | // 116 bytes of a gzipped FileDescriptorProto 190 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0xe2, 0x4a, 0x4d, 0xce, 0xc8, 191 | 0xd7, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x2f, 0x49, 0x2d, 0x2e, 0x89, 0x2f, 0x48, 0x52, 192 | 0x12, 0xe7, 0x62, 0x0e, 0x4a, 0x2d, 0x14, 0x12, 0xe0, 0x62, 0x2e, 0x4b, 0xcc, 0x91, 0x60, 0x54, 193 | 0x60, 0xd4, 0x60, 0x0e, 0x02, 0x31, 0xc1, 0x12, 0xc5, 0x05, 0x98, 0x12, 0x46, 0xc6, 0x5c, 0xdc, 194 | 0xae, 0xc9, 0x19, 0xf9, 0xc1, 0xa9, 0x45, 0x65, 0x99, 0xc9, 0xa9, 0x42, 0x2a, 0x5c, 0x2c, 0x20, 195 | 0xae, 0x10, 0x8f, 0x1e, 0xd4, 0x48, 0xbd, 0xa0, 0xd4, 0x42, 0x29, 0x24, 0x5e, 0x71, 0x81, 0x12, 196 | 0x43, 0x12, 0x1b, 0xd8, 0x5a, 0x63, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0xb8, 0xa9, 0x26, 0x0c, 197 | 0x84, 0x00, 0x00, 0x00, 198 | } 199 | -------------------------------------------------------------------------------- /demo/pb/echo.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package test_pb; 4 | 5 | message Req { 6 | int64 val = 1; 7 | } 8 | 9 | message Rsp { 10 | int64 val = 1; 11 | } 12 | 13 | service EchoService { 14 | rpc Echo(Req) returns(Rsp) {} 15 | } 16 | 17 | -------------------------------------------------------------------------------- /demo/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | echo "github.com/yyzybb537/ketty/demo/pb" 5 | "github.com/yyzybb537/ketty" 6 | "golang.org/x/net/context" 7 | ) 8 | 9 | type EchoServer struct { 10 | } 11 | 12 | func (this *EchoServer) Echo(ctx context.Context, req *echo.Req) (*echo.Rsp, error) { 13 | return &echo.Rsp{Val:req.Val}, nil 14 | } 15 | 16 | func main() { 17 | server, err := ketty.Listen("grpc://127.0.0.1:8090", "") 18 | if err != nil { 19 | ketty.GetLog().Errorf("Listen error:%s", err.Error()) 20 | } 21 | 22 | server.RegisterMethod(echo.EchoServiceHandle, &EchoServer{}) 23 | 24 | err = server.Serve() 25 | if err != nil { 26 | ketty.GetLog().Errorf("Serve error:%s", err.Error()) 27 | } 28 | 29 | q := make(chan int) 30 | <-q 31 | } 32 | 33 | -------------------------------------------------------------------------------- /driver/driver.go: -------------------------------------------------------------------------------- 1 | package driver 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | U "github.com/yyzybb537/ketty/url" 7 | ) 8 | 9 | type Driver interface { 10 | Watch(url U.Url) (up, down <-chan []U.Url, stop func(), err error) 11 | 12 | Register(url, value U.Url) (error) 13 | } 14 | 15 | var drivers = make(map[string]Driver) 16 | 17 | func GetDriver(sDriver string) (Driver, error) { 18 | driver, exists := drivers[strings.ToLower(sDriver)] 19 | if !exists { 20 | return nil, fmt.Errorf("Unkown driver:%s", sDriver) 21 | } 22 | return driver, nil 23 | } 24 | 25 | func RegDriver(sDriver string, driver Driver) { 26 | drivers[strings.ToLower(sDriver)] = driver 27 | } 28 | -------------------------------------------------------------------------------- /driver/etcd.go: -------------------------------------------------------------------------------- 1 | package driver 2 | 3 | import ( 4 | "golang.org/x/net/context" 5 | U "github.com/yyzybb537/ketty/url" 6 | "github.com/yyzybb537/ketty/log" 7 | "github.com/yyzybb537/ketty/driver/etcd" 8 | ) 9 | 10 | type EtcdDriver struct { 11 | } 12 | 13 | func init() { 14 | RegDriver("etcd", new(EtcdDriver)) 15 | U.RegDefaultPort("etcd", 2379) 16 | } 17 | 18 | func (this *EtcdDriver) Watch(url U.Url) (up, down <-chan []U.Url, stop func(), err error) { 19 | sess, err := etcd.GetEtcdMgr().GetSession(url.SAddr) 20 | if err != nil { 21 | return 22 | } 23 | 24 | upC := make(chan []U.Url, 32) 25 | downC := make(chan []U.Url, 32) 26 | up = upC 27 | down = downC 28 | cb := func(s *etcd.Session, str string, h etcd.WatchHandler) error { 29 | values, _, err := sess.GetChildren(url.Path) 30 | if err != nil { 31 | return err 32 | } 33 | 34 | upAddrs := []U.Url{} 35 | downAddrs := []U.Url{} 36 | lastNodes, ok := h.MetaData.(map[string]bool) 37 | if !ok { 38 | lastNodes = map[string]bool{} 39 | h.MetaData = lastNodes 40 | } 41 | newNodes := map[string]bool{} 42 | for _, v := range values { 43 | newNodes[v] = true 44 | 45 | // up 46 | if _, exists := lastNodes[v]; !exists { 47 | addr, err := U.UrlFromDriverString(v) 48 | if err == nil { 49 | upAddrs = append(upAddrs, addr) 50 | log.GetLog().Debugf("up url %s", addr.ToString()) 51 | } else { 52 | log.GetLog().Warningf("unkown up url %s", addr.ToString()) 53 | } 54 | } 55 | } 56 | 57 | // down 58 | for v, _ := range lastNodes { 59 | if _, exists := newNodes[v]; !exists { 60 | addr, err := U.UrlFromDriverString(v) 61 | if err == nil { 62 | downAddrs = append(downAddrs, addr) 63 | log.GetLog().Debugf("down url %s", addr.ToString()) 64 | } else { 65 | log.GetLog().Warningf("unkown down url %s", addr.ToString()) 66 | } 67 | } 68 | } 69 | 70 | h.MetaData = newNodes 71 | 72 | upC <- upAddrs 73 | downC <- downAddrs 74 | return nil 75 | } 76 | 77 | handler := sess.WatchRecursive(url.Path, func(se *etcd.Session, s string, h etcd.WatchHandler){cb(se, s, h);}) 78 | stop = func(){ 79 | sess.UnWatch(url.Path, handler) 80 | close(upC) 81 | close(downC) 82 | } 83 | 84 | err = cb(sess, url.Path, handler) 85 | if err != nil { 86 | stop() 87 | return 88 | } 89 | return 90 | } 91 | 92 | func (this *EtcdDriver) Register(url, value U.Url) (err error) { 93 | sess, err := etcd.GetEtcdMgr().GetSession(url.SAddr) 94 | if err != nil { 95 | return 96 | } 97 | 98 | localIp := sess.GetLocalIp() 99 | saddr, err := this.toDriverString(localIp, value) 100 | if err != nil { 101 | return 102 | } 103 | 104 | err = sess.SetEphemeral(url.Path + "/" + saddr, "1", context.Background()) 105 | return 106 | } 107 | 108 | func (this *EtcdDriver) toDriverString(localIp string, value U.Url) (s string, err error) { 109 | saddrs := value.GetAddrs() 110 | newSaddrs := []string{} 111 | for _, saddr := range saddrs { 112 | var addr U.Addr 113 | addr, err = U.AddrFromString(saddr, value.GetMainProtocol()) 114 | if err != nil { 115 | return 116 | } 117 | switch addr.Host { 118 | case "": 119 | fallthrough 120 | case "0.0.0.0": 121 | addr.Host = localIp 122 | } 123 | newSaddrs = append(newSaddrs, addr.ToString()) 124 | } 125 | value.SetAddrs(newSaddrs) 126 | return value.ToDriverString(), nil 127 | } 128 | -------------------------------------------------------------------------------- /driver/etcd/etcd_mgr.go: -------------------------------------------------------------------------------- 1 | package etcd 2 | 3 | import ( 4 | etcd "github.com/coreos/etcd/client" 5 | "golang.org/x/net/context" 6 | "github.com/yyzybb537/ketty/log" 7 | "net" 8 | "strings" 9 | "sync" 10 | "sync/atomic" 11 | "time" 12 | ) 13 | 14 | const defaultTTL = 12 15 | 16 | type WatchCallback func(*Session, string, WatchHandler) 17 | type WatchHandlerT struct { 18 | id uint64 19 | cb WatchCallback 20 | MetaData interface{} 21 | } 22 | type WatchHandler *WatchHandlerT 23 | type WatchMap map[uint64]WatchHandler 24 | 25 | type Session struct { 26 | client etcd.Client 27 | kapi etcd.KeysAPI 28 | address string 29 | localIp string 30 | 31 | watcherHandlerIndex uint64 32 | watchers map[string]WatchMap 33 | mutex sync.RWMutex 34 | } 35 | 36 | func EtcdErrorCode(err error) int { 37 | etcdErr, ok := err.(etcd.Error) 38 | if !ok { 39 | return 0 40 | } 41 | return etcdErr.Code 42 | } 43 | 44 | func (this *Session) GetLocalIp() string { 45 | if this.localIp != "" { 46 | return this.localIp 47 | } 48 | 49 | remoteAddrs := strings.Split(this.address, ",") 50 | if len(remoteAddrs) == 0 { 51 | return "127.0.0.1" 52 | } 53 | 54 | conn, err := net.Dial("tcp", remoteAddrs[0]) 55 | if err != nil { 56 | log.GetLog().Errorf("net.Dial returns error:%s", err.Error()) 57 | return "127.0.0.1" 58 | } 59 | 60 | local := conn.LocalAddr() 61 | return strings.Split(local.String(), ":")[0] 62 | } 63 | 64 | func (this *Session) CreateInOrder(dir, value string, outPath *string, ctx context.Context) (err error) { 65 | //try to create dir 66 | _, _ = this.kapi.Set(ctx, dir, "", &etcd.SetOptions{Dir: true}) 67 | //delete node in dir when value conflict 68 | resp, err := this.kapi.Get(ctx, dir, &etcd.GetOptions{Recursive: true}) 69 | if err != nil { 70 | return 71 | } 72 | for _, respNode := range resp.Node.Nodes { 73 | if value == respNode.Value { 74 | log.GetLog().Warningf("exist node %s in %s", value, respNode.Key) 75 | _, err = this.kapi.Delete(ctx, respNode.Key, &etcd.DeleteOptions{PrevValue: value}) 76 | if err != nil { 77 | return 78 | } 79 | log.GetLog().Infof("delete node %s success", respNode.Key) 80 | } 81 | } 82 | //create in order first time 83 | createInOrder_opt := etcd.CreateInOrderOptions{TTL: time.Second * defaultTTL} 84 | resp, err = this.kapi.CreateInOrder(ctx, dir, value, &createInOrder_opt) 85 | if err != nil { 86 | log.GetLog().Errorln("create etcd in order node error:", err) 87 | return 88 | } 89 | *outPath = resp.Node.Key 90 | 91 | // refresh ttl 92 | go func() { 93 | var interval time.Duration = 5 94 | for { 95 | select { 96 | case <-ctx.Done(): 97 | return 98 | case <-time.After(time.Second * interval): 99 | refresh_opt := etcd.SetOptions{TTL: time.Second * defaultTTL, PrevValue: value, Refresh: true} 100 | _, err := this.kapi.Set(context.Background(), *outPath, "", &refresh_opt) 101 | if err != nil { 102 | log.GetLog().Errorf("Refresh etcd error. address=%s path=%s error=%s", this.address, *outPath, err) 103 | // set 104 | resp, err = this.kapi.CreateInOrder(context.Background(), dir, value, &createInOrder_opt) 105 | if err != nil { 106 | log.GetLog().Errorln("reset etcd in order node error:", err) 107 | interval = 2 108 | } 109 | *outPath = resp.Node.Key 110 | } else { 111 | interval = 5 112 | } 113 | } 114 | } 115 | }() 116 | 117 | return 118 | } 119 | 120 | func (this *Session) SetEphemeral(path, value string, ctx context.Context) (err error) { 121 | set_opt := etcd.SetOptions{PrevExist: etcd.PrevIgnore, TTL: time.Second * defaultTTL} 122 | _, err = this.kapi.Set(context.Background(), path, value, &set_opt) 123 | if err != nil { 124 | log.GetLog().Errorln("Set etcd ephemeral node error:", err) 125 | return 126 | } 127 | 128 | // refresh ttl 129 | go func() { 130 | var interval time.Duration = 5 131 | for { 132 | select { 133 | case <-ctx.Done(): 134 | return 135 | case <-time.After(time.Second * interval): 136 | refresh_opt := etcd.SetOptions{PrevExist: etcd.PrevIgnore, TTL: time.Second * defaultTTL, PrevValue: "1", Refresh: true} 137 | _, err := this.kapi.Set(context.Background(), path, "", &refresh_opt) 138 | if err != nil { 139 | log.GetLog().Errorf("Refresh etcd error. address=%s path=%s error=%s", this.address, path, err.Error()) 140 | // set 141 | _, err := this.kapi.Set(context.Background(), path, value, &set_opt) 142 | if err != nil { 143 | log.GetLog().Errorln("Reset etcd ephemeral node error:", err) 144 | interval = 2 145 | } 146 | } else { 147 | interval = 5 148 | } 149 | } 150 | } 151 | }() 152 | 153 | return 154 | } 155 | 156 | func (this *Session) Mkdir(path string) (err error) { 157 | _, err = this.kapi.Set(context.Background(), path, "", &etcd.SetOptions{Dir: true}) 158 | return 159 | } 160 | 161 | func (this *Session) GetChildren(path string) (keys []string, values []string, err error) { 162 | opt := etcd.GetOptions{Recursive: true} 163 | rsp, err := this.kapi.Get(context.Background(), path, &opt) 164 | if err != nil { 165 | return 166 | } 167 | 168 | keys = []string{} 169 | values = []string{} 170 | for _, v := range rsp.Node.Nodes { 171 | child_node := v.Key[len(path):] 172 | if strings.HasPrefix(child_node, "/") { 173 | child_node = child_node[1:] 174 | } 175 | keys = append(keys, child_node) 176 | values = append(values, v.Value) 177 | } 178 | return 179 | } 180 | 181 | func (this *Session) Get(path string) (value string, err error) { 182 | opt := etcd.GetOptions{Recursive: false} 183 | rsp, err := this.kapi.Get(context.Background(), path, &opt) 184 | if err != nil { 185 | return 186 | } 187 | 188 | value = rsp.Node.Value 189 | return 190 | } 191 | 192 | func (this *Session) Del(path string, isDir bool, isRecursive bool) (err error) { 193 | opt := etcd.DeleteOptions{Recursive: isRecursive, Dir: isDir} 194 | _, err = this.kapi.Delete(context.Background(), path, &opt) 195 | return 196 | } 197 | 198 | func (this *Session) WatchRecursive(path string, cb WatchCallback) (watcherHandler WatchHandler) { 199 | this.mutex.Lock() 200 | defer this.mutex.Unlock() 201 | m, exists := this.watchers[path] 202 | if !exists { 203 | // If the path not exists, create it. 204 | _, _, err := this.GetChildren(path) 205 | if err != nil && EtcdErrorCode(err) == etcd.ErrorCodeKeyNotFound { 206 | err = this.Mkdir(path) 207 | if err != nil { 208 | log.GetLog().Errorf("Mkdir(%s) error:%v", path, err) 209 | } else { 210 | log.GetLog().Infof("Mkdir(%s) in WatchRecursive", path) 211 | } 212 | } 213 | 214 | this.watchers[path] = make(WatchMap) 215 | m, _ = this.watchers[path] 216 | go func() { 217 | var index uint64 218 | watcher := this.kapi.Watcher(path, &etcd.WatcherOptions{Recursive: true, AfterIndex: index}) 219 | for { 220 | rsp, err := watcher.Next(context.Background()) 221 | if err != nil { 222 | watcher = this.kapi.Watcher(path, &etcd.WatcherOptions{Recursive: true, AfterIndex: index}) 223 | time.Sleep(time.Second * 6) 224 | } else { 225 | index = rsp.Index 226 | } 227 | 228 | log.GetLog().Infof("Watcher begin trigger path=%s. callback len(m)=%d. Index=%d. Err:%+v", path, len(m), index, err) 229 | 230 | for _, handler := range m { 231 | handler.cb(this, path, handler) 232 | } 233 | 234 | log.GetLog().Infof("Watcher end trigger path=%s. callback len(m)=%d", path, len(m)) 235 | } 236 | }() 237 | } 238 | 239 | watcherHandlerIndex := atomic.AddUint64((*uint64)(&this.watcherHandlerIndex), 1) 240 | if watcherHandlerIndex == 0 { 241 | watcherHandlerIndex = atomic.AddUint64((*uint64)(&this.watcherHandlerIndex), 1) 242 | } 243 | watcherHandler = &WatchHandlerT{ 244 | id : watcherHandlerIndex, 245 | cb : cb, 246 | } 247 | m[watcherHandlerIndex] = watcherHandler 248 | return 249 | } 250 | 251 | func (this *Session) UnWatch(path string, watcherHandler WatchHandler) { 252 | this.mutex.Lock() 253 | defer this.mutex.Unlock() 254 | m, exists := this.watchers[path] 255 | if !exists { 256 | return 257 | } 258 | 259 | delete(m, watcherHandler.id) 260 | } 261 | 262 | type etcdMgr struct { 263 | sessions map[string]*Session 264 | mutex sync.RWMutex 265 | } 266 | 267 | var etcd_mgr *etcdMgr = &etcdMgr{ 268 | sessions: make(map[string]*Session), 269 | } 270 | 271 | func GetEtcdMgr() *etcdMgr { 272 | return etcd_mgr 273 | } 274 | 275 | func (this *etcdMgr) GetSession(address string) (sess *Session, err error) { 276 | sess, exists := this.getSession(address) 277 | if exists { 278 | return 279 | } 280 | 281 | s := &Session{ 282 | watcherHandlerIndex: 0, 283 | watchers: make(map[string]WatchMap), 284 | } 285 | 286 | var addrs []string = strings.Split(address, ",") 287 | for index, _ := range addrs { 288 | addrs[index] = "http://" + addrs[index] 289 | } 290 | 291 | cfg := etcd.Config{ 292 | Endpoints: addrs, 293 | Transport: etcd.DefaultTransport, 294 | HeaderTimeoutPerRequest: time.Second * 12, 295 | } 296 | 297 | s.client, err = etcd.New(cfg) 298 | if err != nil { 299 | return 300 | } 301 | s.kapi = etcd.NewKeysAPI(s.client) 302 | s.address = address 303 | 304 | sess = this.setSession(address, s) 305 | return 306 | } 307 | 308 | func (this *etcdMgr) getSession(address string) (*Session, bool) { 309 | this.mutex.RLock() 310 | defer this.mutex.RUnlock() 311 | sess, exists := this.sessions[address] 312 | return sess, exists 313 | } 314 | 315 | func (this *etcdMgr) setSession(address string, sess *Session) *Session { 316 | this.mutex.Lock() 317 | defer this.mutex.Unlock() 318 | if s, exists := this.sessions[address]; exists { 319 | return s 320 | } 321 | 322 | this.sessions[address] = sess 323 | return sess 324 | } 325 | 326 | -------------------------------------------------------------------------------- /extends/.test: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yyzybb537/ketty/96fb003c9655f7a0ec4115a32e19fcbd06cb657c/extends/.test -------------------------------------------------------------------------------- /extends/log/log.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "github.com/yyzybb537/ketty" 5 | "github.com/yyzybb537/ketty/log" 6 | "github.com/yyzybb537/ketty/extends/log/real_log" 7 | ) 8 | 9 | func init() { 10 | el := &real_log.RealLog{} 11 | ketty.SetLog(el) 12 | } 13 | 14 | // --------------------------------------------------- 15 | func Debugf(format string, args ... interface{}) { 16 | ketty.GetLog().Debugf(format, args ...) 17 | } 18 | func Infof(format string, args ... interface{}) { 19 | ketty.GetLog().Infof(format, args ...) 20 | } 21 | func Warningf(format string, args ... interface{}) { 22 | ketty.GetLog().Warningf(format, args ...) 23 | } 24 | func Errorf(format string, args ... interface{}) { 25 | ketty.GetLog().Errorf(format, args ...) 26 | } 27 | func Fatalf(format string, args ... interface{}) { 28 | ketty.GetLog().Fatalf(format, args ...) 29 | } 30 | func Recordf(format string, args ... interface{}) { 31 | ketty.GetLog().Recordf(format, args ...) 32 | } 33 | func Debugln(args ... interface{}) { 34 | ketty.GetLog().Debugln(args ...) 35 | } 36 | func Infoln(args ... interface{}) { 37 | ketty.GetLog().Infoln(args ...) 38 | } 39 | func Warningln(args ... interface{}) { 40 | ketty.GetLog().Warningln(args ...) 41 | } 42 | func Errorln(args ... interface{}) { 43 | ketty.GetLog().Errorln(args ...) 44 | } 45 | func Fatalln(args ... interface{}) { 46 | ketty.GetLog().Fatalln(args ...) 47 | } 48 | func Recordln(args ... interface{}) { 49 | ketty.GetLog().Recordln(args ...) 50 | } 51 | func S(key interface{}) log.LogI { 52 | return ketty.GetLog(key) 53 | } 54 | func EnableSection(key interface{}) { 55 | ketty.EnableLogSection(key) 56 | } 57 | func DisableSection(key interface{}) { 58 | ketty.DisableLogSection(key) 59 | } 60 | -------------------------------------------------------------------------------- /extends/log/real_log/log.go: -------------------------------------------------------------------------------- 1 | package real_log 2 | 3 | import ( 4 | "github.com/yyzybb537/glog" 5 | "github.com/yyzybb537/ketty/log" 6 | ) 7 | 8 | type RealLog struct{} 9 | 10 | func (this *RealLog) Clone(opt *log.LogOption) (log.LogI, error) { 11 | return new(RealLog), nil 12 | } 13 | 14 | func (this *RealLog) Debugf(format string, args ...interface{}) { 15 | //默认不打印 16 | glog.V(0).Infof(format, args ...) 17 | } 18 | func (this *RealLog) Infof(format string, args ...interface{}) { 19 | glog.Infof(format, args ...) 20 | } 21 | func (this *RealLog) Warningf(format string, args ...interface{}) { 22 | glog.Warningf(format, args ...) 23 | } 24 | func (this *RealLog) Errorf(format string, args ...interface{}) { 25 | glog.Errorf(format, args ...) 26 | } 27 | func (this *RealLog) Fatalf(format string, args ...interface{}) { 28 | glog.Fatalf(format, args ...) 29 | } 30 | func (this *RealLog) Recordf(format string, args ...interface{}) { 31 | //TODO 独立打印日志 32 | glog.Infof(format, args ...) 33 | } 34 | func (this *RealLog) Debugln(args ...interface{}) { 35 | glog.V(0).Infoln(args ...) 36 | } 37 | func (this *RealLog) Infoln(args ...interface{}) { 38 | glog.Infoln(args ...) 39 | } 40 | func (this *RealLog) Warningln(args ...interface{}) { 41 | glog.Warningln(args ...) 42 | } 43 | func (this *RealLog) Errorln(args ...interface{}) { 44 | glog.Errorln(args ...) 45 | } 46 | func (this *RealLog) Fatalln(args ...interface{}) { 47 | glog.Fatalln(args ...) 48 | } 49 | func (this *RealLog) Recordln(args ...interface{}) { 50 | //TODO 独立打印日志 51 | glog.Infoln(args ...) 52 | } 53 | func (this *RealLog) Flush() error { 54 | glog.Flush() 55 | return nil 56 | } 57 | -------------------------------------------------------------------------------- /extends/ploy/demo/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/yyzybb537/ketty/extends/ploy" 5 | "github.com/yyzybb537/ketty/extends/ploy/demo/test" 6 | "github.com/yyzybb537/ketty" 7 | //"github.com/yyzybb537/ketty/config" 8 | "golang.org/x/net/context" 9 | "fmt" 10 | ) 11 | 12 | // implement grpc service 13 | type TestApi struct { 14 | ploy.FlowI 15 | } 16 | 17 | type Request struct { 18 | Name string 19 | } 20 | 21 | type Response struct { 22 | Message string 23 | } 24 | 25 | func (this *TestApi) GetHandle() ketty.ServiceHandle { 26 | return test.TestHandle 27 | } 28 | 29 | func (this *TestApi) Ping(ctx context.Context, req *test.TestRequest) (rsp *test.TestResponse, err error) { 30 | iReq, err := this.reqTrans(req) 31 | if err != nil { 32 | return 33 | } 34 | 35 | iRsp := &Response{} 36 | ctx = this.Executor(ctx, iReq, iRsp) 37 | if ctx != nil { 38 | err = ctx.Err() 39 | if err != nil { 40 | return 41 | } 42 | } 43 | 44 | rsp, err = this.rspTrans(iRsp) 45 | return 46 | } 47 | 48 | func (this *TestApi) reqTrans(testReq *test.TestRequest) (req *Request, err error) { 49 | req = &Request{} 50 | req.Name = fmt.Sprintf("%d", testReq.Qr.QVal) 51 | // 协议转换 52 | return 53 | } 54 | func (this *TestApi) rspTrans(rsp *Response) (testRsp *test.TestResponse, err error) { 55 | testRsp = &test.TestResponse{} 56 | testRsp.Message = rsp.Message 57 | // 协议转换 58 | return 59 | } 60 | 61 | type Config struct { 62 | Listen string // http://:8080 63 | TestRouter string // /bidding/test 64 | } 65 | 66 | var gConfig Config 67 | 68 | func main() { 69 | //err := config.Read(&gConfig, "server.toml") 70 | //ketty.Assert(err) 71 | gConfig.Listen = "http.query://:8000" 72 | gConfig.TestRouter = "/bidding/test" 73 | 74 | server, err := ploy.NewServer(gConfig.Listen, "") 75 | ketty.Assert(err) 76 | 77 | TA := &TestApi{} 78 | _, err = server.NewFlow(gConfig.TestRouter, TA) 79 | ketty.Assert(err) 80 | initBidding(TA.FlowI) 81 | 82 | err = server.Serve() 83 | ketty.Assert(err) 84 | 85 | ketty.Hung() 86 | } 87 | 88 | type RegionFilling struct {} 89 | func (*RegionFilling) Run(ctx context.Context, req *Request, rsp *Response) context.Context { 90 | rsp.Message = req.Name 91 | var err error 92 | // todo something 93 | if err != nil { 94 | return ketty.WithError(ctx, err) 95 | } 96 | 97 | return ctx 98 | } 99 | 100 | type RegionFilling1 struct {} 101 | func (*RegionFilling1) Run(ctx context.Context, req int, rsp *Response) context.Context { 102 | return ctx 103 | } 104 | 105 | type TraceTest struct {} 106 | func (*TraceTest) PloyWillRun(ploy interface{}, ctx context.Context, req *Request, rsp *Response) context.Context { 107 | fmt.Printf("PloyWillRun %s\n",req.Name) 108 | return ctx 109 | } 110 | func (*TraceTest) PloyDidRun(ploy interface{}, ctx context.Context, req *Request, rsp *Response) context.Context { 111 | fmt.Printf("PloyDidRun %s\n",rsp.Message) 112 | return ctx 113 | } 114 | 115 | func initBidding(flow ploy.FlowI) { 116 | //flow.AddTrace(new(TraceTest)) 117 | // 填充地区并且做检索 118 | flow.AddPloy(new(RegionFilling)) 119 | flow.AddPloy(new(RegionFilling1)) 120 | } 121 | 122 | -------------------------------------------------------------------------------- /extends/ploy/demo/test/test.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. DO NOT EDIT. 2 | // source: test.proto 3 | 4 | /* 5 | Package test is a generated protocol buffer package. 6 | 7 | It is generated from these files: 8 | test.proto 9 | 10 | It has these top-level messages: 11 | QueryReq 12 | JsonReq 13 | TestRequest 14 | TestResponse 15 | */ 16 | package test 17 | 18 | import proto "github.com/golang/protobuf/proto" 19 | import fmt "fmt" 20 | import math "math" 21 | 22 | import ( 23 | context "golang.org/x/net/context" 24 | grpc "google.golang.org/grpc" 25 | ) 26 | 27 | import ( 28 | ketty "github.com/yyzybb537/ketty" 29 | ) 30 | 31 | // Reference imports to suppress errors if they are not otherwise used. 32 | var _ = proto.Marshal 33 | var _ = fmt.Errorf 34 | var _ = math.Inf 35 | 36 | // This is a compile-time assertion to ensure that this generated file 37 | // is compatible with the proto package it is being compiled against. 38 | // A compilation error at this line likely means your copy of the 39 | // proto package needs to be updated. 40 | const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package 41 | 42 | type QueryReq struct { 43 | QVal int64 `protobuf:"varint,1,opt,name=qVal" json:"qVal,omitempty"` 44 | } 45 | 46 | func (m *QueryReq) Reset() { *m = QueryReq{} } 47 | func (m *QueryReq) String() string { return proto.CompactTextString(m) } 48 | func (*QueryReq) ProtoMessage() {} 49 | func (*QueryReq) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } 50 | 51 | func (m *QueryReq) GetQVal() int64 { 52 | if m != nil { 53 | return m.QVal 54 | } 55 | return 0 56 | } 57 | 58 | type JsonReq struct { 59 | JVal int64 `protobuf:"varint,1,opt,name=JVal" json:"JVal,omitempty"` 60 | } 61 | 62 | func (m *JsonReq) Reset() { *m = JsonReq{} } 63 | func (m *JsonReq) String() string { return proto.CompactTextString(m) } 64 | func (*JsonReq) ProtoMessage() {} 65 | func (*JsonReq) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } 66 | 67 | func (m *JsonReq) GetJVal() int64 { 68 | if m != nil { 69 | return m.JVal 70 | } 71 | return 0 72 | } 73 | 74 | type TestRequest struct { 75 | Qr *QueryReq `protobuf:"bytes,1,opt,name=qr" json:"qr,omitempty"` 76 | Jr *JsonReq `protobuf:"bytes,2,opt,name=jr" json:"jr,omitempty"` 77 | } 78 | 79 | func (m *TestRequest) Reset() { *m = TestRequest{} } 80 | func (m *TestRequest) String() string { return proto.CompactTextString(m) } 81 | func (*TestRequest) ProtoMessage() {} 82 | func (*TestRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } 83 | 84 | func (m *TestRequest) GetQr() *QueryReq { 85 | if m != nil { 86 | return m.Qr 87 | } 88 | return nil 89 | } 90 | 91 | func (m *TestRequest) GetJr() *JsonReq { 92 | if m != nil { 93 | return m.Jr 94 | } 95 | return nil 96 | } 97 | 98 | type TestResponse struct { 99 | Message string `protobuf:"bytes,1,opt,name=message" json:"message,omitempty"` 100 | } 101 | 102 | func (m *TestResponse) Reset() { *m = TestResponse{} } 103 | func (m *TestResponse) String() string { return proto.CompactTextString(m) } 104 | func (*TestResponse) ProtoMessage() {} 105 | func (*TestResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } 106 | 107 | func (m *TestResponse) GetMessage() string { 108 | if m != nil { 109 | return m.Message 110 | } 111 | return "" 112 | } 113 | 114 | func init() { 115 | proto.RegisterType((*QueryReq)(nil), "test.QueryReq") 116 | proto.RegisterType((*JsonReq)(nil), "test.JsonReq") 117 | proto.RegisterType((*TestRequest)(nil), "test.TestRequest") 118 | proto.RegisterType((*TestResponse)(nil), "test.TestResponse") 119 | } 120 | 121 | // Reference imports to suppress errors if they are not otherwise used. 122 | var _ context.Context 123 | var _ grpc.ClientConn 124 | 125 | // This is a compile-time assertion to ensure that this generated file 126 | // is compatible with the grpc package it is being compiled against. 127 | const _ = grpc.SupportPackageIsVersion4 128 | 129 | // Client API for Test service 130 | 131 | type TestClient interface { 132 | Ping(ctx context.Context, in *TestRequest, opts ...grpc.CallOption) (*TestResponse, error) 133 | } 134 | 135 | type testClient struct { 136 | cc *grpc.ClientConn 137 | } 138 | 139 | func NewTestClient(cc *grpc.ClientConn) TestClient { 140 | return &testClient{cc} 141 | } 142 | 143 | func (c *testClient) Ping(ctx context.Context, in *TestRequest, opts ...grpc.CallOption) (*TestResponse, error) { 144 | out := new(TestResponse) 145 | err := grpc.Invoke(ctx, "/test.Test/Ping", in, out, c.cc, opts...) 146 | if err != nil { 147 | return nil, err 148 | } 149 | return out, nil 150 | } 151 | 152 | // Server API for Test service 153 | 154 | type TestServer interface { 155 | Ping(context.Context, *TestRequest) (*TestResponse, error) 156 | } 157 | 158 | func RegisterTestServer(s *grpc.Server, srv TestServer) { 159 | s.RegisterService(&_Test_serviceDesc, srv) 160 | } 161 | 162 | func _Test_Ping_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 163 | in := new(TestRequest) 164 | if err := dec(in); err != nil { 165 | return nil, err 166 | } 167 | if interceptor == nil { 168 | return srv.(TestServer).Ping(ctx, in) 169 | } 170 | info := &grpc.UnaryServerInfo{ 171 | Server: srv, 172 | FullMethod: "/test.Test/Ping", 173 | } 174 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 175 | return srv.(TestServer).Ping(ctx, req.(*TestRequest)) 176 | } 177 | return interceptor(ctx, in, info, handler) 178 | } 179 | 180 | var _Test_serviceDesc = grpc.ServiceDesc{ 181 | ServiceName: "test.Test", 182 | HandlerType: (*TestServer)(nil), 183 | Methods: []grpc.MethodDesc{ 184 | { 185 | MethodName: "Ping", 186 | Handler: _Test_Ping_Handler, 187 | }, 188 | }, 189 | Streams: []grpc.StreamDesc{}, 190 | Metadata: "test.proto", 191 | } 192 | 193 | // Reference imports to suppress errors if they are not otherwise used. 194 | var _ ketty.Dummy 195 | 196 | // This is a compile-time assertion to ensure that this generated file 197 | // is compatible with the ketty package it is being compiled against. 198 | 199 | type TestHandleT struct { 200 | desc *grpc.ServiceDesc 201 | } 202 | 203 | func (h *TestHandleT) Implement() interface{} { 204 | return h.desc 205 | } 206 | 207 | func (h *TestHandleT) ServiceName() string { 208 | return h.desc.ServiceName 209 | } 210 | 211 | var TestHandle = &TestHandleT{desc: &_Test_serviceDesc} 212 | 213 | type KettyTestClient struct { 214 | client ketty.Client 215 | } 216 | 217 | func NewKettyTestClient(client ketty.Client) *KettyTestClient { 218 | return &KettyTestClient{client} 219 | } 220 | 221 | func (this *KettyTestClient) Ping(ctx context.Context, in *TestRequest) (*TestResponse, error) { 222 | out := new(TestResponse) 223 | err := this.client.Invoke(ctx, TestHandle, "Ping", in, out) 224 | if err != nil { 225 | return nil, err 226 | } 227 | return out, nil 228 | } 229 | 230 | func (*QueryReq) KettyTransport() string { 231 | return "query" 232 | } 233 | 234 | func (*JsonReq) KettyMarshal() string { 235 | return "json" 236 | } 237 | 238 | func (*JsonReq) KettyTransport() string { 239 | return "body" 240 | } 241 | 242 | func (*TestRequest) KettyHttpExtendMessage() {} 243 | 244 | func init() { proto.RegisterFile("test.proto", fileDescriptor0) } 245 | 246 | var fileDescriptor0 = []byte{ 247 | // 239 bytes of a gzipped FileDescriptorProto 248 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x4c, 0x90, 0xb1, 0x4e, 0x03, 0x31, 249 | 0x0c, 0x86, 0xb9, 0x60, 0x28, 0xf5, 0x01, 0x02, 0x4f, 0xa7, 0x4a, 0x20, 0x94, 0xa9, 0x2c, 0xad, 250 | 0x54, 0x06, 0xa4, 0x7b, 0x84, 0x4e, 0x10, 0x21, 0xf6, 0x56, 0x58, 0x15, 0x07, 0x5c, 0x9a, 0x38, 251 | 0x1d, 0xee, 0x0d, 0x98, 0x19, 0xfb, 0x96, 0xf7, 0x06, 0x28, 0x49, 0x2b, 0xdd, 0xf6, 0xc7, 0xfe, 252 | 0xf2, 0xd9, 0x32, 0x62, 0x60, 0x09, 0xb3, 0xad, 0xb7, 0xc1, 0x12, 0xc4, 0x3c, 0x29, 0xbf, 0x38, 253 | 0x84, 0x2e, 0x97, 0xf4, 0x23, 0x5e, 0xbc, 0xee, 0xd8, 0x77, 0x86, 0x1d, 0x11, 0x82, 0x7b, 0x5f, 254 | 0x7d, 0x57, 0xc5, 0x43, 0x31, 0x3d, 0x35, 0x29, 0xd7, 0xe3, 0x7d, 0x0f, 0x67, 0x2e, 0x12, 0x7a, 255 | 0x8e, 0xa3, 0xa5, 0xd8, 0xf6, 0x40, 0x2e, 0x07, 0x64, 0xcc, 0xf5, 0xcd, 0x5f, 0x0f, 0xd0, 0x88, 256 | 0x6d, 0xf7, 0x3d, 0xc0, 0xda, 0x7e, 0x74, 0xda, 0x60, 0xf9, 0xc6, 0x12, 0x0c, 0xbb, 0x1d, 0x4b, 257 | 0xa0, 0x7b, 0x54, 0xce, 0xa7, 0x2f, 0xe5, 0xe2, 0x7a, 0x96, 0xd6, 0x3a, 0x8e, 0x36, 0xca, 0x79, 258 | 0xba, 0x43, 0xd5, 0xf8, 0x4a, 0xa5, 0xfe, 0x55, 0xee, 0x1f, 0xe6, 0x19, 0xd5, 0xf8, 0x1a, 0x7e, 259 | 0x7b, 0x28, 0xf4, 0x14, 0x2f, 0xb3, 0x53, 0xb6, 0xb6, 0x15, 0xa6, 0x0a, 0x47, 0x3f, 0x2c, 0xb2, 260 | 0xda, 0x70, 0x32, 0x8f, 0xcd, 0xf1, 0xb9, 0x78, 0x46, 0x88, 0x24, 0xcd, 0x11, 0x5e, 0x3e, 0xdb, 261 | 0x0d, 0xdd, 0x66, 0xe5, 0x60, 0xa3, 0x09, 0x0d, 0x4b, 0x59, 0xa8, 0x4f, 0xd6, 0xe7, 0xe9, 0x32, 262 | 0x4f, 0xff, 0x01, 0x00, 0x00, 0xff, 0xff, 0xac, 0x52, 0xe7, 0xe7, 0x3a, 0x01, 0x00, 0x00, 263 | } 264 | -------------------------------------------------------------------------------- /extends/ploy/demo/test/test.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package test; 4 | 5 | import "ketty.proto"; 6 | 7 | service Test { 8 | rpc Ping(TestRequest) returns (TestResponse) {} 9 | } 10 | 11 | message QueryReq { 12 | option (transport) = "query"; 13 | 14 | int64 qVal = 1; 15 | } 16 | 17 | message JsonReq { 18 | option (transport) = "body"; 19 | option (marshal) = "json"; 20 | 21 | int64 JVal = 1; 22 | } 23 | 24 | message TestRequest { 25 | option (use_ketty_http_extend) = true; 26 | // or 27 | // option (mulit_tranport) = true; 28 | 29 | QueryReq qr = 1; 30 | 31 | JsonReq jr = 2; 32 | } 33 | 34 | message TestResponse { 35 | string message = 1; 36 | } 37 | -------------------------------------------------------------------------------- /extends/ploy/fake_interface/fake_interface.go: -------------------------------------------------------------------------------- 1 | package fake_interface 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "golang.org/x/net/context" 7 | "github.com/yyzybb537/ketty" 8 | ) 9 | 10 | type FakeInterface struct { 11 | m map[string]*methodDesc 12 | realizedI interface{} 13 | } 14 | 15 | type fakeFunc func(args ...interface{}) context.Context 16 | 17 | type methodDesc struct { 18 | Name string 19 | ArgsCount int 20 | Handle fakeFunc 21 | ArgsTypes []reflect.Type 22 | } 23 | 24 | func NewFakeInterface() *FakeInterface{ 25 | return &FakeInterface{ 26 | m: make(map[string]*methodDesc), 27 | } 28 | } 29 | 30 | func (this *FakeInterface) RealizedTypeName() string { 31 | if this.realizedI == nil { 32 | return "unknown" 33 | } 34 | refType := reflect.TypeOf(this.realizedI) 35 | return refType.String() 36 | } 37 | 38 | func (this *FakeInterface) Add(methodName string, argsCount int) error{ 39 | if _, ok := this.m[methodName]; ok{ 40 | return fmt.Errorf("conflict methodName: %s", methodName) 41 | } 42 | m := &methodDesc{ 43 | Name: methodName, 44 | ArgsCount: argsCount, 45 | } 46 | this.m[methodName] = m 47 | return nil 48 | } 49 | 50 | func (this *FakeInterface) Realize(realizedPoint interface{}) error{ 51 | if this.realizedI != nil { 52 | //已经实现就覆盖,如果覆盖出错也会清除已有的interface 53 | newFI := NewFakeInterface() 54 | *this = *newFI 55 | } 56 | for _, m := range this.m { 57 | handle, argsTypes, err := parse(realizedPoint, m.Name, m.ArgsCount) 58 | if err != nil { 59 | return err 60 | } 61 | m.Handle = handle 62 | m.ArgsTypes = argsTypes 63 | } 64 | this.realizedI = realizedPoint 65 | return nil 66 | } 67 | 68 | func (this *FakeInterface) LookLike(other *FakeInterface) bool { 69 | if this.realizedI == nil { 70 | return false 71 | } 72 | for otherMethodName, otherMethod := range other.m { 73 | myMethod, ok := this.m[otherMethodName] 74 | if !ok { 75 | return false 76 | } 77 | for i, otherMethodArgsType := range otherMethod.ArgsTypes { 78 | if !myMethod.ArgsTypes[i].AssignableTo(otherMethodArgsType) { 79 | return false 80 | } 81 | } 82 | } 83 | return true 84 | } 85 | 86 | func (this *FakeInterface) Interface() interface{} { 87 | return this.realizedI 88 | } 89 | 90 | func (this *FakeInterface) Do(methodName string, args ...interface{}) (ctx context.Context) { 91 | if this.realizedI == nil { 92 | return ketty.WithError(context.Background(), fmt.Errorf("invalid FakeInterface: not Realized")) 93 | } 94 | m, ok := this.m[methodName] 95 | if !ok { 96 | return ketty.WithError(ctx, fmt.Errorf("invalid MethodName: %s not Realized", methodName)) 97 | } 98 | return m.Handle(args...) 99 | } 100 | 101 | func parse(realizedPoint interface{}, methodName string, argsCount int) (h fakeFunc, argsTypes []reflect.Type, err error) { 102 | refType := reflect.TypeOf(realizedPoint) 103 | if refType.Kind() != reflect.Ptr { 104 | err = fmt.Errorf("invalid refType kind") 105 | return 106 | } 107 | 108 | refValue := reflect.ValueOf(realizedPoint); 109 | 110 | for i := 0; i < refType.NumMethod(); i++ { 111 | methodType := refType.Method(i) 112 | if methodType.Name != methodName { 113 | continue 114 | } 115 | 116 | if methodType.Type.NumIn() != argsCount + 1{ 117 | err = fmt.Errorf("invalid argsCount: method %s, want: %d, real: %d", methodName, argsCount + 1, methodType.Type.NumIn()) 118 | return 119 | } 120 | 121 | if methodType.Type.NumOut() != 1 { 122 | err = fmt.Errorf("invalid return argsCount: method %s, want: %d, real: %d", methodName, 1, methodType.Type.NumOut()) 123 | return 124 | } 125 | 126 | if methodType.Type.Out(0).Name() != "Context" { 127 | err = fmt.Errorf("invalid return arg: method %s, want: Context, real: %s", methodName, 1, methodType.Type.Out(0).Name()) 128 | return 129 | } 130 | 131 | 132 | for i := 1;i < methodType.Type.NumIn();i++ { 133 | argsTypes = append(argsTypes, methodType.Type.In(i)) 134 | } 135 | 136 | methodValue := refValue.Method(i) 137 | h = func(args ...interface{}) (ctx context.Context){ 138 | var in []reflect.Value 139 | for _, arg := range args { 140 | argValue := reflect.ValueOf(arg) 141 | in = append(in, argValue) 142 | } 143 | returnValues := methodValue.Call(in) 144 | var ok bool 145 | if ctx, ok = returnValues[0].Interface().(context.Context); !ok{ 146 | return nil 147 | } 148 | return ctx 149 | } 150 | return 151 | } 152 | err = fmt.Errorf("no match method, want: %s", methodName) 153 | return 154 | } 155 | -------------------------------------------------------------------------------- /extends/ploy/fake_interface/fake_interface_test.go: -------------------------------------------------------------------------------- 1 | package fake_interface 2 | 3 | import ( 4 | "testing" 5 | "context" 6 | ) 7 | 8 | type A struct { 9 | } 10 | 11 | func (this *A) Run(ctx context.Context, req string, resp string) context.Context { 12 | return nil 13 | } 14 | 15 | type B struct { 16 | } 17 | 18 | func (this *B) Run(ctx context.Context, req string, resp string) context.Context { 19 | return nil 20 | } 21 | 22 | func Test_Fake(t *testing.T) { 23 | var err error 24 | a := NewFakeInterface() 25 | err = a.Add("Run", 3) 26 | if err != nil { 27 | panic(err) 28 | } 29 | err = a.Realize(new(A)) 30 | if err != nil { 31 | panic(err) 32 | } 33 | 34 | b := NewFakeInterface() 35 | err = b.Add("Run", 3) 36 | if err != nil { 37 | panic(err) 38 | } 39 | err = b.Realize(new(B)) 40 | if err != nil { 41 | panic(err) 42 | } 43 | 44 | println(a.RealizedTypeName()) 45 | println(a.LookLike(b)) 46 | } 47 | -------------------------------------------------------------------------------- /extends/ploy/flow.go: -------------------------------------------------------------------------------- 1 | package ploy 2 | 3 | import ( 4 | "golang.org/x/net/context" 5 | "github.com/yyzybb537/ketty/extends/ploy/fake_interface" 6 | "github.com/yyzybb537/ketty" 7 | "fmt" 8 | ) 9 | 10 | type FlowI interface{ 11 | Executor(context.Context, interface{}, interface{}) context.Context 12 | AddPloy(interface{}) 13 | AddTrace(interface{}) 14 | } 15 | 16 | type ployReqKey struct{} 17 | type ployRespKey struct{} 18 | 19 | func GetRequest(ctx context.Context) interface{} { 20 | return ctx.Value(ployReqKey{}) 21 | } 22 | 23 | func GetResponse(ctx context.Context) interface{} { 24 | return ctx.Value(ployRespKey{}) 25 | } 26 | 27 | func putRequest(ctx context.Context, request interface{}) context.Context{ 28 | return context.WithValue(ctx, ployReqKey{}, request) 29 | } 30 | 31 | func putResponse(ctx context.Context, response interface{}) context.Context{ 32 | return context.WithValue(ctx, ployRespKey{}, response) 33 | } 34 | /* 35 | type Ploy interface{ 36 | Run(context.Context, any, any) context.Context 37 | } 38 | type Trace interface{ 39 | TraceBefore(ploy interface{}, ctx context.Context, any, any) 40 | TraceAfter(ploy interface{}, ctx context.Context, any, any) 41 | } 42 | */ 43 | 44 | type BaseFlow struct { 45 | ployFIs []*fake_interface.FakeInterface 46 | traceFI *fake_interface.FakeInterface 47 | realizeTrace bool 48 | } 49 | 50 | func NewBaseFlow() FlowI { 51 | baseFlow := &BaseFlow{} 52 | traceFI := fake_interface.NewFakeInterface() 53 | traceFI.Add("PloyWillRun", 4) 54 | traceFI.Add("PloyDidRun", 4) 55 | 56 | baseFlow.traceFI = traceFI 57 | 58 | return baseFlow 59 | } 60 | 61 | func (this *BaseFlow) Executor(ctx context.Context, req interface{},resp interface{}) context.Context{ 62 | ctx = putRequest(ctx, req) 63 | ctx = putResponse(ctx, resp) 64 | for _, ployFI := range this.ployFIs { 65 | if this.realizeTrace { 66 | ctx = this.traceFI.Do("PloyWillRun", ployFI.Interface(), ctx, req, resp) 67 | } 68 | ctx = ployFI.Do("Run", ctx, req, resp) 69 | if this.realizeTrace { 70 | ctx = this.traceFI.Do("PloyDidRun", ployFI.Interface(), ctx, req, resp) 71 | } 72 | if ctx != nil && ctx.Err() != nil { 73 | return ctx 74 | } 75 | } 76 | return ctx 77 | } 78 | 79 | type _init interface { 80 | Init() 81 | } 82 | 83 | func (this *BaseFlow) AddPloy(imp interface{}) { 84 | ployFI := fake_interface.NewFakeInterface() 85 | ployFI.Add("Run", 3) 86 | err := ployFI.Realize(imp) 87 | ketty.Assert(err) 88 | if len(this.ployFIs) != 0 { 89 | if !ployFI.LookLike(this.ployFIs[0]) { 90 | ketty.Assert(fmt.Errorf("one new ploy %s don't LookLike the first ploy %s", ployFI.RealizedTypeName(), this.ployFIs[0].RealizedTypeName())) 91 | } 92 | } 93 | i, ok := imp.(_init) 94 | if ok { 95 | i.Init() 96 | } 97 | this.ployFIs = append(this.ployFIs, ployFI) 98 | return 99 | } 100 | 101 | func (this *BaseFlow) AddTrace(imp interface{}) { 102 | err := this.traceFI.Realize(imp) 103 | ketty.Assert(err) 104 | this.realizeTrace = true 105 | } 106 | -------------------------------------------------------------------------------- /extends/ploy/server.go: -------------------------------------------------------------------------------- 1 | package ploy 2 | 3 | import ( 4 | "github.com/yyzybb537/ketty" 5 | "github.com/yyzybb537/ketty/log" 6 | "github.com/yyzybb537/ketty/option" 7 | "fmt" 8 | "strings" 9 | "sync" 10 | ) 11 | 12 | type Server struct { 13 | sUrl string 14 | sDriverUrl string 15 | servers []ketty.Server 16 | opt option.OptionI 17 | logKeys []interface{} 18 | ifGrpc bool 19 | lock sync.Mutex 20 | } 21 | 22 | func NewServer(sUrl, sDriverUrl string) (server *Server, err error) { 23 | server = &Server{} 24 | server.sUrl = sUrl 25 | server.sDriverUrl = sDriverUrl 26 | //不太优雅,但是ketty库目前没有实现返回server类型接口 27 | if strings.Contains(sUrl, "grpc") { 28 | server.ifGrpc = true 29 | } 30 | return 31 | } 32 | 33 | type getHandle interface{ 34 | GetHandle() ketty.ServiceHandle 35 | } 36 | 37 | type getGlsLogKey interface{ 38 | GlsLogKey() interface{} 39 | } 40 | 41 | func (this *Server) NewFlow(router string, implement interface{}) (flow FlowI, err error){ 42 | h, ok := implement.(getHandle) 43 | if !ok { 44 | err = fmt.Errorf("not implement GetHandle") 45 | return 46 | } 47 | this.lock.Lock() 48 | defer this.lock.Unlock() 49 | var server ketty.Server 50 | var needAppend bool 51 | if this.ifGrpc && len(this.servers) != 0 { 52 | server = this.servers[0] 53 | }else { 54 | server, err = ketty.Listen(this.sUrl + router, this.sDriverUrl) 55 | if err != nil { 56 | return 57 | } 58 | if this.opt != nil { 59 | err = server.SetOption(this.opt) 60 | if err != nil { 61 | return 62 | } 63 | } 64 | needAppend = true 65 | } 66 | 67 | err = server.RegisterMethod(h.GetHandle(), implement) 68 | if err != nil { 69 | return 70 | } 71 | flow = NewBaseFlow() 72 | err = setInterface(implement, flow, "FlowI") 73 | if err != nil { 74 | return 75 | } 76 | i, ok := implement.(_init) 77 | if ok { 78 | i.Init() 79 | } 80 | 81 | if needAppend { 82 | this.servers = append(this.servers, server) 83 | } 84 | this.appendLogKeys(implement) 85 | return 86 | } 87 | 88 | func (this *Server) NewOverLappedFlow(router string, implement interface{}, flow FlowI) (err error) { 89 | h, ok := implement.(getHandle) 90 | if !ok { 91 | err = fmt.Errorf("not implement getHandle") 92 | return 93 | } 94 | server, err := ketty.Listen(this.sUrl + router, this.sDriverUrl) 95 | if err != nil { 96 | return 97 | } 98 | err = server.RegisterMethod(h.GetHandle(), implement) 99 | if err != nil { 100 | return 101 | } 102 | err = setInterface(implement, flow, "FlowI") 103 | if err != nil { 104 | return 105 | } 106 | 107 | this.servers = append(this.servers, server) 108 | this.appendLogKeys(implement) 109 | return 110 | } 111 | 112 | func (this *Server) appendLogKeys(implement interface{}) { 113 | var logKey interface{} 114 | if logHandle, ok := implement.(getGlsLogKey); ok { 115 | logKey = logHandle.GlsLogKey() 116 | } 117 | this.logKeys = append(this.logKeys, logKey) 118 | } 119 | 120 | func (this *Server) Serve() (err error){ 121 | for i, s := range this.servers { 122 | logKey := this.logKeys[i] 123 | if logKey != nil { 124 | log.SetGlsDefaultKey(logKey) 125 | defer log.CleanupGlsDefaultKey(logKey) 126 | } 127 | err = s.Serve() 128 | if err != nil { 129 | return 130 | } 131 | } 132 | return 133 | } 134 | 135 | func (this *Server) SetOption(opt option.OptionI) (err error){ 136 | this.opt = opt 137 | for _, s := range this.servers { 138 | err = s.SetOption(this.opt) 139 | if err != nil { 140 | return 141 | } 142 | } 143 | return 144 | } 145 | -------------------------------------------------------------------------------- /extends/ploy/set_interface.go: -------------------------------------------------------------------------------- 1 | package ploy 2 | 3 | import ( 4 | "reflect" 5 | "fmt" 6 | ) 7 | 8 | func setInterface(owner interface{}, impl interface{}, interfaceName string) (err error){ 9 | refV := reflect.ValueOf(owner) 10 | if refV.Kind() != reflect.Ptr { 11 | err = fmt.Errorf("invalid refType kind") 12 | return 13 | } 14 | refV = refV.Elem() 15 | for i := 0;i < refV.NumField(); i++ { 16 | field := refV.Type().Field(i) 17 | if field.Anonymous && field.Name == interfaceName { 18 | refV.Field(i).Set(reflect.ValueOf(impl)) 19 | return 20 | } 21 | } 22 | err = fmt.Errorf("not such interfaceName: %s", interfaceName) 23 | return 24 | } 25 | -------------------------------------------------------------------------------- /extends/sql/mysql.go: -------------------------------------------------------------------------------- 1 | package sql 2 | 3 | import ( 4 | "database/sql" 5 | _ "github.com/go-sql-driver/mysql" 6 | "github.com/yyzybb537/ketty/extends/log" 7 | "github.com/yyzybb537/ketty/aop" 8 | "sync/atomic" 9 | "time" 10 | ) 11 | 12 | const LogMysql = "LogMysql" 13 | 14 | func init() { 15 | log.EnableSection(LogMysql) 16 | } 17 | 18 | type MySQLConfig struct { 19 | Address string 20 | Dbname string 21 | User string 22 | Pwd string 23 | } 24 | 25 | type sqlDB struct { 26 | *sql.DB 27 | } 28 | 29 | type Rows struct { 30 | *sql.Rows 31 | } 32 | 33 | type Result struct { 34 | sql.Result 35 | } 36 | 37 | type DB struct { 38 | *sqlDB 39 | Address string 40 | using int64 41 | DbNameBase string 42 | } 43 | 44 | func NewDB(ori *sql.DB, addr string) *DB { 45 | return &DB{ 46 | sqlDB: &sqlDB{ori}, 47 | Address: addr, 48 | } 49 | } 50 | 51 | func (db *DB) Query(query string, args ...interface{}) (*Rows, error) { 52 | nowUsing := atomic.AddInt64(&db.using, 1) - 1 53 | log.S(LogMysql).Infof("Begin(Using=%d) MySQL.Query @%s TraceID:%s run: %s", 54 | nowUsing, db.Address, aop.GetTraceID(), query) 55 | start := time.Now() 56 | rows, err := db.sqlDB.Query(query, args...) 57 | costT := time.Since(start) 58 | cost := costT.String() 59 | nowUsing = atomic.AddInt64(&db.using, -1) 60 | if err != nil { 61 | log.S(LogMysql).Errorf("End(Using=%d) MySQL.Query @%s TraceID:%s run: %s. cost:%s. err=%+v", 62 | nowUsing, db.Address, aop.GetTraceID(), query, cost, err) 63 | } else { 64 | log.S(LogMysql).Infof("End(Using=%d) MySQL.Query @%s TraceID:%s run: %s. cost:%s.", 65 | nowUsing, db.Address, aop.GetTraceID(), query, cost) 66 | } 67 | return &Rows{rows}, err 68 | } 69 | 70 | func (db *DB) Exec(query string, args ...interface{}) (Result, error) { 71 | nowUsing := atomic.AddInt64(&db.using, 1) - 1 72 | log.S(LogMysql).Infof("Begin(Using=%d) MySQL.Exec @%s TraceID:%s run: %s", 73 | nowUsing, db.Address, aop.GetTraceID(), query) 74 | start := time.Now() 75 | result, err := db.sqlDB.Exec(query, args...) 76 | costT := time.Since(start) 77 | cost := costT.String() 78 | nowUsing = atomic.AddInt64(&db.using, -1) 79 | if err != nil { 80 | log.S(LogMysql).Errorf("End(sing=%d) MySQL.Exec @%s TraceID:%s run: %s. cost:%s. err=%+v", 81 | nowUsing, db.Address, aop.GetTraceID(), query, cost, err) 82 | } else { 83 | log.S(LogMysql).Infof("End(Using=%d) MySQL.Exec @%s TraceID:%s run: %s. cost:%s.", 84 | nowUsing, db.Address, aop.GetTraceID(), query, cost) 85 | } 86 | return Result{result}, err 87 | } 88 | 89 | func MySQLConnect(address, user, pwd, db_name string) (db *DB, err error) { 90 | return MySQLConnectWithConnNum(address, user, pwd, db_name, 128) 91 | } 92 | 93 | func MySQLConnectS(conf MySQLConfig) (db *DB, err error) { 94 | return MySQLConnect(conf.Address, conf.User, conf.Pwd, conf.Dbname) 95 | } 96 | 97 | func MySQLConnectWithConnNum(address, user, pwd, db_name string, num_conn int) (db *DB, err error) { 98 | dsn := user + ":" + pwd + "@tcp(" + address + ")/" + db_name + "?parseTime=true&interpolateParams=true" 99 | c, err := sql.Open("mysql", dsn) 100 | if err != nil { 101 | return nil, err 102 | } 103 | 104 | c.SetConnMaxLifetime(300 * time.Second) 105 | c.SetMaxIdleConns(num_conn) 106 | c.SetMaxOpenConns(num_conn) 107 | return NewDB(c, address), nil 108 | } 109 | -------------------------------------------------------------------------------- /ketty.go: -------------------------------------------------------------------------------- 1 | package ketty 2 | 3 | import ( 4 | U "github.com/yyzybb537/ketty/url" 5 | P "github.com/yyzybb537/ketty/protocol" 6 | B "github.com/yyzybb537/ketty/balancer" 7 | A "github.com/yyzybb537/ketty/aop" 8 | _ "github.com/yyzybb537/ketty/protocol/grpc" 9 | _ "github.com/yyzybb537/ketty/protocol/http" 10 | ) 11 | 12 | type Dummy interface{} 13 | type Client P.Client 14 | type Server P.Server 15 | 16 | // @目前支持的组件 17 | //1.Protocol 18 | // grpc, http, https 19 | //2.Balancer 20 | // robin 21 | //3.Driver for find service 22 | // etcd 23 | //4.AOP 24 | // cost, exception, logger, trace 25 | 26 | // @后续计划 27 | //1.Protocol 28 | // none 29 | //2.Balancer 30 | // conhash, random 31 | //3.Driver for find service 32 | // zookeeper 33 | //* Driver mode: 34 | // only-master, master-groups 35 | //4.AOP 36 | // timeout, trace-timeout, statistics 37 | //5.Log 38 | // glog or seelog 39 | 40 | // @sUrl: protocol://ip[:port][,ip[:port]]/path 41 | // E.g: 42 | // http://127.0.0.1:8030/path 43 | // https://127.0.0.1:8030 44 | // grpc://127.0.0.1:8030,127.0.0.1:8031 45 | // 46 | // Support marshal: pb(default), json. 47 | // Support http method: GET, POST(default) 48 | // Support http data transport: body(default), query, multipart 49 | // 50 | // User can make extend marshal. 51 | // If use `query`, the marshal will be ignored. 52 | // 53 | // Others E.g: 54 | // http.json://127.0.0.1:8030/path 55 | // http.json.query://127.0.0.1:8030/path 56 | // http.pb.query://127.0.0.1:8030/path 57 | // http.json.get.query://127.0.0.1:8030/path 58 | // http.json.post.multipart://127.0.0.1:8030/path 59 | // http.get.query://127.0.0.1:8030/path 60 | // http.post.body://127.0.0.1:8030/path 61 | // 62 | // @sBalanceUrl: driver://ip[:port][,ip[:port]]/path 63 | // etcd://127.0.0.1:2379/path 64 | func Listen(sUrl, sDriverUrl string) (server Server, err error) { 65 | url, err := U.UrlFromString(sUrl) 66 | if err != nil { 67 | return 68 | } 69 | 70 | driverUrl, err := U.UrlFromString(sDriverUrl) 71 | if err != nil { 72 | return 73 | } 74 | 75 | proto, err := P.GetProtocol(url.GetMainProtocol()) 76 | if err != nil { 77 | return 78 | } 79 | 80 | server, err = proto.CreateServer(url, driverUrl) 81 | server.AddAop(A.DefaultAop().GetAop()...) 82 | return 83 | } 84 | 85 | func Dial(sUrl, sBalancer string) (client Client, err error) { 86 | url, err := U.UrlFromString(sUrl) 87 | if err != nil { 88 | return 89 | } 90 | 91 | balancer, err := B.GetBalancer(sBalancer) 92 | if err != nil { 93 | return 94 | } 95 | 96 | clients := newClients(url, balancer, nil) 97 | err = clients.dial() 98 | if err != nil { 99 | return 100 | } 101 | 102 | client = clients 103 | client.AddAop(A.DefaultAop().GetAop()...) 104 | return 105 | } 106 | 107 | -------------------------------------------------------------------------------- /log.go: -------------------------------------------------------------------------------- 1 | package ketty 2 | 3 | import ( 4 | "github.com/yyzybb537/ketty/log" 5 | ) 6 | 7 | func SetLog(l log.LogI) { 8 | log.SetLog(l) 9 | } 10 | 11 | func GetLog(opt ... interface{}) log.LogI { 12 | return log.GetLog(opt...) 13 | } 14 | 15 | func EnableLogSection(opt interface{}) { 16 | log.EnableSection(opt) 17 | } 18 | 19 | func DisableLogSection(opt interface{}) { 20 | log.DisableSection(opt) 21 | } 22 | 23 | var Indent log.LogFormatOptions = log.Indent 24 | 25 | func LogFormat(req interface{}, opt ... log.LogFormatOptions) string { 26 | return log.LogFormat(req, opt...) 27 | } 28 | 29 | -------------------------------------------------------------------------------- /log/filelog.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "io" 7 | "github.com/pkg/errors" 8 | "sync" 9 | "sync/atomic" 10 | "time" 11 | "path/filepath" 12 | ) 13 | 14 | const cBufNumber = 32 15 | const cBufSize = 256 * 1024 16 | const cFlushCycle time.Duration = time.Millisecond * 100 17 | 18 | type FileLog struct { 19 | opt *LogOption 20 | f syncWriter 21 | w *RingBuf 22 | mu sync.Mutex 23 | nWriteBytes int64 24 | } 25 | 26 | type syncWriter interface { 27 | io.Writer 28 | Sync() error 29 | } 30 | 31 | func NewFileLog(opt *LogOption) (*FileLog, error) { 32 | filelg := &FileLog{ 33 | opt : opt, 34 | w : NewRingBuf(cBufNumber, cBufSize), 35 | } 36 | if err := filelg.reopen(); err != nil { 37 | return nil, err 38 | } 39 | filelg.goHouseKeeper() 40 | return filelg, nil 41 | } 42 | 43 | func (this *FileLog) Clone(opt *LogOption) (LogI, error) { 44 | return NewFileLog(opt) 45 | } 46 | 47 | //func (this *FileLog) write(level Level, format string, args ... interface{}) { 48 | func (this *FileLog) write(level Level, info string) { 49 | if this.opt.Ignore { 50 | return 51 | } 52 | 53 | writer := this.w.GetWriter() 54 | n1 := this.opt.WriteHeader(level, 3, writer) 55 | n2, _ := writer.WriteString(info) 56 | writer.WriteString("\n") 57 | this.w.Put(writer) 58 | 59 | if this.opt.RotateCategory == "size" { 60 | if atomic.AddInt64(&this.nWriteBytes, int64(n1 + n2 + 1)) >= this.opt.RotateValue { 61 | this.rotate() 62 | } 63 | } 64 | } 65 | 66 | func (this *FileLog) reopen() error { 67 | var err error 68 | _ = err 69 | if this.w != nil { 70 | this.w.LockFlushTo(this.f) 71 | } 72 | if this.f != nil { 73 | this.f.Sync() 74 | } 75 | this.nWriteBytes = 0 76 | 77 | if this.opt.OutputFile == "stdout" { 78 | this.f = os.Stdout 79 | } else if this.opt.OutputFile == "stderr" { 80 | this.f = os.Stderr 81 | } else if this.opt.OutputFile == os.DevNull { 82 | this.f, err = os.OpenFile(this.opt.OutputFile, os.O_WRONLY, 0666) 83 | if err != nil { 84 | return errors.Errorf("open log file(%s) error:%s\n", this.opt.OutputFile, err.Error()) 85 | } 86 | } else { 87 | filename := this.opt.OutputFile + this.opt.GetSuffix() + ".log" 88 | f, err := os.OpenFile(filename, os.O_RDWR | os.O_CREATE | os.O_APPEND, 0666) 89 | if os.IsNotExist(err) { 90 | err = os.MkdirAll(filepath.Dir(filename), 0775) 91 | } 92 | if err == nil { 93 | f, err = os.OpenFile(filename, os.O_RDWR | os.O_CREATE | os.O_APPEND, 0666) 94 | } 95 | if err != nil { 96 | return errors.Errorf("open log file(%s) error:%s\n", this.opt.OutputFile, err.Error()) 97 | } 98 | 99 | fileInfo, err := f.Stat() 100 | if err != nil { 101 | return errors.Errorf("open log file.Stat(%s) error:%s\n", this.opt.OutputFile, err.Error()) 102 | } 103 | this.nWriteBytes = fileInfo.Size() 104 | this.f = f 105 | 106 | symlink := this.opt.OutputFile + ".log" 107 | if symlink != filename { 108 | os.Remove(symlink) 109 | os.Symlink(filepath.Base(filename), symlink) 110 | } 111 | } 112 | 113 | return nil 114 | } 115 | 116 | func (this *FileLog) rotate() { 117 | this.mu.Lock() 118 | defer this.mu.Unlock() 119 | this.rotateWithoutLock() 120 | } 121 | 122 | func (this *FileLog) rotateWithoutLock() { 123 | this.reopen() 124 | } 125 | 126 | func (this *FileLog) goHouseKeeper() { 127 | go func() { 128 | for { 129 | time.Sleep(cFlushCycle) 130 | 131 | this.mu.Lock() 132 | this.w.FlushTo(this.f) 133 | this.mu.Unlock() 134 | } 135 | }() 136 | 137 | if this.opt.RotateCategory == "time" { 138 | go func() { 139 | for { 140 | time.Sleep(time.Duration(this.opt.RotateValue) * time.Second) 141 | this.rotate() 142 | } 143 | }() 144 | } 145 | } 146 | 147 | func (this *FileLog) logf(level Level, format string, args ... interface{}) { 148 | //this.write(level, format, args...) 149 | this.write(level, fmt.Sprintf(format, args...)) 150 | } 151 | func (this *FileLog) logln(level Level, args ... interface{}) { 152 | //this.write(level, "", args...) 153 | this.write(level, fmt.Sprintln(args...)) 154 | } 155 | func (this *FileLog) Debugf(format string, args ... interface{}) { 156 | this.logf(lv_debug, format, args...) 157 | } 158 | func (this *FileLog) Infof(format string, args ... interface{}) { 159 | this.logf(lv_info, format, args...) 160 | } 161 | func (this *FileLog) Warningf(format string, args ... interface{}) { 162 | this.logf(lv_warning, format, args...) 163 | } 164 | func (this *FileLog) Errorf(format string, args ... interface{}) { 165 | this.logf(lv_error, format, args...) 166 | } 167 | func (this *FileLog) Fatalf(format string, args ... interface{}) { 168 | this.logf(lv_fatal, format, args...) 169 | } 170 | func (this *FileLog) Recordf(format string, args ... interface{}) { 171 | this.logf(lv_record, format, args...) 172 | } 173 | func (this *FileLog) Debugln(args ... interface{}) { 174 | this.logln(lv_debug, args...) 175 | } 176 | func (this *FileLog) Infoln(args ... interface{}) { 177 | this.logln(lv_info, args...) 178 | } 179 | func (this *FileLog) Warningln(args ... interface{}) { 180 | this.logln(lv_warning, args...) 181 | } 182 | func (this *FileLog) Errorln(args ... interface{}) { 183 | this.logln(lv_error, args...) 184 | } 185 | func (this *FileLog) Fatalln(args ... interface{}) { 186 | this.logln(lv_fatal, args...) 187 | } 188 | func (this *FileLog) Recordln(args ... interface{}) { 189 | this.logln(lv_record, args...) 190 | } 191 | func (this *FileLog) Flush() error { 192 | this.mu.Lock() 193 | if err := this.w.FlushTo(this.f); err != nil { 194 | this.mu.Unlock() 195 | return err 196 | } 197 | this.mu.Unlock() 198 | return this.f.Sync() 199 | } 200 | -------------------------------------------------------------------------------- /log/log.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "sync" 5 | "github.com/yyzybb537/gls" 6 | COM "github.com/yyzybb537/ketty/common" 7 | "github.com/pkg/errors" 8 | ) 9 | 10 | // --------------------------------------------------- 11 | type LogI interface { 12 | Clone(opt *LogOption) (LogI, error) 13 | 14 | Debugf(format string, args ... interface{}) 15 | Infof(format string, args ... interface{}) 16 | Warningf(format string, args ... interface{}) 17 | Errorf(format string, args ... interface{}) 18 | Fatalf(format string, args ... interface{}) 19 | Recordf(format string, args ... interface{}) 20 | 21 | Debugln(args ... interface{}) 22 | Infoln(args ... interface{}) 23 | Warningln(args ... interface{}) 24 | Errorln(args ... interface{}) 25 | Fatalln(args ... interface{}) 26 | Recordln(args ... interface{}) 27 | 28 | Flush() error 29 | } 30 | 31 | var LogMgr *COM.Manager = COM.NewManager((*LogI)(nil)) 32 | 33 | var logger LogI 34 | var logBindings = make(map[interface{}]LogI) 35 | var logBindingsMtx sync.RWMutex 36 | 37 | func init() { 38 | LogMgr.Register("std", new(StdLog)) 39 | LogMgr.Register("file", new(FileLog)) 40 | LogMgr.Register("fake", new(FakeLog)) 41 | 42 | if logger == nil { 43 | var err error 44 | opt := DefaultLogOption() 45 | logger, err = MakeLogger(opt) 46 | if err != nil { 47 | opt.LogCategory = "std" 48 | logger, err = MakeLogger(opt) 49 | if err != nil { 50 | panic(err) 51 | } 52 | } 53 | } 54 | } 55 | 56 | func MakeLogger(opt *LogOption) (LogI, error) { 57 | lg, ok := LogMgr.Get(opt.LogCategory).(LogI) 58 | if !ok { 59 | return nil, errors.Errorf("Unkown log category in option. LogCategory=%s", opt.LogCategory) 60 | } 61 | 62 | newLg, err := lg.Clone(opt) 63 | if err != nil { 64 | return nil, err 65 | } 66 | 67 | return newLg, nil 68 | } 69 | 70 | func SetLog(l LogI) { 71 | if logger != l { 72 | FlushAll() 73 | logBindingsMtx.Lock() 74 | defer logBindingsMtx.Unlock() 75 | logBindings = make(map[interface{}]LogI) 76 | logger = l 77 | } 78 | } 79 | 80 | func BindOption(key interface{}, opt *LogOption) (LogI, error) { 81 | logBindingsMtx.Lock() 82 | defer logBindingsMtx.Unlock() 83 | lg, err := MakeLogger(opt) 84 | if err != nil { 85 | return nil, err 86 | } 87 | logBindings[key] = lg 88 | return lg, nil 89 | } 90 | 91 | func GetLog(keys ... interface{}) LogI { 92 | var key interface{} 93 | if len(keys) > 0 { 94 | key = keys[0] 95 | } else { 96 | key = GetGlsDefaultKey() 97 | if key == nil { 98 | return logger 99 | } 100 | } 101 | 102 | if !CheckSection(key) { 103 | return gFakeLog 104 | } 105 | 106 | logBindingsMtx.RLock() 107 | defer logBindingsMtx.RUnlock() 108 | if lg, exists := logBindings[key]; exists { 109 | return lg 110 | } 111 | return logger 112 | } 113 | 114 | func FlushAll() { 115 | logBindingsMtx.RLock() 116 | defer logBindingsMtx.RUnlock() 117 | for _, lg := range logBindings { 118 | lg.Flush() 119 | } 120 | logger.Flush() 121 | } 122 | 123 | type priGlsKey struct{} 124 | 125 | // 警告: 调用这个接口的地方, 一定要记得使用结束后调用Cleanup, 否则会内存泄漏 126 | func SetGlsDefaultKey(key interface{}) { 127 | gls.Set(priGlsKey{}, key) 128 | } 129 | 130 | func CleanupGlsDefaultKey(key interface{}) { 131 | glsKey := GetGlsDefaultKey() 132 | if glsKey == key { 133 | gls.Del(priGlsKey{}) 134 | } 135 | } 136 | 137 | func GetGlsDefaultKey() interface{} { 138 | return gls.Get(priGlsKey{}) 139 | } 140 | 141 | // --------------------------------------------------- 142 | type Level int 143 | const ( 144 | lv_debug Level = iota 145 | lv_info 146 | lv_warning 147 | lv_error 148 | lv_fatal 149 | lv_record 150 | ) 151 | 152 | func (lv Level) ToString() string { 153 | switch lv { 154 | case lv_debug: 155 | return "Debug" 156 | case lv_info: 157 | return "Info" 158 | case lv_warning: 159 | return "Warning" 160 | case lv_error: 161 | return "Error" 162 | case lv_record: 163 | return "Record" 164 | case lv_fatal: 165 | return "Fatal" 166 | default: 167 | return "Unkown" 168 | } 169 | } 170 | 171 | func (lv Level) Header() string { 172 | switch lv { 173 | case lv_debug: 174 | return "D" 175 | case lv_info: 176 | return "I" 177 | case lv_warning: 178 | return "W" 179 | case lv_error: 180 | return "E" 181 | case lv_record: 182 | return "R" 183 | case lv_fatal: 184 | return "F" 185 | default: 186 | return "U" 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /log/logformat.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "strings" 7 | "bytes" 8 | ) 9 | 10 | const bytes_limit = 16 11 | const string_limit = 128 12 | 13 | // Provides a hook to intercept the execution of logformat. 14 | type LogFormatInterceptor func(name string, v reflect.Value, handler func(reflect.Value) string) string 15 | 16 | const KindCount = reflect.UnsafePointer 17 | 18 | type LogFormatOptions struct { 19 | Indent bool 20 | NoLimit bool 21 | Interceptors [KindCount]LogFormatInterceptor 22 | 23 | indentNum int 24 | } 25 | 26 | type LogFormatOption func(*LogFormatOptions) 27 | 28 | var EmptyLogFormatOptions LogFormatOptions = LogFormatOptions{} 29 | var IndentLogFormatOptions LogFormatOptions = LogFormatOptions{ Indent : true } 30 | var Indent LogFormatOptions = IndentLogFormatOptions 31 | var NoLimit LogFormatOptions = LogFormatOptions{ NoLimit : true } 32 | 33 | func IndentLogFormatOpt(opt *LogFormatOptions) { 34 | opt.Indent = true 35 | } 36 | 37 | func formatSlice(buf *bytes.Buffer, v reflect.Value, opt *LogFormatOptions) { 38 | if v.Len() == 0 { 39 | buf.WriteRune('[') 40 | buf.WriteRune(']') 41 | return 42 | } 43 | 44 | if v.Index(0).Kind() == reflect.Uint8 { 45 | if opt.NoLimit { 46 | formatHex(buf, v.Bytes()) 47 | } else { 48 | limitBytes(buf, v.Bytes()) 49 | } 50 | return 51 | } 52 | 53 | buf.WriteRune('[') 54 | for i := 0; i < v.Len(); i++ { 55 | e := v.Index(i) 56 | formatV(buf, "", e, opt) 57 | if i + 1 < v.Len() { 58 | buf.WriteRune(' ') 59 | } 60 | } 61 | buf.WriteRune(']') 62 | return 63 | } 64 | 65 | func formatArray(buf *bytes.Buffer, v reflect.Value, opt *LogFormatOptions) { 66 | buf.WriteRune('[') 67 | for i := 0; i < v.Len(); i++ { 68 | e := v.Index(i) 69 | formatV(buf, "", e, opt) 70 | if i + 1 < v.Len() { 71 | buf.WriteRune(' ') 72 | } 73 | } 74 | buf.WriteRune(']') 75 | } 76 | 77 | func formatStruct(buf *bytes.Buffer, struct_v reflect.Value, opt *LogFormatOptions) { 78 | typ := struct_v.Type() 79 | n_field := struct_v.NumField() 80 | for i := 0; i < n_field; i++ { 81 | v := struct_v.Field(i) 82 | field := typ.Field(i) 83 | 84 | if opt.Indent { 85 | buf.WriteRune('\n') 86 | buf.WriteString(strings.Repeat(" ", opt.indentNum)) 87 | } 88 | 89 | buf.WriteString(field.Name) 90 | buf.WriteRune(':') 91 | opt.indentNum++ 92 | formatV(buf, field.Name, v, opt) 93 | opt.indentNum-- 94 | 95 | if opt.Indent { 96 | buf.WriteRune(',') 97 | } else { 98 | if i + 1 < n_field { 99 | buf.WriteRune(',') 100 | buf.WriteRune(' ') 101 | } 102 | } 103 | } 104 | } 105 | 106 | func formatMap(buf *bytes.Buffer, map_v reflect.Value, opt *LogFormatOptions) { 107 | keys := map_v.MapKeys() 108 | var i int 109 | for _, key := range keys { 110 | value := map_v.MapIndex(key) 111 | 112 | if opt.Indent { 113 | buf.WriteRune('\n') 114 | buf.WriteString(strings.Repeat(" ", opt.indentNum)) 115 | } 116 | 117 | formatV(buf, "", key, opt) 118 | buf.WriteRune(':') 119 | opt.indentNum++ 120 | formatV(buf, "", value, opt) 121 | opt.indentNum-- 122 | if opt.Indent { 123 | buf.WriteRune(',') 124 | } else { 125 | if i + 1 < len(keys) { 126 | buf.WriteRune(',') 127 | buf.WriteRune(' ') 128 | } 129 | } 130 | i++ 131 | } 132 | } 133 | 134 | func limitString(buf *bytes.Buffer, v string) { 135 | if len(v) > string_limit { 136 | buf.WriteRune('"') 137 | buf.WriteString(v[:string_limit]) 138 | buf.WriteRune('"') 139 | buf.WriteString(fmt.Sprintf("(%d)", len(v))) 140 | } else { 141 | buf.WriteString(v) 142 | } 143 | } 144 | 145 | func formatHex(buf *bytes.Buffer, v []byte) { 146 | buf.WriteRune('[') 147 | for i, b := range v { 148 | buf.WriteString(fmt.Sprintf("%02x", b)) 149 | if i + 1 < len(v) { 150 | buf.WriteRune(' ') 151 | } 152 | } 153 | buf.WriteRune(']') 154 | return 155 | } 156 | 157 | func limitBytes(buf *bytes.Buffer, v []byte) { 158 | if len(v) > bytes_limit { 159 | formatHex(buf, v[:bytes_limit]) 160 | buf.WriteString(fmt.Sprintf("(%d)", len(v))) 161 | } else { 162 | formatHex(buf, v) 163 | } 164 | } 165 | 166 | func callMethod(method reflect.Value) (ret []reflect.Value) { 167 | defer func(){ 168 | if recover() != nil { 169 | return 170 | } 171 | }() 172 | ret = method.Call(nil) 173 | return 174 | } 175 | 176 | func formatV(buf *bytes.Buffer, name string, v reflect.Value, opt *LogFormatOptions) { 177 | if v.Kind() == reflect.Ptr { 178 | v = v.Elem() 179 | } 180 | 181 | handler := func(v reflect.Value) string { 182 | switch v.Kind() { 183 | case reflect.Struct: 184 | if toStringMethod := v.MethodByName("String"); toStringMethod.IsValid() { 185 | rets := callMethod(toStringMethod) 186 | if len(rets) > 0 { 187 | ret_v := rets[0] 188 | if ret_v.Kind() == reflect.String { 189 | buf.WriteString(ret_v.String()) 190 | break 191 | } 192 | } 193 | } 194 | 195 | if opt.Indent { 196 | buf.WriteRune('\n') 197 | buf.WriteString(strings.Repeat(" ", opt.indentNum)) 198 | buf.WriteRune('{') 199 | opt.indentNum++ 200 | formatStruct(buf, v, opt) 201 | opt.indentNum-- 202 | buf.WriteRune('\n') 203 | buf.WriteString(strings.Repeat(" ", opt.indentNum)) 204 | buf.WriteRune('}') 205 | } else { 206 | buf.WriteRune('{') 207 | formatStruct(buf, v, opt) 208 | buf.WriteRune('}') 209 | } 210 | case reflect.Map: 211 | if opt.Indent { 212 | buf.WriteRune('\n') 213 | buf.WriteString(strings.Repeat(" ", opt.indentNum)) 214 | buf.WriteRune('{') 215 | opt.indentNum++ 216 | formatMap(buf, v, opt) 217 | opt.indentNum-- 218 | buf.WriteRune('\n') 219 | buf.WriteString(strings.Repeat(" ", opt.indentNum)) 220 | buf.WriteRune('}') 221 | } else { 222 | buf.WriteRune('{') 223 | formatMap(buf, v, opt) 224 | buf.WriteRune('}') 225 | } 226 | case reflect.Slice: 227 | formatSlice(buf, v, opt) 228 | case reflect.String: 229 | if opt.NoLimit { 230 | buf.WriteString(v.String()) 231 | } else { 232 | limitString(buf, v.String()) 233 | } 234 | case reflect.Array: 235 | formatArray(buf, v, opt) 236 | case reflect.Bool: 237 | buf.WriteString(fmt.Sprintf("%v", v.Bool())) 238 | case reflect.Int: 239 | fallthrough 240 | case reflect.Int8: 241 | fallthrough 242 | case reflect.Int16: 243 | fallthrough 244 | case reflect.Int32: 245 | fallthrough 246 | case reflect.Int64: 247 | buf.WriteString(fmt.Sprintf("%v", v.Int())) 248 | case reflect.Uint: 249 | fallthrough 250 | case reflect.Uint8: 251 | fallthrough 252 | case reflect.Uint16: 253 | fallthrough 254 | case reflect.Uint32: 255 | fallthrough 256 | case reflect.Uint64: 257 | buf.WriteString(fmt.Sprintf("%v", v.Uint())) 258 | case reflect.Float32: 259 | fallthrough 260 | case reflect.Float64: 261 | buf.WriteString(fmt.Sprintf("%v", v.Float())) 262 | } 263 | 264 | return "" 265 | } 266 | 267 | if opt.Interceptors[v.Kind()] != nil { 268 | buf.WriteString(opt.Interceptors[v.Kind()](name, v, handler)) 269 | } else { 270 | handler(v) 271 | } 272 | } 273 | 274 | func LogFormat(req interface{}, opt ... LogFormatOptions) string { 275 | var o *LogFormatOptions = &EmptyLogFormatOptions 276 | if len(opt) > 0 { 277 | o = &opt[0] 278 | } 279 | buf := bytes.NewBufferString("") 280 | formatV(buf, "", reflect.ValueOf(req), o) 281 | return buf.String() 282 | } 283 | 284 | /* 285 | type Base struct { 286 | x []byte 287 | } 288 | 289 | type A struct { 290 | I int 291 | I64 int64 292 | S string 293 | B []byte 294 | X Base 295 | } 296 | 297 | 298 | func main() { 299 | a := A{ B : []byte(" \\0wsfweifjwoeifjweofjwoefiewjfoiwefweosjidfowoefjweoiofwejfiewjfowejfewfowiejfoiwejofjwoe") } 300 | a.B[0] = 0 301 | a.S = "ccwjoeifojweifhowejofwejofwejfowejfooewfjwefjojfiwjowfojfwjwjffejowjoeifojweifhowejofwejofwejfowejfooewfjwefjojfiwjowfojfwjwjffejo" 302 | fmt.Printf("A:%s", LogFormat(a)) 303 | } 304 | */ 305 | 306 | -------------------------------------------------------------------------------- /log/ringbuf.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "io" 5 | "sync" 6 | "bytes" 7 | ) 8 | 9 | type Buffer struct { 10 | mtx sync.RWMutex 11 | buf *bytes.Buffer 12 | skipSize int 13 | useCount int 14 | } 15 | 16 | func NewBuffer(bufSkipSize int) *Buffer { 17 | return &Buffer{ 18 | buf : bytes.NewBuffer([]byte{}), 19 | skipSize : bufSkipSize, 20 | } 21 | } 22 | 23 | func (this *Buffer) FlushTo(w io.Writer) (n int, err error) { 24 | this.mtx.Lock() 25 | defer this.mtx.Unlock() 26 | if this.buf.Len() == 0 { 27 | return 28 | } 29 | n, err = w.Write(this.buf.Bytes()) 30 | this.buf.Reset() 31 | return 32 | } 33 | 34 | func (this *Buffer) commit(b []byte) (n int, err error) { 35 | this.mtx.Lock() 36 | defer this.mtx.Unlock() 37 | return this.buf.Write(b) 38 | } 39 | 40 | func (this *Buffer) inc() { 41 | this.useCount ++ 42 | } 43 | 44 | func (this *Buffer) dec() { 45 | this.useCount -- 46 | } 47 | 48 | func (this *Buffer) full() bool { 49 | return this.buf.Len() >= this.skipSize 50 | } 51 | 52 | type BufferWrap struct { 53 | *Buffer 54 | poolBuf *bytes.Buffer 55 | } 56 | 57 | func (this *BufferWrap) Write(b []byte) (n int, err error) { 58 | return this.poolBuf.Write(b) 59 | } 60 | 61 | func (this *BufferWrap) WriteString(s string) (n int, err error) { 62 | return this.poolBuf.WriteString(s) 63 | } 64 | 65 | var gBufPool = sync.Pool{ 66 | New : func() interface{} { 67 | return bytes.NewBufferString("") 68 | }, 69 | } 70 | 71 | func NewBufferWrap(base *Buffer) *BufferWrap { 72 | return &BufferWrap{base, gBufPool.Get().(*bytes.Buffer)} 73 | } 74 | 75 | func (this *BufferWrap) commit() { 76 | if this.poolBuf.Len() > 0 { 77 | this.Buffer.commit(this.poolBuf.Bytes()) 78 | this.poolBuf.Reset() 79 | } 80 | gBufPool.Put(this.poolBuf) 81 | } 82 | 83 | type RingBuf struct { 84 | bufs []*Buffer 85 | r int 86 | w int 87 | mtx sync.RWMutex 88 | } 89 | 90 | func NewRingBuf(nBufs int, bufSkipSize int) *RingBuf { 91 | rb := &RingBuf{} 92 | rb.bufs = make([]*Buffer, nBufs, nBufs) 93 | for i := 0; i < nBufs; i++{ 94 | rb.bufs[i] = NewBuffer(bufSkipSize) 95 | } 96 | return rb 97 | } 98 | 99 | func (this *RingBuf) GetWriter() *BufferWrap { 100 | this.mtx.Lock() 101 | defer this.mtx.Unlock() 102 | buf := this.bufs[this.w] 103 | buf.inc() 104 | return NewBufferWrap(buf) 105 | } 106 | 107 | func (this *RingBuf) Put(bufw *BufferWrap) { 108 | bufw.commit() 109 | 110 | this.mtx.Lock() 111 | defer this.mtx.Unlock() 112 | bufw.dec() 113 | if bufw.full() { 114 | // skipW 115 | this.skipW(); 116 | } 117 | } 118 | 119 | func (this *RingBuf) NextReader() *Buffer { 120 | this.mtx.Lock() 121 | defer this.mtx.Unlock() 122 | return this.nextReaderWithoutLock() 123 | } 124 | 125 | func (this *RingBuf) nextReaderWithoutLock() *Buffer { 126 | buf := this.bufs[this.r] 127 | if this.r == this.w { 128 | if buf.buf.Len() <= 0 { 129 | return nil 130 | } 131 | 132 | this.skipW(); 133 | } 134 | 135 | if buf.useCount == 0 { 136 | this.skipR() 137 | return buf 138 | } 139 | 140 | return nil 141 | } 142 | 143 | func (this *RingBuf) FlushTo(w io.Writer) (err error) { 144 | for { 145 | buf := this.NextReader() 146 | if buf == nil { 147 | return 148 | } 149 | 150 | _, err = buf.FlushTo(w) 151 | if err != nil { 152 | return 153 | } 154 | } 155 | return 156 | } 157 | 158 | func (this *RingBuf) LockFlushTo(w io.Writer) (err error) { 159 | this.mtx.Lock() 160 | defer this.mtx.Unlock() 161 | for { 162 | buf := this.nextReaderWithoutLock() 163 | if buf == nil { 164 | return 165 | } 166 | 167 | _, err = buf.FlushTo(w) 168 | if err != nil { 169 | return 170 | } 171 | } 172 | return 173 | } 174 | 175 | func (this *RingBuf) skipW() { 176 | newW := (this.w + 1) % len(this.bufs) 177 | if newW == this.r { 178 | return 179 | } 180 | this.w = newW 181 | } 182 | 183 | func (this *RingBuf) skipR() { 184 | this.r = (this.r + 1) % len(this.bufs) 185 | } 186 | -------------------------------------------------------------------------------- /log/stdlog.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "os" 7 | ) 8 | 9 | type StdLog struct { 10 | opt *LogOption 11 | } 12 | 13 | func (this *StdLog) Clone(opt *LogOption) (LogI, error) { 14 | return &StdLog{opt : opt}, nil 15 | } 16 | 17 | func (this *StdLog) write(level Level, info string) { 18 | if this.opt == nil { 19 | this.opt = DefaultLogOption() 20 | } 21 | if this.opt.Ignore { 22 | return 23 | } 24 | buf := bytes.NewBufferString("") 25 | this.opt.WriteHeader(level, 3, buf) 26 | buf.WriteString(info) 27 | fmt.Println(buf.String()) 28 | } 29 | 30 | func (this *StdLog) logf(level Level, format string, args ... interface{}) { 31 | this.write(level, fmt.Sprintf(format, args...)) 32 | } 33 | func (this *StdLog) logln(level Level, args ... interface{}) { 34 | this.write(level, fmt.Sprintln(args...)) 35 | } 36 | func (this *StdLog) Debugf(format string, args ... interface{}) { 37 | this.logf(lv_debug, format, args...) 38 | } 39 | func (this *StdLog) Infof(format string, args ... interface{}) { 40 | this.logf(lv_info, format, args...) 41 | } 42 | func (this *StdLog) Warningf(format string, args ... interface{}) { 43 | this.logf(lv_warning, format, args...) 44 | } 45 | func (this *StdLog) Errorf(format string, args ... interface{}) { 46 | this.logf(lv_error, format, args...) 47 | } 48 | func (this *StdLog) Fatalf(format string, args ... interface{}) { 49 | this.logf(lv_fatal, format, args...) 50 | } 51 | func (this *StdLog) Recordf(format string, args ... interface{}) { 52 | this.logf(lv_record, format, args...) 53 | } 54 | func (this *StdLog) Debugln(args ... interface{}) { 55 | this.logln(lv_debug, args...) 56 | } 57 | func (this *StdLog) Infoln(args ... interface{}) { 58 | this.logln(lv_info, args...) 59 | } 60 | func (this *StdLog) Warningln(args ... interface{}) { 61 | this.logln(lv_warning, args...) 62 | } 63 | func (this *StdLog) Errorln(args ... interface{}) { 64 | this.logln(lv_error, args...) 65 | } 66 | func (this *StdLog) Fatalln(args ... interface{}) { 67 | this.logln(lv_fatal, args...) 68 | } 69 | func (this *StdLog) Recordln(args ... interface{}) { 70 | this.logln(lv_record, args...) 71 | } 72 | func (this *StdLog) Flush() error { 73 | return os.Stdout.Sync() 74 | } 75 | -------------------------------------------------------------------------------- /log/toggle.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "sync" 5 | ) 6 | 7 | var sections = make(map[interface{}]bool) 8 | var sectionMu sync.RWMutex 9 | 10 | func CheckSection(opt interface{}) bool { 11 | if len(sections) == 0 { 12 | return true 13 | } 14 | 15 | sectionMu.RLock() 16 | defer sectionMu.RUnlock() 17 | if ok, exists := sections[opt]; exists { 18 | return ok 19 | } 20 | return true 21 | } 22 | 23 | func EnableSection(opt interface{}) { 24 | sectionMu.Lock() 25 | defer sectionMu.Unlock() 26 | sections[opt] = true 27 | } 28 | 29 | func DisableSection(opt interface{}) { 30 | sectionMu.Lock() 31 | defer sectionMu.Unlock() 32 | sections[opt] = false 33 | } 34 | 35 | type FakeLog struct {} 36 | func (this *FakeLog) Clone(opt *LogOption) (LogI, error) { return &FakeLog{}, nil } 37 | func (this *FakeLog) Debugf(format string, args ... interface{}) {} 38 | func (this *FakeLog) Infof(format string, args ... interface{}) {} 39 | func (this *FakeLog) Warningf(format string, args ... interface{}) {} 40 | func (this *FakeLog) Errorf(format string, args ... interface{}) {} 41 | func (this *FakeLog) Fatalf(format string, args ... interface{}) {} 42 | func (this *FakeLog) Recordf(format string, args ... interface{}) {} 43 | func (this *FakeLog) Debugln(args ... interface{}) {} 44 | func (this *FakeLog) Infoln(args ... interface{}) {} 45 | func (this *FakeLog) Warningln(args ... interface{}) {} 46 | func (this *FakeLog) Errorln(args ... interface{}) {} 47 | func (this *FakeLog) Fatalln(args ... interface{}) {} 48 | func (this *FakeLog) Recordln(args ... interface{}) {} 49 | func (this *FakeLog) Flush() error { return nil } 50 | 51 | var gFakeLog = &FakeLog{} 52 | -------------------------------------------------------------------------------- /option/option.go: -------------------------------------------------------------------------------- 1 | package option 2 | 3 | type OptionI interface{} 4 | 5 | // 各协议自定义的option必须继承于Option 6 | type Option struct { 7 | OptionI 8 | 9 | TimeoutMilliseconds int64 10 | } 11 | 12 | func DefaultOption() *Option { 13 | return &Option{} 14 | } 15 | -------------------------------------------------------------------------------- /protocol/client.go: -------------------------------------------------------------------------------- 1 | package protocol 2 | 3 | import ( 4 | "golang.org/x/net/context" 5 | A "github.com/yyzybb537/ketty/aop" 6 | O "github.com/yyzybb537/ketty/option" 7 | COM "github.com/yyzybb537/ketty/common" 8 | ) 9 | 10 | type Client interface { 11 | A.AopListI 12 | 13 | SetOption(opt O.OptionI) error 14 | 15 | Invoke(ctx context.Context, handle COM.ServiceHandle, method string, req, rsp interface{}) error 16 | 17 | Close() 18 | } 19 | 20 | -------------------------------------------------------------------------------- /protocol/grpc/client.go: -------------------------------------------------------------------------------- 1 | package grpc_proto 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | A "github.com/yyzybb537/ketty/aop" 7 | O "github.com/yyzybb537/ketty/option" 8 | COM "github.com/yyzybb537/ketty/common" 9 | C "github.com/yyzybb537/ketty/context" 10 | U "github.com/yyzybb537/ketty/url" 11 | "golang.org/x/net/context" 12 | "google.golang.org/grpc" 13 | ) 14 | 15 | type GrpcClient struct { 16 | A.AopList 17 | 18 | Impl *grpc.ClientConn 19 | url U.Url 20 | opt *GrpcOption 21 | } 22 | 23 | func newGrpcClient(url U.Url) (*GrpcClient, error) { 24 | c := new(GrpcClient) 25 | c.url = url 26 | c.opt = defaultGrpcOption() 27 | err := c.dial(url) 28 | if err != nil { 29 | return nil, err 30 | } 31 | return c, nil 32 | } 33 | 34 | func (this *GrpcClient) SetOption(opt O.OptionI) error { 35 | return this.opt.set(opt) 36 | } 37 | 38 | func (this *GrpcClient) dial(url U.Url) (err error) { 39 | this.Impl, err = grpc.Dial(url.SAddr, grpc.WithInsecure(), 40 | grpc.WithPerRPCCredentials(newGrpcMeta(this))) 41 | return 42 | } 43 | 44 | func (this *GrpcClient) Close() { 45 | this.Impl.Close() 46 | } 47 | 48 | func (this *GrpcClient) Invoke(ctx context.Context, handle COM.ServiceHandle, method string, req, rsp interface{}) error { 49 | ctx = this.invoke(ctx, handle, method, req, rsp) 50 | return ctx.Err() 51 | } 52 | 53 | func (this *GrpcClient) invoke(inCtx context.Context, handle COM.ServiceHandle, method string, req, rsp interface{}) (ctx context.Context) { 54 | var err error 55 | if this.opt.TimeoutMilliseconds != 0 { 56 | ctx, _ = context.WithTimeout(inCtx, time.Duration(this.opt.TimeoutMilliseconds) * time.Millisecond) 57 | }else { 58 | ctx = inCtx 59 | } 60 | fullMethodName := fmt.Sprintf("/%s/%s", handle.ServiceName(), method) 61 | aopList := A.GetAop(ctx) 62 | if aopList != nil { 63 | ctx = context.WithValue(ctx, "method", fullMethodName) 64 | ctx = context.WithValue(ctx, "remote", this.url.SAddr) 65 | metadata := map[string]string{} 66 | ctx = context.WithValue(ctx, "metadata", metadata) 67 | 68 | for _, aop := range aopList { 69 | caller, ok := aop.(A.ClientTransportMetaDataAop) 70 | if ok { 71 | ctx = caller.ClientSendMetaData(ctx, metadata) 72 | } 73 | } 74 | 75 | for _, aop := range aopList { 76 | caller, ok := aop.(A.BeforeClientInvokeAop) 77 | if ok { 78 | ctx = caller.BeforeClientInvoke(ctx, req) 79 | } 80 | } 81 | 82 | defer func() { 83 | for _, aop := range aopList { 84 | caller, ok := aop.(A.ClientInvokeCleanupAop) 85 | if ok { 86 | caller.ClientCleanup(ctx) 87 | } 88 | } 89 | }() 90 | 91 | for i, _ := range aopList { 92 | aop := aopList[len(aopList)-i-1] 93 | caller, ok := aop.(A.AfterClientInvokeAop) 94 | if ok { 95 | defer caller.AfterClientInvoke(&ctx, req, rsp) 96 | } 97 | } 98 | } 99 | 100 | if ctx.Err() != nil { 101 | return 102 | } 103 | 104 | err = grpc.Invoke(ctx, fullMethodName, req, rsp, this.Impl) 105 | if err != nil { 106 | ctx = C.WithError(ctx, err) 107 | } 108 | 109 | return 110 | } 111 | -------------------------------------------------------------------------------- /protocol/grpc/grpc.go: -------------------------------------------------------------------------------- 1 | package grpc_proto 2 | 3 | import ( 4 | U "github.com/yyzybb537/ketty/url" 5 | P "github.com/yyzybb537/ketty/protocol" 6 | "google.golang.org/grpc" 7 | ) 8 | 9 | type GrpcProtocol struct {} 10 | 11 | func init() { 12 | P.RegProtocol("grpc", new(GrpcProtocol)) 13 | grpc.EnableTracing = false 14 | } 15 | 16 | func (this *GrpcProtocol) CreateServer(url, driverUrl U.Url) (P.Server, error) { 17 | return newGrpcServer(url, driverUrl), nil 18 | } 19 | 20 | func (this *GrpcProtocol) Dial(url U.Url) (P.Client, error) { 21 | return newGrpcClient(url) 22 | } 23 | -------------------------------------------------------------------------------- /protocol/grpc/meta.go: -------------------------------------------------------------------------------- 1 | package grpc_proto 2 | 3 | import ( 4 | "golang.org/x/net/context" 5 | ) 6 | 7 | type GrpcMeta struct { 8 | owner *GrpcClient 9 | } 10 | 11 | func newGrpcMeta(owner *GrpcClient) *GrpcMeta { 12 | return &GrpcMeta{owner : owner} 13 | } 14 | 15 | func (this *GrpcMeta) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) { 16 | metadata, ok := ctx.Value("metadata").(map[string]string) 17 | if !ok { 18 | return map[string]string{}, nil 19 | } 20 | 21 | return metadata, nil 22 | } 23 | 24 | func (this *GrpcMeta) RequireTransportSecurity() bool { 25 | return false 26 | } 27 | -------------------------------------------------------------------------------- /protocol/grpc/option.go: -------------------------------------------------------------------------------- 1 | package grpc_proto 2 | 3 | import ( 4 | O "github.com/yyzybb537/ketty/option" 5 | "github.com/pkg/errors" 6 | "reflect" 7 | ) 8 | 9 | type GrpcOption struct { 10 | O.Option 11 | } 12 | 13 | func defaultGrpcOption() *GrpcOption { 14 | return new(GrpcOption) 15 | } 16 | 17 | func (this *GrpcOption) set(opt O.OptionI) error { 18 | if o, ok := opt.(*GrpcOption); ok { 19 | *this = *o 20 | } else if o, ok := opt.(GrpcOption); ok { 21 | *this = o 22 | } else if o, ok := opt.(*O.Option); ok { 23 | this.Option = *o 24 | } else if o, ok := opt.(O.Option); ok { 25 | this.Option = o 26 | } else { 27 | typ := reflect.TypeOf(opt) 28 | return errors.Errorf("SetOption argument error. opt={isptr:%t, className:%s}", typ.Kind() == reflect.Ptr, typ.String()) 29 | } 30 | return nil 31 | } 32 | -------------------------------------------------------------------------------- /protocol/grpc/server.go: -------------------------------------------------------------------------------- 1 | package grpc_proto 2 | 3 | import ( 4 | C "github.com/yyzybb537/ketty/context" 5 | D "github.com/yyzybb537/ketty/driver" 6 | O "github.com/yyzybb537/ketty/option" 7 | COM "github.com/yyzybb537/ketty/common" 8 | U "github.com/yyzybb537/ketty/url" 9 | A "github.com/yyzybb537/ketty/aop" 10 | "github.com/yyzybb537/ketty/log" 11 | "golang.org/x/net/context" 12 | "google.golang.org/grpc" 13 | "google.golang.org/grpc/peer" 14 | md "google.golang.org/grpc/metadata" 15 | "net" 16 | "github.com/yyzybb537/gls" 17 | ) 18 | 19 | type GrpcServer struct { 20 | A.AopList 21 | 22 | url U.Url 23 | driverUrl U.Url 24 | Impl *grpc.Server 25 | opt *GrpcOption 26 | logKey interface{} 27 | } 28 | 29 | func newGrpcServer(url, driverUrl U.Url) *GrpcServer { 30 | s := &GrpcServer{ 31 | url: url, 32 | opt : defaultGrpcOption(), 33 | driverUrl: driverUrl, 34 | } 35 | s.Impl = grpc.NewServer(grpc.UnaryInterceptor(s.serverIntercept())) 36 | return s 37 | } 38 | 39 | func (this *GrpcServer) SetOption(opt O.OptionI) error { 40 | return this.opt.set(opt) 41 | } 42 | 43 | func (this *GrpcServer) RegisterMethod(handle COM.ServiceHandle, implement interface{}) error { 44 | this.Impl.RegisterService(handle.Implement().(*grpc.ServiceDesc), implement) 45 | return nil 46 | } 47 | 48 | func (this *GrpcServer) Serve() error { 49 | this.logKey = log.GetGlsDefaultKey() 50 | 51 | addrs := this.url.GetAddrs() 52 | for _, addr := range addrs { 53 | lis, err := net.Listen("tcp", addr) 54 | if err != nil { 55 | return err 56 | } 57 | 58 | gls.Go(func() { 59 | err := this.Impl.Serve(lis) 60 | if err != nil { 61 | log.GetLog().Errorf("Serve lis error:%s. addr:%s", err.Error(), addr) 62 | } 63 | }) 64 | } 65 | 66 | if !this.driverUrl.IsEmpty() { 67 | driver, err := D.GetDriver(this.driverUrl.Protocol) 68 | if err != nil { 69 | return err 70 | } 71 | 72 | err = driver.Register(this.driverUrl, this.url) 73 | if err != nil { 74 | return err 75 | } 76 | } 77 | 78 | return nil 79 | } 80 | 81 | func (this *GrpcServer) serverIntercept() grpc.UnaryServerInterceptor { 82 | return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, 83 | handler grpc.UnaryHandler) (rsp interface{}, err error) { 84 | rsp, err = this.unaryServerInterceptor(ctx, req, info, handler) 85 | return 86 | } 87 | } 88 | 89 | func (this *GrpcServer) unaryServerInterceptor(ctx context.Context, req interface{}, 90 | info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (rsp interface{}, err error) { 91 | //log.GetLog().Infof("unaryServerInterceptor ctx=%+v", ctx) 92 | rsp, ctx = this.unaryServerInterceptorWithContext(ctx, req, info, handler) 93 | err = ctx.Err() 94 | //log.GetLog().Infof("unaryServerInterceptor error:%v", err) 95 | return 96 | } 97 | 98 | func (this *GrpcServer) unaryServerInterceptorWithContext(inCtx context.Context, req interface{}, 99 | info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (rsp interface{}, ctx context.Context) { 100 | 101 | if this.logKey != nil { 102 | log.SetGlsDefaultKey(this.logKey) 103 | defer log.CleanupGlsDefaultKey(this.logKey) 104 | } 105 | 106 | var err error 107 | ctx = inCtx 108 | aopList := this.GetAop() 109 | if aopList != nil { 110 | ctx = context.WithValue(ctx, "method", info.FullMethod) 111 | p, ok := peer.FromContext(ctx) 112 | if ok { 113 | ctx = context.WithValue(ctx, "remote", p.Addr.String()) 114 | } else { 115 | ctx = context.WithValue(ctx, "remote", "") 116 | } 117 | metadata := map[string]string{} 118 | 119 | grpcMD, hasMetaData := md.FromIncomingContext(ctx) 120 | //log.GetLog().Debugf("server hasMetaData:%v meta:%v", hasMetaData, grpcMD) 121 | if hasMetaData { 122 | for k, v := range grpcMD { 123 | if len(v) > 0 { 124 | metadata[k] = v[0] 125 | } 126 | } 127 | } 128 | 129 | for _, aop := range aopList { 130 | caller, ok := aop.(A.ServerTransportMetaDataAop) 131 | if ok { 132 | ctx = caller.ServerRecvMetaData(ctx, metadata) 133 | } 134 | } 135 | 136 | for _, aop := range aopList { 137 | caller, ok := aop.(A.BeforeServerInvokeAop) 138 | if ok { 139 | ctx = caller.BeforeServerInvoke(ctx, req) 140 | } 141 | } 142 | 143 | defer func() { 144 | for _, aop := range aopList { 145 | caller, ok := aop.(A.ServerInvokeCleanupAop) 146 | if ok { 147 | caller.ServerCleanup(ctx) 148 | } 149 | } 150 | }() 151 | 152 | for i, _ := range aopList { 153 | aop := aopList[len(aopList) - i - 1] 154 | caller, ok := aop.(A.AfterServerInvokeAop) 155 | if ok { 156 | if _, isException := caller.(*A.ExceptionAop); isException { 157 | defer caller.AfterServerInvoke(&ctx, req, rsp) 158 | } else { 159 | defer func() { 160 | caller.AfterServerInvoke(&ctx, req, rsp) 161 | }() 162 | } 163 | } 164 | } 165 | } 166 | 167 | if ctx.Err() != nil { 168 | return 169 | } 170 | 171 | rsp, err = handler(ctx, req) 172 | if err != nil { 173 | ctx = C.WithError(ctx, err) 174 | return 175 | } 176 | 177 | return 178 | } 179 | -------------------------------------------------------------------------------- /protocol/http/client.go: -------------------------------------------------------------------------------- 1 | package http_proto 2 | 3 | import ( 4 | "crypto/tls" 5 | "encoding/json" 6 | "fmt" 7 | "time" 8 | "github.com/golang/protobuf/proto" 9 | "github.com/pkg/errors" 10 | A "github.com/yyzybb537/ketty/aop" 11 | COM "github.com/yyzybb537/ketty/common" 12 | C "github.com/yyzybb537/ketty/context" 13 | O "github.com/yyzybb537/ketty/option" 14 | P "github.com/yyzybb537/ketty/protocol" 15 | U "github.com/yyzybb537/ketty/url" 16 | "golang.org/x/net/context" 17 | "io/ioutil" 18 | "net/http" 19 | "reflect" 20 | "strings" 21 | ) 22 | 23 | type HttpClient struct { 24 | A.AopList 25 | 26 | url U.Url 27 | tr *http.Transport 28 | client *http.Client 29 | prt *Proto 30 | opt *HttpOption 31 | } 32 | 33 | func newHttpClient(url U.Url) (*HttpClient, error) { 34 | c := new(HttpClient) 35 | c.url = url 36 | var err error 37 | c.prt, err = ParseProto(url.Protocol) 38 | if err != nil { 39 | return nil, err 40 | } 41 | c.opt = defaultHttpOption() 42 | //SetOption的话需要重新设置超时等等选项 43 | c.tr = &http.Transport{ 44 | TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, 45 | } 46 | c.client = &http.Client{Transport: c.tr} 47 | return c, nil 48 | } 49 | 50 | func (this *HttpClient) SetOption(opt O.OptionI) error { 51 | err := this.opt.set(opt) 52 | if err != nil { 53 | return err 54 | } 55 | this.tr = DefaultTransportManager.GetTransport(time.Duration(this.opt.ConnectTimeoutMillseconds) * time.Millisecond, 56 | time.Duration(this.opt.ReadWriteTimeoutMillseconds) * time.Millisecond, 57 | time.Duration(this.opt.ResponseHeaderTimeoutMillseconds) * time.Millisecond) 58 | this.client.Timeout = time.Duration(this.opt.TimeoutMilliseconds) * time.Millisecond 59 | return nil 60 | } 61 | 62 | func (this *HttpClient) Close() { 63 | this.tr.CloseIdleConnections() 64 | } 65 | 66 | func (this *HttpClient) getUrl() string { 67 | return this.url.ToStringByProtocol(this.url.GetMainProtocol()) 68 | } 69 | 70 | func (this *HttpClient) Invoke(ctx context.Context, handle COM.ServiceHandle, method string, req, rsp interface{}) error { 71 | pbReq, ok := req.(proto.Message) 72 | if !ok { 73 | return fmt.Errorf("Invoke req is not proto.Message") 74 | } 75 | pbRsp, ok := rsp.(proto.Message) 76 | if !ok { 77 | return fmt.Errorf("Invoke rsp is not proto.Message") 78 | } 79 | ctx = this.invoke(ctx, handle, method, pbReq, pbRsp) 80 | return ctx.Err() 81 | } 82 | 83 | func (this *HttpClient) invoke(inCtx context.Context, handle COM.ServiceHandle, method string, req, rsp proto.Message) (ctx context.Context) { 84 | var err error 85 | ctx = inCtx 86 | fullMethodName := fmt.Sprintf("/%s/%s", strings.Replace(handle.ServiceName(), ".", "/", -1), method) 87 | metadata := map[string]string{} 88 | 89 | httpRequest, err := http.NewRequest(strings.ToUpper(this.prt.DefaultMethod), this.getUrl(), nil) 90 | ctx = C.WithError(ctx, errors.WithStack(err)) 91 | 92 | aopList := A.GetAop(ctx) 93 | if aopList != nil { 94 | ctx = context.WithValue(ctx, "method", fullMethodName) 95 | ctx = context.WithValue(ctx, "remote", this.getUrl()) 96 | ctx = setHttpRequest(ctx, httpRequest) 97 | 98 | for _, aop := range aopList { 99 | caller, ok := aop.(A.ClientTransportMetaDataAop) 100 | if ok { 101 | ctx = caller.ClientSendMetaData(ctx, metadata) 102 | } 103 | } 104 | 105 | for _, aop := range aopList { 106 | caller, ok := aop.(A.BeforeClientInvokeAop) 107 | if ok { 108 | ctx = caller.BeforeClientInvoke(ctx, req) 109 | } 110 | } 111 | 112 | defer func() { 113 | for _, aop := range aopList { 114 | caller, ok := aop.(A.ClientInvokeCleanupAop) 115 | if ok { 116 | caller.ClientCleanup(ctx) 117 | } 118 | } 119 | }() 120 | 121 | for i, _ := range aopList { 122 | aop := aopList[len(aopList)-i-1] 123 | caller, ok := aop.(A.AfterClientInvokeAop) 124 | if ok { 125 | defer caller.AfterClientInvoke(&ctx, req, rsp) 126 | } 127 | } 128 | } 129 | 130 | if ctx.Err() != nil { 131 | return 132 | } 133 | 134 | headers := map[string]string{ 135 | "KettyMethod": fullMethodName, 136 | } 137 | 138 | // 鉴权数据用Header发送 139 | if authorization, exists := metadata[COM.AuthorizationMetaKey]; exists { 140 | headers["Authorization"] = authorization 141 | delete(metadata, COM.AuthorizationMetaKey) 142 | } 143 | 144 | if len(metadata) > 0 { 145 | var metadataBuf []byte 146 | metadataBuf, err = json.Marshal(metadata) 147 | if err != nil { 148 | ctx = C.WithError(ctx, errors.WithStack(err)) 149 | return 150 | } 151 | 152 | headers["KettyMetaData"] = string(metadataBuf) 153 | } 154 | 155 | for k, v := range headers { 156 | httpRequest.Header.Set(k, v) 157 | } 158 | 159 | err = this.doHttpRequest(httpRequest, req, rsp) 160 | if err != nil { 161 | ctx = C.WithError(ctx, err) 162 | return 163 | } 164 | 165 | return 166 | } 167 | 168 | func (this *HttpClient) doHttpRequest(httpRequest *http.Request, req, rsp proto.Message) (err error) { 169 | err = this.writeMessage(httpRequest, req) 170 | if err != nil { 171 | err = errors.WithStack(err) 172 | return 173 | } 174 | 175 | httpResponse, err := this.client.Do(httpRequest) 176 | if err != nil { 177 | err = errors.WithStack(err) 178 | return 179 | } 180 | defer httpResponse.Body.Close() 181 | 182 | var buf []byte 183 | buf, err = ioutil.ReadAll(httpResponse.Body) 184 | if err != nil { 185 | err = errors.WithStack(err) 186 | return 187 | } 188 | 189 | if httpResponse.StatusCode == http.StatusBadRequest { 190 | err = errors.Errorf(string(buf)) 191 | return 192 | } 193 | 194 | if httpResponse.StatusCode != http.StatusOK { 195 | err = errors.Errorf("error http status:%d", httpResponse.StatusCode) 196 | return 197 | } 198 | 199 | mr, _ := P.MgrMarshaler.Get(this.prt.DefaultMarshaler).(P.Marshaler) 200 | err = mr.Unmarshal(buf, rsp) 201 | if err != nil { 202 | err = errors.WithStack(err) 203 | return 204 | } 205 | 206 | return 207 | } 208 | 209 | func (this *HttpClient) writeMessage(httpRequest *http.Request, req proto.Message) error { 210 | _, isKettyHttpExtend := req.(KettyHttpExtend) 211 | if !isKettyHttpExtend { 212 | // Not extend, use default or pb setttings. 213 | sTr := this.prt.DefaultTransport 214 | if dtr, ok := req.(DefineTransport); ok { 215 | sTr = dtr.KettyTransport() 216 | } 217 | sMr := this.prt.DefaultMarshaler 218 | if dmr, ok := req.(DefineMarshaler); ok { 219 | sMr = dmr.KettyMarshal() 220 | } 221 | if sTr == "query" { 222 | sMr = "querystring" 223 | } 224 | 225 | mr, _ := P.MgrMarshaler.Get(sMr).(P.Marshaler) 226 | buf, err := mr.Marshal(req) 227 | if err != nil { 228 | return err 229 | } 230 | tr, _ := MgrTransport.Get(sTr).(DataTransport) 231 | err = tr.Write(httpRequest, buf) 232 | if err != nil { 233 | return err 234 | } 235 | 236 | return nil 237 | } 238 | 239 | // use http extend 240 | typ := reflect.TypeOf(req).Elem() 241 | val := reflect.ValueOf(req).Elem() 242 | trMap := map[string]bool{} 243 | numFields := typ.NumField() 244 | for i := 0; i < numFields; i++ { 245 | fvalue := val.Field(i) 246 | ftype := typ.Field(i).Type 247 | if !ftype.ConvertibleTo(typeProtoMessage) { 248 | return fmt.Errorf("Use http extend message, all of fields must be proto.Message! Error message name is %s", typ.Name()) 249 | } 250 | 251 | if fvalue.Interface() == nil { 252 | // skip nil message 253 | continue 254 | } 255 | 256 | sTr := this.prt.DefaultTransport 257 | if ftype.ConvertibleTo(typeDefineTransport) { 258 | sTr = fvalue.Convert(typeDefineTransport).Interface().(DefineTransport).KettyTransport() 259 | } 260 | 261 | // check tr unique 262 | if _, exists := trMap[sTr]; exists { 263 | return fmt.Errorf("The message used http extend, transport must be unique. Too many field use transport:%s", sTr) 264 | } 265 | trMap[sTr] = true 266 | 267 | sMr := this.prt.DefaultMarshaler 268 | if ftype.ConvertibleTo(typeDefineMarshaler) { 269 | sMr = fvalue.Convert(typeDefineMarshaler).Interface().(DefineMarshaler).KettyMarshal() 270 | } 271 | if sTr == "query" { 272 | sMr = "querystring" 273 | } 274 | mr, ok := P.MgrMarshaler.Get(sMr).(P.Marshaler) 275 | if !ok { 276 | return fmt.Errorf("Unknown marshal(%s) in message:%s", sMr, ftype.Name()) 277 | } 278 | buf, err := mr.Marshal(fvalue.Interface().(proto.Message)) 279 | if err != nil { 280 | return err 281 | } 282 | 283 | tr, ok := MgrTransport.Get(sTr).(DataTransport) 284 | if !ok { 285 | return fmt.Errorf("Unknown transport(%s) in message:%s", sTr, ftype.Name()) 286 | } 287 | err = tr.Write(httpRequest, buf) 288 | if err != nil { 289 | return err 290 | } 291 | 292 | if sTr == "body" { 293 | var contentType string 294 | switch sMr { 295 | case "pb": 296 | contentType = "application/octet-stream" 297 | case "querystring": 298 | contentType = "application/x-www-form-urlencoded" 299 | case "multipart": 300 | contentType = "multipart/form-data; boundary=" + DefaultMultipartBoundary 301 | case "json": 302 | contentType = "application/json" 303 | default: 304 | contentType = "text/plain" 305 | } 306 | httpRequest.Header.Set("Content-Type", contentType) 307 | } 308 | } 309 | 310 | return nil 311 | } 312 | -------------------------------------------------------------------------------- /protocol/http/http.go: -------------------------------------------------------------------------------- 1 | package http_proto 2 | 3 | import ( 4 | U "github.com/yyzybb537/ketty/url" 5 | P "github.com/yyzybb537/ketty/protocol" 6 | ) 7 | 8 | type HttpProtocol struct {} 9 | 10 | func init() { 11 | P.RegProtocol("http", new(HttpProtocol)) 12 | U.RegDefaultPort("http", 80) 13 | 14 | P.RegProtocol("https", new(HttpProtocol)) 15 | U.RegDefaultPort("https", 443) 16 | } 17 | 18 | var gCertFile, gKeyFile string 19 | 20 | func InitTLS(certFile, keyFile string) { 21 | gCertFile = certFile 22 | gKeyFile = keyFile 23 | } 24 | 25 | func (this *HttpProtocol) CreateServer(url, driverUrl U.Url) (P.Server, error) { 26 | return newHttpServer(url, driverUrl) 27 | } 28 | 29 | func (this *HttpProtocol) Dial(url U.Url) (P.Client, error) { 30 | return newHttpClient(url) 31 | } 32 | -------------------------------------------------------------------------------- /protocol/http/httpTransportManager.go: -------------------------------------------------------------------------------- 1 | package http_proto 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "time" 7 | "sync" 8 | "net" 9 | "crypto/tls" 10 | ) 11 | 12 | type rwTimeoutConn struct { 13 | *net.TCPConn 14 | rwTimeout time.Duration 15 | } 16 | 17 | func (this *rwTimeoutConn) Read(b []byte) (int, error) { 18 | err := this.TCPConn.SetDeadline(time.Now().Add(this.rwTimeout)) 19 | if err != nil { 20 | return 0, err 21 | } 22 | return this.TCPConn.Read(b) 23 | } 24 | 25 | func (this *rwTimeoutConn) Write(b []byte) (int, error) { 26 | err := this.TCPConn.SetDeadline(time.Now().Add(this.rwTimeout)) 27 | if err != nil { 28 | return 0, err 29 | } 30 | return this.TCPConn.Write(b) 31 | } 32 | 33 | var DefaultTransportManager *TransportManager = &TransportManager{tss: make(map[string]*http.Transport)} 34 | 35 | type TransportManager struct { 36 | tss map[string]*http.Transport 37 | rwlock sync.RWMutex 38 | } 39 | 40 | func (this *TransportManager) GetTransport(ConnectTimeout, RWTimeout, ResponseHeaderTimeout time.Duration) (ts *http.Transport){ 41 | var ok bool 42 | timeoutName := fmt.Sprintf("%s-%s-%s",int(ConnectTimeout.Seconds()),int(RWTimeout.Seconds()),int(ResponseHeaderTimeout.Seconds())) 43 | 44 | this.rwlock.RLock() 45 | if ts, ok = this.tss[timeoutName];!ok { 46 | this.rwlock.RUnlock() 47 | this.rwlock.Lock() 48 | ts = &http.Transport{ 49 | TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, 50 | } 51 | Dial := func(netw, addr string) (net.Conn, error) { 52 | conn, err := net.DialTimeout(netw, addr, ConnectTimeout) 53 | if err != nil { 54 | return nil, err 55 | } 56 | if RWTimeout > 0 { 57 | return &rwTimeoutConn{ 58 | TCPConn: conn.(*net.TCPConn), 59 | rwTimeout: RWTimeout, 60 | }, nil 61 | } else { 62 | return conn, nil 63 | } 64 | } 65 | ts.Dial = Dial 66 | ts.ResponseHeaderTimeout = ResponseHeaderTimeout 67 | ts.MaxIdleConnsPerHost = 2000 68 | this.tss[timeoutName] = ts 69 | this.rwlock.Unlock() 70 | }else { 71 | this.rwlock.RUnlock() 72 | } 73 | return 74 | } 75 | -------------------------------------------------------------------------------- /protocol/http/http_context.go: -------------------------------------------------------------------------------- 1 | package http_proto 2 | 3 | import ( 4 | "golang.org/x/net/context" 5 | "net/http" 6 | ) 7 | 8 | type httpRequestKey struct{} 9 | type httpResponseWriterKey struct{} 10 | 11 | func setHttpRequest(ctx context.Context, req *http.Request) context.Context { 12 | return context.WithValue(ctx, httpRequestKey{}, req) 13 | } 14 | 15 | func GetHttpRequest(ctx context.Context) (*http.Request, bool) { 16 | req, ok := ctx.Value(httpRequestKey{}).(*http.Request) 17 | return req, ok 18 | } 19 | 20 | func setHttpResponseWriter(ctx context.Context, w http.ResponseWriter) context.Context { 21 | return context.WithValue(ctx, httpResponseWriterKey{}, w) 22 | } 23 | 24 | func GetHttpResponseWriter(ctx context.Context) (http.ResponseWriter, bool) { 25 | w, ok := ctx.Value(httpResponseWriterKey{}).(http.ResponseWriter) 26 | return w, ok 27 | } 28 | -------------------------------------------------------------------------------- /protocol/http/multipart.go: -------------------------------------------------------------------------------- 1 | package http_proto 2 | 3 | import ( 4 | "github.com/golang/protobuf/proto" 5 | "github.com/pkg/errors" 6 | P "github.com/yyzybb537/ketty/protocol" 7 | //"reflect" 8 | //"bytes" 9 | //"strings" 10 | //"fmt" 11 | //COM "github.com/yyzybb537/ketty/common" 12 | ) 13 | 14 | const DefaultMultipartBoundary = "----KettyFromBoundary1234567890123456K" 15 | 16 | // ----------- multipart marshaler 17 | type MultipartMarshaler struct{} 18 | 19 | func (this *MultipartMarshaler) Marshal(msg proto.Message) ([]byte, error) { 20 | return nil, errors.Errorf("Unsupport multipart") 21 | } 22 | 23 | func (this *MultipartMarshaler) Unmarshal(buf []byte, msg proto.Message) error { 24 | return errors.Errorf("Unsupport multipart") 25 | } 26 | 27 | func init() { 28 | P.MgrMarshaler.Register("multipart", new(MultipartMarshaler)) 29 | } 30 | -------------------------------------------------------------------------------- /protocol/http/option.go: -------------------------------------------------------------------------------- 1 | package http_proto 2 | 3 | import ( 4 | O "github.com/yyzybb537/ketty/option" 5 | "github.com/pkg/errors" 6 | "reflect" 7 | ) 8 | 9 | type HttpOption struct { 10 | O.Option 11 | ConnectTimeoutMillseconds int64 12 | ReadWriteTimeoutMillseconds int64 13 | ResponseHeaderTimeoutMillseconds int64 14 | } 15 | 16 | func defaultHttpOption() *HttpOption { 17 | return new(HttpOption) 18 | } 19 | 20 | func (this *HttpOption) set(opt O.OptionI) error { 21 | if o, ok := opt.(*HttpOption); ok { 22 | *this = *o 23 | } else if o, ok := opt.(HttpOption); ok { 24 | *this = o 25 | } else if o, ok := opt.(*O.Option); ok { 26 | this.Option = *o 27 | } else if o, ok := opt.(O.Option); ok { 28 | this.Option = o 29 | } else { 30 | typ := reflect.TypeOf(opt) 31 | return errors.Errorf("SetOption argument error. opt={isptr:%t, className:%s}", typ.Kind() == reflect.Ptr, typ.String()) 32 | } 33 | return nil 34 | } 35 | -------------------------------------------------------------------------------- /protocol/http/proto.go: -------------------------------------------------------------------------------- 1 | package http_proto 2 | 3 | import ( 4 | "strings" 5 | "fmt" 6 | P "github.com/yyzybb537/ketty/protocol" 7 | ) 8 | 9 | type Proto struct { 10 | DefaultMethod string 11 | DefaultMarshaler string 12 | DefaultTransport string 13 | } 14 | 15 | func ParseProto(protocol string) (*Proto, error) { 16 | opt := &Proto{} 17 | ss := strings.Split(protocol, ".") 18 | if len(ss) > 1 { 19 | ss = ss[1:] 20 | for _, s := range ss { 21 | s = strings.ToLower(s) 22 | if s == "get" || s == "post" { 23 | if opt.DefaultMethod != "" { 24 | return nil, fmt.Errorf("Http protocol has too many methods. protocol=%s", protocol) 25 | } 26 | opt.DefaultMethod = s 27 | } 28 | 29 | if P.MgrMarshaler.Get(s) != nil { 30 | if opt.DefaultMarshaler != "" { 31 | return nil, fmt.Errorf("Http protocol has too many marshaler. protocol=%s", protocol) 32 | } 33 | opt.DefaultMarshaler = s 34 | } 35 | 36 | if MgrTransport.Get(s) != nil { 37 | if opt.DefaultTransport != "" { 38 | return nil, fmt.Errorf("Http protocol has too many transport. protocol=%s", protocol) 39 | } 40 | opt.DefaultTransport = s 41 | } 42 | } 43 | } 44 | 45 | // default value 46 | if opt.DefaultMethod == "" { 47 | opt.DefaultMethod = "post" 48 | } 49 | 50 | if opt.DefaultMarshaler == "" { 51 | opt.DefaultMarshaler = "pb" 52 | } 53 | 54 | if opt.DefaultTransport == "" { 55 | opt.DefaultTransport = "body" 56 | } 57 | 58 | return opt, nil 59 | } 60 | -------------------------------------------------------------------------------- /protocol/http/protoIf.go: -------------------------------------------------------------------------------- 1 | package http_proto 2 | 3 | import ( 4 | "reflect" 5 | "github.com/golang/protobuf/proto" 6 | ) 7 | 8 | type KettyHttpExtend interface { 9 | KettyHttpExtendMessage() 10 | } 11 | 12 | type DefineMarshaler interface { 13 | KettyMarshal() string 14 | } 15 | var typeDefineMarshaler = reflect.TypeOf((*DefineMarshaler)(nil)).Elem() 16 | 17 | type DefineTransport interface { 18 | KettyTransport() string 19 | } 20 | var typeDefineTransport = reflect.TypeOf((*DefineTransport)(nil)).Elem() 21 | 22 | var typeProtoMessage = reflect.TypeOf((*proto.Message)(nil)).Elem() 23 | -------------------------------------------------------------------------------- /protocol/http/querystring.go: -------------------------------------------------------------------------------- 1 | package http_proto 2 | 3 | import ( 4 | "github.com/golang/protobuf/proto" 5 | "github.com/pkg/errors" 6 | "reflect" 7 | "bytes" 8 | "strings" 9 | "fmt" 10 | stdurl "net/url" 11 | P "github.com/yyzybb537/ketty/protocol" 12 | COM "github.com/yyzybb537/ketty/common" 13 | ) 14 | 15 | // ----------- querystring marshaler 16 | type QueryStringMarshaler struct {} 17 | 18 | func (this *QueryStringMarshaler) Marshal(msg proto.Message) ([]byte, error) { 19 | b := bytes.NewBufferString("") 20 | typ := reflect.TypeOf(msg).Elem() 21 | val := reflect.ValueOf(msg).Elem() 22 | numField := typ.NumField() 23 | for i := 0; i < numField; i++ { 24 | ftype := typ.Field(i).Type 25 | fvalue := val.Field(i) 26 | if ftype.Kind() == reflect.Ptr { 27 | ftype = ftype.Elem() 28 | fvalue = fvalue.Elem() 29 | } 30 | 31 | if ftype.Kind() == reflect.Struct { 32 | return nil, errors.Errorf("QueryString marshal not support multilayer message") 33 | } 34 | 35 | svalue, err := COM.V2String(fvalue) 36 | if err != nil { 37 | return nil, err 38 | } 39 | b.WriteString(fmt.Sprintf("%s=%s", stdurl.QueryEscape(this.getKey(typ.Field(i))), stdurl.QueryEscape(svalue))) 40 | 41 | if i + 1 < numField { 42 | b.WriteRune('&') 43 | } 44 | } 45 | 46 | return b.Bytes(), nil 47 | } 48 | 49 | func (this *QueryStringMarshaler) Unmarshal(buf []byte, msg proto.Message) error { 50 | kvMap, err := stdurl.ParseQuery(string(buf)) 51 | if err != nil { 52 | return errors.WithStack(err) 53 | } 54 | 55 | typ := reflect.TypeOf(msg).Elem() 56 | val := reflect.ValueOf(msg).Elem() 57 | numField := typ.NumField() 58 | for i := 0; i < numField; i++ { 59 | ftype := typ.Field(i).Type 60 | fvalue := val.Field(i) 61 | 62 | if ftype.Kind() == reflect.Ptr { 63 | if ftype.Elem().Kind() == reflect.Struct { 64 | return errors.Errorf("QueryString marshal not support multilayer message") 65 | } 66 | } 67 | 68 | ss, exists := kvMap[this.getKey(typ.Field(i))] 69 | if !exists { 70 | continue 71 | } 72 | 73 | if len(ss) == 0 { 74 | continue 75 | } 76 | 77 | err = COM.String2V(ss[0], fvalue) 78 | if err != nil { 79 | return errors.WithStack(err) 80 | } 81 | } 82 | 83 | return nil 84 | } 85 | 86 | func (this *QueryStringMarshaler) getKey(sf reflect.StructField) string { 87 | k := sf.Tag.Get("json") 88 | if k == "" { 89 | k = sf.Name 90 | } else { 91 | k = strings.SplitN(k, ",", 2)[0] 92 | } 93 | return k 94 | } 95 | 96 | func init() { 97 | P.MgrMarshaler.Register("querystring", new(QueryStringMarshaler)) 98 | } 99 | 100 | -------------------------------------------------------------------------------- /protocol/http/router.go: -------------------------------------------------------------------------------- 1 | package http_proto 2 | 3 | import ( 4 | U "github.com/yyzybb537/ketty/url" 5 | "github.com/yyzybb537/ketty/log" 6 | "github.com/pkg/errors" 7 | "net" 8 | "net/http" 9 | "sync" 10 | "time" 11 | "github.com/yyzybb537/gls" 12 | ) 13 | 14 | type Router struct { 15 | http.Server 16 | mux *http.ServeMux 17 | addr string 18 | proto string 19 | served bool 20 | opt *HttpOption 21 | } 22 | 23 | var gRouters = map[string]*Router{} 24 | var gMtx sync.Mutex 25 | 26 | func getRouter(addr string) *Router { 27 | gMtx.Lock() 28 | defer gMtx.Unlock() 29 | 30 | if r, exists := gRouters[addr]; exists { 31 | return r 32 | } 33 | 34 | r := newRouter(addr) 35 | gRouters[addr] = r 36 | return r 37 | } 38 | 39 | func newRouter(addr string) *Router { 40 | r := new(Router) 41 | r.mux = http.NewServeMux() 42 | r.Handler = r.mux 43 | r.addr = addr 44 | return r 45 | } 46 | 47 | func (this *Router) Register(pattern string, handler func(http.ResponseWriter, *http.Request)) { 48 | this.mux.HandleFunc(pattern, handler) 49 | } 50 | 51 | func (this *Router) RServe(proto string) error { 52 | if this.served { 53 | return nil 54 | } 55 | 56 | if this.opt != nil { 57 | this.Server.WriteTimeout = time.Duration(this.opt.Option.TimeoutMilliseconds) * time.Millisecond 58 | this.Server.ReadTimeout = time.Duration(this.opt.Option.TimeoutMilliseconds) * time.Millisecond 59 | } 60 | if proto == "https" { 61 | gls.Go(func() { 62 | this.Addr = U.FormatAddr(this.addr, proto) 63 | err := this.ListenAndServeTLS(gCertFile, gKeyFile) 64 | if err != nil { 65 | log.GetLog().Fatalf("Http.ServeTLS lis error:%s. addr:%s", err.Error(), this.addr) 66 | } 67 | }) 68 | } else if proto == "http" { 69 | lis, err := net.Listen("tcp", U.FormatAddr(this.addr, proto)) 70 | if err != nil { 71 | return err 72 | } 73 | 74 | gls.Go(func() { 75 | err := this.Serve(lis) 76 | if err != nil { 77 | log.GetLog().Fatalf("Http.Serve lis error:%s. addr:%s", err.Error(), this.addr) 78 | } 79 | }) 80 | } else { 81 | return errors.Errorf("Error protocol:%s", proto) 82 | } 83 | 84 | this.served = true 85 | return nil 86 | } 87 | -------------------------------------------------------------------------------- /protocol/http/server.go: -------------------------------------------------------------------------------- 1 | package http_proto 2 | 3 | import ( 4 | C "github.com/yyzybb537/ketty/context" 5 | COM "github.com/yyzybb537/ketty/common" 6 | D "github.com/yyzybb537/ketty/driver" 7 | U "github.com/yyzybb537/ketty/url" 8 | A "github.com/yyzybb537/ketty/aop" 9 | O "github.com/yyzybb537/ketty/option" 10 | P "github.com/yyzybb537/ketty/protocol" 11 | "github.com/yyzybb537/ketty/log" 12 | "net/http" 13 | "reflect" 14 | "fmt" 15 | "strings" 16 | "google.golang.org/grpc" 17 | "github.com/golang/protobuf/proto" 18 | "golang.org/x/net/context" 19 | "github.com/pkg/errors" 20 | "encoding/json" 21 | ) 22 | 23 | type HttpServer struct { 24 | A.AopList 25 | 26 | router []*Router 27 | url U.Url 28 | driverUrl U.Url 29 | prt *Proto 30 | opt *HttpOption 31 | handler map[string]func(http.ResponseWriter, *http.Request) 32 | logKey interface{} 33 | } 34 | 35 | func newHttpServer(url, driverUrl U.Url) (*HttpServer, error) { 36 | s := &HttpServer { 37 | url : url, 38 | driverUrl : driverUrl, 39 | opt : defaultHttpOption(), 40 | handler : make(map[string]func(http.ResponseWriter, *http.Request)), 41 | } 42 | var err error 43 | s.prt, err = ParseProto(url.Protocol) 44 | if err != nil { 45 | return nil, err 46 | } 47 | pattern := url.Path 48 | if pattern == "" { 49 | pattern = "/" 50 | } 51 | addrs := url.GetAddrs() 52 | for _, addr := range addrs { 53 | s.router = append(s.router, getRouter(addr)) 54 | } 55 | for _, r := range s.router { 56 | r.Register(pattern, func(w http.ResponseWriter, httpRequest *http.Request){ 57 | method := httpRequest.Header.Get("KettyMethod") 58 | // 兼容其他http client, 只允许注册一个Method 59 | if method == "" && len(s.handler) == 1 { 60 | for _, hd := range s.handler { 61 | hd(w, httpRequest) 62 | break 63 | } 64 | return 65 | } 66 | 67 | hd, exists := s.handler[method] 68 | if !exists { 69 | w.WriteHeader(416) 70 | return 71 | } 72 | 73 | hd(w, httpRequest) 74 | }) 75 | } 76 | return s, nil 77 | } 78 | 79 | func (this *HttpServer) SetOption(opt O.OptionI) error { 80 | for _, r := range this.router { 81 | r.opt = this.opt 82 | } 83 | return this.opt.set(opt) 84 | } 85 | 86 | func (this *HttpServer) parseMessage(httpRequest *http.Request, requestType reflect.Type) (proto.Message, error) { 87 | refReq := reflect.New(requestType) 88 | req := refReq.Interface() 89 | _, isKettyHttpExtend := req.(KettyHttpExtend) 90 | if !isKettyHttpExtend { 91 | // Not extend, use default 92 | sTr := this.prt.DefaultTransport 93 | if dtr, ok := req.(DefineTransport); ok { 94 | sTr = dtr.KettyTransport() 95 | } 96 | sMr := this.prt.DefaultMarshaler 97 | if dmr, ok := req.(DefineMarshaler); ok { 98 | sMr = dmr.KettyMarshal() 99 | } 100 | if sTr == "query" { 101 | sMr = "querystring" 102 | } 103 | 104 | tr, _ := MgrTransport.Get(sTr).(DataTransport) 105 | buf, err := tr.Read(httpRequest) 106 | if err != nil { 107 | return nil, err 108 | } 109 | 110 | mr, _ := P.MgrMarshaler.Get(sMr).(P.Marshaler) 111 | err = mr.Unmarshal(buf, req.(proto.Message)) 112 | if err != nil { 113 | return nil, err 114 | } 115 | 116 | return req.(proto.Message), nil 117 | } 118 | 119 | // use http extend 120 | numFields := refReq.Elem().NumField() 121 | trMap := map[string]bool{} 122 | for i := 0; i < numFields; i++ { 123 | fvalue := refReq.Elem().Field(i) 124 | ftype := requestType.Field(i).Type 125 | if !ftype.ConvertibleTo(typeProtoMessage) { 126 | return nil, fmt.Errorf("Use http extend message, all of fields must be proto.Message! Error message name is %s", requestType.Name()) 127 | } 128 | fvalue.Set(reflect.New(ftype.Elem())) 129 | 130 | sTr := this.prt.DefaultTransport 131 | if ftype.ConvertibleTo(typeDefineTransport) { 132 | sTr = fvalue.Convert(typeDefineTransport).Interface().(DefineTransport).KettyTransport() 133 | } 134 | 135 | // check tr unique 136 | if _, exists := trMap[sTr]; exists { 137 | return nil, fmt.Errorf("The message used http extend, transport must be unique. Too many field use transport(%s) in message:%s", sTr, requestType.Name()) 138 | } 139 | trMap[sTr] = true 140 | 141 | tr, ok := MgrTransport.Get(sTr).(DataTransport) 142 | if !ok { 143 | return nil, fmt.Errorf("Unknown transport(%s) in message:%s", sTr, ftype.Name()) 144 | } 145 | 146 | buf, err := tr.Read(httpRequest) 147 | if err != nil { 148 | return nil, err 149 | } 150 | 151 | if len(buf) == 0 { 152 | // skip nil message 153 | continue 154 | } 155 | 156 | sMr := this.prt.DefaultMarshaler 157 | if ftype.ConvertibleTo(typeDefineMarshaler) { 158 | sMr = fvalue.Convert(typeDefineMarshaler).Interface().(DefineMarshaler).KettyMarshal() 159 | } 160 | 161 | if sTr == "query" { 162 | sMr = "querystring" 163 | } 164 | 165 | mr, ok := P.MgrMarshaler.Get(sMr).(P.Marshaler) 166 | if !ok { 167 | return nil, fmt.Errorf("Unknown marshal(%s) in message:%s", sMr, ftype.Name()) 168 | } 169 | 170 | fMessage := fvalue.Interface().(proto.Message) 171 | err = mr.Unmarshal(buf, fMessage) 172 | if err != nil { 173 | return nil, err 174 | } 175 | } 176 | 177 | return req.(proto.Message), nil 178 | } 179 | 180 | func (this *HttpServer) doHandler(fullMethodName string, httpRequest *http.Request, 181 | w http.ResponseWriter, requestType reflect.Type, 182 | reflectMethod reflect.Value) (rsp interface{}, ctx context.Context) { 183 | 184 | ctx = context.Background() 185 | var err error 186 | 187 | //log.GetLog().Debugf("HttpServer Request: %s", log.LogFormat(httpRequest, log.Indent)) 188 | 189 | // metadata 190 | metadata := map[string]string{} 191 | metadataStr := httpRequest.Header.Get("KettyMetaData") 192 | if metadataStr != "" { 193 | err = json.Unmarshal([]byte(metadataStr), &metadata) 194 | if err != nil { 195 | ctx = C.WithError(ctx, errors.WithStack(err)) 196 | return 197 | } 198 | } 199 | 200 | // 鉴权数据从Header提取 201 | authorization := httpRequest.Header.Get("Authorization") 202 | if authorization != "" { 203 | metadata[COM.AuthorizationMetaKey] = authorization 204 | } 205 | 206 | // 解析Message 207 | req, err := this.parseMessage(httpRequest, requestType) 208 | ctx = C.WithError(ctx, errors.WithStack(err)) 209 | 210 | aopList := this.GetAop() 211 | if aopList != nil { 212 | ctx = context.WithValue(ctx, "method", fullMethodName) 213 | ctx = context.WithValue(ctx, "remote", httpRequest.RemoteAddr) 214 | ctx = setHttpRequest(ctx, httpRequest) 215 | ctx = setHttpResponseWriter(ctx, w) 216 | 217 | for _, aop := range aopList { 218 | caller, ok := aop.(A.ServerTransportMetaDataAop) 219 | if ok { 220 | ctx = caller.ServerRecvMetaData(ctx, metadata) 221 | } 222 | } 223 | 224 | for _, aop := range aopList { 225 | caller, ok := aop.(A.BeforeServerInvokeAop) 226 | if ok { 227 | ctx = caller.BeforeServerInvoke(ctx, req) 228 | } 229 | } 230 | 231 | defer func() { 232 | for _, aop := range aopList { 233 | caller, ok := aop.(A.ServerInvokeCleanupAop) 234 | if ok { 235 | caller.ServerCleanup(ctx) 236 | } 237 | } 238 | }() 239 | 240 | for i, _ := range aopList { 241 | aop := aopList[len(aopList) - i - 1] 242 | caller, ok := aop.(A.AfterServerInvokeAop) 243 | if ok { 244 | if _, isException := caller.(*A.ExceptionAop); isException { 245 | defer caller.AfterServerInvoke(&ctx, req, rsp) 246 | } else { 247 | defer func() { 248 | caller.AfterServerInvoke(&ctx, req, rsp) 249 | }() 250 | } 251 | } 252 | } 253 | } 254 | 255 | if ctx.Err() != nil { 256 | return 257 | } 258 | 259 | replies := reflectMethod.Call([]reflect.Value{reflect.ValueOf(context.Background()), reflect.ValueOf(req)}) 260 | rsp = replies[0].Interface() 261 | if replies[1].Interface() != nil { 262 | err = replies[1].Interface().(error) 263 | ctx = C.WithError(ctx, err) 264 | return 265 | } 266 | 267 | return 268 | } 269 | 270 | func (this *HttpServer) RegisterMethod(handle COM.ServiceHandle, implement interface{}) error { 271 | desc := handle.Implement().(*grpc.ServiceDesc) 272 | ht := reflect.TypeOf(desc.HandlerType).Elem() 273 | it := reflect.TypeOf(implement) 274 | if !it.Implements(ht) { 275 | return fmt.Errorf("service struct not implements type `%s` interface", desc.ServiceName) 276 | } 277 | 278 | iv := reflect.ValueOf(implement) 279 | for _, method := range desc.Methods { 280 | reflectMethod := iv.MethodByName(method.MethodName) 281 | requestType := reflectMethod.Type().In(1).Elem() 282 | fullMethodName := fmt.Sprintf("/%s/%s", strings.Replace(handle.ServiceName(), ".", "/", -1), method.MethodName) 283 | this.handler[fullMethodName] = func(w http.ResponseWriter, httpRequest *http.Request){ 284 | if this.logKey != nil { 285 | log.SetGlsDefaultKey(this.logKey) 286 | defer log.CleanupGlsDefaultKey(this.logKey) 287 | } 288 | 289 | var err error 290 | defer func() { 291 | if err != nil { 292 | w.WriteHeader(http.StatusBadRequest) 293 | w.Write([]byte(err.Error())) 294 | } 295 | }() 296 | 297 | rsp, ctx := this.doHandler(fullMethodName, httpRequest, w, requestType, reflectMethod) 298 | err = ctx.Err() 299 | if err != nil { 300 | return 301 | } 302 | 303 | // body 304 | var buf []byte 305 | mr, _ := P.MgrMarshaler.Get(this.prt.DefaultMarshaler).(P.Marshaler) 306 | buf, err = mr.Marshal(rsp.(proto.Message)) 307 | if err != nil { 308 | return 309 | } 310 | 311 | //log.GetLog().Debugf("Write response: %v", buf) 312 | 313 | if len(buf) > 0 { 314 | w.Write(buf) 315 | } 316 | } 317 | } 318 | return nil 319 | } 320 | 321 | func (this *HttpServer) Serve() error { 322 | this.logKey = log.GetGlsDefaultKey() 323 | 324 | for _, r := range this.router { 325 | err := r.RServe(this.url.GetMainProtocol()) 326 | if err != nil { 327 | return err 328 | } 329 | } 330 | 331 | if !this.driverUrl.IsEmpty() { 332 | driver, err := D.GetDriver(this.driverUrl.Protocol) 333 | if err != nil { 334 | return err 335 | } 336 | 337 | err = driver.Register(this.driverUrl, this.url) 338 | if err != nil { 339 | return err 340 | } 341 | } 342 | 343 | return nil 344 | } 345 | 346 | -------------------------------------------------------------------------------- /protocol/http/transport.go: -------------------------------------------------------------------------------- 1 | package http_proto 2 | 3 | import ( 4 | COM "github.com/yyzybb537/ketty/common" 5 | "bytes" 6 | "net/http" 7 | "io/ioutil" 8 | "github.com/yyzybb537/ketty/log" 9 | ) 10 | 11 | var _ = log.GetLog 12 | 13 | type DataTransport interface { 14 | Write(req *http.Request, buf []byte) error 15 | Read(req *http.Request) ([]byte, error) 16 | } 17 | 18 | var MgrTransport = COM.NewManager((*DataTransport)(nil)) 19 | 20 | func init() { 21 | MgrTransport.Register("body", new(BodyTransport)) 22 | MgrTransport.Register("query", new(QueryStringTransport)) 23 | MgrTransport.Register("multipart", new(MultipartTransport)) 24 | } 25 | 26 | type BodyTransport struct {} 27 | func (this *BodyTransport) Write(req *http.Request, buf []byte) error { 28 | b := bytes.NewBuffer(buf) 29 | req.Body = ioutil.NopCloser(b) 30 | req.ContentLength = int64(b.Len()) 31 | return nil 32 | } 33 | func (this *BodyTransport) Read(req *http.Request) ([]byte, error) { 34 | buf, err := ioutil.ReadAll(req.Body) 35 | //println("Body:", string(buf)) 36 | return buf, err 37 | } 38 | 39 | type QueryStringTransport struct {} 40 | func (this *QueryStringTransport) Write(req *http.Request, buf []byte) error { 41 | if req.URL.RawQuery != "" { 42 | req.URL.RawQuery += "&" 43 | } 44 | req.URL.RawQuery += string(buf) 45 | return nil 46 | } 47 | func (this *QueryStringTransport) Read(req *http.Request) ([]byte, error) { 48 | return []byte(req.URL.RawQuery), nil 49 | } 50 | 51 | type MultipartTransport struct {} 52 | func (this *MultipartTransport) Write(req *http.Request, buf []byte) error { 53 | return nil 54 | } 55 | func (this *MultipartTransport) Read(req *http.Request) ([]byte, error) { 56 | return []byte{}, nil 57 | } 58 | -------------------------------------------------------------------------------- /protocol/marshal.go: -------------------------------------------------------------------------------- 1 | package protocol 2 | 3 | import ( 4 | COM "github.com/yyzybb537/ketty/common" 5 | "github.com/golang/protobuf/proto" 6 | "encoding/json" 7 | ) 8 | 9 | type Marshaler interface { 10 | Marshal(msg proto.Message) ([]byte, error) 11 | 12 | Unmarshal(buf []byte, msg proto.Message) error 13 | } 14 | 15 | var MgrMarshaler = COM.NewManager((*Marshaler)(nil)) 16 | 17 | func init() { 18 | MgrMarshaler.Register("pb", new(PbMarshaler)) 19 | MgrMarshaler.Register("json", new(JsonMarshaler)) 20 | } 21 | 22 | // ----------- default protobuf marshaler 23 | type PbMarshaler struct {} 24 | 25 | func (this *PbMarshaler) Marshal(msg proto.Message) ([]byte, error) { 26 | return proto.Marshal(msg) 27 | } 28 | 29 | func (this *PbMarshaler) Unmarshal(buf []byte, msg proto.Message) error { 30 | return proto.Unmarshal(buf, msg) 31 | } 32 | 33 | // ----------- json marshaler 34 | type JsonMarshaler struct {} 35 | 36 | func (this *JsonMarshaler) Marshal(msg proto.Message) ([]byte, error) { 37 | return json.Marshal(msg) 38 | } 39 | 40 | func (this *JsonMarshaler) Unmarshal(buf []byte, msg proto.Message) error { 41 | return json.Unmarshal(buf, msg) 42 | } 43 | 44 | -------------------------------------------------------------------------------- /protocol/protocol.go: -------------------------------------------------------------------------------- 1 | package protocol 2 | 3 | import ( 4 | "fmt" 5 | "github.com/pkg/errors" 6 | "strings" 7 | U "github.com/yyzybb537/ketty/url" 8 | ) 9 | 10 | type Protocol interface { 11 | CreateServer(url, driverUrl U.Url) (Server, error) 12 | 13 | Dial(url U.Url) (Client, error) 14 | } 15 | 16 | var protocols = make(map[string]Protocol) 17 | 18 | func GetProtocol(sproto string) (Protocol, error) { 19 | proto, exists := protocols[strings.ToLower(sproto)] 20 | if !exists { 21 | return nil, errors.Errorf("Unkown Protocol:%s", sproto) 22 | } 23 | return proto, nil 24 | } 25 | 26 | func RegProtocol(sproto string, proto Protocol) { 27 | protocols[strings.ToLower(sproto)] = proto 28 | } 29 | 30 | func DumpProtocols() string { 31 | return fmt.Sprintf("%v", protocols) 32 | } 33 | -------------------------------------------------------------------------------- /protocol/server.go: -------------------------------------------------------------------------------- 1 | package protocol 2 | 3 | import ( 4 | A "github.com/yyzybb537/ketty/aop" 5 | COM "github.com/yyzybb537/ketty/common" 6 | O "github.com/yyzybb537/ketty/option" 7 | ) 8 | 9 | type Server interface { 10 | A.AopListI 11 | 12 | SetOption(opt O.OptionI) error 13 | 14 | RegisterMethod(handle COM.ServiceHandle, implement interface{}) error 15 | 16 | Serve() error 17 | } 18 | -------------------------------------------------------------------------------- /test/auth_test.go: -------------------------------------------------------------------------------- 1 | package grpc_t 2 | 3 | import ( 4 | "testing" 5 | echo "github.com/yyzybb537/ketty/test/test_pb" 6 | "github.com/yyzybb537/ketty" 7 | kettyLog "github.com/yyzybb537/ketty/log" 8 | P "github.com/yyzybb537/ketty/protocol" 9 | kettyHttp "github.com/yyzybb537/ketty/protocol/http" 10 | A "github.com/yyzybb537/ketty/aop" 11 | "time" 12 | "fmt" 13 | context "golang.org/x/net/context" 14 | ) 15 | 16 | type EchoServer struct {} 17 | func (this *EchoServer) Echo(ctx context.Context, req *echo.Req) (*echo.Rsp, error) { 18 | return &echo.Rsp{Val:req.Val}, nil 19 | } 20 | 21 | type Auth struct {} 22 | func (this *Auth) CreateAuthorization(ctx context.Context) string { 23 | return "MyAuth" 24 | } 25 | func (this *Auth) CheckAuthorization(ctx context.Context, authorization string) error { 26 | if authorization != "MyAuth" { 27 | return fmt.Errorf("Auth error") 28 | } 29 | return nil 30 | } 31 | 32 | var gAop *A.AopList 33 | 34 | func startServer(t *testing.T, sUrl string, driverUrl string) { 35 | server, err := ketty.Listen(sUrl, driverUrl) 36 | if err != nil { 37 | t.Fatalf("Listen error:%s", err.Error()) 38 | } 39 | 40 | server.RegisterMethod(echo.EchoServiceHandle, &EchoServer{}) 41 | server.AddAop(gAop.GetAop()...) 42 | 43 | err = server.Serve() 44 | if err != nil { 45 | t.Fatalf("Serve (%s) error:%s", sUrl, err.Error()) 46 | } 47 | } 48 | 49 | func startClient(t *testing.T, sUrl string, exceptError bool) { 50 | client, err := ketty.Dial(sUrl, "") 51 | if err != nil { 52 | t.Fatalf("Dial error:%s", err.Error()) 53 | } 54 | client.AddAop(gAop.GetAop()...) 55 | 56 | req := &echo.Req{ Val : 123 } 57 | stub := echo.NewKettyEchoServiceClient(client) 58 | rsp, err := stub.Echo(context.Background(), req) 59 | if exceptError { 60 | if err == nil { 61 | t.Fatalf("Invoke not error") 62 | } else { 63 | t.Logf("Except error:%v", err) 64 | } 65 | } else { 66 | if err != nil { 67 | t.Fatalf("Invoke error:%+v", err) 68 | } 69 | 70 | t.Logf("Echo Val:%d", rsp.Val) 71 | } 72 | } 73 | 74 | var httpUrl = "http://127.0.0.1:8091" 75 | var httpjsonUrl = "http.json://127.0.0.1:8092" 76 | var httpsUrl = "https://127.0.0.1:3050" 77 | var httpsjsonUrl = "https.json://127.0.0.1:3051" 78 | var grpcUrl = "grpc://127.0.0.1:8090" 79 | var urls = []string{ 80 | httpUrl, 81 | httpsUrl, 82 | grpcUrl, 83 | httpjsonUrl, 84 | httpsjsonUrl, 85 | } 86 | 87 | func TestGrpc(t *testing.T) { 88 | kettyHttp.InitTLS("cert.pem", "key.pem") 89 | ketty.SetLog(new(kettyLog.StdLog)) 90 | ketty.GetLog().Debugf("--------------- Protocols:%s", P.DumpProtocols()) 91 | for _, sUrl := range urls { 92 | testByUrl(t, sUrl) 93 | } 94 | } 95 | 96 | func testByUrl(t *testing.T, sUrl string) { 97 | t.Logf("Do url:%s", sUrl) 98 | // 1.all auth 99 | auth := new(Auth) 100 | gAop = new(A.AopList) 101 | gAop.AddAop(A.NewAuthAop(auth, auth)) 102 | 103 | startServer(t, sUrl, "") 104 | time.Sleep(time.Millisecond * 100) 105 | startClient(t, sUrl, false) 106 | 107 | // 2.Server auth only 108 | gAop = new(A.AopList) 109 | time.Sleep(time.Millisecond * 100) 110 | startClient(t, sUrl, true) 111 | } 112 | -------------------------------------------------------------------------------- /test/cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDXTCCAkWgAwIBAgIJAM6tN/HIo9ZBMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV 3 | BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX 4 | aWRnaXRzIFB0eSBMdGQwHhcNMTgwMjIzMDY1NTE4WhcNMjEwMjIyMDY1NTE4WjBF 5 | MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50 6 | ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB 7 | CgKCAQEAtMfDl54LjKL7+ZkiGWii4LNXrXE/mk9Acqrm6mQMxBQ+AvQi5QuqawZ9 8 | fhgGCu67om6jyu8s3dud8nQZA+UNO5UlLSI6z4KDNfUgEY3sQFvCAVvpDqRXm4ZA 9 | seWjYQF174E3jsgHUZ1r2U/Q/8Z3U4qi4/gxV3/nGKTkzkXMiCAu7kDM0tQokw5a 10 | mUamsFrRG5Ld18fLN52tTgl6UZOqr/gNiNMtNSzSFJ4k8TchZDP/GRZ+pMMb54Xk 11 | 8sVpfiX3hlVt70hYYf1JCH+HGZevIESAN20+T40wQaj0VjGTmgvyISvEktVDB0oa 12 | VdXRNiQaTV+88DsE+B9aD0ICJkDScQIDAQABo1AwTjAdBgNVHQ4EFgQUOB8HadXN 13 | DWn+/Qi21iSvEH8QDOIwHwYDVR0jBBgwFoAUOB8HadXNDWn+/Qi21iSvEH8QDOIw 14 | DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAkMiaertcSolKOi4ZMxks 15 | W9Xe8Islo72gVlLxULM3Q1uji4PVIC05eLuaVqhEvqCrmyQ7h1mERQNLlTtQ+MVQ 16 | mD03kBNcMbmnyAgYKMLDV5swnn5zLoBF2ybRLYp3q7bl5nTPwQvrtKREZmw6T07U 17 | UQ0SO89+n4fcCwaAMpK7Pe7hnzcEUBY8iQw0PXzsVRq4JrhD/6Z2fnSxS3fz0Mtk 18 | uAS34gS7kYopKT7aDWXuJ6gbBtibwxoicdzTFnRgyK4yDbjtuoox1eqUmYy8abNL 19 | Uo7T/paV5IR7nFC55b2Wikn4nl0Fu3MCgmHuZp3KAfJ6qYcfYTe4vw5GmTiS/SQM 20 | PA== 21 | -----END CERTIFICATE----- 22 | -------------------------------------------------------------------------------- /test/client_bench.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | echo "github.com/yyzybb537/ketty/test/test_pb" 5 | "github.com/yyzybb537/ketty" 6 | kettyLog "github.com/yyzybb537/ketty/log" 7 | kettyHttp "github.com/yyzybb537/ketty/protocol/http" 8 | context "golang.org/x/net/context" 9 | "fmt" 10 | "flag" 11 | "time" 12 | "sync/atomic" 13 | ) 14 | 15 | var clientMap = map[string]ketty.Client{} 16 | var qps int64 17 | var lastQps int64 18 | 19 | func bStartClient(sUrl string) { 20 | client, err := ketty.Dial(sUrl, "") 21 | if err != nil { 22 | panic(fmt.Errorf("Dial error:%s", err.Error())) 23 | return 24 | } 25 | defer client.Close() 26 | 27 | for { 28 | req := &echo.Req{ Val : 123 } 29 | stub := echo.NewKettyEchoServiceClient(client) 30 | _, err := stub.Echo(context.Background(), req) 31 | if err != nil { 32 | panic(fmt.Errorf("Invoke error:%+v", err)) 33 | return 34 | } 35 | 36 | atomic.AddInt64(&qps, 1) 37 | } 38 | } 39 | 40 | var httpUrl = "http://127.0.0.1:8091" 41 | var httpsUrl = "https://127.0.0.1:3050" 42 | var grpcUrl = "grpc://127.0.0.1:8090" 43 | var urls = []string{ 44 | //"http://127.0.0.1", 45 | httpUrl, 46 | httpsUrl, 47 | grpcUrl, 48 | } 49 | 50 | var urlType int 51 | var nConnections int 52 | 53 | func init() { 54 | flag.IntVar(&urlType, "url", 2, "0:http, 1:https, 2:grpc") 55 | flag.IntVar(&nConnections, "c", 100, "number of connections") 56 | flag.Parse() 57 | } 58 | 59 | func main() { 60 | kettyHttp.InitTLS("cert.pem", "key.pem") 61 | ketty.SetLog(new(kettyLog.FakeLog)) 62 | println("Connect to", urls[urlType]) 63 | for i := 0; i < nConnections; i++ { 64 | go bStartClient(urls[urlType]) 65 | } 66 | 67 | for { 68 | time.Sleep(time.Second) 69 | cur := atomic.LoadInt64(&qps) 70 | println("QPS:", cur - lastQps) 71 | lastQps = cur 72 | } 73 | } 74 | 75 | -------------------------------------------------------------------------------- /test/create_pem.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | openssl genrsa -out key.pem 2048 4 | openssl req -new -x509 -key key.pem -out cert.pem -days 1095 5 | -------------------------------------------------------------------------------- /test/jsonhyaline/echo.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. DO NOT EDIT. 2 | // source: echo.proto 3 | 4 | /* 5 | Package main is a generated protocol buffer package. 6 | 7 | It is generated from these files: 8 | echo.proto 9 | 10 | It has these top-level messages: 11 | Hyaline 12 | Req 13 | Rsp 14 | */ 15 | package main 16 | 17 | import proto "github.com/golang/protobuf/proto" 18 | import fmt "fmt" 19 | import math "math" 20 | 21 | import ( 22 | context "golang.org/x/net/context" 23 | grpc "google.golang.org/grpc" 24 | ) 25 | 26 | import ( 27 | ketty "github.com/yyzybb537/ketty" 28 | "encoding/json" 29 | ) 30 | 31 | // Reference imports to suppress errors if they are not otherwise used. 32 | var _ = proto.Marshal 33 | var _ = fmt.Errorf 34 | var _ = math.Inf 35 | 36 | // This is a compile-time assertion to ensure that this generated file 37 | // is compatible with the proto package it is being compiled against. 38 | // A compilation error at this line likely means your copy of the 39 | // proto package needs to be updated. 40 | const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package 41 | 42 | type Hyaline struct { 43 | Val []int64 `protobuf:"varint,1,rep,packed,name=val" json:"val"` 44 | } 45 | 46 | func (m *Hyaline) Reset() { *m = Hyaline{} } 47 | func (m *Hyaline) String() string { return proto.CompactTextString(m) } 48 | func (*Hyaline) ProtoMessage() {} 49 | func (*Hyaline) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } 50 | 51 | func (m *Hyaline) GetVal() []int64 { 52 | if m != nil { 53 | return m.Val 54 | } 55 | return nil 56 | } 57 | 58 | type Req struct { 59 | Hy *Hyaline `protobuf:"bytes,1,opt,name=hy" json:"hy"` 60 | } 61 | 62 | func (m *Req) Reset() { *m = Req{} } 63 | func (m *Req) String() string { return proto.CompactTextString(m) } 64 | func (*Req) ProtoMessage() {} 65 | func (*Req) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } 66 | 67 | func (m *Req) GetHy() *Hyaline { 68 | if m != nil { 69 | return m.Hy 70 | } 71 | return nil 72 | } 73 | 74 | type Rsp struct { 75 | Val int64 `protobuf:"varint,1,opt,name=val" json:"val"` 76 | } 77 | 78 | func (m *Rsp) Reset() { *m = Rsp{} } 79 | func (m *Rsp) String() string { return proto.CompactTextString(m) } 80 | func (*Rsp) ProtoMessage() {} 81 | func (*Rsp) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } 82 | 83 | func (m *Rsp) GetVal() int64 { 84 | if m != nil { 85 | return m.Val 86 | } 87 | return 0 88 | } 89 | 90 | func init() { 91 | proto.RegisterType((*Hyaline)(nil), "main.hyaline") 92 | proto.RegisterType((*Req)(nil), "main.Req") 93 | proto.RegisterType((*Rsp)(nil), "main.Rsp") 94 | } 95 | 96 | // Reference imports to suppress errors if they are not otherwise used. 97 | var _ context.Context 98 | var _ grpc.ClientConn 99 | 100 | // This is a compile-time assertion to ensure that this generated file 101 | // is compatible with the grpc package it is being compiled against. 102 | const _ = grpc.SupportPackageIsVersion4 103 | 104 | // Client API for EchoService service 105 | 106 | type EchoServiceClient interface { 107 | Echo(ctx context.Context, in *Req, opts ...grpc.CallOption) (*Rsp, error) 108 | } 109 | 110 | type echoServiceClient struct { 111 | cc *grpc.ClientConn 112 | } 113 | 114 | func NewEchoServiceClient(cc *grpc.ClientConn) EchoServiceClient { 115 | return &echoServiceClient{cc} 116 | } 117 | 118 | func (c *echoServiceClient) Echo(ctx context.Context, in *Req, opts ...grpc.CallOption) (*Rsp, error) { 119 | out := new(Rsp) 120 | err := grpc.Invoke(ctx, "/main.EchoService/Echo", in, out, c.cc, opts...) 121 | if err != nil { 122 | return nil, err 123 | } 124 | return out, nil 125 | } 126 | 127 | // Server API for EchoService service 128 | 129 | type EchoServiceServer interface { 130 | Echo(context.Context, *Req) (*Rsp, error) 131 | } 132 | 133 | func RegisterEchoServiceServer(s *grpc.Server, srv EchoServiceServer) { 134 | s.RegisterService(&_EchoService_serviceDesc, srv) 135 | } 136 | 137 | func _EchoService_Echo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 138 | in := new(Req) 139 | if err := dec(in); err != nil { 140 | return nil, err 141 | } 142 | if interceptor == nil { 143 | return srv.(EchoServiceServer).Echo(ctx, in) 144 | } 145 | info := &grpc.UnaryServerInfo{ 146 | Server: srv, 147 | FullMethod: "/main.EchoService/Echo", 148 | } 149 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 150 | return srv.(EchoServiceServer).Echo(ctx, req.(*Req)) 151 | } 152 | return interceptor(ctx, in, info, handler) 153 | } 154 | 155 | var _EchoService_serviceDesc = grpc.ServiceDesc{ 156 | ServiceName: "main.EchoService", 157 | HandlerType: (*EchoServiceServer)(nil), 158 | Methods: []grpc.MethodDesc{ 159 | { 160 | MethodName: "Echo", 161 | Handler: _EchoService_Echo_Handler, 162 | }, 163 | }, 164 | Streams: []grpc.StreamDesc{}, 165 | Metadata: "echo.proto", 166 | } 167 | 168 | // Reference imports to suppress errors if they are not otherwise used. 169 | var _ ketty.Dummy 170 | 171 | // This is a compile-time assertion to ensure that this generated file 172 | // is compatible with the ketty package it is being compiled against. 173 | 174 | type EchoServiceHandleT struct { 175 | desc *grpc.ServiceDesc 176 | } 177 | 178 | func (h *EchoServiceHandleT) Implement() interface{} { 179 | return h.desc 180 | } 181 | 182 | func (h *EchoServiceHandleT) ServiceName() string { 183 | return h.desc.ServiceName 184 | } 185 | 186 | var EchoServiceHandle = &EchoServiceHandleT{desc: &_EchoService_serviceDesc} 187 | 188 | type KettyEchoServiceClient struct { 189 | client ketty.Client 190 | } 191 | 192 | func NewKettyEchoServiceClient(client ketty.Client) *KettyEchoServiceClient { 193 | return &KettyEchoServiceClient{client} 194 | } 195 | 196 | func (this *KettyEchoServiceClient) Echo(ctx context.Context, in *Req) (*Rsp, error) { 197 | out := new(Rsp) 198 | err := this.client.Invoke(ctx, EchoServiceHandle, "Echo", in, out) 199 | if err != nil { 200 | return nil, err 201 | } 202 | return out, nil 203 | } 204 | 205 | func (this *Hyaline) MarshalJSON() ([]byte, error) { 206 | return json.Marshal(this.Val) 207 | } 208 | 209 | func (this *Hyaline) UnmarshalJSON(data []byte) (err error) { 210 | return json.Unmarshal(data, &this.Val) 211 | } 212 | 213 | func init() { proto.RegisterFile("echo.proto", fileDescriptor0) } 214 | 215 | var fileDescriptor0 = []byte{ 216 | // 167 bytes of a gzipped FileDescriptorProto 217 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x4a, 0x4d, 0xce, 0xc8, 218 | 0xd7, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0xc9, 0x4d, 0xcc, 0xcc, 0x93, 0xe2, 0xce, 0x4e, 219 | 0x2d, 0x29, 0xa9, 0x84, 0x08, 0x29, 0x29, 0x72, 0xb1, 0x67, 0x54, 0x26, 0xe6, 0x64, 0xe6, 0xa5, 220 | 0x0a, 0x09, 0x70, 0x31, 0x97, 0x25, 0xe6, 0x48, 0x30, 0x2a, 0x30, 0x6b, 0x30, 0x07, 0x81, 0x98, 221 | 0x56, 0x2c, 0x2b, 0x3e, 0xb2, 0x30, 0x2a, 0xa9, 0x70, 0x31, 0x07, 0xa5, 0x16, 0x0a, 0xc9, 0x72, 222 | 0x31, 0x65, 0x54, 0x4a, 0x30, 0x2a, 0x30, 0x6a, 0x70, 0x1b, 0xf1, 0xea, 0x81, 0x4c, 0xd2, 0x83, 223 | 0xea, 0x0c, 0x62, 0xca, 0xa8, 0x54, 0x12, 0xe7, 0x62, 0x0e, 0x2a, 0x2e, 0x40, 0x18, 0xc2, 0x08, 224 | 0x35, 0xc4, 0x48, 0x97, 0x8b, 0xdb, 0x35, 0x39, 0x23, 0x3f, 0x38, 0xb5, 0xa8, 0x2c, 0x33, 0x39, 225 | 0x55, 0x48, 0x8e, 0x8b, 0x05, 0xc4, 0x15, 0xe2, 0x84, 0x18, 0x11, 0x94, 0x5a, 0x28, 0x05, 0x63, 226 | 0x16, 0x17, 0x28, 0x31, 0x24, 0xb1, 0x81, 0xdd, 0x65, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0x2e, 227 | 0xba, 0x29, 0xbe, 0xb8, 0x00, 0x00, 0x00, 228 | } 229 | -------------------------------------------------------------------------------- /test/jsonhyaline/echo.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package main; 4 | 5 | import "ketty.proto"; 6 | 7 | message hyaline { 8 | option (json_hyaline) = true; 9 | 10 | repeated int64 val = 1; 11 | } 12 | 13 | message Req { 14 | hyaline hy = 1; 15 | } 16 | 17 | message Rsp { 18 | int64 val = 1; 19 | } 20 | 21 | service EchoService { 22 | rpc Echo(Req) returns(Rsp) {} 23 | } 24 | 25 | -------------------------------------------------------------------------------- /test/jsonhyaline/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | ) 7 | 8 | func main() { 9 | r := &Req{} 10 | r.Hy = new(Hyaline) 11 | r.Hy.Val = []int64{1, 2} 12 | b, _ := json.Marshal(r) 13 | println(string(b)) 14 | w := &Req{} 15 | err := json.Unmarshal(b, w) 16 | if err != nil { 17 | println("Error:", err.Error()) 18 | } else { 19 | fmt.Printf("w.Val: %v\n", w.GetHy().GetVal()) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /test/ketty_test.go: -------------------------------------------------------------------------------- 1 | package grpc_t 2 | 3 | import ( 4 | "testing" 5 | echo "github.com/yyzybb537/ketty/test/test_pb" 6 | "github.com/yyzybb537/ketty" 7 | kettyLog "github.com/yyzybb537/ketty/log" 8 | P "github.com/yyzybb537/ketty/protocol" 9 | O "github.com/yyzybb537/ketty/option" 10 | kettyHttp "github.com/yyzybb537/ketty/protocol/http" 11 | "time" 12 | context "golang.org/x/net/context" 13 | ) 14 | 15 | type EchoServer struct { 16 | } 17 | 18 | func (this *EchoServer) Echo(ctx context.Context, req *echo.Req) (*echo.Rsp, error) { 19 | return &echo.Rsp{Val:req.Val}, nil 20 | } 21 | 22 | type ExceptionServer struct { 23 | } 24 | 25 | func (this *ExceptionServer) Echo(ctx context.Context, req *echo.Req) (*echo.Rsp, error) { 26 | ketty.GetLog().Infof("panic") 27 | panic("Echo_Panic") 28 | return &echo.Rsp{Val:req.Val}, nil 29 | } 30 | 31 | func startServer(t *testing.T, sUrl string, driverUrl string) { 32 | server, err := ketty.Listen(sUrl, driverUrl) 33 | if err != nil { 34 | t.Fatalf("Listen error:%s", err.Error()) 35 | } 36 | 37 | server.RegisterMethod(echo.EchoServiceHandle, &EchoServer{}) 38 | 39 | err = server.Serve() 40 | if err != nil { 41 | t.Fatalf("Serve (%s) error:%s", sUrl, err.Error()) 42 | } 43 | } 44 | 45 | func startExceptionServer(t *testing.T, sUrl string) { 46 | server, err := ketty.Listen(sUrl, "") 47 | if err != nil { 48 | t.Fatalf("Listen error:%s", err.Error()) 49 | } 50 | 51 | server.RegisterMethod(echo.EchoServiceHandle, &ExceptionServer{}) 52 | 53 | err = server.Serve() 54 | if err != nil { 55 | t.Fatalf("Serve error:%s", err.Error()) 56 | } 57 | } 58 | func startClient(t *testing.T, sUrl string, exceptError bool) { 59 | client, err := ketty.Dial(sUrl, "") 60 | if err != nil { 61 | t.Fatalf("Dial error:%s", err.Error()) 62 | } 63 | 64 | err = client.SetOption(&O.Option{TimeoutMilliseconds:2000}) 65 | if err != nil { 66 | t.Fatalf("SetOption error:%s", err.Error()) 67 | } 68 | 69 | req := &echo.Req{ Val : 123 } 70 | stub := echo.NewKettyEchoServiceClient(client) 71 | rsp, err := stub.Echo(context.Background(), req) 72 | if exceptError { 73 | if err == nil { 74 | t.Fatalf("Invoke not error") 75 | } 76 | } else { 77 | if err != nil { 78 | t.Fatalf("Invoke error:%+v", err) 79 | } 80 | 81 | t.Logf("Echo Val:%d", rsp.Val) 82 | } 83 | } 84 | 85 | var clientMap = map[string]ketty.Client{} 86 | 87 | func bStartClient(b *testing.B, sUrl string) { 88 | var err error 89 | client, exists := clientMap[sUrl] 90 | if !exists { 91 | client, err = ketty.Dial(sUrl, "") 92 | if err != nil { 93 | b.Fatalf("Dial error:%s", err.Error()) 94 | } 95 | clientMap[sUrl] = client 96 | } 97 | 98 | for i := 0; i < b.N; i++ { 99 | req := &echo.Req{ Val : 123 } 100 | stub := echo.NewKettyEchoServiceClient(client) 101 | _, err := stub.Echo(context.Background(), req) 102 | if err != nil { 103 | b.Fatalf("Invoke error:%+v", err) 104 | } 105 | } 106 | } 107 | 108 | var httpUrl = "http://127.0.0.1:8091" 109 | var httpjsonUrl = "http.json://127.0.0.1:8092/hj/abc" 110 | var httpsUrl = "https://127.0.0.1:3050" 111 | var httpsjsonUrl = "https.json://127.0.0.1:3051" 112 | var grpcUrl = "grpc://127.0.0.1:8090" 113 | var etcdUrl = "grpc://127.0.0.1:8090" 114 | var urls = []string{ 115 | //"http://127.0.0.1", 116 | httpUrl, 117 | httpsUrl, 118 | grpcUrl, 119 | httpjsonUrl, 120 | httpsjsonUrl, 121 | } 122 | 123 | func TestGrpc(t *testing.T) { 124 | kettyHttp.InitTLS("cert.pem", "key.pem") 125 | ketty.SetLog(new(kettyLog.StdLog)) 126 | ketty.GetLog().Debugf("Protocols:%s", P.DumpProtocols()) 127 | for _, sUrl := range urls { 128 | t.Logf("Do url:%s", sUrl) 129 | startServer(t, sUrl, "") 130 | time.Sleep(time.Millisecond * 100) 131 | startClient(t, sUrl, false) 132 | } 133 | } 134 | 135 | func TestEtcd(t *testing.T) { 136 | kettyHttp.InitTLS("cert.pem", "key.pem") 137 | ketty.SetLog(new(kettyLog.StdLog)) 138 | sUrl := "grpc://127.0.0.1:33009" 139 | t.Logf("Do url:%s", sUrl) 140 | driverUrl := "etcd://127.0.0.1:2379/ketty_test" 141 | startServer(t, sUrl, driverUrl) 142 | time.Sleep(time.Millisecond * 100) 143 | startClient(t, driverUrl, false) 144 | 145 | startClient(t, driverUrl + "/none", true) 146 | } 147 | 148 | 149 | func TestException(t *testing.T) { 150 | kettyHttp.InitTLS("cert.pem", "key.pem") 151 | ketty.SetLog(new(kettyLog.StdLog)) 152 | sUrl := "grpc://127.0.0.1:33008" 153 | t.Logf("Do url:%s", sUrl) 154 | startExceptionServer(t, sUrl) 155 | time.Sleep(time.Millisecond * 100) 156 | startClient(t, sUrl, true) 157 | } 158 | 159 | func TestRouter(t *testing.T) { 160 | sUrl := "http.json://127.0.0.1:33099" 161 | startServer(t, sUrl + "/a", "") 162 | startServer(t, sUrl + "/b", "") 163 | time.Sleep(time.Millisecond * 100) 164 | startClient(t, sUrl + "/b", false) 165 | } 166 | 167 | func Benchmark_Grpc(b *testing.B) { 168 | ketty.SetLog(new(kettyLog.FakeLog)) 169 | bStartClient(b, grpcUrl) 170 | } 171 | 172 | func Benchmark_Http(b *testing.B) { 173 | ketty.SetLog(new(kettyLog.FakeLog)) 174 | bStartClient(b, httpUrl) 175 | } 176 | 177 | func Benchmark_Https(b *testing.B) { 178 | ketty.SetLog(new(kettyLog.FakeLog)) 179 | bStartClient(b, httpsUrl) 180 | } 181 | 182 | -------------------------------------------------------------------------------- /test/key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpAIBAAKCAQEAtMfDl54LjKL7+ZkiGWii4LNXrXE/mk9Acqrm6mQMxBQ+AvQi 3 | 5QuqawZ9fhgGCu67om6jyu8s3dud8nQZA+UNO5UlLSI6z4KDNfUgEY3sQFvCAVvp 4 | DqRXm4ZAseWjYQF174E3jsgHUZ1r2U/Q/8Z3U4qi4/gxV3/nGKTkzkXMiCAu7kDM 5 | 0tQokw5amUamsFrRG5Ld18fLN52tTgl6UZOqr/gNiNMtNSzSFJ4k8TchZDP/GRZ+ 6 | pMMb54Xk8sVpfiX3hlVt70hYYf1JCH+HGZevIESAN20+T40wQaj0VjGTmgvyISvE 7 | ktVDB0oaVdXRNiQaTV+88DsE+B9aD0ICJkDScQIDAQABAoIBAQCWglaKja+p6CwG 8 | 84JbSy2IDGhr44SDkYXjsfvMm32xZfT2+pFuxpacwmuKpq+pPHeFVb4Sf6yIDYdj 9 | y1rkTjgFKjgoan4yM8PQEYQgeVt/faV5Ai933EWAG3cTZU/vvIqW26sIueq1/W1E 10 | INqtiGviZuMVD5bs9XTOpMJGJgCSN2pPXSVTujlHE8BhyR2ElsNs30+RuzwtLkux 11 | dTrY7ksyZfP70G/6RJKJ1qX3gW0VfBkCZyeVxinMu8CfmJwlJwfNFNnx/6sqqWXG 12 | PXFBoS5UJGttvBFawbjfI3Lmt79ndXbfViRm+LI5fJfMSlz/lx2TUxpGcKI69LiY 13 | dPhn08pRAoGBAOUROrh2zi7xBCYwFfQj+4VrR5lpL+sY7qKJrS2ach8lkYP3x3ja 14 | 8ApD/kAAMZDnJGUzwnlTD4nIkFyS46E2Sa+QN6AKl7/jIkwemBeZdl5Tz0ZJCmKo 15 | eGu7qQqjetdbem14U1Qx+LgR1Q7bZEHCArnzEOe9VlOxeTiI3kP+/Ho1AoGBAMoJ 16 | ISODwEmVvhF/G6wF/NoLhLUN6hmhWoFfcJS77humxXOAhOZlmTNz7WZC8tKPKA14 17 | y9EysI5SL8Gu/Cpqf9fOuIbEFpEn7Wa8GyuCzWva2oZYoVRI9BFBEhC7uUhMkJQK 18 | UDfAkJrOk7DEYZyLRkzdlP3VB3SscWgGAl++1d7NAoGASS7p/cknMmQaGUE/olJk 19 | p0mRBHhQsOG0TAWEaI9xanYMOYdOGVSJYB48+qGVNGBEhni05yIcUr7bMFhVlUnM 20 | 3ORMgTshJFMiwAL+QHvtD1HjZOctBDwizPiVlyqHsootYRASP1H/zn8T/L0nGMMt 21 | L89yVTdufyqyOdewezTZ4QkCgYBatFSTbEA1CSxnEIjs51dI0WkIHDIgxacrdBzI 22 | OcNB2NLG80k3Yx+v0PkhMydvTuQtgDEJP9eyXcWhEB2YMW4qBl4j6v8ythlISWVt 23 | WHE/XedD76CmhYZGBM/GgXFCu1UhkavIDAUf62S1NMWaEZFDSyvr9es9/3l+uUvx 24 | jmzYdQKBgQDRb677QRA/GY1T1il+2El7dHYABZG1ZIcsjSyBqTdrmgs+MtUZWrxR 25 | iRPYQey9+Pf9XcgguOm6vvBSnhhzbJZDVOKGW7fJWiqlzoVMAd/JHla82e9aB37k 26 | csizun16j5d/+E6CPGPzQXAECyvrWIBYC4vk2l9Vdt7apY6rqYmyhw== 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /test/log_test.go: -------------------------------------------------------------------------------- 1 | package log_t 2 | 3 | import ( 4 | "testing" 5 | "github.com/yyzybb537/ketty" 6 | "github.com/yyzybb537/ketty/log" 7 | "time" 8 | "strings" 9 | ) 10 | 11 | var _ = log.BindOption 12 | 13 | func TestLog(t *testing.T) { 14 | ketty.GetLog().Debugf("stdout log") 15 | 16 | opt := log.DefaultLogOption() 17 | opt.HeaderFormat = "$L $datetime-ms $$ $gid $file:$line] " 18 | log.BindOption("key", opt) 19 | ketty.GetLog("key").Debugf("key log") 20 | 21 | opt.OutputFile = "logdir/ketty" 22 | flg, err := log.NewFileLog(opt) 23 | if err != nil { 24 | println(err.Error()) 25 | return 26 | } 27 | log.SetLog(flg) 28 | ketty.GetLog().Debugf("file log") 29 | log.FlushAll() 30 | // time.Sleep(time.Second) 31 | } 32 | 33 | func Benchmark_FileLog(b *testing.B) { 34 | b.StopTimer() 35 | opt := log.DefaultLogOption() 36 | opt.OutputFile = "logdir/ketty" 37 | flg, err := log.NewFileLog(opt) 38 | if err != nil { 39 | println(err.Error()) 40 | return 41 | } 42 | log.SetLog(flg) 43 | b.StartTimer() 44 | for i := 0; i < b.N; i++ { 45 | ketty.GetLog().Debugf("file log") 46 | } 47 | // log.FlushAll() 48 | } 49 | 50 | func Benchmark_LogNull(b *testing.B) { 51 | b.StopTimer() 52 | opt := log.DefaultLogOption() 53 | // opt.HeaderFormat = "$datetime" 54 | opt.OutputFile = "/dev/null" 55 | flg, err := log.NewFileLog(opt) 56 | if err != nil { 57 | println(err.Error()) 58 | return 59 | } 60 | log.SetLog(flg) 61 | b.StartTimer() 62 | for i := 0; i < b.N; i++ { 63 | ketty.GetLog().Debugf("file log") 64 | } 65 | } 66 | 67 | func Benchmark_LogTime(b *testing.B) { 68 | b.StopTimer() 69 | w := new(fakeWriter) 70 | b.StartTimer() 71 | for i := 0; i < b.N; i++ { 72 | w.Write([]byte(time.Now().Format("2006-01-02 15:04:05"))) 73 | } 74 | } 75 | 76 | func Benchmark_LogWithoutHeader(b *testing.B) { 77 | b.StopTimer() 78 | opt := log.DefaultLogOption() 79 | opt.HeaderFormat = "" 80 | opt.OutputFile = "/dev/null" 81 | flg, err := log.NewFileLog(opt) 82 | if err != nil { 83 | println(err.Error()) 84 | return 85 | } 86 | log.SetLog(flg) 87 | b.StartTimer() 88 | for i := 0; i < b.N; i++ { 89 | ketty.GetLog().Debugf("file log") 90 | } 91 | } 92 | 93 | type fakeWriter struct {} 94 | func (this *fakeWriter) Write(b []byte) (int, error) { 95 | return len(b), nil 96 | } 97 | 98 | var _ = strings.Compare 99 | func Benchmark_LogHeader(b *testing.B) { 100 | b.StopTimer() 101 | opt := log.DefaultLogOption() 102 | // opt.HeaderFormat = strings.Repeat("$datetime", 1) 103 | // opt.HeaderFormat = strings.Repeat("$gid", 1) 104 | w := new(fakeWriter) 105 | opt.WriteHeader(log.Level(1), 0, w) 106 | b.StartTimer() 107 | for i := 0; i < b.N; i++ { 108 | opt.WriteHeader(log.Level(1), 0, w) 109 | } 110 | } 111 | 112 | -------------------------------------------------------------------------------- /test/server_bench.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | echo "github.com/yyzybb537/ketty/test/test_pb" 5 | "github.com/yyzybb537/ketty" 6 | kettyLog "github.com/yyzybb537/ketty/log" 7 | kettyHttp "github.com/yyzybb537/ketty/protocol/http" 8 | context "golang.org/x/net/context" 9 | ) 10 | 11 | type EchoServer struct { 12 | } 13 | 14 | func (this *EchoServer) Echo(ctx context.Context, req *echo.Req) (*echo.Rsp, error) { 15 | return &echo.Rsp{Val:req.Val}, nil 16 | } 17 | 18 | 19 | func startServer(sUrl string) { 20 | server, err := ketty.Listen(sUrl, "") 21 | if err != nil { 22 | ketty.GetLog().Errorf("Listen error:%s", err.Error()) 23 | } 24 | 25 | server.RegisterMethod(echo.EchoServiceHandle, &EchoServer{}) 26 | 27 | err = server.Serve() 28 | if err != nil { 29 | ketty.GetLog().Errorf("Serve error:%s", err.Error()) 30 | } 31 | } 32 | 33 | var httpUrl = "http://127.0.0.1:8091" 34 | var httpsUrl = "https://127.0.0.1:3050" 35 | var grpcUrl = "grpc://127.0.0.1:8090" 36 | var urls = []string{ 37 | //"http://127.0.0.1", 38 | httpUrl, 39 | httpsUrl, 40 | grpcUrl, 41 | } 42 | 43 | func main() { 44 | kettyHttp.InitTLS("cert.pem", "key.pem") 45 | for _, sUrl := range urls { 46 | ketty.GetLog().Infof("Listen url:%s", sUrl) 47 | startServer(sUrl) 48 | } 49 | ketty.SetLog(new(kettyLog.FakeLog)) 50 | q := make(chan int) 51 | <-q 52 | } 53 | 54 | -------------------------------------------------------------------------------- /test/test_pb/echo.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. DO NOT EDIT. 2 | // source: echo.proto 3 | 4 | /* 5 | Package test_pb is a generated protocol buffer package. 6 | 7 | It is generated from these files: 8 | echo.proto 9 | 10 | It has these top-level messages: 11 | Req 12 | Rsp 13 | */ 14 | package test_pb 15 | 16 | import proto "github.com/golang/protobuf/proto" 17 | import fmt "fmt" 18 | import math "math" 19 | 20 | import ( 21 | context "golang.org/x/net/context" 22 | grpc "google.golang.org/grpc" 23 | ) 24 | 25 | import ( 26 | ketty "github.com/yyzybb537/ketty" 27 | ) 28 | 29 | // Reference imports to suppress errors if they are not otherwise used. 30 | var _ = proto.Marshal 31 | var _ = fmt.Errorf 32 | var _ = math.Inf 33 | 34 | // This is a compile-time assertion to ensure that this generated file 35 | // is compatible with the proto package it is being compiled against. 36 | // A compilation error at this line likely means your copy of the 37 | // proto package needs to be updated. 38 | const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package 39 | 40 | type Req struct { 41 | Val int64 `protobuf:"varint,1,opt,name=val" json:"val,omitempty"` 42 | } 43 | 44 | func (m *Req) Reset() { *m = Req{} } 45 | func (m *Req) String() string { return proto.CompactTextString(m) } 46 | func (*Req) ProtoMessage() {} 47 | func (*Req) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } 48 | 49 | func (m *Req) GetVal() int64 { 50 | if m != nil { 51 | return m.Val 52 | } 53 | return 0 54 | } 55 | 56 | type Rsp struct { 57 | Val int64 `protobuf:"varint,1,opt,name=val" json:"val,omitempty"` 58 | } 59 | 60 | func (m *Rsp) Reset() { *m = Rsp{} } 61 | func (m *Rsp) String() string { return proto.CompactTextString(m) } 62 | func (*Rsp) ProtoMessage() {} 63 | func (*Rsp) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } 64 | 65 | func (m *Rsp) GetVal() int64 { 66 | if m != nil { 67 | return m.Val 68 | } 69 | return 0 70 | } 71 | 72 | func init() { 73 | proto.RegisterType((*Req)(nil), "test_pb.Req") 74 | proto.RegisterType((*Rsp)(nil), "test_pb.Rsp") 75 | } 76 | 77 | // Reference imports to suppress errors if they are not otherwise used. 78 | var _ context.Context 79 | var _ grpc.ClientConn 80 | 81 | // This is a compile-time assertion to ensure that this generated file 82 | // is compatible with the grpc package it is being compiled against. 83 | const _ = grpc.SupportPackageIsVersion4 84 | 85 | // Client API for EchoService service 86 | 87 | type EchoServiceClient interface { 88 | Echo(ctx context.Context, in *Req, opts ...grpc.CallOption) (*Rsp, error) 89 | } 90 | 91 | type echoServiceClient struct { 92 | cc *grpc.ClientConn 93 | } 94 | 95 | func NewEchoServiceClient(cc *grpc.ClientConn) EchoServiceClient { 96 | return &echoServiceClient{cc} 97 | } 98 | 99 | func (c *echoServiceClient) Echo(ctx context.Context, in *Req, opts ...grpc.CallOption) (*Rsp, error) { 100 | out := new(Rsp) 101 | err := grpc.Invoke(ctx, "/test_pb.EchoService/Echo", in, out, c.cc, opts...) 102 | if err != nil { 103 | return nil, err 104 | } 105 | return out, nil 106 | } 107 | 108 | // Server API for EchoService service 109 | 110 | type EchoServiceServer interface { 111 | Echo(context.Context, *Req) (*Rsp, error) 112 | } 113 | 114 | func RegisterEchoServiceServer(s *grpc.Server, srv EchoServiceServer) { 115 | s.RegisterService(&_EchoService_serviceDesc, srv) 116 | } 117 | 118 | func _EchoService_Echo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 119 | in := new(Req) 120 | if err := dec(in); err != nil { 121 | return nil, err 122 | } 123 | if interceptor == nil { 124 | return srv.(EchoServiceServer).Echo(ctx, in) 125 | } 126 | info := &grpc.UnaryServerInfo{ 127 | Server: srv, 128 | FullMethod: "/test_pb.EchoService/Echo", 129 | } 130 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 131 | return srv.(EchoServiceServer).Echo(ctx, req.(*Req)) 132 | } 133 | return interceptor(ctx, in, info, handler) 134 | } 135 | 136 | var _EchoService_serviceDesc = grpc.ServiceDesc{ 137 | ServiceName: "test_pb.EchoService", 138 | HandlerType: (*EchoServiceServer)(nil), 139 | Methods: []grpc.MethodDesc{ 140 | { 141 | MethodName: "Echo", 142 | Handler: _EchoService_Echo_Handler, 143 | }, 144 | }, 145 | Streams: []grpc.StreamDesc{}, 146 | Metadata: "echo.proto", 147 | } 148 | 149 | // Reference imports to suppress errors if they are not otherwise used. 150 | var _ ketty.Dummy 151 | 152 | // This is a compile-time assertion to ensure that this generated file 153 | // is compatible with the ketty package it is being compiled against. 154 | 155 | type EchoServiceHandleT struct { 156 | desc *grpc.ServiceDesc 157 | } 158 | 159 | func (h *EchoServiceHandleT) Implement() interface{} { 160 | return h.desc 161 | } 162 | 163 | func (h *EchoServiceHandleT) ServiceName() string { 164 | return h.desc.ServiceName 165 | } 166 | 167 | var EchoServiceHandle = &EchoServiceHandleT{desc: &_EchoService_serviceDesc} 168 | 169 | type KettyEchoServiceClient struct { 170 | client ketty.Client 171 | } 172 | 173 | func NewKettyEchoServiceClient(client ketty.Client) *KettyEchoServiceClient { 174 | return &KettyEchoServiceClient{client} 175 | } 176 | 177 | func (this *KettyEchoServiceClient) Echo(ctx context.Context, in *Req) (*Rsp, error) { 178 | out := new(Rsp) 179 | err := this.client.Invoke(ctx, EchoServiceHandle, "Echo", in, out) 180 | if err != nil { 181 | return nil, err 182 | } 183 | return out, nil 184 | } 185 | 186 | func init() { proto.RegisterFile("echo.proto", fileDescriptor0) } 187 | 188 | var fileDescriptor0 = []byte{ 189 | // 116 bytes of a gzipped FileDescriptorProto 190 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0xe2, 0x4a, 0x4d, 0xce, 0xc8, 191 | 0xd7, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x2f, 0x49, 0x2d, 0x2e, 0x89, 0x2f, 0x48, 0x52, 192 | 0x12, 0xe7, 0x62, 0x0e, 0x4a, 0x2d, 0x14, 0x12, 0xe0, 0x62, 0x2e, 0x4b, 0xcc, 0x91, 0x60, 0x54, 193 | 0x60, 0xd4, 0x60, 0x0e, 0x02, 0x31, 0xc1, 0x12, 0xc5, 0x05, 0x98, 0x12, 0x46, 0xc6, 0x5c, 0xdc, 194 | 0xae, 0xc9, 0x19, 0xf9, 0xc1, 0xa9, 0x45, 0x65, 0x99, 0xc9, 0xa9, 0x42, 0x2a, 0x5c, 0x2c, 0x20, 195 | 0xae, 0x10, 0x8f, 0x1e, 0xd4, 0x48, 0xbd, 0xa0, 0xd4, 0x42, 0x29, 0x24, 0x5e, 0x71, 0x81, 0x12, 196 | 0x43, 0x12, 0x1b, 0xd8, 0x5a, 0x63, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0xb8, 0xa9, 0x26, 0x0c, 197 | 0x84, 0x00, 0x00, 0x00, 198 | } 199 | -------------------------------------------------------------------------------- /test/test_pb/echo.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package test_pb; 4 | 5 | message Req { 6 | int64 val = 1; 7 | } 8 | 9 | message Rsp { 10 | int64 val = 1; 11 | } 12 | 13 | service EchoService { 14 | rpc Echo(Req) returns(Rsp) {} 15 | } 16 | 17 | -------------------------------------------------------------------------------- /url/url.go: -------------------------------------------------------------------------------- 1 | package url 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | "strconv" 7 | "regexp" 8 | ) 9 | 10 | var re *regexp.Regexp 11 | 12 | func init() { 13 | re = regexp.MustCompile("([\\w\\.]+)://([^/]+)(.*)") 14 | } 15 | 16 | type Addr struct { 17 | Host string 18 | Port int 19 | MetaData interface{} 20 | } 21 | 22 | var defaultPorts = map[string]int{} 23 | 24 | func RegDefaultPort(protocol string, port int) { 25 | defaultPorts[strings.ToLower(protocol)] = port 26 | } 27 | 28 | func GetDefaultPort(protocol string) (port int) { 29 | port, _ = defaultPorts[strings.ToLower(protocol)] 30 | return 31 | } 32 | 33 | func AddrFromString(hostport string, protocol string) (a Addr, err error) { 34 | dPort := GetDefaultPort(GetMainProtocol(protocol)) 35 | sIndex := strings.Index(hostport, ":") 36 | if sIndex >= 0 { 37 | a.Host = hostport[:sIndex] 38 | a.Port, err = strconv.Atoi(hostport[sIndex+1:]) 39 | } else { 40 | a.Host = hostport 41 | a.Port = dPort 42 | } 43 | return 44 | } 45 | 46 | func FormatAddr(hostport string, protocol string) string { 47 | addr, err := AddrFromString(hostport, protocol) 48 | if err != nil { 49 | return hostport 50 | } 51 | return addr.ToString() 52 | } 53 | 54 | func (this *Addr) ToString() string { 55 | return fmt.Sprintf("%s:%d", this.Host, this.Port) 56 | } 57 | 58 | type Url struct { 59 | Protocol string 60 | SAddr string 61 | Path string 62 | MetaData interface{} 63 | } 64 | 65 | // @url: protocol://[balance@]ip[:port][,ip[:port]]/balancePath 66 | func UrlFromString(url string) (Url, error) { 67 | u := Url{} 68 | if url == "" { 69 | return u, nil 70 | } 71 | 72 | matchs := re.FindStringSubmatch(url) 73 | if len(matchs) != 4 { 74 | return u, fmt.Errorf("Url regex parse error. url=%s. matchs=%d", url, len(matchs)) 75 | } 76 | 77 | u.Protocol = matchs[1] 78 | u.SAddr = matchs[2] 79 | u.Path = matchs[3] 80 | return u, nil 81 | } 82 | 83 | func UrlFromDriverString(url string) (Url, error) { 84 | return UrlFromString(strings.Replace(url, "|", "/", -1)) 85 | } 86 | 87 | func (this *Url) GetAddrs() []string { 88 | return strings.Split(this.SAddr, ",") 89 | } 90 | 91 | func (this *Url) SetAddrs(ss []string) { 92 | this.SAddr = strings.Join(ss, ",") 93 | } 94 | 95 | func (this *Url) GetMainProtocol() string { 96 | return GetMainProtocol(this.Protocol) 97 | } 98 | 99 | func GetMainProtocol(protocol string) string { 100 | ss := strings.Split(protocol, ".") 101 | if len(ss) > 0 { 102 | return ss[0] 103 | } 104 | return protocol 105 | } 106 | 107 | func (this *Url) ToString() string { 108 | if this.IsEmpty() { 109 | return "" 110 | } 111 | 112 | if this.Path == "" { 113 | return fmt.Sprintf("%s://%s", this.Protocol, this.SAddr) 114 | } else { 115 | return fmt.Sprintf("%s://%s%s", this.Protocol, this.SAddr, this.Path) 116 | } 117 | } 118 | 119 | func (this *Url) ToStringByProtocol(protocol string) string { 120 | if this.IsEmpty() { 121 | return "" 122 | } 123 | 124 | if this.Path == "" { 125 | return fmt.Sprintf("%s://%s", protocol, this.SAddr) 126 | } else { 127 | return fmt.Sprintf("%s://%s%s", protocol, this.SAddr, this.Path) 128 | } 129 | } 130 | 131 | func (this *Url) ToDriverString() string { 132 | return strings.Replace(this.ToString(), "/", "|", -1) 133 | } 134 | 135 | func (this *Url) IsEmpty() bool { 136 | return this.Protocol == "" 137 | } 138 | 139 | --------------------------------------------------------------------------------