go-kit|go-kit 上手之example stringsvc1 函数即服务
-
- 使用包
- 服务模型定义
- 接口实现
- 定义RPC输入输出参数
- endpoint封装
- 从Request解码输入参数编码输出到ResponseWriter
- 服务启动
- 测试结果
- 增加log
go-kit stringsvc1动手笔记
go-kit官网
go-kit官方示例代码
使用包
package mainimport "context"
import "errors"
import "strings"
import "github.com/go-kit/kit/endpoint"
import httptransport "github.com/go-kit/kit/transport/http"
import "encoding/json"
import "net/http"
import "github.com/go-kit/kit/log"
import "os"
服务模型定义 //业务逻辑 在go-kit中,我们把服务模型定义为一个接口,接口中的方法代表了服务提供的功能:如下
//该服务提供两个功能:字符串大写化,计算字符串中字符数
type StringService interface {
Uppercase(context.Context, string) (string, error)
Count(context.Context, string) int
}
【go-kit|go-kit 上手之example stringsvc1 函数即服务】接口定义好后就需要定义结构体实现这个接口,也就是真是的业务处理。
接口实现
var ErrEmpty = errors.New("empty string")type stringService struct {
}//真正处理Uppercase业务
func (stringService) Uppercase(_ context.Context, s string) (string, error) {
if s == "" {
return "", ErrEmpty
}return strings.ToUpper(s), nil
}//真正处理Count业务
func (stringService) Count(_ context.Context, s string) int {
return len(s)
}
定义RPC输入输出参数 //go kit中主要的消息传递方式为RPC,所以接口中的每一个方法都要用 remote procedure call 实现。
//对每一个方法,我们要定义对应的request和response方法,request用来获取入参,response用来传递输出参数。
定义Uppercase的输入参数的结构
type uppercaseRequest struct {
S string `json:"s"`
}
定义Uppercase的输出接口
type uppercaseResponse struct {
Vstring `json:"v"`
Err string `json:"err,omitempty"`
}
定义Count的输入参数结构
type countRequest struct {
S string `json:"s"`
}
定义Count的输入结构
type countResponse struct {
V int `json:"v"`
}
endpoint封装 go-kit中,如果使用
go-kit/kit/transport/http
,那么还需要把StringService封装为endpoint来供调用。抽象 uppercase的RPC调用
func makeUppercaseEndpoint(svc StringService) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(uppercaseRequest)
v, err := svc.Uppercase(ctx, req.S)
if err != nil {
return uppercaseResponse{v, err.Error()}, nil
}
return uppercaseResponse{v, ""}, nil
}
}
抽象 len的RPC调用
func makeCountEndpoint(svc stringService) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(countRequest)
v := svc.Count(ctx, req.S)
return countResponse{v}, nil
}
}
可以看到,上述封装其实就是把RPC调用封装成了更加通用的接口,输入参数和输出参数都为interface
从Request解码输入参数,编码输出到ResponseWriter
根据go-kit/kit/transport/http ServerHttp中的代码request, err := s.dec(ctx, r)//将请求转为对应的输入参数
if err != nil {
s.logger.Log("err", err)
s.errorEncoder(ctx, err, w)
return
}//执行对应功能
response, err := s.e(ctx, request)
if err != nil {
s.logger.Log("err", err)
s.errorEncoder(ctx, err, w)
return
}for _, f := range s.after {
ctx = f(ctx, w)
}//将输入参数放入到response
if err := s.enc(ctx, w, response);
err != nil {
s.logger.Log("err", err)
s.errorEncoder(ctx, err, w)
return
}
可以看出网络请求到达后服务器主要进行三步处理 :
- s.dec(ctx, r) 将request中提取输入参数
- s.e(ctx, request) 传入输入参数执行对应业务功能
- s.enc(ctx, w, response) 将业务处理结构返回到ResponseWriter,从而传递给客户端
第二步就是调用的是上面生成的endpoint,第一步需要我们传入解码器,用于将Request解码为输入参数,第三部需要我们传入编码器,输出到ResponseWriter。
Uppercase输入解码器
func decodeUpperCaseRequest(_ context.Context, r *http.Request) (interface{}, error) {
var request uppercaseRequest
if err := json.NewDecoder(r.Body).Decode(&request);
err != nil {
return nil, err
}
return request, nil
}
Count输入解码器
func decodeCountRequest(_ context.Context, r *http.Request) (interface{}, error) {
var request countRequest
if err := json.NewDecoder(r.Body).Decode(&request);
err != nil {
return nil, err
}
return request, nil
}
由于Uppercase和Count对输出的处理一样,所以可以用一个通用的编码器,将结果写入到ResponseWriter
func encodeResponse(_ context.Context, w http.ResponseWriter, respon interface{}) error {
return json.NewEncoder(w).Encode(respon)
}
服务启动
func main() {svc := stringService{}
uppercaseHandler := httptransport.NewServer(
makeUppercaseEndpoint(svc),
decodeUpperCaseRequest,
encodeResponse,
)countHandler := httptransport.NewServer(
makeCountEndpoint(svc),
decodeCountRequest,
encodeResponse,
)
http.Handle("/uppercase", uppercaseHandler)
http.Handle("/count", countHandler)
http.ListenAndServe(":8082", nil)
}
测试结果
Sean-MacBook-Air:goproject kes$ curl -XPOST -d'{"s":"Hello"}' localhost:8081/count
{"v":5}
Sean-MacBook-Air:goproject kes$ curl -XPOST -d'{"s":"Hello"}' localhost:8081/uppercase
{"v":"HELLO"}
增加log 如果一个系统中业务很多,怎么才能快速的添加日志到每个业务里呢?
go-kit中的Middlewre可以让开发者快速的增加新的需求到原业务中,并且支持功能的串联,这都是通过 endpoint.Middleware实现的。
go-kit中 endpoint.Middleware定义如下:
type Middleware func(Endpoint) Endpoint
我们可以定义一个日志Middleware,将一个Endpoint封装为加入了日志功能的另外一种Endpoint。
func loggingMiddleware(logger log.Logger) endpoint.Middleware {return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
logger.Log("msg", "calling endpoint")
defer logger.Log("msg", "callend endpoing")
return next(ctx, request)
}
}
}
然后利用loggingMiddleware对UppercaseEndpoin和CountEndpoint进行封装
func main() {
logger := log.NewLogfmtLogger(os.Stderr)
mid := loggingMiddleware(log.With(logger, "LOGMETHOD", "uppercase"))
svc := stringService{}
uppercaseHandler := httptransport.NewServer(
mid(makeUppercaseEndpoint(svc)),
decodeUpperCaseRequest,
encodeResponse,
)countHandler := httptransport.NewServer(
mid(makeCountEndpoint(svc)),
decodeCountRequest,
encodeResponse,
)
http.Handle("/uppercase", uppercaseHandler)
http.Handle("/count", countHandler)
http.ListenAndServe(":8082", nil)
}
请求
Sean-MacBook-Air:sringsrv1 kes$ curl -XPOST -d'{"s":"Hello"}' localhost:8082/count
{"v":5}
Sean-MacBook-Air:sringsrv1 kes$ curl -XPOST -d'{"s":"Hello"}' localhost:8082/uppercase
{"v":"HELLO"}
服务器打印
文章图片
推荐阅读
- PMSJ寻平面设计师之现代(Hyundai)
- 太平之莲
- 闲杂“细雨”
- 七年之痒之后
- 深入理解Go之generate
- 由浅入深理解AOP
- 期刊|期刊 | 国内核心期刊之(北大核心)
- 生活随笔|好天气下的意外之喜
- 感恩之旅第75天
- python学习之|python学习之 实现QQ自动发送消息