go-scaffold
go-scaffold copied to clipboard
Golang development basic scaffold
- ä»ç»
- æ¶æå¾
- çå½å¨æ
- ç®å½ç»æ
- å¦ä½è¿è¡
- go build æ go run
- make
- docker-compose
- çéå¯
- è¿è¡åå½ä»¤æèæ¬
- ä¾èµæ³¨å ¥
- é ç½®
- æ¥å¿
- é误å¤ç
- ç»ä»¶
- Casbin
- Client
- Discovery æå¡åç°ä¸æ³¨å
- Ent
- orm
- Redis 客æ·ç«¯
- trace
- uid
- transport å±
- service å±
- å½ä»¤è¡åè½æ¨¡å
- cron 宿¶ä»»å¡åè½æ¨¡å
- å¦ä½é¨ç½²
- Dockerfile
- docker-compose
- kubernetes
ä»ç»
go-scaffold æ¯ä¸ä¸ªåºäº cobra å kratos æ¡æ¶çèææ¶ï¼è®¾è®¡ææ³æ¯åºäº wire å®ç°æ¨¡åååè½çç»ä»¶å
go-scaffold å¼ç®±å³ç¨ï¼ä½¿ç¨ç®åï¼å¯ä»¥å¿«éæå»ºèµ·ä¸ä¸ªå¾®æå¡è¿è¡ä¸å¡ä»£ç çå¼åï¼æ¯æåè½ï¼
- ä¾èµæ³¨å ¥
- cobra å½ä»¤è¡
- cron 宿¶ä»»å¡
apolloè¿ç¨é ç½®ä¸å¿åé ç½®çå¬- æ¥å¿åå²
- æå¡æ³¨åååç°
jaegeré¾è·¯è¿½è¸ªSwaggerææ¡£çædocker-composeåKubernetesé¨ç½²
æ¶æå¾

çå½å¨æ

