├── .gitignore ├── README.md ├── biz_circuitbreaker ├── gateway │ ├── main.go │ └── router.go └── register │ ├── endpoint.go │ ├── instrument.go │ ├── logging.go │ ├── main.go │ ├── register.go │ ├── service.go │ └── transport.go ├── biz_consul ├── discover │ ├── endpoint.go │ ├── factory.go │ ├── main.go │ └── transport.go ├── gateway │ └── main.go └── register │ ├── endpoint.go │ ├── instrument.go │ ├── logging.go │ ├── main.go │ ├── register.go │ ├── service.go │ └── transport.go ├── biz_jwt ├── gateway │ ├── main.go │ └── router.go └── register │ ├── endpoint.go │ ├── instrument.go │ ├── jwt.go │ ├── logging.go │ ├── main.go │ ├── register.go │ ├── service.go │ └── transport.go ├── biz_log ├── endpoint.go ├── logging.go ├── main.go ├── service.go └── transport.go ├── biz_monitor ├── endpoint.go ├── instrument.go ├── logging.go ├── main.go ├── service.go └── transport.go ├── biz_rate ├── endpoint.go ├── instrument.go ├── logging.go ├── main.go ├── service.go └── transport.go ├── biz_rest ├── endpoint.go ├── main.go ├── service.go └── transport.go ├── biz_trace ├── gateway │ └── main.go └── register │ ├── endpoint.go │ ├── instrument.go │ ├── logging.go │ ├── main.go │ ├── register.go │ ├── service.go │ └── transport.go └── pictrue ├── 01.png ├── 02.png ├── 03.png ├── 04.png ├── 05.png ├── 06.png └── 07.png /.gitignore: -------------------------------------------------------------------------------- 1 | # ---> Go 2 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 3 | *.o 4 | *.a 5 | *.so 6 | 7 | # Folders 8 | _obj 9 | _test 10 | 11 | # Architecture specific extensions/prefixes 12 | *.[568vq] 13 | [568vq].out 14 | 15 | *.cgo1.go 16 | *.cgo2.c 17 | _cgo_defun.c 18 | _cgo_gotypes.go 19 | _cgo_export.* 20 | 21 | _testmain.go 22 | 23 | etl.go 24 | 25 | *.exe 26 | *.test 27 | *.prof 28 | deploypkg/* 29 | .idea/ 30 | .DS_Store 31 | .vscode 32 | debug -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## biz_rest: http restful api 2 | * server 3 | ```shell script 4 | cd $GOPATH/src/go-kit-one/biz_rest 5 | go run *.go 6 | ``` 7 | * client 8 | ```shell script 9 | curl -X POST -H "Content-Type:application/json" \ 10 | http://127.0.0.1:8000/biz/add/1/2 11 | ``` 12 | 13 | ## biz_log: 日志功能 14 | * server 15 | ```shell script 16 | cd $GOPATH/src/go-kit-one/biz_log 17 | go run *.go 18 | ``` 19 | * client 20 | ```shell script 21 | curl -X POST -H "Content-Type:application/json" \ 22 | http://127.0.0.1:8000/biz/add/1/2 23 | ``` 24 | 25 | ## biz_rate: 令牌桶算法限流(juju/ratelimit方案、gokit内置实现方案) 26 | * server 27 | ```shell script 28 | cd $GOPATH/src/go-kit-one/biz_rate 29 | go run *.go 30 | ``` 31 | * client 32 | ```shell script 33 | curl -X POST -H "Content-Type:application/json" \ 34 | http://127.0.0.1:8000/biz/add/1/2 35 | ``` 36 | 37 | ## biz_monitor: 服务监控 38 | * docker prometheus 39 | ```shell script 40 | docker search prometheus 41 | docker pull prom/prometheus 42 | docker run --name vc-prometheus -d -p 9090:9090 prom/prometheus 43 | ``` 44 | ```yaml 45 | /etc/prometheus/prometheus.yaml 46 | global: 47 | scrape_interval: 15s 48 | external_labels: 49 | monitor: 'vc-monitor' 50 | 51 | scrape_configs: 52 | - job_name: 'prometheus' 53 | scrape_interval: 5s 54 | static_configs: 55 | - targets: ['localhost:9090'] 56 | labels: 57 | group: 'local' 58 | 59 | - job_name: 'vc-biz-service' 60 | scrape_interval: 5s 61 | static_configs: 62 | - targets: ['192.168.65.2:8000'] 63 | labels: 64 | group: 'biz-service' 65 | ``` 66 | * docker grafana 67 | ```shell script 68 | docker search grafana 69 | docker pull grafana/grafana 70 | docker run --name vc-grafana -d -p 3000:3000 grafana/grafana 71 | ``` 72 | * server 73 | ```shell script 74 | cd $GOPATH/src/go-kit-one/biz_monitor 75 | go run *.go 76 | ``` 77 | * client 78 | ```shell script 79 | curl -X POST -H "Content-Type:application/json" \ 80 | http://127.0.0.1:8000/biz/add/1/2 81 | ``` 82 | ## biz_consul: 服务注册与发现 83 | * docker consul 84 | ```shell script 85 | dokcer search consul 86 | docker pull consul 87 | docker run --name vc-consul -d -p 8500:8500 consul 88 | ``` 89 | * server register 90 | ```shell script 91 | cd $GOPATH/src/go-kit-one/biz_consul/register 92 | go build 93 | ./register -consul.host localhost -consul.port 8500 -service.host 192.168.0.103 -service.port 8000 94 | ./register -consul.host localhost -consul.port 8500 -service.host 192.168.0.103 -service.port 8001 95 | ``` 96 | 97 | * server discover 98 | ```shell script 99 | cd $GOPATH/src/go-kit-one/biz_consul/discover 100 | go build 101 | ./discover -consul.host localhost -consul.port 8500 102 | curl -XPOST -H "Content-Type:application/json" \ 103 | http://localhost:8002/biz \ 104 | -d'{"type":"add","a":1,"b":2}' 105 | ``` 106 | * gateway proxy 107 | ```shell script 108 | cd $GOPATH/src/go-kit-one/biz_consul/gateway 109 | go run *.go 110 | curl -XPOST -H "Content-Type:application/json" \ 111 | http://127.0.0.1:8003/biz/biz/add/1/2 112 | ``` 113 | ## biz_trace: 服务链路跟踪 114 | * docker zipkin 115 | ```shell script 116 | docker search zipkin 117 | dokcer pull openzipkin/zipkin 118 | docker run --name vc-zipkin -d -p 9411:9411 openzipkin/zipkin 119 | ``` 120 | * server register 121 | ```shell script 122 | cd $GOPATH/src/go-kit-one/biz_trace/register 123 | go build 124 | ./register -consul.host localhost -consul.port 8500 -service.host 192.168.0.103 -service.port 8000 125 | ./register -consul.host localhost -consul.port 8500 -service.host 192.168.0.103 -service.port 8001 126 | ``` 127 | * gateway proxy 128 | ```shell script 129 | cd $GOPATH/src/go-kit-one/biz_trace/gateway 130 | go run *.go 131 | curl -XPOST -H "Content-Type:application/json" \ 132 | http://127.0.0.1:8003/biz/biz/add/1/2 133 | ``` 134 | ## biz_circuitbreaker: 服务熔断 135 | * docker hystrix-dashboard 136 | ```shell script 137 | docker search hystrix-dashboard 138 | docker pull mlabouardy/hystrix-dashboard 139 | docker run --name vc-hystrix-dashboard -d -p 9002:9002 mlabouardy/hystrix-dashboard 140 | ``` 141 | * server register 142 | ```shell script 143 | cd $GOPATH/src/go-kit-one/biz_circuitbreaker/register 144 | go build 145 | ./register -consul.host localhost -consul.port 8500 -service.host 192.168.0.103 -service.port 8000 146 | ./register -consul.host localhost -consul.port 8500 -service.host 192.168.0.103 -service.port 8001 147 | ``` 148 | * gateway proxy 149 | ```shell script 150 | cd $GOPATH/src/go-kit-one/biz_circuitbreaker/gateway 151 | go run *.go 152 | curl -XPOST -H "Content-Type:application/json" \ 153 | http://127.0.0.1:8003/biz/biz/add/1/2 154 | ``` 155 | 156 | ## biz_jwt: JWT身份认证 157 | * server register 158 | ```shell script 159 | cd $GOPATH/src/go-kit-one/biz_jwt/register 160 | go build 161 | ./register -consul.host localhost -consul.port 8500 -service.host 192.168.0.103 -service.port 8000 162 | ./register -consul.host localhost -consul.port 8500 -service.host 192.168.0.103 -service.port 8001 163 | ``` 164 | * gateway proxy 165 | ```shell script 166 | cd $GOPATH/src/go-kit-one/biz_jwt/gateway 167 | go run *.go 168 | curl -XPOST -H "Content-Type:application/json" \ 169 | http://127.0.0.1:8003/biz/login -d'{"name":"admin","pwd":"admin"}' 170 | curl -XPOST -H "Content-Type:application/json" \ 171 | http://127.0.0.1:8003/biz/biz/add/1/2 \ 172 | -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySUQiOiJhZG1pbiIsIm5hbWUiOiJhZG1pbiIsImV4cCI6MTU2ODEwMzg0NiwiaXNzIjoic3lzdGVtIn0.NjKW_DqfEhk9QHmgM0hsOCYxN6wX7O4by0h2NvhPP_c" 173 | ``` -------------------------------------------------------------------------------- /biz_circuitbreaker/gateway/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "github.com/afex/hystrix-go/hystrix" 7 | "github.com/go-kit/kit/log" 8 | "github.com/hashicorp/consul/api" 9 | "github.com/openzipkin/zipkin-go" 10 | zipkinHttpsvr "github.com/openzipkin/zipkin-go/middleware/http" 11 | zipkinHttp "github.com/openzipkin/zipkin-go/reporter/http" 12 | "math/rand" 13 | "net" 14 | "net/http" 15 | "net/http/httputil" 16 | "os" 17 | "os/signal" 18 | "strings" 19 | "syscall" 20 | ) 21 | 22 | var ( 23 | consulHost = flag.String("consul.host", "192.168.0.103", "consul server ip address") 24 | consulPort = flag.String("consul.port", "8500", "consul server port") 25 | zipkinUrl = flag.String("zipkin.url", "http://192.168.0.103:9411/api/v2/spans", "zipkin server url") 26 | ) 27 | 28 | func main() { 29 | flag.Parse() 30 | 31 | var logger log.Logger 32 | { 33 | logger = log.NewLogfmtLogger(os.Stderr) 34 | logger = log.With(logger, "ts", log.DefaultTimestampUTC) 35 | logger = log.With(logger, "caller", log.DefaultCaller) 36 | } 37 | 38 | var zipKinTracer *zipkin.Tracer 39 | { 40 | var ( 41 | err error 42 | hostPort = "localhost:8003" 43 | serviceName = "gateway-service" 44 | useNoopTracer = *zipkinUrl == "" 45 | reporter = zipkinHttp.NewReporter(*zipkinUrl) 46 | ) 47 | 48 | defer reporter.Close() 49 | 50 | zEP, _ := zipkin.NewEndpoint(serviceName, hostPort) 51 | zipKinTracer, err = zipkin.NewTracer( 52 | reporter, zipkin.WithLocalEndpoint(zEP), zipkin.WithNoopTracer(useNoopTracer), 53 | ) 54 | if err != nil { 55 | logger.Log("error", err) 56 | os.Exit(1) 57 | } 58 | if !useNoopTracer { 59 | logger.Log("tracer", "zipkin", "type", "native", "url", *zipkinUrl) 60 | } 61 | } 62 | 63 | consulCfg := api.DefaultConfig() 64 | consulCfg.Address = "http://" + *consulHost + ":" + *consulPort 65 | consulClient, err := api.NewClient(consulCfg) 66 | 67 | if err != nil { 68 | logger.Log("err", err) 69 | os.Exit(1) 70 | } 71 | 72 | //proxy := NewReverseProxy(consulClient, zipKinTracer, logger) 73 | hystrixRouter := NewRoutes(consulClient, zipKinTracer, "circuit breaker:service unavailable", logger) 74 | 75 | tags := map[string]string{ 76 | "component": "gateway_server", 77 | } 78 | 79 | handler := zipkinHttpsvr.NewServerMiddleware( 80 | zipKinTracer, 81 | zipkinHttpsvr.SpanName("gateway"), 82 | zipkinHttpsvr.TagResponseSize(true), 83 | zipkinHttpsvr.ServerTags(tags), 84 | )(hystrixRouter) 85 | 86 | errChan := make(chan error) 87 | 88 | hystrixHandler := hystrix.NewStreamHandler() 89 | hystrixHandler.Start() 90 | go func() { 91 | errChan <- http.ListenAndServe(net.JoinHostPort("", "8010"), hystrixHandler) 92 | }() 93 | 94 | go func() { 95 | c := make(chan os.Signal) 96 | signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) 97 | errChan <- fmt.Errorf("%s", <-c) 98 | }() 99 | 100 | go func() { 101 | logger.Log("transport", "http", "addr", "8003") 102 | errChan <- http.ListenAndServe(":8003", handler) 103 | }() 104 | 105 | logger.Log("exit", <-errChan) 106 | } 107 | 108 | func NewReverseProxy(client *api.Client, tracer *zipkin.Tracer, logger log.Logger) *httputil.ReverseProxy { 109 | director := func(req *http.Request) { 110 | reqPath := req.URL.Path 111 | if reqPath == "" { 112 | return 113 | } 114 | // /biz/add/1/2 115 | pathArray := strings.Split(reqPath, "/") 116 | serviceName := pathArray[1] 117 | logger.Log("serviceName:", serviceName) 118 | 119 | result, _, err := client.Catalog().Service(serviceName, "", nil) 120 | if err != nil { 121 | logger.Log("reverseProxy failed", "query service instance error", err.Error()) 122 | return 123 | } 124 | 125 | if len(result) == 0 { 126 | logger.Log("reverseProxy failed", "no such service instance", serviceName) 127 | return 128 | } 129 | 130 | destPath := strings.Join(pathArray[2:], "/") 131 | tgt := result[rand.Int()%len(result)] 132 | logger.Log("service id", tgt.ServiceID) 133 | 134 | req.URL.Scheme = "http" 135 | req.URL.Host = fmt.Sprintf("%s:%d", tgt.ServiceAddress, tgt.ServicePort) 136 | req.URL.Path = "/" + destPath 137 | } 138 | 139 | roundTrip, _ := zipkinHttpsvr.NewTransport(tracer, zipkinHttpsvr.TransportTrace(true)) 140 | 141 | return &httputil.ReverseProxy{ 142 | Director: director, 143 | Transport: roundTrip, 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /biz_circuitbreaker/gateway/router.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "github.com/afex/hystrix-go/hystrix" 7 | "github.com/go-kit/kit/log" 8 | "github.com/hashicorp/consul/api" 9 | "github.com/openzipkin/zipkin-go" 10 | zipkinHttpsvr "github.com/openzipkin/zipkin-go/middleware/http" 11 | "math/rand" 12 | "net/http" 13 | "net/http/httputil" 14 | "strings" 15 | "sync" 16 | ) 17 | 18 | type HystrixRouter struct { 19 | svcMap *sync.Map 20 | logger log.Logger 21 | fallbackMsg string 22 | consulClient *api.Client 23 | tracer *zipkin.Tracer 24 | } 25 | 26 | func NewRoutes(client *api.Client, tracer *zipkin.Tracer, fbMsg string, logger log.Logger) http.Handler { 27 | return &HystrixRouter{ 28 | svcMap: &sync.Map{}, 29 | logger: logger, 30 | fallbackMsg: fbMsg, 31 | consulClient: client, 32 | tracer: tracer, 33 | } 34 | } 35 | 36 | func (router *HystrixRouter) ServeHTTP(w http.ResponseWriter, r *http.Request) { 37 | // /biz/add/1/2 38 | reqPath := r.URL.Path 39 | if reqPath == "" { 40 | return 41 | } 42 | 43 | pathArray := strings.Split(reqPath, "/") 44 | serviceName := pathArray[1] 45 | 46 | if _, ok := router.svcMap.Load(serviceName); !ok { 47 | hystrix.ConfigureCommand(serviceName, hystrix.CommandConfig{Timeout: 1000}) 48 | router.svcMap.Store(serviceName, serviceName) 49 | } 50 | 51 | err := hystrix.Do(serviceName, func() (err error) { 52 | result, _, err := router.consulClient.Catalog().Service(serviceName, "", nil) 53 | if err != nil { 54 | router.logger.Log("reverseProxy failed", "query service instance error", err.Error()) 55 | return 56 | } 57 | 58 | if len(result) == 0 { 59 | router.logger.Log("reverseProxy failed", "no such service instance", serviceName) 60 | return errors.New("no such service instance") 61 | } 62 | 63 | director := func(req *http.Request) { 64 | destPath := strings.Join(pathArray[2:], "/") 65 | tgt := result[rand.Int()%len(result)] 66 | router.logger.Log("service id", tgt.ServiceID) 67 | 68 | req.URL.Scheme = "http" 69 | req.URL.Host = fmt.Sprintf("%s:%d", tgt.ServiceAddress, tgt.ServicePort) 70 | req.URL.Path = "/" + destPath 71 | } 72 | 73 | var proxyError error = nil 74 | roundTrip, _ := zipkinHttpsvr.NewTransport(router.tracer, zipkinHttpsvr.TransportTrace(true)) 75 | 76 | errorHandler := func(ew http.ResponseWriter, er *http.Request, err error) { 77 | proxyError = err 78 | } 79 | proxy := &httputil.ReverseProxy{ 80 | Director: director, 81 | Transport: roundTrip, 82 | ErrorHandler: errorHandler, 83 | } 84 | 85 | proxy.ServeHTTP(w, r) 86 | return proxyError 87 | }, func(err error) error { 88 | router.logger.Log("fallback error desc", err.Error()) 89 | return errors.New(router.fallbackMsg) 90 | }) 91 | 92 | if err != nil { 93 | w.WriteHeader(500) 94 | w.Write([]byte(err.Error())) 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /biz_circuitbreaker/register/endpoint.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | "github.com/go-kit/kit/endpoint" 8 | "strings" 9 | ) 10 | 11 | type BizRequest struct { 12 | ReqType string `json:"type"` 13 | A int `json:"a"` 14 | B int `json:"b"` 15 | } 16 | 17 | type BizResponse struct { 18 | Result int `json:"result"` 19 | Error error `json:"error"` 20 | } 21 | 22 | type BizEndpoints struct { 23 | BizEndpoint endpoint.Endpoint 24 | HealthEndpoint endpoint.Endpoint 25 | } 26 | 27 | var ( 28 | ErrInvalidType = errors.New("requestType has 4 types: Add, Sub, Mul and Div") 29 | ) 30 | 31 | func MakeBizEndpoint(svc Service) endpoint.Endpoint { 32 | return func(ctx context.Context, request interface{}) (response interface{}, err error) { 33 | req := request.(*BizRequest) 34 | fmt.Printf("req:#%v\n", req) 35 | var ( 36 | res, a, b int 37 | calError error 38 | ) 39 | 40 | a = req.A 41 | b = req.B 42 | bizRes := &BizResponse{} 43 | 44 | if strings.EqualFold(req.ReqType, "Add") { 45 | res = svc.Add(a, b) 46 | } else if strings.EqualFold(req.ReqType, "Sub") { 47 | res = svc.Sub(a, b) 48 | } else if strings.EqualFold(req.ReqType, "Mul") { 49 | res = svc.Mul(a, b) 50 | } else if strings.EqualFold(req.ReqType, "Div") { 51 | res, calError = svc.Div(a, b) 52 | } else { 53 | return bizRes, ErrInvalidType 54 | } 55 | bizRes.Result = res 56 | bizRes.Error = calError 57 | return bizRes, nil 58 | } 59 | } 60 | 61 | type HealthRequest struct { 62 | } 63 | 64 | type HealthResponse struct { 65 | Status bool `json:"status"` 66 | } 67 | 68 | func MakeHealthEndpoint(srv Service) endpoint.Endpoint { 69 | return func(ctx context.Context, request interface{}) (response interface{}, err error) { 70 | status := srv.HealthCheck() 71 | bizRes := &HealthResponse{} 72 | bizRes.Status = status 73 | return bizRes, nil 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /biz_circuitbreaker/register/instrument.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "github.com/go-kit/kit/endpoint" 7 | "github.com/go-kit/kit/metrics" 8 | "github.com/juju/ratelimit" 9 | "golang.org/x/time/rate" 10 | "time" 11 | ) 12 | 13 | var ( 14 | ErrLimitExceed = errors.New("rate limit exceed") 15 | ) 16 | 17 | func NewTokenBucketLimiterWithJuju(bkt *ratelimit.Bucket) endpoint.Middleware { 18 | return func(next endpoint.Endpoint) endpoint.Endpoint { 19 | return func(ctx context.Context, request interface{}) (response interface{}, err error) { 20 | if bkt.TakeAvailable(1) == 0 { 21 | return nil, ErrLimitExceed 22 | } 23 | return next(ctx, request) 24 | } 25 | } 26 | } 27 | 28 | func NewTokenBucketLimiterWithBuildIn(bkt *rate.Limiter) endpoint.Middleware { 29 | return func(next endpoint.Endpoint) endpoint.Endpoint { 30 | return func(ctx context.Context, request interface{}) (response interface{}, err error) { 31 | if !bkt.Allow() { 32 | return nil, ErrLimitExceed 33 | } 34 | return next(ctx, request) 35 | } 36 | } 37 | } 38 | 39 | type MetricMiddleware struct { 40 | Service 41 | RequestCount metrics.Counter 42 | RequestLatency metrics.Histogram 43 | } 44 | 45 | func NewMetrics(counter metrics.Counter, histogram metrics.Histogram) ServiceMiddleware { 46 | return func(next Service) Service { 47 | return MetricMiddleware{ 48 | Service: next, 49 | RequestCount: counter, 50 | RequestLatency: histogram, 51 | } 52 | } 53 | } 54 | 55 | func (mw MetricMiddleware) Add(a, b int) (ret int) { 56 | defer func(begin time.Time) { 57 | lvs := []string{"method", "Add"} 58 | mw.RequestCount.With(lvs...).Add(1) 59 | mw.RequestLatency.With(lvs...).Observe(time.Since(begin).Seconds()) 60 | }(time.Now()) 61 | 62 | ret = mw.Service.Add(a, b) 63 | return 64 | } 65 | 66 | func (mw MetricMiddleware) Sub(a, b int) (ret int) { 67 | defer func(begin time.Time) { 68 | lvs := []string{"method", "Sub"} 69 | mw.RequestCount.With(lvs...).Add(1) 70 | mw.RequestLatency.With(lvs...).Observe(time.Since(begin).Seconds()) 71 | }(time.Now()) 72 | 73 | ret = mw.Service.Sub(a, b) 74 | return 75 | } 76 | 77 | func (mw MetricMiddleware) Mul(a, b int) (ret int) { 78 | defer func(begin time.Time) { 79 | lvs := []string{"method", "Mul"} 80 | mw.RequestCount.With(lvs...).Add(1) 81 | mw.RequestLatency.With(lvs...).Observe(time.Since(begin).Seconds()) 82 | }(time.Now()) 83 | 84 | ret = mw.Service.Mul(a, b) 85 | return 86 | } 87 | 88 | func (mw MetricMiddleware) Div(a, b int) (ret int, err error) { 89 | defer func(begin time.Time) { 90 | lvs := []string{"method", "Div"} 91 | mw.RequestCount.With(lvs...).Add(1) 92 | mw.RequestLatency.With(lvs...).Observe(time.Since(begin).Seconds()) 93 | }(time.Now()) 94 | 95 | ret, err = mw.Service.Div(a, b) 96 | return 97 | } 98 | 99 | func (mw MetricMiddleware) HealthCheck() (ret bool) { 100 | defer func(begin time.Time) { 101 | lvs := []string{"method", "HealthCheck"} 102 | mw.RequestCount.With(lvs...).Add(1) 103 | mw.RequestLatency.With(lvs...).Observe(time.Since(begin).Seconds()) 104 | }(time.Now()) 105 | 106 | ret = mw.Service.HealthCheck() 107 | return 108 | } 109 | -------------------------------------------------------------------------------- /biz_circuitbreaker/register/logging.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/go-kit/kit/log" 5 | "time" 6 | ) 7 | 8 | type LoggingMiddleware struct { 9 | Service 10 | logger log.Logger 11 | } 12 | 13 | func NewLoggingMiddleware(logger log.Logger) ServiceMiddleware { 14 | return func(next Service) Service { 15 | return LoggingMiddleware{next, logger} 16 | } 17 | } 18 | 19 | func (mw LoggingMiddleware) Add(a, b int) (ret int) { 20 | defer func(begin time.Time) { 21 | mw.logger.Log( 22 | "function", "Add", 23 | "a", a, 24 | "b", b, 25 | "result", ret, 26 | "cost", time.Since(begin), 27 | ) 28 | 29 | }(time.Now()) 30 | 31 | ret = mw.Service.Add(a, b) 32 | return 33 | } 34 | 35 | func (mw LoggingMiddleware) Sub(a, b int) (ret int) { 36 | defer func(begin time.Time) { 37 | mw.logger.Log( 38 | "function", "Sub", 39 | "a", a, 40 | "b", b, 41 | "result", ret, 42 | "cost", time.Since(begin), 43 | ) 44 | 45 | }(time.Now()) 46 | 47 | ret = mw.Service.Sub(a, b) 48 | return 49 | } 50 | 51 | func (mw LoggingMiddleware) Mul(a, b int) (ret int) { 52 | defer func(begin time.Time) { 53 | mw.logger.Log( 54 | "function", "Mul", 55 | "a", a, 56 | "b", b, 57 | "result", ret, 58 | "cost", time.Since(begin), 59 | ) 60 | 61 | }(time.Now()) 62 | 63 | ret = mw.Service.Mul(a, b) 64 | return 65 | } 66 | 67 | func (mw LoggingMiddleware) Div(a, b int) (ret int, err error) { 68 | defer func(begin time.Time) { 69 | mw.logger.Log( 70 | "function", "Div", 71 | "a", a, 72 | "b", b, 73 | "result", ret, 74 | "cost", time.Since(begin), 75 | ) 76 | 77 | }(time.Now()) 78 | 79 | ret, err = mw.Service.Div(a, b) 80 | return 81 | } 82 | 83 | func (mw LoggingMiddleware) HealthCheck() (ret bool) { 84 | defer func(begin time.Time) { 85 | mw.logger.Log( 86 | "function", "HealthCheck", 87 | "result", ret, 88 | "cost", time.Since(begin), 89 | ) 90 | }(time.Now()) 91 | 92 | ret = mw.Service.HealthCheck() 93 | return 94 | } 95 | -------------------------------------------------------------------------------- /biz_circuitbreaker/register/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "flag" 6 | "fmt" 7 | "github.com/go-kit/kit/log" 8 | kitPrometheus "github.com/go-kit/kit/metrics/prometheus" 9 | kitZipkin "github.com/go-kit/kit/tracing/zipkin" 10 | "github.com/openzipkin/zipkin-go" 11 | zipkinHttp "github.com/openzipkin/zipkin-go/reporter/http" 12 | stdPrometheus "github.com/prometheus/client_golang/prometheus" 13 | "golang.org/x/time/rate" 14 | "net/http" 15 | "os" 16 | "os/signal" 17 | "syscall" 18 | "time" 19 | ) 20 | 21 | var ( 22 | consulHost = flag.String("consul.host", "192.168.0.103", "consul ip address") 23 | consulPort = flag.String("consul.port", "8500", "consul port") 24 | serviceHost = flag.String("service.host", "192.168.0.103", "service ip address") 25 | servicePort = flag.String("service.port", "8000", "service port") 26 | zipkinUrl = flag.String("zipkin.url", "http://192.168.0.103:9411/api/v2/spans", "zipkin server url") 27 | ) 28 | 29 | func main() { 30 | flag.Parse() 31 | 32 | ctx := context.Background() 33 | errChan := make(chan error) 34 | 35 | var logger log.Logger 36 | { 37 | logger = log.NewLogfmtLogger(os.Stderr) 38 | logger = log.With(logger, "ts", log.DefaultTimestampUTC) 39 | logger = log.With(logger, "caller", log.DefaultCaller) 40 | } 41 | 42 | fieldKeys := []string{"method"} 43 | requestCount := kitPrometheus.NewCounterFrom(stdPrometheus.CounterOpts{ 44 | Namespace: "vince_cfl", 45 | Subsystem: "biz_service", 46 | Name: "request_count", 47 | Help: "numbers of request received", 48 | }, fieldKeys) 49 | 50 | requestLatency := kitPrometheus.NewSummaryFrom(stdPrometheus.SummaryOpts{ 51 | Namespace: "vince_cfl", 52 | Subsystem: "biz_service", 53 | Name: "request_latency", 54 | Help: "total duration of request in microseconds", 55 | }, fieldKeys) 56 | 57 | var zipKinTracer *zipkin.Tracer 58 | { 59 | var ( 60 | err error 61 | hostPort = *serviceHost + ":" + *servicePort 62 | serviceName = "biz-service" 63 | useNoopTracer = *zipkinUrl == "" 64 | reporter = zipkinHttp.NewReporter(*zipkinUrl) 65 | ) 66 | 67 | defer reporter.Close() 68 | 69 | zEP, _ := zipkin.NewEndpoint(serviceName, hostPort) 70 | zipKinTracer, err = zipkin.NewTracer( 71 | reporter, zipkin.WithLocalEndpoint(zEP), zipkin.WithNoopTracer(useNoopTracer), 72 | ) 73 | if err != nil { 74 | logger.Log("error", err) 75 | os.Exit(1) 76 | } 77 | if !useNoopTracer { 78 | logger.Log("tracer", "zipkin", "type", "native", "url", *zipkinUrl) 79 | } 80 | } 81 | 82 | svc := NewBizService() 83 | 84 | svc = NewLoggingMiddleware(logger)(svc) 85 | 86 | svc = NewMetrics(requestCount, requestLatency)(svc) 87 | 88 | rateBucket := rate.NewLimiter(rate.Every(time.Second*1), 100) 89 | 90 | endpoint := MakeBizEndpoint(svc) 91 | 92 | //rateBucket := ratelimit.NewBucket(time.Second*1, 1) 93 | //endpoint = NewTokenBucketLimiterWithJuju(rateBucket)(endpoint) 94 | 95 | endpoint = NewTokenBucketLimiterWithBuildIn(rateBucket)(endpoint) 96 | endpoint = kitZipkin.TraceEndpoint(zipKinTracer, "biz-endpoint")(endpoint) 97 | 98 | healthEndpoint := MakeHealthEndpoint(svc) 99 | healthEndpoint = NewTokenBucketLimiterWithBuildIn(rateBucket)(healthEndpoint) 100 | healthEndpoint = kitZipkin.TraceEndpoint(zipKinTracer, "health-endpoint")(healthEndpoint) 101 | 102 | endpoints := BizEndpoints{ 103 | BizEndpoint: endpoint, 104 | HealthEndpoint: healthEndpoint, 105 | } 106 | 107 | r := MakeHttpHandler(ctx, endpoints, zipKinTracer, logger) 108 | 109 | registrar := Register(*consulHost, *consulPort, *serviceHost, *servicePort, logger) 110 | 111 | go func() { 112 | fmt.Println("http server start at port:" + *servicePort) 113 | registrar.Register() 114 | handler := r 115 | errChan <- http.ListenAndServe(":"+*servicePort, handler) 116 | }() 117 | 118 | go func() { 119 | c := make(chan os.Signal, 1) 120 | signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) 121 | errChan <- fmt.Errorf("%s", <-c) 122 | }() 123 | 124 | error := <-errChan 125 | registrar.Deregister() 126 | fmt.Printf("service stop:%v\n", error) 127 | } 128 | -------------------------------------------------------------------------------- /biz_circuitbreaker/register/register.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/go-kit/kit/log" 5 | "github.com/go-kit/kit/sd" 6 | "github.com/go-kit/kit/sd/consul" 7 | "github.com/hashicorp/consul/api" 8 | "github.com/pborman/uuid" 9 | "os" 10 | "strconv" 11 | ) 12 | 13 | func Register(consulHost, consulPort, svcHost, svcPort string, logger log.Logger) (registrar sd.Registrar) { 14 | var client consul.Client 15 | { 16 | consulCfg := api.DefaultConfig() 17 | consulCfg.Address = consulHost + ":" + consulPort 18 | consultClient, err := api.NewClient(consulCfg) 19 | if err != nil { 20 | logger.Log("create consul error:", err) 21 | os.Exit(1) 22 | } 23 | 24 | client = consul.NewClient(consultClient) 25 | } 26 | 27 | check := api.AgentServiceCheck{ 28 | HTTP: "http://" + svcHost + ":" + svcPort + "/health", 29 | Interval: "10s", 30 | Timeout: "1s", 31 | Notes: "consul check service health status", 32 | } 33 | 34 | port, _ := strconv.Atoi(svcPort) 35 | 36 | reg := api.AgentServiceRegistration{ 37 | ID: "biz-" + uuid.New(), 38 | Name: "biz", 39 | Address: svcHost, 40 | Port: port, 41 | Tags: []string{"biz", "vc"}, 42 | Check: &check, 43 | } 44 | 45 | registrar = consul.NewRegistrar(client, ®, logger) 46 | return 47 | } 48 | -------------------------------------------------------------------------------- /biz_circuitbreaker/register/service.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | type Service interface { 8 | Add(a, b int) int 9 | 10 | Sub(a, b int) int 11 | 12 | Mul(a, b int) int 13 | 14 | Div(a, b int) (int, error) 15 | 16 | HealthCheck() bool 17 | } 18 | 19 | type BizService struct { 20 | } 21 | 22 | func NewBizService() Service { 23 | return &BizService{} 24 | } 25 | 26 | func (s *BizService) Add(a, b int) int { 27 | return a + b 28 | } 29 | 30 | func (s *BizService) Sub(a, b int) int { 31 | return a - b 32 | } 33 | 34 | func (s *BizService) Mul(a, b int) int { 35 | return a * b 36 | } 37 | 38 | func (s *BizService) Div(a, b int) (int, error) { 39 | if b == 0 { 40 | return 0, errors.New("the divisor is zero") 41 | } 42 | return a / b, nil 43 | } 44 | 45 | func (s *BizService) HealthCheck() bool { 46 | return true 47 | } 48 | 49 | type ServiceMiddleware func(Service) Service 50 | -------------------------------------------------------------------------------- /biz_circuitbreaker/register/transport.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "errors" 7 | "fmt" 8 | "github.com/go-kit/kit/log" 9 | "github.com/go-kit/kit/tracing/zipkin" 10 | kitHttp "github.com/go-kit/kit/transport/http" 11 | "github.com/gorilla/mux" 12 | goZipkin "github.com/openzipkin/zipkin-go" 13 | "github.com/prometheus/client_golang/prometheus/promhttp" 14 | "net/http" 15 | "strconv" 16 | ) 17 | 18 | var ( 19 | ErrBadRequest = errors.New("invalid request parameter") 20 | ) 21 | 22 | func MakeHttpHandler(ctx context.Context, endpoints BizEndpoints, tracer *goZipkin.Tracer, logger log.Logger) http.Handler { 23 | r := mux.NewRouter() 24 | 25 | zipkinServer := zipkin.HTTPServerTrace(tracer, zipkin.Name("http-transport")) 26 | 27 | options := []kitHttp.ServerOption{ 28 | kitHttp.ServerErrorLogger(logger), 29 | kitHttp.ServerErrorEncoder(kitHttp.DefaultErrorEncoder), 30 | zipkinServer, 31 | } 32 | 33 | r.Methods("POST").Path("/biz/{type}/{a}/{b}").Handler(kitHttp.NewServer( 34 | endpoints.BizEndpoint, 35 | decodeBizRequest, 36 | encodeBizResponse, 37 | options..., 38 | )) 39 | 40 | r.Path("/metrics").Handler(promhttp.Handler()) 41 | 42 | r.Methods("GET").Path("/health").Handler(kitHttp.NewServer( 43 | endpoints.HealthEndpoint, 44 | decodeHealthRequest, 45 | encodeBizResponse, 46 | options..., 47 | )) 48 | 49 | return r 50 | } 51 | 52 | func decodeBizRequest(ctx context.Context, r *http.Request) (interface{}, error) { 53 | vars := mux.Vars(r) 54 | fmt.Printf("vars:%v\n", vars) 55 | 56 | reqType, ok := vars["type"] 57 | if !ok { 58 | return nil, ErrBadRequest 59 | } 60 | pa, ok := vars["a"] 61 | if !ok { 62 | return nil, ErrBadRequest 63 | } 64 | pb, ok := vars["b"] 65 | if !ok { 66 | return nil, ErrBadRequest 67 | } 68 | a, _ := strconv.Atoi(pa) 69 | b, _ := strconv.Atoi(pb) 70 | 71 | return &BizRequest{ 72 | ReqType: reqType, 73 | A: a, 74 | B: b, 75 | }, nil 76 | } 77 | 78 | func encodeBizResponse(ctx context.Context, w http.ResponseWriter, response interface{}) error { 79 | fmt.Printf("res:%#v\n", response) 80 | w.Header().Set("Content-Type", "application/json;charset=utf-8") 81 | return json.NewEncoder(w).Encode(response) 82 | } 83 | 84 | func decodeHealthRequest(ctx context.Context, r *http.Request) (interface{}, error) { 85 | return &HealthRequest{}, nil 86 | } 87 | -------------------------------------------------------------------------------- /biz_consul/discover/endpoint.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "github.com/go-kit/kit/endpoint" 6 | "github.com/go-kit/kit/log" 7 | "github.com/go-kit/kit/sd" 8 | "github.com/go-kit/kit/sd/consul" 9 | "github.com/go-kit/kit/sd/lb" 10 | "time" 11 | ) 12 | 13 | func MakeDiscoverEndpoint(ctx context.Context, client consul.Client, logger log.Logger) endpoint.Endpoint { 14 | serviceName := "biz" 15 | tags := []string{"biz", "vc"} 16 | passingOnly := true 17 | duration := 500 * time.Millisecond 18 | 19 | instancer := consul.NewInstancer(client, logger, serviceName, tags, passingOnly) 20 | 21 | factory := BizFactory(ctx, "POST", "biz") 22 | 23 | endpointer := sd.NewEndpointer(instancer, factory, logger) 24 | 25 | balancer := lb.NewRoundRobin(endpointer) 26 | 27 | retry := lb.Retry(1, duration, balancer) 28 | 29 | return retry 30 | } 31 | -------------------------------------------------------------------------------- /biz_consul/discover/factory.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "errors" 7 | "fmt" 8 | "github.com/go-kit/kit/endpoint" 9 | "github.com/go-kit/kit/sd" 10 | kitHttp "github.com/go-kit/kit/transport/http" 11 | "io" 12 | "net/http" 13 | "net/url" 14 | "strconv" 15 | "strings" 16 | ) 17 | 18 | func BizFactory(ctx context.Context, method, path string) sd.Factory { 19 | return func(instance string) (endpoint endpoint.Endpoint, closer io.Closer, err error) { 20 | if !strings.HasPrefix(instance, "http") { 21 | instance = "http://" + instance 22 | } 23 | tgt, err := url.Parse(instance) 24 | if err != nil { 25 | return nil, nil, err 26 | } 27 | tgt.Path = path 28 | var ( 29 | enc kitHttp.EncodeRequestFunc 30 | dec kitHttp.DecodeResponseFunc 31 | ) 32 | enc, dec = encodeBizRequest, decodeVizResponse 33 | 34 | return kitHttp.NewClient(method, tgt, enc, dec).Endpoint(), nil, nil 35 | } 36 | } 37 | 38 | func encodeBizRequest(ctx context.Context, r *http.Request, request interface{}) error { 39 | bizReq := request.(BizRequest) 40 | p := "/" + bizReq.ReqType + "/" + strconv.Itoa(bizReq.A) + "/" + strconv.Itoa(bizReq.B) 41 | r.URL.Path += p 42 | fmt.Printf("r.URL.Path:%v\n", r.URL.Path) 43 | return nil 44 | } 45 | 46 | func decodeVizResponse(ctx context.Context, resp *http.Response) (interface{}, error) { 47 | response := &BizResponse{} 48 | var s map[string]interface{} 49 | 50 | if respCode := resp.StatusCode; respCode >= 400 { 51 | if err := json.NewDecoder(resp.Body).Decode(&s); err != nil { 52 | return nil, err 53 | } 54 | return nil, errors.New(s["error"].(string) + "\n") 55 | } 56 | 57 | if err := json.NewDecoder(resp.Body).Decode(response); err != nil { 58 | return nil, err 59 | } 60 | 61 | return response, nil 62 | } 63 | -------------------------------------------------------------------------------- /biz_consul/discover/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "flag" 6 | "fmt" 7 | "github.com/go-kit/kit/log" 8 | "github.com/go-kit/kit/sd/consul" 9 | "github.com/hashicorp/consul/api" 10 | "net/http" 11 | "os" 12 | "os/signal" 13 | "syscall" 14 | ) 15 | 16 | var ( 17 | consulHost = flag.String("consul.host", "localhost", "consul server ip address") 18 | consulPort = flag.String("consul.port", "8500", "consul server port") 19 | ) 20 | 21 | func main() { 22 | flag.Parse() 23 | 24 | var logger log.Logger 25 | { 26 | logger = log.NewLogfmtLogger(os.Stderr) 27 | logger = log.With(logger, "ts", log.DefaultTimestampUTC) 28 | logger = log.With(logger, "caller", log.DefaultCaller) 29 | } 30 | 31 | var client consul.Client 32 | { 33 | consulCfg := api.DefaultConfig() 34 | consulCfg.Address = "http://" + *consulHost + ":" + *consulPort 35 | consulClient, err := api.NewClient(consulCfg) 36 | 37 | if err != nil { 38 | logger.Log("err", err) 39 | os.Exit(1) 40 | } 41 | 42 | client = consul.NewClient(consulClient) 43 | } 44 | 45 | ctx := context.Background() 46 | 47 | discoverEndpoint := MakeDiscoverEndpoint(ctx, client, logger) 48 | 49 | r := MakeHttpHandler(discoverEndpoint, logger) 50 | 51 | errChan := make(chan error) 52 | go func() { 53 | c := make(chan os.Signal) 54 | signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) 55 | errChan <- fmt.Errorf("%s", <-c) 56 | }() 57 | 58 | go func() { 59 | logger.Log("transport", "http", "addr", "8002") 60 | handler := r 61 | errChan <- http.ListenAndServe(":8002", handler) 62 | }() 63 | 64 | logger.Log("exit", <-errChan) 65 | } 66 | -------------------------------------------------------------------------------- /biz_consul/discover/transport.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "fmt" 7 | "github.com/go-kit/kit/endpoint" 8 | "github.com/go-kit/kit/log" 9 | kitHttp "github.com/go-kit/kit/transport/http" 10 | "github.com/gorilla/mux" 11 | "net/http" 12 | ) 13 | 14 | type BizRequest struct { 15 | ReqType string `json:"type"` 16 | A int `json:"a"` 17 | B int `json:"b"` 18 | } 19 | 20 | type BizResponse struct { 21 | Result int `json:"result"` 22 | Error error `json:"error"` 23 | } 24 | 25 | func MakeHttpHandler(endpoint endpoint.Endpoint, logger log.Logger) http.Handler { 26 | r := mux.NewRouter() 27 | 28 | options := []kitHttp.ServerOption{ 29 | kitHttp.ServerErrorLogger(logger), 30 | kitHttp.ServerErrorEncoder(kitHttp.DefaultErrorEncoder), 31 | } 32 | 33 | r.Methods("POST").Path("/biz").Handler(kitHttp.NewServer( 34 | endpoint, 35 | decodeDiscoverRequest, 36 | encodeDiscoverResponse, 37 | options... 38 | )) 39 | 40 | return r 41 | } 42 | 43 | func decodeDiscoverRequest(ctx context.Context, r *http.Request) (interface{}, error) { 44 | var request BizRequest 45 | if err := json.NewDecoder(r.Body).Decode(&request); err != nil { 46 | return nil, err 47 | } 48 | fmt.Printf("request:#%v\n", request) 49 | return request, nil 50 | } 51 | 52 | func encodeDiscoverResponse(ctx context.Context, w http.ResponseWriter, response interface{}) error { 53 | w.Header().Set("Content-Type", "application/json;charset=utf-8") 54 | return json.NewEncoder(w).Encode(response) 55 | } 56 | -------------------------------------------------------------------------------- /biz_consul/gateway/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "github.com/go-kit/kit/log" 7 | "github.com/hashicorp/consul/api" 8 | "math/rand" 9 | "net/http" 10 | "net/http/httputil" 11 | "os" 12 | "os/signal" 13 | "strings" 14 | "syscall" 15 | ) 16 | 17 | var ( 18 | consulHost = flag.String("consul.host", "localhost", "consul server ip address") 19 | consulPort = flag.String("consul.port", "8500", "consul server port") 20 | ) 21 | 22 | func main() { 23 | flag.Parse() 24 | 25 | var logger log.Logger 26 | { 27 | logger = log.NewLogfmtLogger(os.Stderr) 28 | logger = log.With(logger, "ts", log.DefaultTimestampUTC) 29 | logger = log.With(logger, "caller", log.DefaultCaller) 30 | } 31 | 32 | consulCfg := api.DefaultConfig() 33 | consulCfg.Address = "http://" + *consulHost + ":" + *consulPort 34 | consulClient, err := api.NewClient(consulCfg) 35 | 36 | if err != nil { 37 | logger.Log("err", err) 38 | os.Exit(1) 39 | } 40 | 41 | proxy := NewReverseProxy(consulClient, logger) 42 | 43 | errChan := make(chan error) 44 | go func() { 45 | c := make(chan os.Signal) 46 | signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) 47 | errChan <- fmt.Errorf("%s", <-c) 48 | }() 49 | 50 | go func() { 51 | logger.Log("transport", "http", "addr", "8003") 52 | handler := proxy 53 | errChan <- http.ListenAndServe(":8003", handler) 54 | }() 55 | 56 | logger.Log("exit", <-errChan) 57 | } 58 | 59 | func NewReverseProxy(client *api.Client, logger log.Logger) *httputil.ReverseProxy{ 60 | director := func(req *http.Request) { 61 | reqPath := req.URL.Path 62 | if reqPath == "" { 63 | return 64 | } 65 | // /biz/add/1/2 66 | pathArray := strings.Split(reqPath, "/") 67 | serviceName := pathArray[1] 68 | logger.Log("serviceName:", serviceName) 69 | 70 | result, _, err := client.Catalog().Service(serviceName, "", nil) 71 | if err != nil { 72 | logger.Log("reverseProxy failed", "query service instance error", err.Error()) 73 | return 74 | } 75 | 76 | if len(result) == 0 { 77 | logger.Log("reverseProxy failed", "no such service instance", serviceName) 78 | return 79 | } 80 | 81 | destPath := strings.Join(pathArray[2:], "/") 82 | tgt := result[rand.Int()%len(result)] 83 | logger.Log("service id", tgt.ServiceID) 84 | 85 | req.URL.Scheme = "http" 86 | req.URL.Host = fmt.Sprintf("%s:%d", tgt.ServiceAddress, tgt.ServicePort) 87 | req.URL.Path = "/" + destPath 88 | } 89 | 90 | return &httputil.ReverseProxy{Director:director} 91 | } 92 | -------------------------------------------------------------------------------- /biz_consul/register/endpoint.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | "github.com/go-kit/kit/endpoint" 8 | "strings" 9 | ) 10 | 11 | type BizRequest struct { 12 | ReqType string `json:"type"` 13 | A int `json:"a"` 14 | B int `json:"b"` 15 | } 16 | 17 | type BizResponse struct { 18 | Result int `json:"result"` 19 | Error error `json:"error"` 20 | } 21 | 22 | type BizEndpoints struct { 23 | BizEndpoint endpoint.Endpoint 24 | HealthEndpoint endpoint.Endpoint 25 | } 26 | 27 | var ( 28 | ErrInvalidType = errors.New("requestType has 4 types: Add, Sub, Mul and Div") 29 | ) 30 | 31 | func MakeBizEndpoint(svc Service) endpoint.Endpoint { 32 | return func(ctx context.Context, request interface{}) (response interface{}, err error) { 33 | req := request.(*BizRequest) 34 | fmt.Printf("req:#%v\n", req) 35 | var ( 36 | res, a, b int 37 | calError error 38 | ) 39 | 40 | a = req.A 41 | b = req.B 42 | bizRes := &BizResponse{} 43 | 44 | if strings.EqualFold(req.ReqType, "Add") { 45 | res = svc.Add(a, b) 46 | } else if strings.EqualFold(req.ReqType, "Sub") { 47 | res = svc.Sub(a, b) 48 | } else if strings.EqualFold(req.ReqType, "Mul") { 49 | res = svc.Mul(a, b) 50 | } else if strings.EqualFold(req.ReqType, "Div") { 51 | res, calError = svc.Div(a, b) 52 | } else { 53 | return bizRes, ErrInvalidType 54 | } 55 | bizRes.Result = res 56 | bizRes.Error = calError 57 | return bizRes, nil 58 | } 59 | } 60 | 61 | type HealthRequest struct { 62 | } 63 | 64 | type HealthResponse struct { 65 | Status bool `json:"status"` 66 | } 67 | 68 | func MakeHealthEndpoint(srv Service) endpoint.Endpoint { 69 | return func(ctx context.Context, request interface{}) (response interface{}, err error) { 70 | status := srv.HealthCheck() 71 | bizRes := &HealthResponse{} 72 | bizRes.Status = status 73 | return bizRes, nil 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /biz_consul/register/instrument.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "github.com/go-kit/kit/endpoint" 7 | "github.com/go-kit/kit/metrics" 8 | "github.com/juju/ratelimit" 9 | "golang.org/x/time/rate" 10 | "time" 11 | ) 12 | 13 | var ( 14 | ErrLimitExceed = errors.New("rate limit exceed") 15 | ) 16 | 17 | func NewTokenBucketLimiterWithJuju(bkt *ratelimit.Bucket) endpoint.Middleware { 18 | return func(next endpoint.Endpoint) endpoint.Endpoint { 19 | return func(ctx context.Context, request interface{}) (response interface{}, err error) { 20 | if bkt.TakeAvailable(1) == 0 { 21 | return nil, ErrLimitExceed 22 | } 23 | return next(ctx, request) 24 | } 25 | } 26 | } 27 | 28 | func NewTokenBucketLimiterWithBuildIn(bkt *rate.Limiter) endpoint.Middleware { 29 | return func(next endpoint.Endpoint) endpoint.Endpoint { 30 | return func(ctx context.Context, request interface{}) (response interface{}, err error) { 31 | if !bkt.Allow() { 32 | return nil, ErrLimitExceed 33 | } 34 | return next(ctx, request) 35 | } 36 | } 37 | } 38 | 39 | type MetricMiddleware struct { 40 | Service 41 | RequestCount metrics.Counter 42 | RequestLatency metrics.Histogram 43 | } 44 | 45 | func NewMetrics(counter metrics.Counter, histogram metrics.Histogram) ServiceMiddleware { 46 | return func(next Service) Service { 47 | return MetricMiddleware{ 48 | Service: next, 49 | RequestCount: counter, 50 | RequestLatency: histogram, 51 | } 52 | } 53 | } 54 | 55 | func (mw MetricMiddleware) Add(a, b int) (ret int) { 56 | defer func(begin time.Time) { 57 | lvs := []string{"method", "Add"} 58 | mw.RequestCount.With(lvs...).Add(1) 59 | mw.RequestLatency.With(lvs...).Observe(time.Since(begin).Seconds()) 60 | }(time.Now()) 61 | 62 | ret = mw.Service.Add(a, b) 63 | return 64 | } 65 | 66 | func (mw MetricMiddleware) Sub(a, b int) (ret int) { 67 | defer func(begin time.Time) { 68 | lvs := []string{"method", "Sub"} 69 | mw.RequestCount.With(lvs...).Add(1) 70 | mw.RequestLatency.With(lvs...).Observe(time.Since(begin).Seconds()) 71 | }(time.Now()) 72 | 73 | ret = mw.Service.Sub(a, b) 74 | return 75 | } 76 | 77 | func (mw MetricMiddleware) Mul(a, b int) (ret int) { 78 | defer func(begin time.Time) { 79 | lvs := []string{"method", "Mul"} 80 | mw.RequestCount.With(lvs...).Add(1) 81 | mw.RequestLatency.With(lvs...).Observe(time.Since(begin).Seconds()) 82 | }(time.Now()) 83 | 84 | ret = mw.Service.Mul(a, b) 85 | return 86 | } 87 | 88 | func (mw MetricMiddleware) Div(a, b int) (ret int, err error) { 89 | defer func(begin time.Time) { 90 | lvs := []string{"method", "Div"} 91 | mw.RequestCount.With(lvs...).Add(1) 92 | mw.RequestLatency.With(lvs...).Observe(time.Since(begin).Seconds()) 93 | }(time.Now()) 94 | 95 | ret, err = mw.Service.Div(a, b) 96 | return 97 | } 98 | 99 | func (mw MetricMiddleware) HealthCheck() (ret bool) { 100 | defer func(begin time.Time) { 101 | lvs := []string{"method", "HealthCheck"} 102 | mw.RequestCount.With(lvs...).Add(1) 103 | mw.RequestLatency.With(lvs...).Observe(time.Since(begin).Seconds()) 104 | }(time.Now()) 105 | 106 | ret = mw.Service.HealthCheck() 107 | return 108 | } 109 | -------------------------------------------------------------------------------- /biz_consul/register/logging.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/go-kit/kit/log" 5 | "time" 6 | ) 7 | 8 | type LoggingMiddleware struct { 9 | Service 10 | logger log.Logger 11 | } 12 | 13 | func NewLoggingMiddleware(logger log.Logger) ServiceMiddleware { 14 | return func(next Service) Service { 15 | return LoggingMiddleware{next, logger} 16 | } 17 | } 18 | 19 | func (mw LoggingMiddleware) Add(a, b int) (ret int) { 20 | defer func(begin time.Time) { 21 | mw.logger.Log( 22 | "function", "Add", 23 | "a", a, 24 | "b", b, 25 | "result", ret, 26 | "cost", time.Since(begin), 27 | ) 28 | 29 | }(time.Now()) 30 | 31 | ret = mw.Service.Add(a, b) 32 | return 33 | } 34 | 35 | func (mw LoggingMiddleware) Sub(a, b int) (ret int) { 36 | defer func(begin time.Time) { 37 | mw.logger.Log( 38 | "function", "Sub", 39 | "a", a, 40 | "b", b, 41 | "result", ret, 42 | "cost", time.Since(begin), 43 | ) 44 | 45 | }(time.Now()) 46 | 47 | ret = mw.Service.Sub(a, b) 48 | return 49 | } 50 | 51 | func (mw LoggingMiddleware) Mul(a, b int) (ret int) { 52 | defer func(begin time.Time) { 53 | mw.logger.Log( 54 | "function", "Mul", 55 | "a", a, 56 | "b", b, 57 | "result", ret, 58 | "cost", time.Since(begin), 59 | ) 60 | 61 | }(time.Now()) 62 | 63 | ret = mw.Service.Mul(a, b) 64 | return 65 | } 66 | 67 | func (mw LoggingMiddleware) Div(a, b int) (ret int, err error) { 68 | defer func(begin time.Time) { 69 | mw.logger.Log( 70 | "function", "Div", 71 | "a", a, 72 | "b", b, 73 | "result", ret, 74 | "cost", time.Since(begin), 75 | ) 76 | 77 | }(time.Now()) 78 | 79 | ret, err = mw.Service.Div(a, b) 80 | return 81 | } 82 | 83 | func (mw LoggingMiddleware) HealthCheck() (ret bool) { 84 | defer func(begin time.Time) { 85 | mw.logger.Log( 86 | "function", "HealthCheck", 87 | "result", ret, 88 | "cost", time.Since(begin), 89 | ) 90 | }(time.Now()) 91 | 92 | ret = mw.Service.HealthCheck() 93 | return 94 | } 95 | -------------------------------------------------------------------------------- /biz_consul/register/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "flag" 6 | "fmt" 7 | "github.com/go-kit/kit/log" 8 | kitPrometheus "github.com/go-kit/kit/metrics/prometheus" 9 | stdPrometheus "github.com/prometheus/client_golang/prometheus" 10 | "golang.org/x/time/rate" 11 | "net/http" 12 | "os" 13 | "os/signal" 14 | "syscall" 15 | "time" 16 | ) 17 | 18 | var ( 19 | consulHost = flag.String("consul.host", "localhost", "consul ip address") 20 | consulPort = flag.String("consul.port", "8500", "consul port") 21 | serviceHost = flag.String("service.host", "192.168.0.103", "service ip address") 22 | servicePort = flag.String("service.port", "8000", "service port") 23 | ) 24 | 25 | func main() { 26 | flag.Parse() 27 | 28 | ctx := context.Background() 29 | errChan := make(chan error) 30 | 31 | var logger log.Logger 32 | { 33 | logger = log.NewLogfmtLogger(os.Stderr) 34 | logger = log.With(logger, "ts", log.DefaultTimestampUTC) 35 | logger = log.With(logger, "caller", log.DefaultCaller) 36 | } 37 | 38 | fieldKeys := []string{"method"} 39 | requestCount := kitPrometheus.NewCounterFrom(stdPrometheus.CounterOpts{ 40 | Namespace: "vince_cfl", 41 | Subsystem: "biz_service", 42 | Name: "request_count", 43 | Help: "numbers of request received", 44 | }, fieldKeys) 45 | 46 | requestLatency := kitPrometheus.NewSummaryFrom(stdPrometheus.SummaryOpts{ 47 | Namespace: "vince_cfl", 48 | Subsystem: "biz_service", 49 | Name: "request_latency", 50 | Help: "total duration of request in microseconds", 51 | }, fieldKeys) 52 | 53 | svc := NewBizService() 54 | 55 | svc = NewLoggingMiddleware(logger)(svc) 56 | 57 | svc = NewMetrics(requestCount, requestLatency)(svc) 58 | 59 | endpoint := MakeBizEndpoint(svc) 60 | 61 | //rateBucket := ratelimit.NewBucket(time.Second*1, 1) 62 | //endpoint = NewTokenBucketLimiterWithJuju(rateBucket)(endpoint) 63 | 64 | rateBucket := rate.NewLimiter(rate.Every(time.Second*1), 100) 65 | endpoint = NewTokenBucketLimiterWithBuildIn(rateBucket)(endpoint) 66 | 67 | healthEndpoint := MakeHealthEndpoint(svc) 68 | 69 | endpoints := BizEndpoints{ 70 | BizEndpoint: endpoint, 71 | HealthEndpoint: healthEndpoint, 72 | } 73 | 74 | r := MakeHttpHandler(ctx, endpoints, logger) 75 | 76 | registrar := Register(*consulHost, *consulPort, *serviceHost, *servicePort, logger) 77 | 78 | go func() { 79 | fmt.Println("http server start at port:" + *servicePort) 80 | registrar.Register() 81 | handler := r 82 | errChan <- http.ListenAndServe(":"+*servicePort, handler) 83 | }() 84 | 85 | go func() { 86 | c := make(chan os.Signal, 1) 87 | signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) 88 | errChan <- fmt.Errorf("%s", <-c) 89 | }() 90 | 91 | error := <-errChan 92 | registrar.Deregister() 93 | fmt.Printf("service stop:%v\n", error) 94 | } 95 | -------------------------------------------------------------------------------- /biz_consul/register/register.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/go-kit/kit/log" 5 | "github.com/go-kit/kit/sd" 6 | "github.com/go-kit/kit/sd/consul" 7 | "github.com/hashicorp/consul/api" 8 | "github.com/pborman/uuid" 9 | "os" 10 | "strconv" 11 | ) 12 | 13 | func Register(consulHost, consulPort, svcHost, svcPort string, logger log.Logger) (registrar sd.Registrar) { 14 | var client consul.Client 15 | { 16 | consulCfg := api.DefaultConfig() 17 | consulCfg.Address = consulHost + ":" + consulPort 18 | consultClient, err := api.NewClient(consulCfg) 19 | if err != nil { 20 | logger.Log("create consul error:", err) 21 | os.Exit(1) 22 | } 23 | 24 | client = consul.NewClient(consultClient) 25 | } 26 | 27 | check := api.AgentServiceCheck{ 28 | HTTP: "http://" + svcHost + ":" + svcPort + "/health", 29 | Interval: "10s", 30 | Timeout: "1s", 31 | Notes: "consul check service health status", 32 | } 33 | 34 | port, _ := strconv.Atoi(svcPort) 35 | 36 | reg := api.AgentServiceRegistration{ 37 | ID: "biz-" + uuid.New(), 38 | Name: "biz", 39 | Address: svcHost, 40 | Port: port, 41 | Tags: []string{"biz", "vc"}, 42 | Check: &check, 43 | } 44 | 45 | registrar = consul.NewRegistrar(client, ®, logger) 46 | return 47 | } 48 | -------------------------------------------------------------------------------- /biz_consul/register/service.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | type Service interface { 8 | Add(a, b int) int 9 | 10 | Sub(a, b int) int 11 | 12 | Mul(a, b int) int 13 | 14 | Div(a, b int) (int, error) 15 | 16 | HealthCheck() bool 17 | } 18 | 19 | type BizService struct { 20 | } 21 | 22 | func NewBizService() Service { 23 | return &BizService{} 24 | } 25 | 26 | func (s *BizService) Add(a, b int) int { 27 | return a + b 28 | } 29 | 30 | func (s *BizService) Sub(a, b int) int { 31 | return a - b 32 | } 33 | 34 | func (s *BizService) Mul(a, b int) int { 35 | return a * b 36 | } 37 | 38 | func (s *BizService) Div(a, b int) (int, error) { 39 | if b == 0 { 40 | return 0, errors.New("the divisor is zero") 41 | } 42 | return a / b, nil 43 | } 44 | 45 | func (s *BizService) HealthCheck() bool { 46 | return true 47 | } 48 | 49 | type ServiceMiddleware func(Service) Service 50 | -------------------------------------------------------------------------------- /biz_consul/register/transport.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "errors" 7 | "fmt" 8 | "github.com/go-kit/kit/log" 9 | kitHttp "github.com/go-kit/kit/transport/http" 10 | "github.com/gorilla/mux" 11 | "github.com/prometheus/client_golang/prometheus/promhttp" 12 | "net/http" 13 | "strconv" 14 | ) 15 | 16 | var ( 17 | ErrBadRequest = errors.New("invalid request parameter") 18 | ) 19 | 20 | func MakeHttpHandler(ctx context.Context, endpoints BizEndpoints, logger log.Logger) http.Handler { 21 | r := mux.NewRouter() 22 | 23 | options := []kitHttp.ServerOption{ 24 | kitHttp.ServerErrorLogger(logger), 25 | kitHttp.ServerErrorEncoder(kitHttp.DefaultErrorEncoder), 26 | } 27 | 28 | r.Methods("POST").Path("/biz/{type}/{a}/{b}").Handler(kitHttp.NewServer( 29 | endpoints.BizEndpoint, 30 | decodeBizRequest, 31 | encodeBizResponse, 32 | options..., 33 | )) 34 | 35 | r.Path("/metrics").Handler(promhttp.Handler()) 36 | 37 | r.Methods("GET").Path("/health").Handler(kitHttp.NewServer( 38 | endpoints.HealthEndpoint, 39 | decodeHealthRequest, 40 | encodeBizResponse, 41 | options..., 42 | )) 43 | 44 | return r 45 | } 46 | 47 | func decodeBizRequest(ctx context.Context, r *http.Request) (interface{}, error) { 48 | vars := mux.Vars(r) 49 | fmt.Printf("vars:%v\n", vars) 50 | 51 | reqType, ok := vars["type"] 52 | if !ok { 53 | return nil, ErrBadRequest 54 | } 55 | pa, ok := vars["a"] 56 | if !ok { 57 | return nil, ErrBadRequest 58 | } 59 | pb, ok := vars["b"] 60 | if !ok { 61 | return nil, ErrBadRequest 62 | } 63 | a, _ := strconv.Atoi(pa) 64 | b, _ := strconv.Atoi(pb) 65 | 66 | return &BizRequest{ 67 | ReqType: reqType, 68 | A: a, 69 | B: b, 70 | }, nil 71 | } 72 | 73 | func encodeBizResponse(ctx context.Context, w http.ResponseWriter, response interface{}) error { 74 | fmt.Printf("res:%#v\n", response) 75 | w.Header().Set("Content-Type", "application/json;charset=utf-8") 76 | return json.NewEncoder(w).Encode(response) 77 | } 78 | 79 | func decodeHealthRequest(ctx context.Context, r *http.Request) (interface{}, error) { 80 | return &HealthRequest{}, nil 81 | } 82 | -------------------------------------------------------------------------------- /biz_jwt/gateway/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "github.com/afex/hystrix-go/hystrix" 7 | "github.com/go-kit/kit/log" 8 | "github.com/hashicorp/consul/api" 9 | "github.com/openzipkin/zipkin-go" 10 | zipkinHttpsvr "github.com/openzipkin/zipkin-go/middleware/http" 11 | zipkinHttp "github.com/openzipkin/zipkin-go/reporter/http" 12 | "math/rand" 13 | "net" 14 | "net/http" 15 | "net/http/httputil" 16 | "os" 17 | "os/signal" 18 | "strings" 19 | "syscall" 20 | ) 21 | 22 | var ( 23 | consulHost = flag.String("consul.host", "192.168.0.103", "consul server ip address") 24 | consulPort = flag.String("consul.port", "8500", "consul server port") 25 | zipkinUrl = flag.String("zipkin.url", "http://192.168.0.103:9411/api/v2/spans", "zipkin server url") 26 | ) 27 | 28 | func main() { 29 | flag.Parse() 30 | 31 | var logger log.Logger 32 | { 33 | logger = log.NewLogfmtLogger(os.Stderr) 34 | logger = log.With(logger, "ts", log.DefaultTimestampUTC) 35 | logger = log.With(logger, "caller", log.DefaultCaller) 36 | } 37 | 38 | var zipKinTracer *zipkin.Tracer 39 | { 40 | var ( 41 | err error 42 | hostPort = "localhost:8003" 43 | serviceName = "gateway-service" 44 | useNoopTracer = *zipkinUrl == "" 45 | reporter = zipkinHttp.NewReporter(*zipkinUrl) 46 | ) 47 | 48 | defer reporter.Close() 49 | 50 | zEP, _ := zipkin.NewEndpoint(serviceName, hostPort) 51 | zipKinTracer, err = zipkin.NewTracer( 52 | reporter, zipkin.WithLocalEndpoint(zEP), zipkin.WithNoopTracer(useNoopTracer), 53 | ) 54 | if err != nil { 55 | logger.Log("error", err) 56 | os.Exit(1) 57 | } 58 | if !useNoopTracer { 59 | logger.Log("tracer", "zipkin", "type", "native", "url", *zipkinUrl) 60 | } 61 | } 62 | 63 | consulCfg := api.DefaultConfig() 64 | consulCfg.Address = "http://" + *consulHost + ":" + *consulPort 65 | consulClient, err := api.NewClient(consulCfg) 66 | 67 | if err != nil { 68 | logger.Log("err", err) 69 | os.Exit(1) 70 | } 71 | 72 | //proxy := NewReverseProxy(consulClient, zipKinTracer, logger) 73 | hystrixRouter := NewRoutes(consulClient, zipKinTracer, "circuit breaker:service unavailable", logger) 74 | 75 | tags := map[string]string{ 76 | "component": "gateway_server", 77 | } 78 | 79 | handler := zipkinHttpsvr.NewServerMiddleware( 80 | zipKinTracer, 81 | zipkinHttpsvr.SpanName("gateway"), 82 | zipkinHttpsvr.TagResponseSize(true), 83 | zipkinHttpsvr.ServerTags(tags), 84 | )(hystrixRouter) 85 | 86 | errChan := make(chan error) 87 | 88 | hystrixHandler := hystrix.NewStreamHandler() 89 | hystrixHandler.Start() 90 | go func() { 91 | errChan <- http.ListenAndServe(net.JoinHostPort("", "8010"), hystrixHandler) 92 | }() 93 | 94 | go func() { 95 | c := make(chan os.Signal) 96 | signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) 97 | errChan <- fmt.Errorf("%s", <-c) 98 | }() 99 | 100 | go func() { 101 | logger.Log("transport", "http", "addr", "8003") 102 | errChan <- http.ListenAndServe(":8003", handler) 103 | }() 104 | 105 | logger.Log("exit", <-errChan) 106 | } 107 | 108 | func NewReverseProxy(client *api.Client, tracer *zipkin.Tracer, logger log.Logger) *httputil.ReverseProxy { 109 | director := func(req *http.Request) { 110 | reqPath := req.URL.Path 111 | if reqPath == "" { 112 | return 113 | } 114 | // /biz/add/1/2 115 | pathArray := strings.Split(reqPath, "/") 116 | serviceName := pathArray[1] 117 | logger.Log("serviceName:", serviceName) 118 | 119 | result, _, err := client.Catalog().Service(serviceName, "", nil) 120 | if err != nil { 121 | logger.Log("reverseProxy failed", "query service instance error", err.Error()) 122 | return 123 | } 124 | 125 | if len(result) == 0 { 126 | logger.Log("reverseProxy failed", "no such service instance", serviceName) 127 | return 128 | } 129 | 130 | destPath := strings.Join(pathArray[2:], "/") 131 | tgt := result[rand.Int()%len(result)] 132 | logger.Log("service id", tgt.ServiceID) 133 | 134 | req.URL.Scheme = "http" 135 | req.URL.Host = fmt.Sprintf("%s:%d", tgt.ServiceAddress, tgt.ServicePort) 136 | req.URL.Path = "/" + destPath 137 | } 138 | 139 | roundTrip, _ := zipkinHttpsvr.NewTransport(tracer, zipkinHttpsvr.TransportTrace(true)) 140 | 141 | return &httputil.ReverseProxy{ 142 | Director: director, 143 | Transport: roundTrip, 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /biz_jwt/gateway/router.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "github.com/afex/hystrix-go/hystrix" 7 | "github.com/go-kit/kit/log" 8 | "github.com/hashicorp/consul/api" 9 | "github.com/openzipkin/zipkin-go" 10 | zipkinHttpsvr "github.com/openzipkin/zipkin-go/middleware/http" 11 | "math/rand" 12 | "net/http" 13 | "net/http/httputil" 14 | "strings" 15 | "sync" 16 | ) 17 | 18 | type HystrixRouter struct { 19 | svcMap *sync.Map 20 | logger log.Logger 21 | fallbackMsg string 22 | consulClient *api.Client 23 | tracer *zipkin.Tracer 24 | } 25 | 26 | func NewRoutes(client *api.Client, tracer *zipkin.Tracer, fbMsg string, logger log.Logger) http.Handler { 27 | return &HystrixRouter{ 28 | svcMap: &sync.Map{}, 29 | logger: logger, 30 | fallbackMsg: fbMsg, 31 | consulClient: client, 32 | tracer: tracer, 33 | } 34 | } 35 | 36 | func (router *HystrixRouter) ServeHTTP(w http.ResponseWriter, r *http.Request) { 37 | // /biz/add/1/2 38 | reqPath := r.URL.Path 39 | if reqPath == "" { 40 | return 41 | } 42 | 43 | pathArray := strings.Split(reqPath, "/") 44 | serviceName := pathArray[1] 45 | 46 | if _, ok := router.svcMap.Load(serviceName); !ok { 47 | hystrix.ConfigureCommand(serviceName, hystrix.CommandConfig{Timeout: 1000}) 48 | router.svcMap.Store(serviceName, serviceName) 49 | } 50 | 51 | err := hystrix.Do(serviceName, func() (err error) { 52 | result, _, err := router.consulClient.Catalog().Service(serviceName, "", nil) 53 | if err != nil { 54 | router.logger.Log("reverseProxy failed", "query service instance error", err.Error()) 55 | return 56 | } 57 | 58 | if len(result) == 0 { 59 | router.logger.Log("reverseProxy failed", "no such service instance", serviceName) 60 | return errors.New("no such service instance") 61 | } 62 | 63 | director := func(req *http.Request) { 64 | destPath := strings.Join(pathArray[2:], "/") 65 | tgt := result[rand.Int()%len(result)] 66 | router.logger.Log("service id", tgt.ServiceID) 67 | 68 | req.URL.Scheme = "http" 69 | req.URL.Host = fmt.Sprintf("%s:%d", tgt.ServiceAddress, tgt.ServicePort) 70 | req.URL.Path = "/" + destPath 71 | } 72 | 73 | var proxyError error = nil 74 | roundTrip, _ := zipkinHttpsvr.NewTransport(router.tracer, zipkinHttpsvr.TransportTrace(true)) 75 | 76 | errorHandler := func(ew http.ResponseWriter, er *http.Request, err error) { 77 | proxyError = err 78 | } 79 | proxy := &httputil.ReverseProxy{ 80 | Director: director, 81 | Transport: roundTrip, 82 | ErrorHandler: errorHandler, 83 | } 84 | 85 | proxy.ServeHTTP(w, r) 86 | return proxyError 87 | }, func(err error) error { 88 | router.logger.Log("fallback error desc", err.Error()) 89 | return errors.New(router.fallbackMsg) 90 | }) 91 | 92 | if err != nil { 93 | w.WriteHeader(500) 94 | w.Write([]byte(err.Error())) 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /biz_jwt/register/endpoint.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | "github.com/go-kit/kit/endpoint" 8 | "strings" 9 | ) 10 | 11 | type BizRequest struct { 12 | ReqType string `json:"type"` 13 | A int `json:"a"` 14 | B int `json:"b"` 15 | } 16 | 17 | type BizResponse struct { 18 | Result int `json:"result"` 19 | Error error `json:"error"` 20 | } 21 | 22 | type BizEndpoints struct { 23 | BizEndpoint endpoint.Endpoint 24 | HealthEndpoint endpoint.Endpoint 25 | AuthEndpoint endpoint.Endpoint 26 | } 27 | 28 | var ( 29 | ErrInvalidType = errors.New("requestType has 4 types: Add, Sub, Mul and Div") 30 | ) 31 | 32 | func MakeBizEndpoint(svc Service) endpoint.Endpoint { 33 | return func(ctx context.Context, request interface{}) (response interface{}, err error) { 34 | req := request.(*BizRequest) 35 | fmt.Printf("req:#%v\n", req) 36 | var ( 37 | res, a, b int 38 | calError error 39 | ) 40 | 41 | a = req.A 42 | b = req.B 43 | bizRes := &BizResponse{} 44 | 45 | if strings.EqualFold(req.ReqType, "Add") { 46 | res = svc.Add(a, b) 47 | } else if strings.EqualFold(req.ReqType, "Sub") { 48 | res = svc.Sub(a, b) 49 | } else if strings.EqualFold(req.ReqType, "Mul") { 50 | res = svc.Mul(a, b) 51 | } else if strings.EqualFold(req.ReqType, "Div") { 52 | res, calError = svc.Div(a, b) 53 | } else { 54 | return bizRes, ErrInvalidType 55 | } 56 | bizRes.Result = res 57 | bizRes.Error = calError 58 | return bizRes, nil 59 | } 60 | } 61 | 62 | type HealthRequest struct { 63 | } 64 | 65 | type HealthResponse struct { 66 | Status bool `json:"status"` 67 | } 68 | 69 | func MakeHealthEndpoint(srv Service) endpoint.Endpoint { 70 | return func(ctx context.Context, request interface{}) (response interface{}, err error) { 71 | status := srv.HealthCheck() 72 | bizRes := &HealthResponse{} 73 | bizRes.Status = status 74 | return bizRes, nil 75 | } 76 | } 77 | 78 | type AuthRequest struct { 79 | Name string `json:"name"` 80 | Pwd string `json:"pwd"` 81 | } 82 | 83 | type AuthResponse struct { 84 | Success bool `json:"success"` 85 | Token string `json:"token"` 86 | Error string `json:"error"` 87 | } 88 | 89 | func MakeAuthEndpoint(svc Service) endpoint.Endpoint { 90 | return func(ctx context.Context, request interface{}) (response interface{}, err error) { 91 | req := request.(*AuthRequest) 92 | 93 | token, err := svc.Login(req.Name, req.Pwd) 94 | 95 | resp := &AuthResponse{} 96 | if err != nil { 97 | resp.Success = false 98 | resp.Token = token 99 | resp.Error = err.Error() 100 | } else { 101 | resp.Success = true 102 | resp.Token = token 103 | } 104 | return resp, nil 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /biz_jwt/register/instrument.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "github.com/go-kit/kit/endpoint" 7 | "github.com/go-kit/kit/metrics" 8 | "github.com/juju/ratelimit" 9 | "golang.org/x/time/rate" 10 | "time" 11 | ) 12 | 13 | var ( 14 | ErrLimitExceed = errors.New("rate limit exceed") 15 | ) 16 | 17 | func NewTokenBucketLimiterWithJuju(bkt *ratelimit.Bucket) endpoint.Middleware { 18 | return func(next endpoint.Endpoint) endpoint.Endpoint { 19 | return func(ctx context.Context, request interface{}) (response interface{}, err error) { 20 | if bkt.TakeAvailable(1) == 0 { 21 | return nil, ErrLimitExceed 22 | } 23 | return next(ctx, request) 24 | } 25 | } 26 | } 27 | 28 | func NewTokenBucketLimiterWithBuildIn(bkt *rate.Limiter) endpoint.Middleware { 29 | return func(next endpoint.Endpoint) endpoint.Endpoint { 30 | return func(ctx context.Context, request interface{}) (response interface{}, err error) { 31 | if !bkt.Allow() { 32 | return nil, ErrLimitExceed 33 | } 34 | return next(ctx, request) 35 | } 36 | } 37 | } 38 | 39 | type MetricMiddleware struct { 40 | Service 41 | RequestCount metrics.Counter 42 | RequestLatency metrics.Histogram 43 | } 44 | 45 | func NewMetrics(counter metrics.Counter, histogram metrics.Histogram) ServiceMiddleware { 46 | return func(next Service) Service { 47 | return MetricMiddleware{ 48 | Service: next, 49 | RequestCount: counter, 50 | RequestLatency: histogram, 51 | } 52 | } 53 | } 54 | 55 | func (mw MetricMiddleware) Add(a, b int) (ret int) { 56 | defer func(begin time.Time) { 57 | lvs := []string{"method", "Add"} 58 | mw.RequestCount.With(lvs...).Add(1) 59 | mw.RequestLatency.With(lvs...).Observe(time.Since(begin).Seconds()) 60 | }(time.Now()) 61 | 62 | ret = mw.Service.Add(a, b) 63 | return 64 | } 65 | 66 | func (mw MetricMiddleware) Sub(a, b int) (ret int) { 67 | defer func(begin time.Time) { 68 | lvs := []string{"method", "Sub"} 69 | mw.RequestCount.With(lvs...).Add(1) 70 | mw.RequestLatency.With(lvs...).Observe(time.Since(begin).Seconds()) 71 | }(time.Now()) 72 | 73 | ret = mw.Service.Sub(a, b) 74 | return 75 | } 76 | 77 | func (mw MetricMiddleware) Mul(a, b int) (ret int) { 78 | defer func(begin time.Time) { 79 | lvs := []string{"method", "Mul"} 80 | mw.RequestCount.With(lvs...).Add(1) 81 | mw.RequestLatency.With(lvs...).Observe(time.Since(begin).Seconds()) 82 | }(time.Now()) 83 | 84 | ret = mw.Service.Mul(a, b) 85 | return 86 | } 87 | 88 | func (mw MetricMiddleware) Div(a, b int) (ret int, err error) { 89 | defer func(begin time.Time) { 90 | lvs := []string{"method", "Div"} 91 | mw.RequestCount.With(lvs...).Add(1) 92 | mw.RequestLatency.With(lvs...).Observe(time.Since(begin).Seconds()) 93 | }(time.Now()) 94 | 95 | ret, err = mw.Service.Div(a, b) 96 | return 97 | } 98 | 99 | func (mw MetricMiddleware) HealthCheck() (ret bool) { 100 | defer func(begin time.Time) { 101 | lvs := []string{"method", "HealthCheck"} 102 | mw.RequestCount.With(lvs...).Add(1) 103 | mw.RequestLatency.With(lvs...).Observe(time.Since(begin).Seconds()) 104 | }(time.Now()) 105 | 106 | ret = mw.Service.HealthCheck() 107 | return 108 | } 109 | 110 | func (mw MetricMiddleware) Login(name, pwd string) (token string, err error) { 111 | defer func(begin time.Time) { 112 | lvs := []string{"method", "Login"} 113 | mw.RequestCount.With(lvs...).Add(1) 114 | mw.RequestLatency.With(lvs...).Observe(time.Since(begin).Seconds()) 115 | }(time.Now()) 116 | 117 | token, err = mw.Service.Login(name, pwd) 118 | return 119 | } 120 | -------------------------------------------------------------------------------- /biz_jwt/register/jwt.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/dgrijalva/jwt-go" 5 | "time" 6 | ) 7 | 8 | var ( 9 | secretKey = []byte("adcd1234!@#$") 10 | ) 11 | 12 | type BizCustomClaim struct { 13 | UserID string `json:"userID"` 14 | Name string `json:"name"` 15 | jwt.StandardClaims 16 | } 17 | 18 | func JwtKeyFunc(token *jwt.Token) (interface{}, error) { 19 | return secretKey, nil 20 | } 21 | 22 | func Sign(name, uid string) (string, error) { 23 | expAt := time.Now().Add(time.Duration(10) * time.Minute).Unix() 24 | 25 | claims := BizCustomClaim{ 26 | UserID: uid, 27 | Name: name, 28 | StandardClaims: jwt.StandardClaims{ 29 | ExpiresAt: expAt, 30 | Issuer: "system", 31 | }, 32 | } 33 | 34 | token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) 35 | 36 | return token.SignedString(secretKey) 37 | } 38 | -------------------------------------------------------------------------------- /biz_jwt/register/logging.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/go-kit/kit/log" 5 | "time" 6 | ) 7 | 8 | type LoggingMiddleware struct { 9 | Service 10 | logger log.Logger 11 | } 12 | 13 | func NewLoggingMiddleware(logger log.Logger) ServiceMiddleware { 14 | return func(next Service) Service { 15 | return LoggingMiddleware{next, logger} 16 | } 17 | } 18 | 19 | func (mw LoggingMiddleware) Add(a, b int) (ret int) { 20 | defer func(begin time.Time) { 21 | mw.logger.Log( 22 | "function", "Add", 23 | "a", a, 24 | "b", b, 25 | "result", ret, 26 | "cost", time.Since(begin), 27 | ) 28 | 29 | }(time.Now()) 30 | 31 | ret = mw.Service.Add(a, b) 32 | return 33 | } 34 | 35 | func (mw LoggingMiddleware) Sub(a, b int) (ret int) { 36 | defer func(begin time.Time) { 37 | mw.logger.Log( 38 | "function", "Sub", 39 | "a", a, 40 | "b", b, 41 | "result", ret, 42 | "cost", time.Since(begin), 43 | ) 44 | 45 | }(time.Now()) 46 | 47 | ret = mw.Service.Sub(a, b) 48 | return 49 | } 50 | 51 | func (mw LoggingMiddleware) Mul(a, b int) (ret int) { 52 | defer func(begin time.Time) { 53 | mw.logger.Log( 54 | "function", "Mul", 55 | "a", a, 56 | "b", b, 57 | "result", ret, 58 | "cost", time.Since(begin), 59 | ) 60 | 61 | }(time.Now()) 62 | 63 | ret = mw.Service.Mul(a, b) 64 | return 65 | } 66 | 67 | func (mw LoggingMiddleware) Div(a, b int) (ret int, err error) { 68 | defer func(begin time.Time) { 69 | mw.logger.Log( 70 | "function", "Div", 71 | "a", a, 72 | "b", b, 73 | "result", ret, 74 | "cost", time.Since(begin), 75 | ) 76 | 77 | }(time.Now()) 78 | 79 | ret, err = mw.Service.Div(a, b) 80 | return 81 | } 82 | 83 | func (mw LoggingMiddleware) HealthCheck() (ret bool) { 84 | defer func(begin time.Time) { 85 | mw.logger.Log( 86 | "function", "HealthCheck", 87 | "result", ret, 88 | "cost", time.Since(begin), 89 | ) 90 | }(time.Now()) 91 | 92 | ret = mw.Service.HealthCheck() 93 | return 94 | } 95 | 96 | func (mw LoggingMiddleware) Login(name, pwd string) (token string, err error) { 97 | defer func(begin time.Time) { 98 | mw.logger.Log( 99 | "function", "Login", 100 | "result", token, 101 | "took", time.Since(begin), 102 | ) 103 | }(time.Now()) 104 | 105 | token, err = mw.Service.Login(name, pwd) 106 | return 107 | } 108 | -------------------------------------------------------------------------------- /biz_jwt/register/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "flag" 6 | "fmt" 7 | "github.com/dgrijalva/jwt-go" 8 | kitJwt "github.com/go-kit/kit/auth/jwt" 9 | "github.com/go-kit/kit/log" 10 | kitPrometheus "github.com/go-kit/kit/metrics/prometheus" 11 | kitZipkin "github.com/go-kit/kit/tracing/zipkin" 12 | "github.com/openzipkin/zipkin-go" 13 | zipkinHttp "github.com/openzipkin/zipkin-go/reporter/http" 14 | stdPrometheus "github.com/prometheus/client_golang/prometheus" 15 | "golang.org/x/time/rate" 16 | "net/http" 17 | "os" 18 | "os/signal" 19 | "syscall" 20 | "time" 21 | ) 22 | 23 | var ( 24 | consulHost = flag.String("consul.host", "192.168.0.103", "consul ip address") 25 | consulPort = flag.String("consul.port", "8500", "consul port") 26 | serviceHost = flag.String("service.host", "192.168.0.103", "service ip address") 27 | servicePort = flag.String("service.port", "8000", "service port") 28 | zipkinUrl = flag.String("zipkin.url", "http://192.168.0.103:9411/api/v2/spans", "zipkin server url") 29 | ) 30 | 31 | func main() { 32 | flag.Parse() 33 | 34 | ctx := context.Background() 35 | errChan := make(chan error) 36 | 37 | var logger log.Logger 38 | { 39 | logger = log.NewLogfmtLogger(os.Stderr) 40 | logger = log.With(logger, "ts", log.DefaultTimestampUTC) 41 | logger = log.With(logger, "caller", log.DefaultCaller) 42 | } 43 | 44 | fieldKeys := []string{"method"} 45 | requestCount := kitPrometheus.NewCounterFrom(stdPrometheus.CounterOpts{ 46 | Namespace: "vince_cfl", 47 | Subsystem: "biz_service", 48 | Name: "request_count", 49 | Help: "numbers of request received", 50 | }, fieldKeys) 51 | 52 | requestLatency := kitPrometheus.NewSummaryFrom(stdPrometheus.SummaryOpts{ 53 | Namespace: "vince_cfl", 54 | Subsystem: "biz_service", 55 | Name: "request_latency", 56 | Help: "total duration of request in microseconds", 57 | }, fieldKeys) 58 | 59 | var zipKinTracer *zipkin.Tracer 60 | { 61 | var ( 62 | err error 63 | hostPort = *serviceHost + ":" + *servicePort 64 | serviceName = "biz-service" 65 | useNoopTracer = *zipkinUrl == "" 66 | reporter = zipkinHttp.NewReporter(*zipkinUrl) 67 | ) 68 | 69 | defer reporter.Close() 70 | 71 | zEP, _ := zipkin.NewEndpoint(serviceName, hostPort) 72 | zipKinTracer, err = zipkin.NewTracer( 73 | reporter, zipkin.WithLocalEndpoint(zEP), zipkin.WithNoopTracer(useNoopTracer), 74 | ) 75 | if err != nil { 76 | logger.Log("error", err) 77 | os.Exit(1) 78 | } 79 | if !useNoopTracer { 80 | logger.Log("tracer", "zipkin", "type", "native", "url", *zipkinUrl) 81 | } 82 | } 83 | 84 | svc := NewBizService() 85 | 86 | svc = NewLoggingMiddleware(logger)(svc) 87 | 88 | svc = NewMetrics(requestCount, requestLatency)(svc) 89 | 90 | rateBucket := rate.NewLimiter(rate.Every(time.Second*1), 100) 91 | 92 | bizEndpoint := MakeBizEndpoint(svc) 93 | 94 | //rateBucket := ratelimit.NewBucket(time.Second*1, 1) 95 | //endpoint = NewTokenBucketLimiterWithJuju(rateBucket)(endpoint) 96 | 97 | bizEndpoint = NewTokenBucketLimiterWithBuildIn(rateBucket)(bizEndpoint) 98 | bizEndpoint = kitZipkin.TraceEndpoint(zipKinTracer, "biz-endpoint")(bizEndpoint) 99 | bizEndpoint = kitJwt.NewParser(JwtKeyFunc, jwt.SigningMethodHS256, kitJwt.StandardClaimsFactory)(bizEndpoint) 100 | 101 | healthEndpoint := MakeHealthEndpoint(svc) 102 | healthEndpoint = NewTokenBucketLimiterWithBuildIn(rateBucket)(healthEndpoint) 103 | healthEndpoint = kitZipkin.TraceEndpoint(zipKinTracer, "health-endpoint")(healthEndpoint) 104 | 105 | authEndpoint := MakeAuthEndpoint(svc) 106 | authEndpoint = NewTokenBucketLimiterWithBuildIn(rateBucket)(authEndpoint) 107 | authEndpoint = kitZipkin.TraceEndpoint(zipKinTracer, "login-endpoint")(authEndpoint) 108 | 109 | endpoints := BizEndpoints{ 110 | BizEndpoint: bizEndpoint, 111 | HealthEndpoint: healthEndpoint, 112 | AuthEndpoint: authEndpoint, 113 | } 114 | 115 | r := MakeHttpHandler(ctx, endpoints, zipKinTracer, logger) 116 | 117 | registrar := Register(*consulHost, *consulPort, *serviceHost, *servicePort, logger) 118 | 119 | go func() { 120 | fmt.Println("http server start at port:" + *servicePort) 121 | registrar.Register() 122 | handler := r 123 | errChan <- http.ListenAndServe(":"+*servicePort, handler) 124 | }() 125 | 126 | go func() { 127 | c := make(chan os.Signal, 1) 128 | signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) 129 | errChan <- fmt.Errorf("%s", <-c) 130 | }() 131 | 132 | error := <-errChan 133 | registrar.Deregister() 134 | fmt.Printf("service stop:%v\n", error) 135 | } 136 | -------------------------------------------------------------------------------- /biz_jwt/register/register.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/go-kit/kit/log" 5 | "github.com/go-kit/kit/sd" 6 | "github.com/go-kit/kit/sd/consul" 7 | "github.com/hashicorp/consul/api" 8 | "github.com/pborman/uuid" 9 | "os" 10 | "strconv" 11 | ) 12 | 13 | func Register(consulHost, consulPort, svcHost, svcPort string, logger log.Logger) (registrar sd.Registrar) { 14 | var client consul.Client 15 | { 16 | consulCfg := api.DefaultConfig() 17 | consulCfg.Address = consulHost + ":" + consulPort 18 | consultClient, err := api.NewClient(consulCfg) 19 | if err != nil { 20 | logger.Log("create consul error:", err) 21 | os.Exit(1) 22 | } 23 | 24 | client = consul.NewClient(consultClient) 25 | } 26 | 27 | check := api.AgentServiceCheck{ 28 | HTTP: "http://" + svcHost + ":" + svcPort + "/health", 29 | Interval: "10s", 30 | Timeout: "1s", 31 | Notes: "consul check service health status", 32 | } 33 | 34 | port, _ := strconv.Atoi(svcPort) 35 | 36 | reg := api.AgentServiceRegistration{ 37 | ID: "biz-" + uuid.New(), 38 | Name: "biz", 39 | Address: svcHost, 40 | Port: port, 41 | Tags: []string{"biz", "vc"}, 42 | Check: &check, 43 | } 44 | 45 | registrar = consul.NewRegistrar(client, ®, logger) 46 | return 47 | } 48 | -------------------------------------------------------------------------------- /biz_jwt/register/service.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | type Service interface { 8 | Add(a, b int) int 9 | 10 | Sub(a, b int) int 11 | 12 | Mul(a, b int) int 13 | 14 | Div(a, b int) (int, error) 15 | 16 | HealthCheck() bool 17 | 18 | Login(name, pwd string) (string, error) 19 | } 20 | 21 | type BizService struct { 22 | } 23 | 24 | func NewBizService() Service { 25 | return &BizService{} 26 | } 27 | 28 | func (s *BizService) Add(a, b int) int { 29 | return a + b 30 | } 31 | 32 | func (s *BizService) Sub(a, b int) int { 33 | return a - b 34 | } 35 | 36 | func (s *BizService) Mul(a, b int) int { 37 | return a * b 38 | } 39 | 40 | func (s *BizService) Div(a, b int) (int, error) { 41 | if b == 0 { 42 | return 0, errors.New("the divisor is zero") 43 | } 44 | return a / b, nil 45 | } 46 | 47 | func (s *BizService) HealthCheck() bool { 48 | return true 49 | } 50 | 51 | func (s *BizService) Login(name, pwd string) (string, error) { 52 | if name == "admin" && pwd == "admin" { 53 | token, err := Sign(name, pwd) 54 | return token, err 55 | } 56 | 57 | return "", errors.New("name or password error") 58 | } 59 | 60 | type ServiceMiddleware func(Service) Service 61 | -------------------------------------------------------------------------------- /biz_jwt/register/transport.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "errors" 7 | "fmt" 8 | kitJwt "github.com/go-kit/kit/auth/jwt" 9 | "github.com/go-kit/kit/log" 10 | "github.com/go-kit/kit/tracing/zipkin" 11 | kitHttp "github.com/go-kit/kit/transport/http" 12 | "github.com/gorilla/mux" 13 | goZipkin "github.com/openzipkin/zipkin-go" 14 | "github.com/prometheus/client_golang/prometheus/promhttp" 15 | "net/http" 16 | "strconv" 17 | ) 18 | 19 | var ( 20 | ErrBadRequest = errors.New("invalid request parameter") 21 | ) 22 | 23 | func MakeHttpHandler(ctx context.Context, endpoints BizEndpoints, tracer *goZipkin.Tracer, logger log.Logger) http.Handler { 24 | r := mux.NewRouter() 25 | 26 | zipkinServer := zipkin.HTTPServerTrace(tracer, zipkin.Name("http-transport")) 27 | 28 | options := []kitHttp.ServerOption{ 29 | kitHttp.ServerErrorLogger(logger), 30 | kitHttp.ServerErrorEncoder(kitHttp.DefaultErrorEncoder), 31 | zipkinServer, 32 | } 33 | 34 | r.Methods("POST").Path("/biz/{type}/{a}/{b}").Handler(kitHttp.NewServer( 35 | endpoints.BizEndpoint, 36 | decodeBizRequest, 37 | encodeBizResponse, 38 | append(options, kitHttp.ServerBefore(kitJwt.HTTPToContext()))..., 39 | )) 40 | 41 | r.Path("/metrics").Handler(promhttp.Handler()) 42 | 43 | r.Methods("GET").Path("/health").Handler(kitHttp.NewServer( 44 | endpoints.HealthEndpoint, 45 | decodeHealthRequest, 46 | encodeBizResponse, 47 | options..., 48 | )) 49 | 50 | r.Methods("POST").Path("/login").Handler(kitHttp.NewServer( 51 | endpoints.AuthEndpoint, 52 | decodeLoginRequest, 53 | encodeLoginResponse, 54 | options..., 55 | )) 56 | 57 | return r 58 | } 59 | 60 | func decodeBizRequest(ctx context.Context, r *http.Request) (interface{}, error) { 61 | vars := mux.Vars(r) 62 | fmt.Printf("vars:%v\n", vars) 63 | 64 | reqType, ok := vars["type"] 65 | if !ok { 66 | return nil, ErrBadRequest 67 | } 68 | pa, ok := vars["a"] 69 | if !ok { 70 | return nil, ErrBadRequest 71 | } 72 | pb, ok := vars["b"] 73 | if !ok { 74 | return nil, ErrBadRequest 75 | } 76 | a, _ := strconv.Atoi(pa) 77 | b, _ := strconv.Atoi(pb) 78 | 79 | return &BizRequest{ 80 | ReqType: reqType, 81 | A: a, 82 | B: b, 83 | }, nil 84 | } 85 | 86 | func encodeBizResponse(ctx context.Context, w http.ResponseWriter, response interface{}) error { 87 | fmt.Printf("res:%#v\n", response) 88 | w.Header().Set("Content-Type", "application/json;charset=utf-8") 89 | return json.NewEncoder(w).Encode(response) 90 | } 91 | 92 | func decodeHealthRequest(ctx context.Context, r *http.Request) (interface{}, error) { 93 | return &HealthRequest{}, nil 94 | } 95 | 96 | func encodeLoginResponse(ctx context.Context, w http.ResponseWriter, response interface{}) error { 97 | w.Header().Set("Content-Type", "application/json;charset=utf-8") 98 | return json.NewEncoder(w).Encode(response) 99 | } 100 | 101 | func decodeLoginRequest(ctx context.Context, r *http.Request) (interface{}, error) { 102 | loginRequest := &AuthRequest{} 103 | fmt.Printf("r.Body:#%v\n", r.Body) 104 | 105 | if err := json.NewDecoder(r.Body).Decode(loginRequest); err != nil { 106 | return nil, err 107 | } 108 | fmt.Printf("loginRequest:#%v\n", loginRequest) 109 | return loginRequest, nil 110 | } 111 | -------------------------------------------------------------------------------- /biz_log/endpoint.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | "github.com/go-kit/kit/endpoint" 8 | "strings" 9 | ) 10 | 11 | type BizRequest struct { 12 | ReqType string `json:"type"` 13 | A int `json:"a"` 14 | B int `json:"b"` 15 | } 16 | 17 | type BizResponse struct { 18 | Result int `json:"result"` 19 | Error error `json:"error"` 20 | } 21 | 22 | type BizEndpoint endpoint.Endpoint 23 | 24 | var ( 25 | ErrInvalidType = errors.New("requestType has 4 types: Add, Sub, Mul and Div") 26 | ) 27 | 28 | func MakeBizEndpoint(svc Service) endpoint.Endpoint { 29 | return func(ctx context.Context, request interface{}) (response interface{}, err error) { 30 | req := request.(*BizRequest) 31 | fmt.Printf("req:#%v\n", req) 32 | var ( 33 | res, a, b int 34 | calError error 35 | ) 36 | 37 | a = req.A 38 | b = req.B 39 | bizRes := &BizResponse{} 40 | 41 | if strings.EqualFold(req.ReqType, "Add") { 42 | res = svc.Add(a, b) 43 | } else if strings.EqualFold(req.ReqType, "Sub") { 44 | res = svc.Sub(a, b) 45 | } else if strings.EqualFold(req.ReqType, "Mul") { 46 | res = svc.Mul(a, b) 47 | } else if strings.EqualFold(req.ReqType, "Div") { 48 | res, calError = svc.Div(a, b) 49 | } else { 50 | return bizRes, ErrInvalidType 51 | } 52 | bizRes.Result = res 53 | bizRes.Error = calError 54 | return bizRes, nil 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /biz_log/logging.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/go-kit/kit/log" 5 | "time" 6 | ) 7 | 8 | type LoggingMiddleware struct { 9 | Service 10 | logger log.Logger 11 | } 12 | 13 | func NewLoggingMiddleware(logger log.Logger) ServiceMiddleware { 14 | return func(next Service) Service { 15 | return LoggingMiddleware{next, logger} 16 | } 17 | } 18 | 19 | func (mw LoggingMiddleware) Add(a, b int) (ret int) { 20 | defer func(begin time.Time) { 21 | mw.logger.Log( 22 | "function", "Add", 23 | "a", a, 24 | "b", b, 25 | "result", ret, 26 | "cost", time.Since(begin), 27 | ) 28 | 29 | }(time.Now()) 30 | 31 | ret = mw.Service.Add(a, b) 32 | return 33 | } 34 | 35 | func (mw LoggingMiddleware) Sub(a, b int) (ret int) { 36 | defer func(begin time.Time) { 37 | mw.logger.Log( 38 | "function", "Sub", 39 | "a", a, 40 | "b", b, 41 | "result", ret, 42 | "cost", time.Since(begin), 43 | ) 44 | 45 | }(time.Now()) 46 | 47 | ret = mw.Service.Sub(a, b) 48 | return 49 | } 50 | 51 | func (mw LoggingMiddleware) Mul(a, b int) (ret int) { 52 | defer func(begin time.Time) { 53 | mw.logger.Log( 54 | "function", "Mul", 55 | "a", a, 56 | "b", b, 57 | "result", ret, 58 | "cost", time.Since(begin), 59 | ) 60 | 61 | }(time.Now()) 62 | 63 | ret = mw.Service.Mul(a, b) 64 | return 65 | } 66 | 67 | func (mw LoggingMiddleware) Div(a, b int) (ret int, err error) { 68 | defer func(begin time.Time) { 69 | mw.logger.Log( 70 | "function", "Div", 71 | "a", a, 72 | "b", b, 73 | "result", ret, 74 | "cost", time.Since(begin), 75 | ) 76 | 77 | }(time.Now()) 78 | 79 | ret, err = mw.Service.Div(a, b) 80 | return 81 | } 82 | -------------------------------------------------------------------------------- /biz_log/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "github.com/go-kit/kit/log" 7 | "net/http" 8 | "os" 9 | "os/signal" 10 | "syscall" 11 | ) 12 | 13 | func main() { 14 | ctx := context.Background() 15 | errChan := make(chan error) 16 | 17 | var logger log.Logger 18 | { 19 | logger = log.NewLogfmtLogger(os.Stderr) 20 | logger = log.With(logger, "ts", log.DefaultTimestampUTC) 21 | logger = log.With(logger, "caller", log.DefaultCaller) 22 | } 23 | 24 | svc := NewBizService() 25 | 26 | svc = NewLoggingMiddleware(logger)(svc) 27 | 28 | endpoint := MakeBizEndpoint(svc) 29 | 30 | r := MakeHttpHandler(ctx, endpoint, logger) 31 | 32 | go func() { 33 | fmt.Println("http server start at port:8000") 34 | handler := r 35 | errChan <- http.ListenAndServe(":8000", handler) 36 | }() 37 | 38 | go func() { 39 | c := make(chan os.Signal, 1) 40 | signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) 41 | errChan <- fmt.Errorf("%s", <-c) 42 | }() 43 | 44 | fmt.Printf("http.ListenAndServe error:%v\n", <-errChan) 45 | } 46 | -------------------------------------------------------------------------------- /biz_log/service.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | type Service interface { 8 | Add(a, b int) int 9 | 10 | Sub(a, b int) int 11 | 12 | Mul(a, b int) int 13 | 14 | Div(a, b int) (int, error) 15 | } 16 | 17 | type BizService struct { 18 | } 19 | 20 | func NewBizService() Service { 21 | return &BizService{} 22 | } 23 | 24 | func (s *BizService) Add(a, b int) int { 25 | return a + b 26 | } 27 | 28 | func (s *BizService) Sub(a, b int) int { 29 | return a - b 30 | } 31 | 32 | func (s *BizService) Mul(a, b int) int { 33 | return a * b 34 | } 35 | 36 | func (s *BizService) Div(a, b int) (int, error) { 37 | if b == 0 { 38 | return 0, errors.New("the divisor is zero") 39 | } 40 | return a / b, nil 41 | } 42 | 43 | type ServiceMiddleware func(Service) Service -------------------------------------------------------------------------------- /biz_log/transport.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "errors" 7 | "fmt" 8 | "github.com/go-kit/kit/endpoint" 9 | "github.com/go-kit/kit/log" 10 | kitHttp "github.com/go-kit/kit/transport/http" 11 | "github.com/gorilla/mux" 12 | "net/http" 13 | "strconv" 14 | ) 15 | 16 | var ( 17 | ErrBadRequest = errors.New("invalid request parameter") 18 | ) 19 | 20 | func MakeHttpHandler(ctx context.Context, endpoint endpoint.Endpoint, logger log.Logger) http.Handler { 21 | r := mux.NewRouter() 22 | 23 | options := []kitHttp.ServerOption{ 24 | kitHttp.ServerErrorLogger(logger), 25 | kitHttp.ServerErrorEncoder(kitHttp.DefaultErrorEncoder), 26 | } 27 | 28 | r.Methods("POST").Path("/biz/{type}/{a}/{b}").Handler(kitHttp.NewServer( 29 | endpoint, 30 | decodeBizRequest, 31 | encodeBizResponse, 32 | options..., 33 | )) 34 | 35 | return r 36 | } 37 | 38 | func decodeBizRequest(ctx context.Context, r *http.Request) (interface{}, error) { 39 | vars := mux.Vars(r) 40 | fmt.Printf("vars:%v\n", vars) 41 | 42 | reqType, ok := vars["type"] 43 | if !ok { 44 | return nil, ErrBadRequest 45 | } 46 | pa, ok := vars["a"] 47 | if !ok { 48 | return nil, ErrBadRequest 49 | } 50 | pb, ok := vars["b"] 51 | if !ok { 52 | return nil, ErrBadRequest 53 | } 54 | a, _ := strconv.Atoi(pa) 55 | b, _ := strconv.Atoi(pb) 56 | 57 | return &BizRequest{ 58 | ReqType: reqType, 59 | A: a, 60 | B: b, 61 | }, nil 62 | } 63 | 64 | func encodeBizResponse(ctx context.Context, w http.ResponseWriter, response interface{}) error { 65 | fmt.Printf("res:%#v\n", response) 66 | w.Header().Set("Content-Type", "application/json;charset=utf-8") 67 | return json.NewEncoder(w).Encode(response) 68 | } 69 | -------------------------------------------------------------------------------- /biz_monitor/endpoint.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | "github.com/go-kit/kit/endpoint" 8 | "strings" 9 | ) 10 | 11 | type BizRequest struct { 12 | ReqType string `json:"type"` 13 | A int `json:"a"` 14 | B int `json:"b"` 15 | } 16 | 17 | type BizResponse struct { 18 | Result int `json:"result"` 19 | Error error `json:"error"` 20 | } 21 | 22 | type BizEndpoint endpoint.Endpoint 23 | 24 | var ( 25 | ErrInvalidType = errors.New("requestType has 4 types: Add, Sub, Mul and Div") 26 | ) 27 | 28 | func MakeBizEndpoint(svc Service) endpoint.Endpoint { 29 | return func(ctx context.Context, request interface{}) (response interface{}, err error) { 30 | req := request.(*BizRequest) 31 | fmt.Printf("req:#%v\n", req) 32 | var ( 33 | res, a, b int 34 | calError error 35 | ) 36 | 37 | a = req.A 38 | b = req.B 39 | bizRes := &BizResponse{} 40 | 41 | if strings.EqualFold(req.ReqType, "Add") { 42 | res = svc.Add(a, b) 43 | } else if strings.EqualFold(req.ReqType, "Sub") { 44 | res = svc.Sub(a, b) 45 | } else if strings.EqualFold(req.ReqType, "Mul") { 46 | res = svc.Mul(a, b) 47 | } else if strings.EqualFold(req.ReqType, "Div") { 48 | res, calError = svc.Div(a, b) 49 | } else { 50 | return bizRes, ErrInvalidType 51 | } 52 | bizRes.Result = res 53 | bizRes.Error = calError 54 | return bizRes, nil 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /biz_monitor/instrument.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "github.com/go-kit/kit/endpoint" 7 | "github.com/go-kit/kit/metrics" 8 | "github.com/juju/ratelimit" 9 | "golang.org/x/time/rate" 10 | "time" 11 | ) 12 | 13 | var ( 14 | ErrLimitExceed = errors.New("rate limit exceed") 15 | ) 16 | 17 | func NewTokenBucketLimiterWithJuju(bkt *ratelimit.Bucket) endpoint.Middleware { 18 | return func(next endpoint.Endpoint) endpoint.Endpoint { 19 | return func(ctx context.Context, request interface{}) (response interface{}, err error) { 20 | if bkt.TakeAvailable(1) == 0 { 21 | return nil, ErrLimitExceed 22 | } 23 | return next(ctx, request) 24 | } 25 | } 26 | } 27 | 28 | func NewTokenBucketLimiterWithBuildIn(bkt *rate.Limiter) endpoint.Middleware { 29 | return func(next endpoint.Endpoint) endpoint.Endpoint { 30 | return func(ctx context.Context, request interface{}) (response interface{}, err error) { 31 | if !bkt.Allow() { 32 | return nil, ErrLimitExceed 33 | } 34 | return next(ctx, request) 35 | } 36 | } 37 | } 38 | 39 | type MetricMiddleware struct { 40 | Service 41 | RequestCount metrics.Counter 42 | RequestLatency metrics.Histogram 43 | } 44 | 45 | func NewMetrics(counter metrics.Counter, histogram metrics.Histogram) ServiceMiddleware { 46 | return func(next Service) Service { 47 | return MetricMiddleware{ 48 | Service: next, 49 | RequestCount: counter, 50 | RequestLatency: histogram, 51 | } 52 | } 53 | } 54 | 55 | func (mw MetricMiddleware) Add(a, b int) (ret int) { 56 | defer func(begin time.Time) { 57 | lvs := []string{"method", "Add"} 58 | mw.RequestCount.With(lvs...).Add(1) 59 | mw.RequestLatency.With(lvs...).Observe(time.Since(begin).Seconds()) 60 | }(time.Now()) 61 | 62 | ret = mw.Service.Add(a, b) 63 | return 64 | } 65 | 66 | func (mw MetricMiddleware) Sub(a, b int) (ret int) { 67 | defer func(begin time.Time) { 68 | lvs := []string{"method", "Sub"} 69 | mw.RequestCount.With(lvs...).Add(1) 70 | mw.RequestLatency.With(lvs...).Observe(time.Since(begin).Seconds()) 71 | }(time.Now()) 72 | 73 | ret = mw.Service.Sub(a, b) 74 | return 75 | } 76 | 77 | func (mw MetricMiddleware) Mul(a, b int) (ret int) { 78 | defer func(begin time.Time) { 79 | lvs := []string{"method", "Mul"} 80 | mw.RequestCount.With(lvs...).Add(1) 81 | mw.RequestLatency.With(lvs...).Observe(time.Since(begin).Seconds()) 82 | }(time.Now()) 83 | 84 | ret = mw.Service.Mul(a, b) 85 | return 86 | } 87 | 88 | func (mw MetricMiddleware) Div(a, b int) (ret int, err error) { 89 | defer func(begin time.Time) { 90 | lvs := []string{"method", "Div"} 91 | mw.RequestCount.With(lvs...).Add(1) 92 | mw.RequestLatency.With(lvs...).Observe(time.Since(begin).Seconds()) 93 | }(time.Now()) 94 | 95 | ret, err = mw.Service.Div(a, b) 96 | return 97 | } 98 | -------------------------------------------------------------------------------- /biz_monitor/logging.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/go-kit/kit/log" 5 | "time" 6 | ) 7 | 8 | type LoggingMiddleware struct { 9 | Service 10 | logger log.Logger 11 | } 12 | 13 | func NewLoggingMiddleware(logger log.Logger) ServiceMiddleware { 14 | return func(next Service) Service { 15 | return LoggingMiddleware{next, logger} 16 | } 17 | } 18 | 19 | func (mw LoggingMiddleware) Add(a, b int) (ret int) { 20 | defer func(begin time.Time) { 21 | mw.logger.Log( 22 | "function", "Add", 23 | "a", a, 24 | "b", b, 25 | "result", ret, 26 | "cost", time.Since(begin), 27 | ) 28 | 29 | }(time.Now()) 30 | 31 | ret = mw.Service.Add(a, b) 32 | return 33 | } 34 | 35 | func (mw LoggingMiddleware) Sub(a, b int) (ret int) { 36 | defer func(begin time.Time) { 37 | mw.logger.Log( 38 | "function", "Sub", 39 | "a", a, 40 | "b", b, 41 | "result", ret, 42 | "cost", time.Since(begin), 43 | ) 44 | 45 | }(time.Now()) 46 | 47 | ret = mw.Service.Sub(a, b) 48 | return 49 | } 50 | 51 | func (mw LoggingMiddleware) Mul(a, b int) (ret int) { 52 | defer func(begin time.Time) { 53 | mw.logger.Log( 54 | "function", "Mul", 55 | "a", a, 56 | "b", b, 57 | "result", ret, 58 | "cost", time.Since(begin), 59 | ) 60 | 61 | }(time.Now()) 62 | 63 | ret = mw.Service.Mul(a, b) 64 | return 65 | } 66 | 67 | func (mw LoggingMiddleware) Div(a, b int) (ret int, err error) { 68 | defer func(begin time.Time) { 69 | mw.logger.Log( 70 | "function", "Div", 71 | "a", a, 72 | "b", b, 73 | "result", ret, 74 | "cost", time.Since(begin), 75 | ) 76 | 77 | }(time.Now()) 78 | 79 | ret, err = mw.Service.Div(a, b) 80 | return 81 | } 82 | -------------------------------------------------------------------------------- /biz_monitor/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "github.com/go-kit/kit/log" 7 | kitPrometheus "github.com/go-kit/kit/metrics/prometheus" 8 | stdPrometheus "github.com/prometheus/client_golang/prometheus" 9 | "golang.org/x/time/rate" 10 | "net/http" 11 | "os" 12 | "os/signal" 13 | "syscall" 14 | "time" 15 | ) 16 | 17 | func main() { 18 | ctx := context.Background() 19 | errChan := make(chan error) 20 | 21 | var logger log.Logger 22 | { 23 | logger = log.NewLogfmtLogger(os.Stderr) 24 | logger = log.With(logger, "ts", log.DefaultTimestampUTC) 25 | logger = log.With(logger, "caller", log.DefaultCaller) 26 | } 27 | 28 | fieldKeys := []string{"method"} 29 | requestCount := kitPrometheus.NewCounterFrom(stdPrometheus.CounterOpts{ 30 | Namespace: "vince_cfl", 31 | Subsystem: "biz_service", 32 | Name: "request_count", 33 | Help: "numbers of request received", 34 | }, fieldKeys) 35 | 36 | requestLatency := kitPrometheus.NewSummaryFrom(stdPrometheus.SummaryOpts{ 37 | Namespace: "vince_cfl", 38 | Subsystem: "biz_service", 39 | Name: "request_latency", 40 | Help: "total duration of request in microseconds", 41 | }, fieldKeys) 42 | 43 | svc := NewBizService() 44 | 45 | svc = NewLoggingMiddleware(logger)(svc) 46 | 47 | svc = NewMetrics(requestCount, requestLatency)(svc) 48 | 49 | endpoint := MakeBizEndpoint(svc) 50 | 51 | //rateBucket := ratelimit.NewBucket(time.Second*1, 1) 52 | //endpoint = NewTokenBucketLimiterWithJuju(rateBucket)(endpoint) 53 | 54 | rateBucket := rate.NewLimiter(rate.Every(time.Second*1), 100) 55 | endpoint = NewTokenBucketLimiterWithBuildIn(rateBucket)(endpoint) 56 | 57 | r := MakeHttpHandler(ctx, endpoint, logger) 58 | 59 | go func() { 60 | fmt.Println("http server start at port:8000") 61 | handler := r 62 | errChan <- http.ListenAndServe(":8000", handler) 63 | }() 64 | 65 | go func() { 66 | c := make(chan os.Signal, 1) 67 | signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) 68 | errChan <- fmt.Errorf("%s", <-c) 69 | }() 70 | 71 | fmt.Printf("http.ListenAndServe error:%v\n", <-errChan) 72 | } 73 | -------------------------------------------------------------------------------- /biz_monitor/service.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | type Service interface { 8 | Add(a, b int) int 9 | 10 | Sub(a, b int) int 11 | 12 | Mul(a, b int) int 13 | 14 | Div(a, b int) (int, error) 15 | } 16 | 17 | type BizService struct { 18 | } 19 | 20 | func NewBizService() Service { 21 | return &BizService{} 22 | } 23 | 24 | func (s *BizService) Add(a, b int) int { 25 | return a + b 26 | } 27 | 28 | func (s *BizService) Sub(a, b int) int { 29 | return a - b 30 | } 31 | 32 | func (s *BizService) Mul(a, b int) int { 33 | return a * b 34 | } 35 | 36 | func (s *BizService) Div(a, b int) (int, error) { 37 | if b == 0 { 38 | return 0, errors.New("the divisor is zero") 39 | } 40 | return a / b, nil 41 | } 42 | 43 | type ServiceMiddleware func(Service) Service -------------------------------------------------------------------------------- /biz_monitor/transport.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "errors" 7 | "fmt" 8 | "github.com/go-kit/kit/endpoint" 9 | "github.com/go-kit/kit/log" 10 | kitHttp "github.com/go-kit/kit/transport/http" 11 | "github.com/gorilla/mux" 12 | "github.com/prometheus/client_golang/prometheus/promhttp" 13 | "net/http" 14 | "strconv" 15 | ) 16 | 17 | var ( 18 | ErrBadRequest = errors.New("invalid request parameter") 19 | ) 20 | 21 | func MakeHttpHandler(ctx context.Context, endpoint endpoint.Endpoint, logger log.Logger) http.Handler { 22 | r := mux.NewRouter() 23 | 24 | options := []kitHttp.ServerOption{ 25 | kitHttp.ServerErrorLogger(logger), 26 | kitHttp.ServerErrorEncoder(kitHttp.DefaultErrorEncoder), 27 | } 28 | 29 | r.Methods("POST").Path("/biz/{type}/{a}/{b}").Handler(kitHttp.NewServer( 30 | endpoint, 31 | decodeBizRequest, 32 | encodeBizResponse, 33 | options..., 34 | )) 35 | 36 | r.Path("/metrics").Handler(promhttp.Handler()) 37 | 38 | return r 39 | } 40 | 41 | func decodeBizRequest(ctx context.Context, r *http.Request) (interface{}, error) { 42 | vars := mux.Vars(r) 43 | fmt.Printf("vars:%v\n", vars) 44 | 45 | reqType, ok := vars["type"] 46 | if !ok { 47 | return nil, ErrBadRequest 48 | } 49 | pa, ok := vars["a"] 50 | if !ok { 51 | return nil, ErrBadRequest 52 | } 53 | pb, ok := vars["b"] 54 | if !ok { 55 | return nil, ErrBadRequest 56 | } 57 | a, _ := strconv.Atoi(pa) 58 | b, _ := strconv.Atoi(pb) 59 | 60 | return &BizRequest{ 61 | ReqType: reqType, 62 | A: a, 63 | B: b, 64 | }, nil 65 | } 66 | 67 | func encodeBizResponse(ctx context.Context, w http.ResponseWriter, response interface{}) error { 68 | fmt.Printf("res:%#v\n", response) 69 | w.Header().Set("Content-Type", "application/json;charset=utf-8") 70 | return json.NewEncoder(w).Encode(response) 71 | } 72 | -------------------------------------------------------------------------------- /biz_rate/endpoint.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | "github.com/go-kit/kit/endpoint" 8 | "strings" 9 | ) 10 | 11 | type BizRequest struct { 12 | ReqType string `json:"type"` 13 | A int `json:"a"` 14 | B int `json:"b"` 15 | } 16 | 17 | type BizResponse struct { 18 | Result int `json:"result"` 19 | Error error `json:"error"` 20 | } 21 | 22 | type BizEndpoint endpoint.Endpoint 23 | 24 | var ( 25 | ErrInvalidType = errors.New("requestType has 4 types: Add, Sub, Mul and Div") 26 | ) 27 | 28 | func MakeBizEndpoint(svc Service) endpoint.Endpoint { 29 | return func(ctx context.Context, request interface{}) (response interface{}, err error) { 30 | req := request.(*BizRequest) 31 | fmt.Printf("req:#%v\n", req) 32 | var ( 33 | res, a, b int 34 | calError error 35 | ) 36 | 37 | a = req.A 38 | b = req.B 39 | bizRes := &BizResponse{} 40 | 41 | if strings.EqualFold(req.ReqType, "Add") { 42 | res = svc.Add(a, b) 43 | } else if strings.EqualFold(req.ReqType, "Sub") { 44 | res = svc.Sub(a, b) 45 | } else if strings.EqualFold(req.ReqType, "Mul") { 46 | res = svc.Mul(a, b) 47 | } else if strings.EqualFold(req.ReqType, "Div") { 48 | res, calError = svc.Div(a, b) 49 | } else { 50 | return bizRes, ErrInvalidType 51 | } 52 | bizRes.Result = res 53 | bizRes.Error = calError 54 | return bizRes, nil 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /biz_rate/instrument.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "github.com/go-kit/kit/endpoint" 7 | "github.com/juju/ratelimit" 8 | "golang.org/x/time/rate" 9 | ) 10 | 11 | var ( 12 | ErrLimitExceed = errors.New("rate limit exceed") 13 | ) 14 | 15 | func NewTokenBucketLimiterWithJuju(bkt *ratelimit.Bucket) endpoint.Middleware { 16 | return func(next endpoint.Endpoint) endpoint.Endpoint { 17 | return func(ctx context.Context, request interface{}) (response interface{}, err error) { 18 | if bkt.TakeAvailable(1) == 0 { 19 | return nil, ErrLimitExceed 20 | } 21 | return next(ctx, request) 22 | } 23 | } 24 | } 25 | 26 | func NewTokenBucketLimiterWithBuildIn(bkt *rate.Limiter) endpoint.Middleware { 27 | return func(next endpoint.Endpoint) endpoint.Endpoint { 28 | return func(ctx context.Context, request interface{}) (response interface{}, err error) { 29 | if !bkt.Allow() { 30 | return nil, ErrLimitExceed 31 | } 32 | return next(ctx, request) 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /biz_rate/logging.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/go-kit/kit/log" 5 | "time" 6 | ) 7 | 8 | type LoggingMiddleware struct { 9 | Service 10 | logger log.Logger 11 | } 12 | 13 | func NewLoggingMiddleware(logger log.Logger) ServiceMiddleware { 14 | return func(next Service) Service { 15 | return LoggingMiddleware{next, logger} 16 | } 17 | } 18 | 19 | func (mw LoggingMiddleware) Add(a, b int) (ret int) { 20 | defer func(begin time.Time) { 21 | mw.logger.Log( 22 | "function", "Add", 23 | "a", a, 24 | "b", b, 25 | "result", ret, 26 | "cost", time.Since(begin), 27 | ) 28 | 29 | }(time.Now()) 30 | 31 | ret = mw.Service.Add(a, b) 32 | return 33 | } 34 | 35 | func (mw LoggingMiddleware) Sub(a, b int) (ret int) { 36 | defer func(begin time.Time) { 37 | mw.logger.Log( 38 | "function", "Sub", 39 | "a", a, 40 | "b", b, 41 | "result", ret, 42 | "cost", time.Since(begin), 43 | ) 44 | 45 | }(time.Now()) 46 | 47 | ret = mw.Service.Sub(a, b) 48 | return 49 | } 50 | 51 | func (mw LoggingMiddleware) Mul(a, b int) (ret int) { 52 | defer func(begin time.Time) { 53 | mw.logger.Log( 54 | "function", "Mul", 55 | "a", a, 56 | "b", b, 57 | "result", ret, 58 | "cost", time.Since(begin), 59 | ) 60 | 61 | }(time.Now()) 62 | 63 | ret = mw.Service.Mul(a, b) 64 | return 65 | } 66 | 67 | func (mw LoggingMiddleware) Div(a, b int) (ret int, err error) { 68 | defer func(begin time.Time) { 69 | mw.logger.Log( 70 | "function", "Div", 71 | "a", a, 72 | "b", b, 73 | "result", ret, 74 | "cost", time.Since(begin), 75 | ) 76 | 77 | }(time.Now()) 78 | 79 | ret, err = mw.Service.Div(a, b) 80 | return 81 | } 82 | -------------------------------------------------------------------------------- /biz_rate/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "github.com/go-kit/kit/log" 7 | "golang.org/x/time/rate" 8 | "net/http" 9 | "os" 10 | "os/signal" 11 | "syscall" 12 | "time" 13 | ) 14 | 15 | func main() { 16 | ctx := context.Background() 17 | errChan := make(chan error) 18 | 19 | var logger log.Logger 20 | { 21 | logger = log.NewLogfmtLogger(os.Stderr) 22 | logger = log.With(logger, "ts", log.DefaultTimestampUTC) 23 | logger = log.With(logger, "caller", log.DefaultCaller) 24 | } 25 | 26 | svc := NewBizService() 27 | 28 | svc = NewLoggingMiddleware(logger)(svc) 29 | 30 | endpoint := MakeBizEndpoint(svc) 31 | 32 | //rateBucket := ratelimit.NewBucket(time.Second*1, 1) 33 | //endpoint = NewTokenBucketLimiterWithJuju(rateBucket)(endpoint) 34 | 35 | rateBucket := rate.NewLimiter(rate.Every(time.Second*1), 1) 36 | endpoint = NewTokenBucketLimiterWithBuildIn(rateBucket)(endpoint) 37 | 38 | r := MakeHttpHandler(ctx, endpoint, logger) 39 | 40 | go func() { 41 | fmt.Println("http server start at port:8000") 42 | handler := r 43 | errChan <- http.ListenAndServe(":8000", handler) 44 | }() 45 | 46 | go func() { 47 | c := make(chan os.Signal, 1) 48 | signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) 49 | errChan <- fmt.Errorf("%s", <-c) 50 | }() 51 | 52 | fmt.Printf("http.ListenAndServe error:%v\n", <-errChan) 53 | } 54 | -------------------------------------------------------------------------------- /biz_rate/service.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | type Service interface { 8 | Add(a, b int) int 9 | 10 | Sub(a, b int) int 11 | 12 | Mul(a, b int) int 13 | 14 | Div(a, b int) (int, error) 15 | } 16 | 17 | type BizService struct { 18 | } 19 | 20 | func NewBizService() Service { 21 | return &BizService{} 22 | } 23 | 24 | func (s *BizService) Add(a, b int) int { 25 | return a + b 26 | } 27 | 28 | func (s *BizService) Sub(a, b int) int { 29 | return a - b 30 | } 31 | 32 | func (s *BizService) Mul(a, b int) int { 33 | return a * b 34 | } 35 | 36 | func (s *BizService) Div(a, b int) (int, error) { 37 | if b == 0 { 38 | return 0, errors.New("the divisor is zero") 39 | } 40 | return a / b, nil 41 | } 42 | 43 | type ServiceMiddleware func(Service) Service -------------------------------------------------------------------------------- /biz_rate/transport.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "errors" 7 | "fmt" 8 | "github.com/go-kit/kit/endpoint" 9 | "github.com/go-kit/kit/log" 10 | kitHttp "github.com/go-kit/kit/transport/http" 11 | "github.com/gorilla/mux" 12 | "net/http" 13 | "strconv" 14 | ) 15 | 16 | var ( 17 | ErrBadRequest = errors.New("invalid request parameter") 18 | ) 19 | 20 | func MakeHttpHandler(ctx context.Context, endpoint endpoint.Endpoint, logger log.Logger) http.Handler { 21 | r := mux.NewRouter() 22 | 23 | options := []kitHttp.ServerOption{ 24 | kitHttp.ServerErrorLogger(logger), 25 | kitHttp.ServerErrorEncoder(kitHttp.DefaultErrorEncoder), 26 | } 27 | 28 | r.Methods("POST").Path("/biz/{type}/{a}/{b}").Handler(kitHttp.NewServer( 29 | endpoint, 30 | decodeBizRequest, 31 | encodeBizResponse, 32 | options..., 33 | )) 34 | 35 | return r 36 | } 37 | 38 | func decodeBizRequest(ctx context.Context, r *http.Request) (interface{}, error) { 39 | vars := mux.Vars(r) 40 | fmt.Printf("vars:%v\n", vars) 41 | 42 | reqType, ok := vars["type"] 43 | if !ok { 44 | return nil, ErrBadRequest 45 | } 46 | pa, ok := vars["a"] 47 | if !ok { 48 | return nil, ErrBadRequest 49 | } 50 | pb, ok := vars["b"] 51 | if !ok { 52 | return nil, ErrBadRequest 53 | } 54 | a, _ := strconv.Atoi(pa) 55 | b, _ := strconv.Atoi(pb) 56 | 57 | return &BizRequest{ 58 | ReqType: reqType, 59 | A: a, 60 | B: b, 61 | }, nil 62 | } 63 | 64 | func encodeBizResponse(ctx context.Context, w http.ResponseWriter, response interface{}) error { 65 | fmt.Printf("res:%#v\n", response) 66 | w.Header().Set("Content-Type", "application/json;charset=utf-8") 67 | return json.NewEncoder(w).Encode(response) 68 | } 69 | -------------------------------------------------------------------------------- /biz_rest/endpoint.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | "github.com/go-kit/kit/endpoint" 8 | "strings" 9 | ) 10 | 11 | type BizRequest struct { 12 | ReqType string `json:"type"` 13 | A int `json:"a"` 14 | B int `json:"b"` 15 | } 16 | 17 | type BizResponse struct { 18 | Result int `json:"result"` 19 | Error error `json:"error"` 20 | } 21 | 22 | type BizEndpoint endpoint.Endpoint 23 | 24 | var ( 25 | ErrInvalidType = errors.New("requestType has 4 types: Add, Sub, Mul and Div") 26 | ) 27 | 28 | func MakeBizEndpoint(svc Service) endpoint.Endpoint { 29 | return func(ctx context.Context, request interface{}) (response interface{}, err error) { 30 | req := request.(*BizRequest) 31 | fmt.Printf("req:#%v\n", req) 32 | var ( 33 | res, a, b int 34 | calError error 35 | ) 36 | 37 | a = req.A 38 | b = req.B 39 | bizRes := &BizResponse{} 40 | 41 | if strings.EqualFold(req.ReqType, "Add") { 42 | res = svc.Add(a, b) 43 | } else if strings.EqualFold(req.ReqType, "Sub") { 44 | res = svc.Sub(a, b) 45 | } else if strings.EqualFold(req.ReqType, "Mul") { 46 | res = svc.Mul(a, b) 47 | } else if strings.EqualFold(req.ReqType, "Div") { 48 | res, calError = svc.Div(a, b) 49 | } else { 50 | return bizRes, ErrInvalidType 51 | } 52 | bizRes.Result = res 53 | bizRes.Error = calError 54 | return bizRes, nil 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /biz_rest/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "github.com/go-kit/kit/log" 7 | "net/http" 8 | "os" 9 | "os/signal" 10 | "syscall" 11 | ) 12 | 13 | func main() { 14 | ctx := context.Background() 15 | errChan := make(chan error) 16 | 17 | var logger log.Logger 18 | { 19 | logger = log.NewLogfmtLogger(os.Stderr) 20 | logger = log.With(logger, "ts", log.DefaultTimestampUTC) 21 | logger = log.With(logger, "caller", log.DefaultCaller) 22 | } 23 | 24 | svc := NewBizService() 25 | 26 | endpoint := MakeBizEndpoint(svc) 27 | 28 | r := MakeHttpHandler(ctx, endpoint, logger) 29 | 30 | go func() { 31 | fmt.Println("http server start at port:8000") 32 | handler := r 33 | errChan <- http.ListenAndServe(":8000", handler) 34 | }() 35 | 36 | go func() { 37 | c := make(chan os.Signal, 1) 38 | signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) 39 | errChan <- fmt.Errorf("%s", <-c) 40 | }() 41 | 42 | fmt.Printf("http.ListenAndServe error:%v\n", <-errChan) 43 | } 44 | -------------------------------------------------------------------------------- /biz_rest/service.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | type Service interface { 8 | Add(a, b int) int 9 | 10 | Sub(a, b int) int 11 | 12 | Mul(a, b int) int 13 | 14 | Div(a, b int) (int, error) 15 | } 16 | 17 | type BizService struct { 18 | } 19 | 20 | func NewBizService() Service { 21 | return &BizService{} 22 | } 23 | 24 | func (s *BizService) Add(a, b int) int { 25 | return a + b 26 | } 27 | 28 | func (s *BizService) Sub(a, b int) int { 29 | return a - b 30 | } 31 | 32 | func (s *BizService) Mul(a, b int) int { 33 | return a * b 34 | } 35 | 36 | func (s *BizService) Div(a, b int) (int, error) { 37 | if b == 0 { 38 | return 0, errors.New("the divisor is zero") 39 | } 40 | return a / b, nil 41 | } 42 | -------------------------------------------------------------------------------- /biz_rest/transport.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "errors" 7 | "fmt" 8 | "github.com/go-kit/kit/endpoint" 9 | "github.com/go-kit/kit/log" 10 | kitHttp "github.com/go-kit/kit/transport/http" 11 | "github.com/gorilla/mux" 12 | "net/http" 13 | "strconv" 14 | ) 15 | 16 | var ( 17 | ErrBadRequest = errors.New("invalid request parameter") 18 | ) 19 | 20 | func MakeHttpHandler(ctx context.Context, endpoint endpoint.Endpoint, logger log.Logger) http.Handler { 21 | r := mux.NewRouter() 22 | 23 | options := []kitHttp.ServerOption{ 24 | kitHttp.ServerErrorLogger(logger), 25 | kitHttp.ServerErrorEncoder(kitHttp.DefaultErrorEncoder), 26 | } 27 | 28 | r.Methods("POST").Path("/biz/{type}/{a}/{b}").Handler(kitHttp.NewServer( 29 | endpoint, 30 | decodeBizRequest, 31 | encodeBizResponse, 32 | options..., 33 | )) 34 | 35 | return r 36 | } 37 | 38 | func decodeBizRequest(ctx context.Context, r *http.Request) (interface{}, error) { 39 | vars := mux.Vars(r) 40 | fmt.Printf("vars:%v\n", vars) 41 | 42 | reqType, ok := vars["type"] 43 | if !ok { 44 | return nil, ErrBadRequest 45 | } 46 | pa, ok := vars["a"] 47 | if !ok { 48 | return nil, ErrBadRequest 49 | } 50 | pb, ok := vars["b"] 51 | if !ok { 52 | return nil, ErrBadRequest 53 | } 54 | a, _ := strconv.Atoi(pa) 55 | b, _ := strconv.Atoi(pb) 56 | 57 | return &BizRequest{ 58 | ReqType: reqType, 59 | A: a, 60 | B: b, 61 | }, nil 62 | } 63 | 64 | func encodeBizResponse(ctx context.Context, w http.ResponseWriter, response interface{}) error { 65 | fmt.Printf("res:%#v\n", response) 66 | w.Header().Set("Content-Type", "application/json;charset=utf-8") 67 | return json.NewEncoder(w).Encode(response) 68 | } 69 | -------------------------------------------------------------------------------- /biz_trace/gateway/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "github.com/go-kit/kit/log" 7 | "github.com/hashicorp/consul/api" 8 | "github.com/openzipkin/zipkin-go" 9 | zipkinHttpsvr "github.com/openzipkin/zipkin-go/middleware/http" 10 | zipkinHttp "github.com/openzipkin/zipkin-go/reporter/http" 11 | "math/rand" 12 | "net/http" 13 | "net/http/httputil" 14 | "os" 15 | "os/signal" 16 | "strings" 17 | "syscall" 18 | ) 19 | 20 | var ( 21 | consulHost = flag.String("consul.host", "192.168.0.103", "consul server ip address") 22 | consulPort = flag.String("consul.port", "8500", "consul server port") 23 | zipkinUrl = flag.String("zipkin.url", "http://192.168.0.103:9411/api/v2/spans", "zipkin server url") 24 | ) 25 | 26 | func main() { 27 | flag.Parse() 28 | 29 | var logger log.Logger 30 | { 31 | logger = log.NewLogfmtLogger(os.Stderr) 32 | logger = log.With(logger, "ts", log.DefaultTimestampUTC) 33 | logger = log.With(logger, "caller", log.DefaultCaller) 34 | } 35 | 36 | var zipKinTracer *zipkin.Tracer 37 | { 38 | var ( 39 | err error 40 | hostPort = "localhost:8003" 41 | serviceName = "gateway-service" 42 | useNoopTracer = *zipkinUrl == "" 43 | reporter = zipkinHttp.NewReporter(*zipkinUrl) 44 | ) 45 | 46 | defer reporter.Close() 47 | 48 | zEP, _ := zipkin.NewEndpoint(serviceName, hostPort) 49 | zipKinTracer, err = zipkin.NewTracer( 50 | reporter, zipkin.WithLocalEndpoint(zEP), zipkin.WithNoopTracer(useNoopTracer), 51 | ) 52 | if err != nil { 53 | logger.Log("error", err) 54 | os.Exit(1) 55 | } 56 | if !useNoopTracer { 57 | logger.Log("tracer", "zipkin", "type", "native", "url", *zipkinUrl) 58 | } 59 | } 60 | 61 | consulCfg := api.DefaultConfig() 62 | consulCfg.Address = "http://" + *consulHost + ":" + *consulPort 63 | consulClient, err := api.NewClient(consulCfg) 64 | 65 | if err != nil { 66 | logger.Log("err", err) 67 | os.Exit(1) 68 | } 69 | 70 | proxy := NewReverseProxy(consulClient, zipKinTracer, logger) 71 | 72 | tags := map[string]string{ 73 | "component": "gateway_server", 74 | } 75 | 76 | handler := zipkinHttpsvr.NewServerMiddleware( 77 | zipKinTracer, 78 | zipkinHttpsvr.SpanName("gateway"), 79 | zipkinHttpsvr.TagResponseSize(true), 80 | zipkinHttpsvr.ServerTags(tags), 81 | )(proxy) 82 | 83 | errChan := make(chan error) 84 | go func() { 85 | c := make(chan os.Signal) 86 | signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) 87 | errChan <- fmt.Errorf("%s", <-c) 88 | }() 89 | 90 | go func() { 91 | logger.Log("transport", "http", "addr", "8003") 92 | errChan <- http.ListenAndServe(":8003", handler) 93 | }() 94 | 95 | logger.Log("exit", <-errChan) 96 | } 97 | 98 | func NewReverseProxy(client *api.Client, tracer *zipkin.Tracer, logger log.Logger) *httputil.ReverseProxy { 99 | director := func(req *http.Request) { 100 | reqPath := req.URL.Path 101 | if reqPath == "" { 102 | return 103 | } 104 | // /biz/add/1/2 105 | pathArray := strings.Split(reqPath, "/") 106 | serviceName := pathArray[1] 107 | logger.Log("serviceName:", serviceName) 108 | 109 | result, _, err := client.Catalog().Service(serviceName, "", nil) 110 | if err != nil { 111 | logger.Log("reverseProxy failed", "query service instance error", err.Error()) 112 | return 113 | } 114 | 115 | if len(result) == 0 { 116 | logger.Log("reverseProxy failed", "no such service instance", serviceName) 117 | return 118 | } 119 | 120 | destPath := strings.Join(pathArray[2:], "/") 121 | tgt := result[rand.Int()%len(result)] 122 | logger.Log("service id", tgt.ServiceID) 123 | 124 | req.URL.Scheme = "http" 125 | req.URL.Host = fmt.Sprintf("%s:%d", tgt.ServiceAddress, tgt.ServicePort) 126 | req.URL.Path = "/" + destPath 127 | } 128 | 129 | roundTrip, _ := zipkinHttpsvr.NewTransport(tracer, zipkinHttpsvr.TransportTrace(true)) 130 | 131 | return &httputil.ReverseProxy{ 132 | Director: director, 133 | Transport: roundTrip, 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /biz_trace/register/endpoint.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | "github.com/go-kit/kit/endpoint" 8 | "strings" 9 | ) 10 | 11 | type BizRequest struct { 12 | ReqType string `json:"type"` 13 | A int `json:"a"` 14 | B int `json:"b"` 15 | } 16 | 17 | type BizResponse struct { 18 | Result int `json:"result"` 19 | Error error `json:"error"` 20 | } 21 | 22 | type BizEndpoints struct { 23 | BizEndpoint endpoint.Endpoint 24 | HealthEndpoint endpoint.Endpoint 25 | } 26 | 27 | var ( 28 | ErrInvalidType = errors.New("requestType has 4 types: Add, Sub, Mul and Div") 29 | ) 30 | 31 | func MakeBizEndpoint(svc Service) endpoint.Endpoint { 32 | return func(ctx context.Context, request interface{}) (response interface{}, err error) { 33 | req := request.(*BizRequest) 34 | fmt.Printf("req:#%v\n", req) 35 | var ( 36 | res, a, b int 37 | calError error 38 | ) 39 | 40 | a = req.A 41 | b = req.B 42 | bizRes := &BizResponse{} 43 | 44 | if strings.EqualFold(req.ReqType, "Add") { 45 | res = svc.Add(a, b) 46 | } else if strings.EqualFold(req.ReqType, "Sub") { 47 | res = svc.Sub(a, b) 48 | } else if strings.EqualFold(req.ReqType, "Mul") { 49 | res = svc.Mul(a, b) 50 | } else if strings.EqualFold(req.ReqType, "Div") { 51 | res, calError = svc.Div(a, b) 52 | } else { 53 | return bizRes, ErrInvalidType 54 | } 55 | bizRes.Result = res 56 | bizRes.Error = calError 57 | return bizRes, nil 58 | } 59 | } 60 | 61 | type HealthRequest struct { 62 | } 63 | 64 | type HealthResponse struct { 65 | Status bool `json:"status"` 66 | } 67 | 68 | func MakeHealthEndpoint(srv Service) endpoint.Endpoint { 69 | return func(ctx context.Context, request interface{}) (response interface{}, err error) { 70 | status := srv.HealthCheck() 71 | bizRes := &HealthResponse{} 72 | bizRes.Status = status 73 | return bizRes, nil 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /biz_trace/register/instrument.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "github.com/go-kit/kit/endpoint" 7 | "github.com/go-kit/kit/metrics" 8 | "github.com/juju/ratelimit" 9 | "golang.org/x/time/rate" 10 | "time" 11 | ) 12 | 13 | var ( 14 | ErrLimitExceed = errors.New("rate limit exceed") 15 | ) 16 | 17 | func NewTokenBucketLimiterWithJuju(bkt *ratelimit.Bucket) endpoint.Middleware { 18 | return func(next endpoint.Endpoint) endpoint.Endpoint { 19 | return func(ctx context.Context, request interface{}) (response interface{}, err error) { 20 | if bkt.TakeAvailable(1) == 0 { 21 | return nil, ErrLimitExceed 22 | } 23 | return next(ctx, request) 24 | } 25 | } 26 | } 27 | 28 | func NewTokenBucketLimiterWithBuildIn(bkt *rate.Limiter) endpoint.Middleware { 29 | return func(next endpoint.Endpoint) endpoint.Endpoint { 30 | return func(ctx context.Context, request interface{}) (response interface{}, err error) { 31 | if !bkt.Allow() { 32 | return nil, ErrLimitExceed 33 | } 34 | return next(ctx, request) 35 | } 36 | } 37 | } 38 | 39 | type MetricMiddleware struct { 40 | Service 41 | RequestCount metrics.Counter 42 | RequestLatency metrics.Histogram 43 | } 44 | 45 | func NewMetrics(counter metrics.Counter, histogram metrics.Histogram) ServiceMiddleware { 46 | return func(next Service) Service { 47 | return MetricMiddleware{ 48 | Service: next, 49 | RequestCount: counter, 50 | RequestLatency: histogram, 51 | } 52 | } 53 | } 54 | 55 | func (mw MetricMiddleware) Add(a, b int) (ret int) { 56 | defer func(begin time.Time) { 57 | lvs := []string{"method", "Add"} 58 | mw.RequestCount.With(lvs...).Add(1) 59 | mw.RequestLatency.With(lvs...).Observe(time.Since(begin).Seconds()) 60 | }(time.Now()) 61 | 62 | ret = mw.Service.Add(a, b) 63 | return 64 | } 65 | 66 | func (mw MetricMiddleware) Sub(a, b int) (ret int) { 67 | defer func(begin time.Time) { 68 | lvs := []string{"method", "Sub"} 69 | mw.RequestCount.With(lvs...).Add(1) 70 | mw.RequestLatency.With(lvs...).Observe(time.Since(begin).Seconds()) 71 | }(time.Now()) 72 | 73 | ret = mw.Service.Sub(a, b) 74 | return 75 | } 76 | 77 | func (mw MetricMiddleware) Mul(a, b int) (ret int) { 78 | defer func(begin time.Time) { 79 | lvs := []string{"method", "Mul"} 80 | mw.RequestCount.With(lvs...).Add(1) 81 | mw.RequestLatency.With(lvs...).Observe(time.Since(begin).Seconds()) 82 | }(time.Now()) 83 | 84 | ret = mw.Service.Mul(a, b) 85 | return 86 | } 87 | 88 | func (mw MetricMiddleware) Div(a, b int) (ret int, err error) { 89 | defer func(begin time.Time) { 90 | lvs := []string{"method", "Div"} 91 | mw.RequestCount.With(lvs...).Add(1) 92 | mw.RequestLatency.With(lvs...).Observe(time.Since(begin).Seconds()) 93 | }(time.Now()) 94 | 95 | ret, err = mw.Service.Div(a, b) 96 | return 97 | } 98 | 99 | func (mw MetricMiddleware) HealthCheck() (ret bool) { 100 | defer func(begin time.Time) { 101 | lvs := []string{"method", "HealthCheck"} 102 | mw.RequestCount.With(lvs...).Add(1) 103 | mw.RequestLatency.With(lvs...).Observe(time.Since(begin).Seconds()) 104 | }(time.Now()) 105 | 106 | ret = mw.Service.HealthCheck() 107 | return 108 | } 109 | -------------------------------------------------------------------------------- /biz_trace/register/logging.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/go-kit/kit/log" 5 | "time" 6 | ) 7 | 8 | type LoggingMiddleware struct { 9 | Service 10 | logger log.Logger 11 | } 12 | 13 | func NewLoggingMiddleware(logger log.Logger) ServiceMiddleware { 14 | return func(next Service) Service { 15 | return LoggingMiddleware{next, logger} 16 | } 17 | } 18 | 19 | func (mw LoggingMiddleware) Add(a, b int) (ret int) { 20 | defer func(begin time.Time) { 21 | mw.logger.Log( 22 | "function", "Add", 23 | "a", a, 24 | "b", b, 25 | "result", ret, 26 | "cost", time.Since(begin), 27 | ) 28 | 29 | }(time.Now()) 30 | 31 | ret = mw.Service.Add(a, b) 32 | return 33 | } 34 | 35 | func (mw LoggingMiddleware) Sub(a, b int) (ret int) { 36 | defer func(begin time.Time) { 37 | mw.logger.Log( 38 | "function", "Sub", 39 | "a", a, 40 | "b", b, 41 | "result", ret, 42 | "cost", time.Since(begin), 43 | ) 44 | 45 | }(time.Now()) 46 | 47 | ret = mw.Service.Sub(a, b) 48 | return 49 | } 50 | 51 | func (mw LoggingMiddleware) Mul(a, b int) (ret int) { 52 | defer func(begin time.Time) { 53 | mw.logger.Log( 54 | "function", "Mul", 55 | "a", a, 56 | "b", b, 57 | "result", ret, 58 | "cost", time.Since(begin), 59 | ) 60 | 61 | }(time.Now()) 62 | 63 | ret = mw.Service.Mul(a, b) 64 | return 65 | } 66 | 67 | func (mw LoggingMiddleware) Div(a, b int) (ret int, err error) { 68 | defer func(begin time.Time) { 69 | mw.logger.Log( 70 | "function", "Div", 71 | "a", a, 72 | "b", b, 73 | "result", ret, 74 | "cost", time.Since(begin), 75 | ) 76 | 77 | }(time.Now()) 78 | 79 | ret, err = mw.Service.Div(a, b) 80 | return 81 | } 82 | 83 | func (mw LoggingMiddleware) HealthCheck() (ret bool) { 84 | defer func(begin time.Time) { 85 | mw.logger.Log( 86 | "function", "HealthCheck", 87 | "result", ret, 88 | "cost", time.Since(begin), 89 | ) 90 | }(time.Now()) 91 | 92 | ret = mw.Service.HealthCheck() 93 | return 94 | } 95 | -------------------------------------------------------------------------------- /biz_trace/register/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "flag" 6 | "fmt" 7 | "github.com/go-kit/kit/log" 8 | kitPrometheus "github.com/go-kit/kit/metrics/prometheus" 9 | kitZipkin "github.com/go-kit/kit/tracing/zipkin" 10 | "github.com/openzipkin/zipkin-go" 11 | zipkinHttp "github.com/openzipkin/zipkin-go/reporter/http" 12 | stdPrometheus "github.com/prometheus/client_golang/prometheus" 13 | "golang.org/x/time/rate" 14 | "net/http" 15 | "os" 16 | "os/signal" 17 | "syscall" 18 | "time" 19 | ) 20 | 21 | var ( 22 | consulHost = flag.String("consul.host", "192.168.0.103", "consul ip address") 23 | consulPort = flag.String("consul.port", "8500", "consul port") 24 | serviceHost = flag.String("service.host", "192.168.0.103", "service ip address") 25 | servicePort = flag.String("service.port", "8000", "service port") 26 | zipkinUrl = flag.String("zipkin.url", "http://192.168.0.103:9411/api/v2/spans", "zipkin server url") 27 | ) 28 | 29 | func main() { 30 | flag.Parse() 31 | 32 | ctx := context.Background() 33 | errChan := make(chan error) 34 | 35 | var logger log.Logger 36 | { 37 | logger = log.NewLogfmtLogger(os.Stderr) 38 | logger = log.With(logger, "ts", log.DefaultTimestampUTC) 39 | logger = log.With(logger, "caller", log.DefaultCaller) 40 | } 41 | 42 | fieldKeys := []string{"method"} 43 | requestCount := kitPrometheus.NewCounterFrom(stdPrometheus.CounterOpts{ 44 | Namespace: "vince_cfl", 45 | Subsystem: "biz_service", 46 | Name: "request_count", 47 | Help: "numbers of request received", 48 | }, fieldKeys) 49 | 50 | requestLatency := kitPrometheus.NewSummaryFrom(stdPrometheus.SummaryOpts{ 51 | Namespace: "vince_cfl", 52 | Subsystem: "biz_service", 53 | Name: "request_latency", 54 | Help: "total duration of request in microseconds", 55 | }, fieldKeys) 56 | 57 | var zipKinTracer *zipkin.Tracer 58 | { 59 | var ( 60 | err error 61 | hostPort = *serviceHost + ":" + *servicePort 62 | serviceName = "biz-service" 63 | useNoopTracer = *zipkinUrl == "" 64 | reporter = zipkinHttp.NewReporter(*zipkinUrl) 65 | ) 66 | 67 | defer reporter.Close() 68 | 69 | zEP, _ := zipkin.NewEndpoint(serviceName, hostPort) 70 | zipKinTracer, err = zipkin.NewTracer( 71 | reporter, zipkin.WithLocalEndpoint(zEP), zipkin.WithNoopTracer(useNoopTracer), 72 | ) 73 | if err != nil { 74 | logger.Log("error", err) 75 | os.Exit(1) 76 | } 77 | if !useNoopTracer { 78 | logger.Log("tracer", "zipkin", "type", "native", "url", *zipkinUrl) 79 | } 80 | } 81 | 82 | svc := NewBizService() 83 | 84 | svc = NewLoggingMiddleware(logger)(svc) 85 | 86 | svc = NewMetrics(requestCount, requestLatency)(svc) 87 | 88 | rateBucket := rate.NewLimiter(rate.Every(time.Second*1), 100) 89 | 90 | endpoint := MakeBizEndpoint(svc) 91 | 92 | //rateBucket := ratelimit.NewBucket(time.Second*1, 1) 93 | //endpoint = NewTokenBucketLimiterWithJuju(rateBucket)(endpoint) 94 | 95 | endpoint = NewTokenBucketLimiterWithBuildIn(rateBucket)(endpoint) 96 | endpoint = kitZipkin.TraceEndpoint(zipKinTracer, "biz-endpoint")(endpoint) 97 | 98 | healthEndpoint := MakeHealthEndpoint(svc) 99 | healthEndpoint = NewTokenBucketLimiterWithBuildIn(rateBucket)(healthEndpoint) 100 | healthEndpoint = kitZipkin.TraceEndpoint(zipKinTracer, "health-endpoint")(healthEndpoint) 101 | 102 | endpoints := BizEndpoints{ 103 | BizEndpoint: endpoint, 104 | HealthEndpoint: healthEndpoint, 105 | } 106 | 107 | r := MakeHttpHandler(ctx, endpoints, zipKinTracer, logger) 108 | 109 | registrar := Register(*consulHost, *consulPort, *serviceHost, *servicePort, logger) 110 | 111 | go func() { 112 | fmt.Println("http server start at port:" + *servicePort) 113 | registrar.Register() 114 | handler := r 115 | errChan <- http.ListenAndServe(":"+*servicePort, handler) 116 | }() 117 | 118 | go func() { 119 | c := make(chan os.Signal, 1) 120 | signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) 121 | errChan <- fmt.Errorf("%s", <-c) 122 | }() 123 | 124 | error := <-errChan 125 | registrar.Deregister() 126 | fmt.Printf("service stop:%v\n", error) 127 | } 128 | -------------------------------------------------------------------------------- /biz_trace/register/register.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/go-kit/kit/log" 5 | "github.com/go-kit/kit/sd" 6 | "github.com/go-kit/kit/sd/consul" 7 | "github.com/hashicorp/consul/api" 8 | "github.com/pborman/uuid" 9 | "os" 10 | "strconv" 11 | ) 12 | 13 | func Register(consulHost, consulPort, svcHost, svcPort string, logger log.Logger) (registrar sd.Registrar) { 14 | var client consul.Client 15 | { 16 | consulCfg := api.DefaultConfig() 17 | consulCfg.Address = consulHost + ":" + consulPort 18 | consultClient, err := api.NewClient(consulCfg) 19 | if err != nil { 20 | logger.Log("create consul error:", err) 21 | os.Exit(1) 22 | } 23 | 24 | client = consul.NewClient(consultClient) 25 | } 26 | 27 | check := api.AgentServiceCheck{ 28 | HTTP: "http://" + svcHost + ":" + svcPort + "/health", 29 | Interval: "10s", 30 | Timeout: "1s", 31 | Notes: "consul check service health status", 32 | } 33 | 34 | port, _ := strconv.Atoi(svcPort) 35 | 36 | reg := api.AgentServiceRegistration{ 37 | ID: "biz-" + uuid.New(), 38 | Name: "biz", 39 | Address: svcHost, 40 | Port: port, 41 | Tags: []string{"biz", "vc"}, 42 | Check: &check, 43 | } 44 | 45 | registrar = consul.NewRegistrar(client, ®, logger) 46 | return 47 | } 48 | -------------------------------------------------------------------------------- /biz_trace/register/service.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | type Service interface { 8 | Add(a, b int) int 9 | 10 | Sub(a, b int) int 11 | 12 | Mul(a, b int) int 13 | 14 | Div(a, b int) (int, error) 15 | 16 | HealthCheck() bool 17 | } 18 | 19 | type BizService struct { 20 | } 21 | 22 | func NewBizService() Service { 23 | return &BizService{} 24 | } 25 | 26 | func (s *BizService) Add(a, b int) int { 27 | return a + b 28 | } 29 | 30 | func (s *BizService) Sub(a, b int) int { 31 | return a - b 32 | } 33 | 34 | func (s *BizService) Mul(a, b int) int { 35 | return a * b 36 | } 37 | 38 | func (s *BizService) Div(a, b int) (int, error) { 39 | if b == 0 { 40 | return 0, errors.New("the divisor is zero") 41 | } 42 | return a / b, nil 43 | } 44 | 45 | func (s *BizService) HealthCheck() bool { 46 | return true 47 | } 48 | 49 | type ServiceMiddleware func(Service) Service 50 | -------------------------------------------------------------------------------- /biz_trace/register/transport.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "errors" 7 | "fmt" 8 | "github.com/go-kit/kit/log" 9 | "github.com/go-kit/kit/tracing/zipkin" 10 | kitHttp "github.com/go-kit/kit/transport/http" 11 | "github.com/gorilla/mux" 12 | goZipkin "github.com/openzipkin/zipkin-go" 13 | "github.com/prometheus/client_golang/prometheus/promhttp" 14 | "net/http" 15 | "strconv" 16 | ) 17 | 18 | var ( 19 | ErrBadRequest = errors.New("invalid request parameter") 20 | ) 21 | 22 | func MakeHttpHandler(ctx context.Context, endpoints BizEndpoints, tracer *goZipkin.Tracer, logger log.Logger) http.Handler { 23 | r := mux.NewRouter() 24 | 25 | zipkinServer := zipkin.HTTPServerTrace(tracer, zipkin.Name("http-transport")) 26 | 27 | options := []kitHttp.ServerOption{ 28 | kitHttp.ServerErrorLogger(logger), 29 | kitHttp.ServerErrorEncoder(kitHttp.DefaultErrorEncoder), 30 | zipkinServer, 31 | } 32 | 33 | r.Methods("POST").Path("/biz/{type}/{a}/{b}").Handler(kitHttp.NewServer( 34 | endpoints.BizEndpoint, 35 | decodeBizRequest, 36 | encodeBizResponse, 37 | options..., 38 | )) 39 | 40 | r.Path("/metrics").Handler(promhttp.Handler()) 41 | 42 | r.Methods("GET").Path("/health").Handler(kitHttp.NewServer( 43 | endpoints.HealthEndpoint, 44 | decodeHealthRequest, 45 | encodeBizResponse, 46 | options..., 47 | )) 48 | 49 | return r 50 | } 51 | 52 | func decodeBizRequest(ctx context.Context, r *http.Request) (interface{}, error) { 53 | vars := mux.Vars(r) 54 | fmt.Printf("vars:%v\n", vars) 55 | 56 | reqType, ok := vars["type"] 57 | if !ok { 58 | return nil, ErrBadRequest 59 | } 60 | pa, ok := vars["a"] 61 | if !ok { 62 | return nil, ErrBadRequest 63 | } 64 | pb, ok := vars["b"] 65 | if !ok { 66 | return nil, ErrBadRequest 67 | } 68 | a, _ := strconv.Atoi(pa) 69 | b, _ := strconv.Atoi(pb) 70 | 71 | return &BizRequest{ 72 | ReqType: reqType, 73 | A: a, 74 | B: b, 75 | }, nil 76 | } 77 | 78 | func encodeBizResponse(ctx context.Context, w http.ResponseWriter, response interface{}) error { 79 | fmt.Printf("res:%#v\n", response) 80 | w.Header().Set("Content-Type", "application/json;charset=utf-8") 81 | return json.NewEncoder(w).Encode(response) 82 | } 83 | 84 | func decodeHealthRequest(ctx context.Context, r *http.Request) (interface{}, error) { 85 | return &HealthRequest{}, nil 86 | } 87 | -------------------------------------------------------------------------------- /pictrue/01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bg-vc/go-kit-one/4ac22ee289a06e3fabcffcaf71f54631257e74e2/pictrue/01.png -------------------------------------------------------------------------------- /pictrue/02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bg-vc/go-kit-one/4ac22ee289a06e3fabcffcaf71f54631257e74e2/pictrue/02.png -------------------------------------------------------------------------------- /pictrue/03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bg-vc/go-kit-one/4ac22ee289a06e3fabcffcaf71f54631257e74e2/pictrue/03.png -------------------------------------------------------------------------------- /pictrue/04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bg-vc/go-kit-one/4ac22ee289a06e3fabcffcaf71f54631257e74e2/pictrue/04.png -------------------------------------------------------------------------------- /pictrue/05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bg-vc/go-kit-one/4ac22ee289a06e3fabcffcaf71f54631257e74e2/pictrue/05.png -------------------------------------------------------------------------------- /pictrue/06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bg-vc/go-kit-one/4ac22ee289a06e3fabcffcaf71f54631257e74e2/pictrue/06.png -------------------------------------------------------------------------------- /pictrue/07.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bg-vc/go-kit-one/4ac22ee289a06e3fabcffcaf71f54631257e74e2/pictrue/07.png --------------------------------------------------------------------------------