查看原文。
在本文中,将使用Go编程语言和go-kit作为标准微服务库创建一个简单的微服务。该服务将通过REST端点公开。
在当今世界,微服务体系结构已得到普及。 我不会特别解释什么是微服务架构,因为互联网上已经讨论了很多。 但是,我将提供两个有关微服务的良好网站。
首先是我最喜欢的martinfowler.com,可以在这里看到他的惊人解释。
另一个来自microservices.io,里面有很多关于模式和示例的好文章。
Go是一种开放源代码的编程语言,可轻松构建简单,可靠和高效的软件。该语言由Google设计,旨在解决Google的问题。因此,人们可以希望这种语言能够大规模运行,适用于大型程序和依赖项。
Go-kit确实有助于简化构建微服务架构的过程。 这是因为它具有许多功能,例如服务连接性,指标和日志记录。 因此,我要特别感谢Peter Bourgon(@(https://peter.bourgon.org/))和所有提供此出色库的贡献者。
我在本文中的重点是将REST端点公开为服务。服务本身将使用HTTP POST方法公开端点。然后它将以JSON格式返回lorem ipsum消息。
端点URL格式为/lorem/{type}/{min}/{max}
,其中包含以下说明:
在逐步开始之前,此示例需要几个库。那些是:
go get github.com/go-kit/kit go get github.com/drhodes/golorem go get github.com/gorilla/mux
无论使用哪种最酷的工具,都需要在第一时间创建业务逻辑。如用例所述,我们的业务逻辑是根据单词,句子或段落创建lorem ipsum文本。因此,让我们在工作区下创建lorem文件夹。就我而言,我的文件夹是$GOPATH/github.com/ru-rocker/gokit-playground/lorem
。然后在该文件夹下创建文件service.go
并添加以下代码:
// Define service interface type Service interface { Word(min, max int) string // generate a word with at least min letters and at most max letters. Sentence(min, max int) string // generate a sentence with at least min words and at most max words. Paragraph(min, max int) string // generate a paragraph with at least min sentences and at most max sentences. } // Implement service with empty struct type LoremService struct {}
现在我们已经有了接口,但是,没有方法实现的接口将毫无意义。为service.go,添加以下实现:
// Implement service functions func (LoremService) Word(min, max int) string { return golorem.Word(min, max) } func (LoremService) Sentence(min, max int) string { return golorem.Sentence(min, max) } func (LoremService) Paragraph(min, max int) string { return golorem.Paragraph(min, max) }
注意,对每个方法实现都使用了golorem函数。
由于此服务是HTTP的一部分,因此下一步是对请求和响应进行建模。如果回头看一下用例,将发现实现需要三个属性,分别是:type、min、max。
对于响应本身,只需要两个字段。
因此,让我们创建另一个文件,并为其命名为endpoints.go
,并添加以下代码:
//request type LoremRequest struct { RequestType string Min int Max int } //response type LoremResponse struct { Message string `json:"message"` Err error `json:"err,omitempty"` //omitempty 表示,如果值为nil,则不会显示此字段 }
端点是go-kit中的特殊功能,可以将它包装到http.Handler
中。为了使我们的service变成endpoint.Endpoint
函数,我们将要制作一个函数来处理LoremRequest,在函数内部做一些逻辑,然后返回LoremResponse。对于endpoints.go,添加如下代码:
var ( ErrRequestTypeNotFound = errors.New("Request type only valid for word, sentence and paragraph") ) // endpoints wrapper type Endpoints struct { LoremEndpoint endpoint.Endpoint } // creating Lorem Ipsum Endpoint func MakeLoremEndpoint(svc Service) endpoint.Endpoint { return func(ctx context.Context, request interface{}) (interface{}, error) { req := request.(LoremRequest) var ( txt string min, max int ) min = req.Min max = req.Max if strings.EqualFold(req.RequestType, "Word") { txt = svc.Word(min, max) } else if strings.EqualFold(req.RequestType, "Sentence"){ txt = svc.Sentence(min, max) } else if strings.EqualFold(req.RequestType, "Paragraph") { txt = svc.Paragraph(min, max) } else { return nil, ErrRequestTypeNotFound } return LoremResponse{Message: txt}, nil } }
在处理http
请求和响应之前,首先需要创建从struct
到json
或相反的编码器和解码器。为此,创建一个新文件,命名为transport.go
并添加以下代码:
// decode url path variables into request func decodeLoremRequest(_ context.Context, r *http.Request) (interface{}, error) { vars := mux.Vars(r) requestType, ok := vars["type"] if !ok { return nil, ErrBadRouting } vmin, ok := vars["min"] if !ok { return nil, ErrBadRouting } vmax, ok := vars["max"] if !ok { return nil, ErrBadRouting } min, _ := strconv.Atoi(vmin) max, _ := strconv.Atoi(vmax) return LoremRequest{ RequestType: requestType, Min: min, Max: max, }, nil } // errorer由可能包含错误的所有具体响应类型实现 // 它使我们能够更改HTTP响应代码,而无需触发端点(传输级)错误 type errorer interface { error() error } // encodeResponse是对客户端的所有响应类型进行编码的常用方法 func encodeResponse(ctx context.Context, w http.ResponseWriter, response interface{}) error { if e, ok := response.(errorer); ok && e.error() != nil { // 不是Go kit传输错误,而是业务逻辑错误 // 提供这些作为HTTP错误 encodeError(ctx, e.error(), w) return nil } w.Header().Set("Content-Type", "application/json; charset=utf-8") return json.NewEncoder(w).Encode(response) } // encode error func encodeError(_ context.Context, err error, w http.ResponseWriter) { if err == nil { panic("encodeError with nil error") } w.Header().Set("Content-Type", "application/json; charset=utf-8") w.WriteHeader(http.StatusInternalServerError) json.NewEncoder(w).Encode(map[string]interface{}{ "error": err.Error(), }) }
一旦声明了编码器和解码器功能,就可以创建http
处理程序了。在transport.go
中添加以下代码:
var ( // 如果缺少预期的路径变量,则返回ErrBadRouting ErrBadRouting = errors.New("inconsistent mapping between route and handler (programmer error)") ) // Make Http Handler func MakeHttpHandler(ctx context.Context, endpoint Endpoints, logger log.Logger) http.Handler { r := mux.NewRouter() options := []httptransport.ServerOption{ httptransport.ServerErrorLogger(logger), httptransport.ServerErrorEncoder(encodeError), } //POST /lorem/{type}/{min}/{max} // 注意:请看这行。这是描述URL路径和HTTP请求方法的方法 r.Methods("POST").Path("/lorem/{type}/{min}/{max}").Handler(httptransport.NewServer( ctx, endpoint.LoremEndpoint, decodeLoremRequest, encodeResponse, options..., )) return r }
到目前为止,已经为服务提供了服务/业务层,端点和传输。设置完成后,就该创建main函数了。main函数基本上是构造端点并使其可用于HTTP传输。
因此,在lorem文件夹下,创建另一个文件夹,名为lorem.d
(点d表示守护程序)。可以随意命名,然后创建文件main.go
,并添加以下代码:
func main() { ctx := context.Background() errChan := make(chan error) var svc lorem.Service svc = lorem.LoremService{} endpoint := lorem.Endpoints{ LoremEndpoint: lorem.MakeLoremEndpoint(svc), } // Logging domain. var logger log.Logger { logger = log.NewLogfmtLogger(os.Stderr) logger = log.NewContext(logger).With("ts", log.DefaultTimestampUTC) logger = log.NewContext(logger).With("caller", log.DefaultCaller) } r := lorem.MakeHttpHandler(ctx, endpoint, logger) // HTTP transport go func() { fmt.Println("Starting server at port 8080") handler := r errChan <- http.ListenAndServe(":8080", handler) }() go func() { c := make(chan os.Signal, 1) signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) errChan <- fmt.Errorf("%s", <-c) }() fmt.Println(<- errChan) }
现在该运行示例了。打开shell输入如下命令:
cd $GOPATH go run src/github.com/ru-rocker/gokit-playground/lorem/lorem.d/main.go
使用curl或postman测试端点。
每当您对本文感兴趣并愿意了解更多信息时,都可以在我的github上进行检查。