ç®å½ç»æ
|-- bin # äºè¿å¶æä»¶ç®å½
|-- cmd # ç¼è¯å
¥å£
| `-- app
|-- deploy # ç¯å¢åé¨ç½²ç¸å
³ç®å½
| |-- docker-compose # docker-compose 容å¨ç¼æç®å½
| `-- kubernetes # k8s ç¼æé
ç½®ç®å½
|-- docs # ææ¡£ç®å½
|-- etc # é
ç½®æä»¶ç®å½
|-- internal
| `-- app
| |-- command # å½ä»¤è¡åè½æ¨¡å
| | |-- handler
| | `-- script # 临æ¶èæ¬
| |-- component # åè½ç»ä»¶ï¼å¦ï¼db, redis ç
| |-- config # é
置模å
| |-- cron # 宿¶ä»»å¡åè½æ¨¡å
| | `-- job
| |-- model # æ°æ®åºæ¨¡å
| |-- pkg # åè½ç±»åº
| |-- repository # æ°æ®å¤çå±
| |-- service # ä¸å¡é»è¾å±
| |-- test
| `-- transport
| |-- grpc
| | |-- api # proto æä»¶ç®å½
| | |-- handler # æ§å¶å±
| | `-- middleware # ä¸é´ä»¶
| `-- http
| |-- api # swagger ææ¡£
| |-- handler # æ§å¶å±
| |-- middleware # ä¸é´ä»¶
| `-- router # è·¯ç±
|-- logs # æ¥å¿ç®å½
|-- pkg # åè½ç±»åº
`-- proto # ç¬¬ä¸æ¹ proto æä»¶ç®å½
å¦ä½è¿è¡
é¦å
å° etc/config.yaml.example æ·è´ä¸º etc/config.yaml
go build æ go run
go buildæ¹å¼
$ go generate ./...
$ go build -o bin/app cmd/app/main.go cmd/app/wire_gen.go
$ ./bin/app
go runæ¹å¼
$ go generate ./...
$ go run cmd/app/main.go cmd/app/wire_gen.go
make
# ä¸è½½ä¾èµ
$ make download
$ make build
# æä¾æ®å¹³å°ç¼è¯
$ make linux-build
$ make windows-build
$ make mac-build
# è¿è¡
$ ./bin/app
docker-compose
docker-compose çå¯å¨æ¹å¼æä¸¤ç§ï¼ä¸ç§æ¯åºäº air éåï¼ä¸ç§æ¯åºäº Dockerfile æ¥æå»ºéå
注æï¼
- åºäº
airéåçæ¹å¼åªéç¨äºå¼åé¶æ®µï¼è¯·å¿ç¨äºç产ç¯å¢ > - å¨Windowsç³»ç»ç¯å¢ä¸ï¼çæ´æ°å¯è½ä¸ä¼çæï¼è¿æ¯å 为fsnotifyæ æ³æ¶å°wslæä»¶ç³»ç»çåæ´éç¥- åºäº
Dockerfileçæ¹å¼å¦æç¨äºå¼åé¶æ®µï¼ä¿®æ¹ç代ç å°ä¸ä¼æ´æ°ï¼é¤éå¨docker-composeå¯å¨æ¶æå®--buildåæ°ï¼ä½æ¯è¿å°ä¼å¯¼è´æ¯æ¬¡å¯å¨æ¶é½éæ°æå»ºéåï¼å¯è½éè¦çå¾ å¾é¿æ¶é´
# åºäº air
$ docker-compose -f deploy/docker-compose/docker-compose-dev.yaml up
# åºäº Dockerfile
$ docker-compose -f deploy/docker-compose/docker-compose.yaml up
çéå¯
çéå¯åè½åºäº air
$ air
è¿è¡åå½ä»¤æèæ¬
å½ä»¤è¡ç¨åºåè½åºäº cobra
$ ./bin/app [æ å¿] <åå½ä»¤> [æ å¿] [åæ°]
# 帮å©ä¿¡æ¯
$ ./bin/app -h
$ ./bin/app <åå½ä»¤> -h
ä¾èµæ³¨å ¥
ä¾èµéè¿èªå¨çæä»£ç çæ¹å¼å¨ç¼è¯æå®ææ³¨å ¥
ä¾èµç»æï¼

