深圳幻海软件技术有限公司 欢迎您!

依葫芦画瓢理解一个小型Go框架

2023-02-28

最近在开发Go程序,同事(github.com/WiFeng/go-sky)参考go-kit框架封装了一个简易的轮子,包含了Api和Task任务,已经能满足大部分Web需求,依葫芦画瓢,自己理解了下,参考下图:1:cmd/service.go复制packagemainimport("github.c

最近在开发Go程序,同事(github.com/WiFeng/go-sky)参考go-kit框架封装了一个简易的轮子,包含了Api和Task任务,已经能满足大部分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)
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.

初始化service、endpoint,NewHandler注册路由作为web服务,再注册Task运行后台任务。

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),
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.

返回一个大的Endpoints,其中包含子的Endpoints,会将service.Service结构体传递给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)
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.

每一个子的Endpoint应该包含同一种类型的服务,最终调用对应的service服务方法。

不过ArticleInfoMGetRequest也可以在service\article.go(例子中注册在entity) 中定义。MakeArticleMGetEndpoint返回一个闭包,注册了一个路由。

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"`
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.

entity包含特定的工具方法。

5:pkg\service\service.go:

package service

type Service struct {
    Article ArticleService
}

func New() Service {
    return Service{
        Article: ArticleService{},
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.

service大结构体初始化,包括子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
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.

具体的service服务,包含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)  {
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.

dao方法,主要进行数据库等资源的操作。

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
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.

注册http路由,endpoint作为参数传递给handler。