go-kit 上手之example stringsvc1 函数即服务

使用包服务模型定义接口实现定义RPC输入输出参数endpoint封装从Request解码输入参数编码输出到ResponseWriter服务启动测试结果 增加loggo-kit stringsvc1动手笔记 go-kit官网 go-kit官方示例代码使用包package mainimport "context"import "errors"import "strings"im
摘要由CSDN通过智能技术生成

go-kit stringsvc1动手笔记
go-kit官网
go-kit官方示例代码

使用包

package main

import "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
}

接口定义好后就需要定义结构体实现这个接口,也就是真是的业务处理。

接口实现

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 {
    V   string `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"}

服务器打印

这里写图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值