依葫蘆畫瓢理解一個(gè)小型Go框架
最近在開發(fā)Go程序,同事(github.com/WiFeng/go-sky)參考go-kit框架封裝了一個(gè)簡易的輪子,包含了Api和Task任務(wù),已經(jīng)能滿足大部分Web需求,依葫蘆畫瓢,自己理解了下,參考下圖:
1:cmd/service.go
package main
import (
"github.com/WiFeng/go-sky"
"pkg/config"
"pkg/endpoint"
"pkg/service"
"pkg/task"
"pkg/transport/http"
)
func main() {
var (
service = service.New()
endpoints = endpoint.New(service)
httpHandler = http.NewHandler(endpoints)
)
sky.LoadAppConfig(&config.GlobalAppConfig)
sky.RegisterTask(task.Start, nil, true)
sky.Run(httpHandler)
}
初始化service、endpoint,NewHandler注冊路由作為web服務(wù),再注冊Task運(yùn)行后臺(tái)任務(wù)。
2:pkg\endpoint\endpoint.go:
package endpoint
import "pkg/service"
type Endpoints struct {
Article ArticleEndpoints
}
func New(s service.Service) Endpoints {
return Endpoints{
Article: NewArticleEndpoints(s),
}
}
返回一個(gè)大的Endpoints,其中包含子的Endpoints,會(huì)將service.Service結(jié)構(gòu)體傳遞給Endpoints。
3:pkg\endpoint\article.go:
package endpoint
import (
"context"
kitendpoint "github.com/go-kit/kit/endpoint"
. "pkg/entity"
"pkg/service"
)
type ArticleEndpoints struct {
MGet kitendpoint.Endpoint
}
func NewArticleEndpoints(s service.Service) ArticleEndpoints {
return ArticleEndpoints{
MGet: MakeArticleMGetEndpoint(s),
}
}
func MakeArticleMGetEndpoint(s service.Service) kitendpoint.Endpoint {
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
req := request.(ArticleInfoMGetRequest)
return s.Article.MGet(ctx, req)
}
}
每一個(gè)子的Endpoint應(yīng)該包含同一種類型的服務(wù),最終調(diào)用對應(yīng)的service服務(wù)方法。
不過ArticleInfoMGetRequest也可以在service\article.go(例子中注冊在entity) 中定義。MakeArticleMGetEndpoint返回一個(gè)閉包,注冊了一個(gè)路由。
4:pkg\entity\article.go
package entity
type ArticleInfo struct {
ArticleId int64 `json:"aid"`
Uid int64 `json:"uid"`
}
type ArticleInfoMGetRequest struct {
BaseRequest
ArticleIds []int64 `json:"aids"`
ForceNoCache bool `json:"force_no_cache"`
}
type ArticleInfoMGetRespData struct {
Infos []ArticleInfo `json:"infos"`
}
type ArticleInfoMGetResponse struct {
BaseResponse
Data ArticleInfoMGetRespData `json:"data"`
}
entity包含特定的工具方法。
5:pkg\service\service.go:
package service
type Service struct {
Article ArticleService
}
func New() Service {
return Service{
Article: ArticleService{},
}
}
service大結(jié)構(gòu)體初始化,包括子service初始化。
6:pkg\service\article.go:
package service
import (
"context"
"pkg/dao"
. "github.com/xiwujie/article/pkg/entity"
)
type ArticleSyncJobRequest struct {
BaseRequest
Limit int `json:"limit"`
JobName string `json:"job_name"`
}
type ArticleSyncJobResponse struct {
BaseResponse
}
type ArticleService struct {
}
func (s *ArticleService) MGet(ctx context.Context, req ArticleInfoMGetRequest) (interface{}, error) {
var resp ArticleInfoMGetResponse
if req.ArticleIds == nil || len(req.ArticleIds) < 1 {
return resp, nil
}
sdao = dao.NewSearchActivityTable(ctx)
sdao.FetchById()
return resp, nil
}
具體的service服務(wù),包含req,response的定義,也可以定義到 entry 目錄下。
7:pkg/dao/article.go
package dao
import (
"context"
"database/sql"
"fmt"
skydb "github.com/WiFeng/go-sky/database"
)
const (
searchActivityTableName = ""
)
type SearchActivityTable struct {
db *sql.DB
}
func NewSearchActivityTable(ctx context.Context) (*SearchActivityTable, error) {
}
func (t *SearchActivityTable) FetchById(ctx context.Context, id int) {
}
dao方法,主要進(jìn)行數(shù)據(jù)庫等資源的操作。
8:pkg\transport\http\handler.go
func NewHandler(endpoints endpoint.Endpoints) http.Handler {
r := skyhttp.NewRouter()
genericOptions := []kithttp.ServerOption{
kithttp.ServerErrorEncoder(genericErrorEncoder),
}
r.Methods(http.MethodPost).Path(ArticleInfoMgetURI).Handler(skyhttp.NewServer(
endpoints.Article.MGet,
decodeHTTPArticleInfoMgetRequest,
encodeHTTPGenericResponse,
genericOptions...,
))
return r
}
注冊http路由,endpoint作為參數(shù)傳遞給handler。