go-kit 上手之example stringsvc3 通过代理实现分布式处理
来源:互联网 发布:mac重装系统无法启动 编辑:程序博客网 时间:2024/06/05 21:56
- 使用的包
- 创建到特定地址代理服务器的client
- 定义使用了代理机制的新服务
- 根据用户输入的代理服务器地址生成对应的代理服务器中间件
- main
- 运行结果
代理中间件
stringsvc3没有完全按照官网中stringsvc3的写法,而是在stringsvc2的基础上增加了proxy.go
主要就是给uppercase增加了代理中间件,主要步骤分三步:
1)向特定地址代理服务器发送请求的client的编码和解码函数。
2)生成向特定地址代理服务器发送请求的client。
3)用client配合load balancer构建代理服务器中间件。
使用的包
package mainimport ( "bytes" "context" "encoding/json" "errors" "fmt" "net/http" "net/url" "strings" "time" "github.com/go-kit/kit/sd/lb" "golang.org/x/time/rate" "github.com/go-kit/kit/endpoint" "github.com/go-kit/kit/ratelimit" "github.com/sony/gobreaker" "io/ioutil" "github.com/go-kit/kit/circuitbreaker" "github.com/go-kit/kit/log" "github.com/go-kit/kit/sd" httptransport "github.com/go-kit/kit/transport/http")
定义所需类型
type ServiceMiddleware func(StringService) StringService
获取用户指定的代理服务器地址列表,本样例中,用户输入多个代理服务器用”,”分割
func split(s string) []string { a := strings.Split(s, ",") for i := range a { a[i] = strings.TrimSpace(a[i]) } return a}
根据httptransport.NewClient的参数需要,需要一个将client的request编码的函数(如下encodeRequest),以及将代理服务器返回的数据转为response的函数(如下:decodeUppercaseResponse)
往代理服务发送请求时,将request转为io.ReaderCloser
func encodeRequest(_ context.Context, r *http.Request, request interface{}) error { var buf bytes.Buffer if err := json.NewEncoder(&buf).Encode(request); err != nil { return err } r.Body = ioutil.NopCloser(&buf) return nil}func decodeUppercaseResponse(_ context.Context, r *http.Response) (interface{}, error) { var response uppercaseResponse if err := json.NewDecoder(r.Body).Decode(&response); err != nil { return nil, err } return response, nil}
创建到特定地址代理服务器的client
func makeUppercaseProxy(ctc context.Context, instance string) endpoint.Endpoint { if !strings.HasPrefix(instance, "http") { instance = "http://" + instance } u, err := url.Parse(instance) if err != nil { panic(err) } if u.Path == "" { u.Path = "/uppercase" } return httptransport.NewClient( "GET", u, encodeRequest, decodeUppercaseResponse, ).Endpoint()}
定义使用了代理机制的新服务
type proxymw struct { next StringService //用于处理Count请求 uppercase endpoint.Endpoint //load balance处理uppercase}//直接用当前服务处理Count请求func (mw proxymw) Count(ctx context.Context, s string) int { return mw.next.Count(ctx, s)}//将uppercase请求发往各个代理服务器中(后面会讲到通过Load balancer实现)func (mw proxymw) Uppercase(ctx context.Context, s string) (string, error) { response, err := mw.uppercase(ctx, uppercaseRequest{S: s}) if err != nil { return "", err } resp := response.(uppercaseResponse) if resp.Err != "" { return resp.V, errors.New(resp.Err) } return resp.V, nil}
根据用户输入的代理服务器地址生成对应的代理服务器中间件
//根据用户输入的多个地址,创建到多个服务器的代理func proxyMiddleware(ctx context.Context, instances string, logger log.Logger) ServiceMiddleware { if instances == "" { logger.Log("proxy_to", "none") return func(next StringService) StringService { return next } } var ( qps = 100 //请求频率超过多少会返回错误 maxAttempts = 3 //请求在放弃前重试多少次,用于 load balancer maxTime = 250 * time.Millisecond // 请求在放弃前的超时时间,用于 load balancer ) var ( instanceList = split(instances) endpointer sd.FixedEndpointer ) logger.Log("proxy_to", fmt.Sprint(instanceList)) for _, instance := range instanceList { var e endpoint.Endpoint e = makeUppercaseProxy(ctx, instance) //创建client e = circuitbreaker.Gobreaker(gobreaker.NewCircuitBreaker(gobreaker.Settings{}))(e) //添加breader e = ratelimit.NewErroringLimiter(rate.NewLimiter(rate.Every(time.Second), qps))(e) //添加limiter endpointer = append(endpointer, e) } balancer := lb.NewRoundRobin(endpointer) //添加load balancer //Retry封装一个service load balancer,返回面向特定service method的load balancer。到这个endpoint的请求会自动通过 //load balancer进行分配到各个代理服务器中。返回失败的请求会自动retry直到成功或者到达最大失败次数或者超时。 retry := lb.Retry(maxAttempts, maxTime, balancer)//添加retry机制 return func(next StringService) StringService { return proxymw{next, retry} }}
main
func main() { var ( listen = flag.String("listen", ":8080", "http lisetern address") proxy = flag.String("proxy", "", "optional ") ) flag.Parse() logger := log.NewLogfmtLogger(os.Stderr) logger = log.With(logger, "listern", *listen, "caller", log.DefaultCaller) //注意这里的 filedkeys要和 methodField 中的一致,不然会报错 //fieldKeys := []string{"metod", "error"} //2017/10/19 18:09:28 http: panic serving [::1]:55246: label name "metod" missing in label map fieldKeys := []string{"method", "error"} requestCount := kitprometheus.NewCounterFrom(stdprometheus.CounterOpts{ Namespace: "my_gropu", Subsystem: "string_service", Name: "request_count", Help: "Number of requests received.", }, fieldKeys) requestLatency := kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{ Namespace: "my_gropu", Subsystem: "string_service", Name: "request_latence_microseconds", Help: "Number of requests in misroseconds.", }, fieldKeys) countResult := kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{ Namespace: "my_gropu", Subsystem: "string_service", Name: "count_result", Help: "The result of each count method.", }, []string{}) //svc := stringService{} //cannot use logMiddleware literal (type logMiddleware) as type stringService in assignment var svc StringService svc = stringService{} svc = proxyMiddleware(context.Background(), *proxy, logger)(svc) svc = logMiddleware{logger, svc} svc = instrumentingMiddleware{requestCount, requestLatency, countResult, svc} uppercaseHandler := httptransport.NewServer( makeUppercaseEndpoint(svc), decodeUpperCaseRequest, encodeResponse, ) countHandler := httptransport.NewServer( makeCountEndpoint(svc), decodeCountRequest, encodeResponse, ) http.Handle("/uppercase", uppercaseHandler) http.Handle("/count", countHandler) http.Handle("/metrics", promhttp.Handler()) logger.Log("msg", "HTTP", "addr", *listen) logger.Log("err", http.ListenAndServe(*listen, nil))}
相对于stringsvc2.md,就增加了
svc = proxyMiddleware(context.Background(), *proxy, logger)(svc)
可以看到,利用gokit的middlerware方式书写代码,增加功能非常简单。
运行结果
Sean-MacBook-Air:stringsrv3 kes$ ./main -listen=:8001listern=:8001 caller=proxy.go:109 proxy_to=nonelistern=:8001 caller=main.go:329 msg=HTTP addr=:8001listern=:8001 caller=main.go:186 logmethod=uppercase input=foo output=FOO err=null took=1.883µsSean-MacBook-Air:stringsrv3 kes$ ./main -listen=:8082listern=:8082 caller=proxy.go:109 proxy_to=nonelistern=:8082 caller=main.go:329 msg=HTTP addr=:8082listern=:8082 caller=main.go:186 logmethod=uppercase input=bar output=BAR err=null took=1.993µsSean-MacBook-Air:stringsrv3 kes$ ./main -listen=:8080 -proxy=localhost:8001,localhost:8082listern=:8080 caller=proxy.go:123 proxy_to="[localhost:8001 localhost:8082]"listern=:8080 caller=main.go:329 msg=HTTP addr=:8080listern=:8080 caller=main.go:186 logmethod=uppercase input=foo output=FOO err=null took=4.496073mslistern=:8080 caller=main.go:186 logmethod=uppercase input=bar output=BAR err=null took=1.983719msSean-MacBook-Air:goproject kes$ for s in foo bar ;do curl -d"{\"s\":\"$s\"}" localhost:8080/uppercase;done{"v":"FOO"}{"v":"BAR"}
阅读全文
0 0
- go-kit 上手之example stringsvc3 通过代理实现分布式处理
- go-kit 上手之example stringsvc2 添加日志和监控
- go-kit 上手之example stringsvc1 函数即服务
- go example之旅(上)
- go example之旅(中)
- go example之旅(下)
- go by example 之arrays.go
- go by example之channel-synchronization.go
- [Go]网络代理实现
- iphone开发之通过代理模式实现自定义控件——代码示例图片库的处理
- Scene Kit上手指南
- go通过代理访问百度地图api实现GPS角坐标转换
- [转]go-kit 入门
- 通过代理实现AOP
- 基于Go实现的分布式MQ
- 基于Go实现的分布式MQ
- Go CopyFile 异常处理 实现
- Gearman 实现分布式处理
- 一个java项目是如何自动加载log4j.properties
- 多表查询的几种方法
- MVC模式(三层架构模式)-转载
- 技术路上,多个同伴...
- Region拆分策略
- go-kit 上手之example stringsvc3 通过代理实现分布式处理
- 梦里不觉秋已深
- IntelliJ IDEA Spring Boot(9)打成war包
- 宋宝华:LEP(Linux易用剖析器) 是什么,为什么以及怎么办(2)
- Python机器学习实战教程
- pb中函数大全
- 从单亲家庭内向小男生到哈佛耶鲁全奖,百万年薪的“男神学长”活出了一部励志偶像剧!
- 搜索智能提示suggestion,附近地点搜索
- 1-bit and 2-bit Characters问题及解法