é ç½®
é»è®¤é
ç½®æä»¶è·¯å¾ä¸ºï¼etc/app/config.yaml
å¯ä»¥å¨è¿è¡ç¨åºæ¶éè¿ --config æ -f é项æå®å
¶å®é
ç½®æä»¶
é 置模å
é
ç½®æä»¶çå
容å¨ç¨åºå¯å¨æ¶ä¼è¢«å è½½å°é
置模åä¸ï¼ç¸å
³ç®å½ï¼internal/app/config
internal/app/config/declare.goï¼é ç½®çç»æä½å®ä¹internal/app/config/config.goï¼å£°æProvideråçå¬çé ç½®Key
å¦ä½è·åé 置模åï¼
- 注å
¥é
置模åç±»åï¼
*config.Config - 注å
¥
Appé 置模åç±»åï¼*config.App - ...
ä¾ï¼
package trace
import "go-scaffold/internal/app/config"
type Handler struct {
conf *config.Config
appConf *config.App
}
func NewHandler(
conf *config.Config,
appConf *config.App,
) *Handler {
return &Handler{
conf: conf,
appConf: appConf,
}
}
è¿ç¨é ç½®
å¨å¯å¨ç¨åºæ¶ï¼å¯éè¿ä»¥ä¸é项é ç½®è¿ç¨é ç½®ä¸å¿
--config.apollo.enable:apolloæ¯å¦å¯ç¨--config.apollo.endpoint: è¿æ¥å°å--config.apollo.appid:appID--config.apollo.cluster:cluster--config.apollo.namespace: å½å空é´--config.apollo.secret:secret
çå¬é ç½®åæ´
å¨ internal/app/config/config.go æä»¶ç watchKeys åé䏿³¨åéè¦çå¬çé
ç½®é®
注å宿åï¼å¦æé ç½®æä»¶å 容åçåæ´ï¼æ éé坿å¡ï¼æ´æ¹å 容ä¼èªå¨åæ¥å°é ç½®å®ä¾ä¸
ä¾ï¼
var watchKeys = []string{
"services.self",
"jwt.key",
}
æ¥å¿
æ¥å¿åºäº zapï¼æ¥å¿ç轮转åºäº file-rotatelogs
æ¥å¿å
容é»è®¤è¾åºå° logs ç®å½ä¸ï¼å¹¶ä¸æ ¹æ®æ¯å¤©çæ¥æè¿è¡åå²
å¯å¨ç¨åºå¯å¨æ¶ï¼éè¿ä»¥ä¸é项æ¹åæ¥å¿è¡ä¸ºï¼
--log.path: æ¥å¿è¾åºè·¯å¾--log.level: æ¥å¿ç级ï¼debugãinfoãwarnãerrorãpanicãfatalï¼--log.format: æ¥å¿è¾åºæ ¼å¼ï¼textãjsonï¼--log.caller-skip: æ¥å¿callerè·³è¿å±æ°
å¦ä½è·åæ¥å¿å®ä¾ï¼
- 注å
¥ç±»åï¼
log.Logger
ä¾ï¼
package greet
import "github.com/go-kratos/kratos/v2/log"
type Service struct {
logger *log.Helper
}
func NewService(logger log.Logger) *Service {
return &Service{
logger: log.NewHelper(logger),
}
}
é误å¤ç
èææ¶å®ä¹äºç»ä¸çéè¯¯æ ¼å¼
type Error struct {
// Code ç¶æç
Code ErrorCode
// Message é误信æ¯
Message string
// Metadata å
æ°æ®
Metadata map[string]string
}
å¿«æ·å½æ°ï¼
// ServerError æå¡å¨é误
func ServerError(options ...Option) *Error {
return New(ServerErrorCode, ServerErrorCode.String(), options...)
}
// ClientError 客æ·ç«¯é误
func ClientError(options ...Option) *Error {
return New(ClientErrorCode, ClientErrorCode.String(), options...)
}
// ValidateError åæ°æ ¡éªé误
func ValidateError(options ...Option) *Error {
return New(ValidateErrorCode, ValidateErrorCode.String(), options...)
}
// Unauthorized æªè®¤è¯
func Unauthorized(options ...Option) *Error {
return New(UnauthorizedCode, UnauthorizedCode.String(), options...)
}
// PermissionDenied æéæç»é误
func PermissionDenied(options ...Option) *Error {
return New(PermissionDeniedCode, PermissionDeniedCode.String(), options...)
}
// ResourceNotFound èµæºä¸åå¨
func ResourceNotFound(options ...Option) *Error {
return New(ResourceNotFoundCode, ResourceNotFoundCode.String(), options...)
}
// TooManyRequest 请æ±å¤ªè¿é¢ç¹
func TooManyRequest(options ...Option) *Error {
return New(TooManyRequestCode, TooManyRequestCode.String(), options...)
}
转æ¢ä¸º HTTP ç¶æç
Code 屿§å®ç°äº HTTP ç¶æç ç转æ¢
ä¾ï¼
func (s *Service) Hello(ctx context.Context, req HelloRequest) (*HelloResponse, error) {
// ...
// è¿å Error
return nil, errors.ServerError()
// ...
}
// ...
// è°ç¨ service æ¹æ³
ret, err := h.service.Hello(ctx.Request.Context(), *req)
if err != nil {
// response.Error æ¹æ³ä¼èªå¨å° Error 转æ¢ä¸ºå¯¹åºç HTTP ç¶æ
response.Error(ctx, err)
return
}
// ...
å° GRPC é误转æ¢ä¸º Error
Error å®ç°äº GRPCStatus() æ¥å£ï¼éè¿ FromGRPCError 彿°å¯å° GRPC é误转æ¢ä¸º Error
ä¾ï¼
// ...
client := greet.NewGreetClient(conn)
resp, err := client.Hello(reqCtx, &greet.HelloRequest{Name: "Example"})
if err != nil {
// å° GRPC é误转æ¢ä¸º Error
e := errors.FromGRPCError(err)
response.Error(ctx, fmt.Errorf("GRPC è°ç¨é误ï¼%s", e.Message))
return
}
// ...
ç»ä»¶
Casbin
åºäº casbin è¿è¡å°è£
ï¼ç°æ¯æ file å gorm 两ç§ç±»åç adapterï¼å¦æåæ¶é
ç½®ï¼file ç±»åçæ
å¦ä½è·å Enforcer å®ä¾ï¼
- 注å
¥ç±»åï¼
*casbin.Enforcer
ä¾ï¼
package permission
import "github.com/casbin/casbin/v2"
type Service struct {
enforcer *casbin.Enforcer
}
func NewService(enforcer *casbin.Enforcer) *Service {
return &Service{
enforcer: enforcer,
}
}
å¦ä½è¿è¡é ç½®ï¼
casbin:
model: # casbin 模å
path: "assets/casbin/rbac_model.conf"
adapter: # éé
å¨é
ç½®
file:
path: "assets/casbin/rbac_policy.csv"
gorm:
tableName: "casbin_rules" # æ°æ®è¡¨åç§°
å¦ä½èªå®ä¹ casbin policy çæ°æ®åºå卿¨¡åï¼
å¨ internal/app/config/config.go æä»¶ç Loaded 彿°ä¸å¢å 代ç
func Loaded(hLogger log.Logger, cfg config.Config, conf *Config) error {
// ...
if conf.Casbin != nil {
if conf.Casbin.Adapter != nil {
if conf.Casbin.Adapter.Gorm != nil {
conf.Casbin.Adapter.Gorm.SetMigration(func(db *gorm.DB) error {
return (&model.CasbinRule{}).Migrate(db)
})
}
}
}
// ...
}
Client
gRPC 客æ·ç«¯
åºäº kratos ç gRPC 客æ·ç«¯è¿è¡å°è£
ï¼æ ¹æ®ä¼ å
¥çå°åèªå¨å¤ææ¯èµ°ç´è¿è¿æ¯æå¡åç°
å¦ä½è·å客æ·ç«¯å®ä¾ï¼
- 注å
¥ç±»åï¼
*grpc.Client
ä¾ï¼
package trace
import "go-scaffold/internal/app/component/client/grpc"
type Handler struct {
grpcClient *grpc.Client
}
func NewHandler(
grpcClient *grpc.Client,
) *Handler {
return &Handler{
grpcClient: grpcClient,
}
}
å¦ä½è¿è¡é ç½®ï¼
services:
self: "127.0.0.1:9528"
# self: "discovery:///go-scaffold" # æå¡åç°å°å
Discovery æå¡åç°ä¸æ³¨å
åºäº kratos çæå¡æ³¨åä¸åç°è¿è¡å°è£
ï¼ç°æ¯æ etcd å consulï¼å¯æ ¹æ®é
ç½®è¿è¡åæ¢ï¼å¦æåæ¶é
ç½®ï¼etcd çæ
å¦ä½è·å Discovery å®ä¾ï¼
- 注å
¥ç±»åï¼
discovery.Discovery
ä¾ï¼
package transport
import "go-scaffold/internal/app/component/discovery"
type Transport struct {
// ...
}
func New(discovery discovery.Discovery) *Transport {
// ...
}
å¦ä½è¿è¡é ç½®ï¼
discovery:
etcd:
endpoints:
- "localhost:12379"
# consul:
# addr: "localhost:8500"
# schema: "http"
Ent
ent ç»ä»¶åºäº ent
å¦ä½è·å ent 客æ·ç«¯ï¼
- 注å
¥ç±»åï¼
*ent.Client
ä¾ï¼
package user
import "go-scaffold/internal/app/component/ent/ent"
type Repository struct {
ent *ent.Client
}
func NewRepository(ent *ent.Client) *Repository {
return &Repository{
ent: ent,
}
}
orm
orm ç»ä»¶åºäº gorm
å¦ä½è·å orm å®ä¾ï¼
- 注å
¥ç±»åï¼
*gorm.DB
ä¾ï¼
package user
import "gorm.io/gorm"
type Repository struct {
db *gorm.DB
}
func NewRepository(db *gorm.DB) *Repository {
return &Repository{
db: db,
}
}
å¦ä½é ç½®å¤æ°æ®åº
åèï¼https://gorm.io/docs/dbresolver.html
etc/config.yaml:
db:
driver: "mysql"
host: "127.0.0.1"
port: 13306
database: "go-scaffold"
username: "root"
password: "root"
options:
- "charset=utf8mb4"
- "parseTime=True"
- "loc=Local"
maxIdleConn: 5
maxOpenConn: 10
connMaxIdleTime: 120
connMaxLifeTime: 120
logLevel: "info"
# 夿°æ®åºé
ç½®
resolvers:
- type: "replica" # source æ replica
host: "127.0.0.1"
port: 13307
database: "go-scaffold"
username: "root"
password: "root"
options:
- "charset=utf8mb4"
- "parseTime=True"
- "loc=Local"
- type: "replica"
host: "127.0.0.1"
port: 13308
database: "go-scaffold"
username: "root"
password: "root"
options:
- "charset=utf8mb4"
- "parseTime=True"
- "loc=Local"
internal/app/config/config.go:
func Loaded(hLogger log.Logger, cfg config.Config, conf *Config) error {
// ...
// é
ç½®å¤æ°æ®åº
if conf.DB != nil {
if len(conf.DB.Resolvers) > 0 {
var (
sources = make([]gorm.Dialector, 0, len(conf.DB.Resolvers))
replicas = make([]gorm.Dialector, 0, len(conf.DB.Resolvers))
)
for _, resolver := range conf.DB.Resolvers {
dial, err := orm.BuildDialector(conf.DB.Driver, resolver.DSN)
if err != nil {
return err
}
switch resolver.Type {
case orm.Source:
sources = append(sources, dial)
case orm.Replica:
replicas = append(replicas, dial)
default:
return fmt.Errorf("unsupported resolver type %s", resolver.Type)
}
}
conf.DB.Plugins = func(db *gorm.DB) ([]gorm.Plugin, error) {
return []gorm.Plugin{
dbresolver.Register(dbresolver.Config{
Sources: sources,
Replicas: replicas,
Policy: dbresolver.RandomPolicy{},
}),
}, nil
}
}
}
// ...
}
Redis 客æ·ç«¯
Redis 客æ·ç«¯åºäº go-redis
å¦ä½è·å Redis 客æ·ç«¯ï¼
- 注å
¥ç±»åï¼
*redis.Client
ä¾ï¼
package user
import "github.com/go-redis/redis/v8"
type Repository struct {
rdb *redis.Client
}
func NewRepository(rdb *redis.Client) *Repository {
return &Repository{
rdb: rdb,
}
}
trace
èææ¶åºäº opentelemetry-go å®ç°äº OpenTelemetry è§èçé¾è·¯è¿½è¸ª
transport ä¸ HTTP å gRPC å已注åé¾è·¯è¿½è¸ªçä¸é´ä»¶
å¦ä½è·å tracerProvider å tracerï¼
- 注å
¥ç±»åï¼
*redis.Client
ä¾ï¼
package trace
import "go-scaffold/internal/app/component/trace"
type Handler struct {
trace *trace.Tracer
}
func NewHandler(
trace *trace.Tracer,
) *Handler {
return &Handler{
trace: trace,
}
}
uid
uid ç»ä»¶æ¯åºäº snowflake å®ç°ç uid çæå¨ï¼å¯ç¨äºæ°æ®åºä¸»é®
å¦ä½è·å uid å®ä¾ï¼
- 注å
¥ç±»åï¼
uid.Generator
ä¾ï¼
package user
import "go-scaffold/internal/app/component/uid"
type Repository struct {
id uid.Generator
}
func NewRepository(id uid.Generator) *Repository {
return &Repository{
id: id,
}
}
transport å±
HTTP
ååº
å¨ internal/app/transport/http/pkg/response å
ä¸ï¼å¯¹ JSON ååºè¿è¡äºå°è£
æåååºç¤ºä¾ï¼
func (h *Handler) Hello(ctx *gin.Context) {
// ...
response.Success(ctx, response.WithData(ret))
return
}
é误ååºç¤ºä¾ï¼
func (h *Handler) Hello(ctx *gin.Context) {
// ...
ret, err := h.service.Hello(ctx.Request.Context(), *req)
if err != nil {
response.Error(ctx, err)
return
}
// ...
}
swagger ææ¡£çæ
swagger ææ¡£ççæåºäº swagï¼ç»ä¸çæå° internal/app/transport/http/api ç®å½ä¸ï¼å¦åæ æ³è®¿é®
çæ swagger ææ¡£çæ¹å¼æä¸ç§
swagå½ä»¤æ¹å¼
$ swag fmt -d internal/app -g app.go
$ swag init -d internal/app -g app.go -o internal/app/transport/http/api
makeæ¹å¼
$ make doc
go generateæ¹å¼
$ go generate ./...
å¦ä½è®¿é® swagger ææ¡£
æµè§å¨æå¼ <host>/api/docs
service å±
service å±å¤çä¸å¡é»è¾ transport å±ä¸ç HTTP å gRPCï¼æå½ä»¤è¡é½åªæ¯å
¶ä¸ä¸ä¸ªå
¥å£
åæ°çæ ¡éªåºäº ozzo-validationï¼ç»ä¸æ¾å° service å±
ä¾ï¼
type CreateRequest struct {
Name string `json:"name"`
Age int8 `json:"age"`
Phone string `json:"phone"`
}
func (r CreateRequest) Validate() error {
return validation.ValidateStruct(&r,
validation.Field(&r.Name, validation.Required.Error("åç§°ä¸è½ä¸ºç©º")),
validation.Field(&r.Phone, validation.By(validator.IsMobilePhone)),
)
}
type CreateResponse struct {
Id uint64 `json:"id"`
Name string `json:"name"`
Age int8 `json:"age"`
Phone string `json:"phone"`
}
func (s *Service) Create(ctx context.Context, req CreateRequest) (*CreateResponse, error) {
// åæ°æ ¡éª
if err := req.Validate(); err != nil {
return nil, errorsx.ValidateError(errorsx.WithMessage(err.Error()))
}
// ...
}
å½ä»¤è¡åè½æ¨¡å
å½ä»¤è¡åè½æ¨¡ååºäº cobra
å½ä»¤è¡åè½è¢«æ½è±¡ä¸ºä¸¤é¨åï¼ä¸é¨å称为âä¸å¡å½ä»¤âï¼commandï¼ï¼ä¸é¨å称为âèæ¬âï¼scriptï¼
- âä¸å¡å½ä»¤â设计ç¨äºéè¿å½ä»¤è¡çæ¹å¼è°ç¨ä¸å¡é»è¾
- âèæ¬â设计ç¨äºæ§è¡å¼åè¿ç¨ä¸ç临æ¶èæ¬ä»»å¡ï¼ä¾å¦ï¼è¿è¡æ°æ®ä¿®å¤
- âä¸å¡å½ä»¤â被注å为åºç¨ç¨åºç
businessåå½ä»¤ï¼âèæ¬â被注å为åºç¨ç¨åºçscriptåå½ä»¤
å½ä»¤è¡ç®å½è§èï¼
- âä¸å¡å½ä»¤âåâèæ¬âçæ³¨åä½äº
internal/app/command/command.goæä»¶ä¸ - âä¸å¡å½ä»¤âé¨åï¼
- âä¸å¡å½ä»¤âå¨
internal/app/command/handlerç®å½ä¸è¿è¡å®ä¹ - åºæç
§ä¸åçè责对å
è¿è¡çºµåæåï¼ä¾å¦ï¼
postãuserãcommentä¸ä¸ªä¸å¡æ¨¡åï¼æ¯ä¸ä¸ªæ¨¡åé½ç¬ç«å¯¹å¤æä¾ç¸åºçåè½ - æ¯ä¸ªä¸å¡æ¨¡å齿¯ä¸ä¸ªåç¬çå
ï¼å¯¹åº
businesså½ä»¤çåå½ä»¤ï¼ä¾å¦ï¼./bin/app business post - ä¸å¡æ¨¡åä¸çæ¯ä¸ªæ¹æ³é½æ½ç¦»ä¸ºä¸ä¸ªåç¬çæä»¶ï¼å¯¹åºä¸å¡æ¨¡åå½ä»¤çåå½ä»¤ï¼ä¾å¦ï¼
./bin/app business post add
- âä¸å¡å½ä»¤âå¨
- âèæ¬âé¨åï¼
- âèæ¬âå¨
internal/app/command/scriptç®å½ä¸è¿è¡å®ä¹ - èæ¬æä»¶çå称为
S+10使¶é´æ³ï¼è¯´æèæ¬çå建æ¶é´ - æä»¶ä¸çç»æä½åç§°ä¸ºèæ¬æä»¶åï¼å¹¶ä¸å®ç°
Scriptæ¥å£ - ç»æä½ç注éåºè¯¥è¯´ææ¤èæ¬çç¨é
- âèæ¬âå¨
注æï¼
ä¸è¦éè¿ç³»ç»ç宿¶ä»»å¡æ¥é¢ç¹è°ç¨å½ä»¤è¡åè½çâä¸å¡å½ä»¤âæâèæ¬âï¼å ä¸ºæ¯æ¬¡æ§è¡é½ä¼åå§åæ°æ®åºè¿æ¥ãæ¥å¿çèµæºï¼è¿å¯è½ä¼é ææ§è½é®é¢
妿éè¦é¢ç¹è°ç¨æä¸ªä¸å¡é»è¾ï¼å¯ä»¥èèæ¯å¦åºè¯¥ä½¿ç¨
cronåè½æ¨¡å
cron 宿¶ä»»å¡åè½æ¨¡å
宿¶ä»»å¡åè½æ¨¡ååºäº cron
- å ¶å¯ä»¥æä¾æå°æ¶é´åä½ä¸ºç§ç宿¶ä»»å¡
- å¯æç¡®ç¥é项ç®ä¸æé£äºå®æ¶ä»»å¡
宿¶ä»»å¡è§èï¼
- ä»»å¡å¨
internal/app/cron/cron.goæä»¶ä¸è¿è¡æ³¨å - å¨
internal/app/cron/jobç®å½ä¸è¿è¡å®ä¹ - ä»»å¡ç»æä½çåç§°ä¸ºä»»å¡æä»¶åï¼å¹¶ä¸å®ç°
cron.Jobæ¥å£ - ç»æä½ç注éåºè¯¥è¯´ææ¤ä»»å¡çç¨é
å¦ä½é¨ç½²
Dockerfile
Dockerfile æä»¶ä½äºé¡¹ç®æ ¹ç®å½
docker-compose
docker-compose ç¼ææä»¶ä½äº deploy/docker-compose ç®å½ä¸
é¨ç½²åæ ¹æ®éè¦å° docker-compose.yaml.example æ docker-compose-dev.yaml.example æ·è´ä¸º docker-compose.yamlï¼ç¶åæ ¹æ® docker-compose è¿è¡
Kubernetes
Kubernetes ç¼ææä»¶ä½äº deploy/kubernetes ç®å½ä¸
Kubernetes çæ¹å¼åºäº helmï¼é¨ç½²åéè¦å° values.yaml.example æ·è´ä¸º values.yaml
ç¶åæ§è¡ï¼
$ kubectl apply -Rf deploy/kubernetes
# æ
$ helm install go-scaffold kubernetes/