Commit fcbe6006 authored by kzkzzzz's avatar kzkzzzz

feat: init

parent e5e4a7e8
bin
tmp
\ No newline at end of file
FROM golang:1.16 AS builder
COPY ../../demo /src
WORKDIR /src
RUN GOPROXY=https://goproxy.cn make build
FROM debian:stable-slim
RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates \
netbase \
&& rm -rf /var/lib/apt/lists/ \
&& apt-get autoremove -y && apt-get autoclean -y
COPY --from=builder /src/bin /app
WORKDIR /app
EXPOSE 8000
EXPOSE 9000
VOLUME /data/conf
CMD ["./server", "-conf", "/data/conf"]
root = "."
testdata_dir = "bin"
tmp_dir = "bin"
[build]
args_bin = []
bin = "./bin/demo"
cmd = "go build -o bin/ ./..."
full_bin = ""
delay = 1000
exclude_dir = ["bin", "assets", "tmp", "vendor", "testdata"]
exclude_file = []
exclude_regex = ["_test.go"]
exclude_unchanged = false
follow_symlink = false
include_dir = []
include_ext = ["go", "tpl", "tmpl", "html"]
kill_delay = "0.3s"
log = "build-errors.log"
send_interrupt = true
stop_on_error = true
[color]
app = ""
build = "yellow"
main = "magenta"
runner = "green"
watcher = "cyan"
[log]
time = false
[misc]
clean_on_exit = false
[screen]
clear_on_rebuild = false
package main
import (
"flag"
"github.com/go-kratos/kratos/contrib/registry/consul/v2"
"github.com/go-kratos/kratos/v2"
"github.com/go-kratos/kratos/v2/middleware/tracing"
"github.com/go-kratos/kratos/v2/transport/grpc"
"github.com/go-kratos/kratos/v2/transport/http"
"github.com/hashicorp/consul/api"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/exporters/jaeger"
"go.opentelemetry.io/otel/sdk/resource"
tracesdk "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.10.0"
"go.uber.org/zap"
"gokratos-base/app/service/demo/internal/conf"
"gokratos-base/common/logz"
"os"
"github.com/go-kratos/kratos/v2/log"
)
// go build -ldflags "-X main.Version=x.y.z"
var (
// Name is the name of the compiled software.
Name string
// Version is the version of the compiled software.
Version string
id, _ = os.Hostname()
)
func main() {
flag.Parse()
conf.LoadConfig()
err := initTracer(conf.Conf.Jaeger.Url)
if err != nil {
panic(err)
}
l := logz.NewLogger(conf.Conf.Log, zap.AddCaller(), zap.AddCallerSkip(3))
defer l.Flush()
logger := log.With(l,
"service.id", id,
"service.name", conf.Conf.Server.Name,
"service.version", conf.Conf.Server.Version,
"trace.id", tracing.TraceID(),
"span.id", tracing.SpanID(),
)
log.SetLogger(logger)
app, cleanup, err := wireApp(conf.Conf.Server, conf.Conf.Data, logger)
if err != nil {
panic(err)
}
defer cleanup()
// start and wait for stop signal
if err := app.Run(); err != nil {
panic(err)
}
}
func newApp(logger log.Logger, hs *http.Server, gs *grpc.Server) *kratos.App {
cfg := api.DefaultConfig()
cfg.Address = conf.Conf.Consul.Addr
consulClient, err := api.NewClient(cfg)
if err != nil {
panic(err)
}
registry := consul.New(consulClient)
return kratos.New(
kratos.ID(id),
kratos.Name(conf.Conf.Server.Name),
kratos.Version(conf.Conf.Server.Version),
kratos.Metadata(map[string]string{}),
kratos.Logger(logger),
kratos.Server(
hs,
gs,
),
kratos.Registrar(registry),
)
}
// 设置全局trace
func initTracer(url string) error {
// 创建 Jaeger exporter
exp, err := jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint(url)))
if err != nil {
return err
}
tp := tracesdk.NewTracerProvider(
// 将基于父span的采样率设置为100%
tracesdk.WithSampler(tracesdk.ParentBased(tracesdk.TraceIDRatioBased(1.0))),
// 始终确保再生成中批量处理
tracesdk.WithBatcher(exp),
// 在资源中记录有关此应用程序的信息
tracesdk.WithResource(resource.NewSchemaless(
semconv.ServiceNameKey.String("kratos-trace"),
attribute.String("exporter", "jaeger"),
attribute.Float64("float", 312.23),
)),
)
otel.SetTracerProvider(tp)
return nil
}
//go:build wireinject
// +build wireinject
// The build tag makes sure the stub is not built in the final build.
package main
import (
"github.com/go-kratos/kratos/v2"
"github.com/go-kratos/kratos/v2/log"
"github.com/google/wire"
"gokratos-base/app/service/demo/internal/biz"
"gokratos-base/app/service/demo/internal/conf"
"gokratos-base/app/service/demo/internal/data"
"gokratos-base/app/service/demo/internal/server"
"gokratos-base/app/service/demo/internal/service"
)
// wireApp init kratos application.
func wireApp(*conf.Server, *conf.Data, log.Logger) (*kratos.App, func(), error) {
panic(wire.Build(server.ProviderSet, data.ProviderSet, biz.ProviderSet, service.ProviderSet, newApp))
}
// Code generated by Wire. DO NOT EDIT.
//go:generate go run github.com/google/wire/cmd/wire
//go:build !wireinject
// +build !wireinject
package main
import (
"github.com/go-kratos/kratos/v2"
"github.com/go-kratos/kratos/v2/log"
"gokratos-base/app/service/demo/internal/biz"
"gokratos-base/app/service/demo/internal/conf"
"gokratos-base/app/service/demo/internal/data"
"gokratos-base/app/service/demo/internal/server"
"gokratos-base/app/service/demo/internal/service"
)
// Injectors from wire.go:
// wireApp init kratos application.
func wireApp(confServer *conf.Server, confData *conf.Data, logger log.Logger) (*kratos.App, func(), error) {
dataData, cleanup, err := data.NewData(confData)
if err != nil {
return nil, nil, err
}
demoRepo := data.NewDemoRepo(dataData)
demoUseCase := biz.NewDemoUseCase(demoRepo)
demoService := service.NewDemoService(demoUseCase)
httpServer := server.NewHTTPServer(confServer, demoService)
grpcServer := server.NewGRPCServer(confServer, demoService)
app := newApp(logger, httpServer, grpcServer)
return app, func() {
cleanup()
}, nil
}
# 服务配置
Server:
Name: "service.demo"
Version: "1.0.0"
Grpc:
Addr: "0.0.0.0:8001" # 为空随机端口, 使用注册中心服务发现
Timeout: 3 # 服务超时时间, 单位秒
Http:
Addr: "0.0.0.0:8000" # 为空随机端口, 使用注册中心服务发现
Timeout: 3 # 服务超时时间, 单位秒
# 注册中心
Consul:
Addr: 192.168.233.1:8500
# jaeger链路追踪 https://github.com/jaegertracing/jaeger
Jaeger:
Url: http://192.168.233.1:14268/api/traces
# 数据源
Data:
Mysql:
Dsn: temp:temp666@tcp(127.0.0.1:3306)/site?loc=Local&charset=utf8mb4&writeTimeout=3s&readTimeout=3s&timeout=2s&parseTime=true
MaxConn: 16 # 连接池最大连接数
MaxIdleConn: 4 # 连接池最小连接数
MaxLifetime: 1800 # 连接池内连接有效时间,单位秒
Debug: true
Redis:
Addr: 127.0.0.1:6379
Password: ""
DB: 15
# 日志
Log:
Color: true
Level: DEBUG
\ No newline at end of file
package biz
import "github.com/google/wire"
// ProviderSet is biz providers.
var ProviderSet = wire.NewSet(NewDemoUseCase)
package biz
import (
"context"
"github.com/go-kratos/kratos/v2/log"
"gokratos-base/app/service/demo/internal/model"
)
// DemoRepo is a Greater repo.
type DemoRepo interface {
Save(ctx context.Context, m *model.Demo) error
Update(ctx context.Context, m *model.Demo) error
GetByID(ctx context.Context, id int) (res *model.Demo, err error)
List(ctx context.Context, limit, offset int) (res []*model.Demo, err error)
DeleteByID(ctx context.Context, id int) (err error)
}
// DemoUseCase is a Demo useCase.
type DemoUseCase struct {
repo DemoRepo
log *log.Helper
}
type ListReq struct {
Page int
Size int
}
// NewDemoUseCase new a Demo useCase.
func NewDemoUseCase(repo DemoRepo) *DemoUseCase {
return &DemoUseCase{
repo: repo,
log: log.NewHelper(log.GetLogger()),
}
}
// CreateDemo creates a Demo, and returns the new Demo.
func (uc *DemoUseCase) CreateDemo(ctx context.Context, m *model.Demo) error {
return uc.repo.Save(ctx, m)
}
func (uc *DemoUseCase) UpdateDemo(ctx context.Context, m *model.Demo) error {
return uc.repo.Update(ctx, m)
}
func (uc *DemoUseCase) GetDemo(ctx context.Context, id int) (*model.Demo, error) {
uc.log.WithContext(ctx).Info("qqqqqqqqqqqqqqqq")
m, err := uc.repo.GetByID(ctx, id)
if err != nil {
return nil, err
}
return m, nil
}
func (uc *DemoUseCase) ListDemo(ctx context.Context, req *ListReq) ([]*model.Demo, error) {
if req.Page <= 0 {
req.Page = 1
}
if req.Size <= 0 {
req.Size = 20
}
if req.Size >= 100 {
req.Size = 100
}
offset := (req.Page - 1) * req.Size
return uc.repo.List(ctx, req.Size, offset)
}
func (uc *DemoUseCase) DeleteDemo(ctx context.Context, id int) error {
return uc.repo.DeleteByID(ctx, int(id))
}
package conf
import (
"flag"
"gokratos-base/common/conf"
"gokratos-base/common/logz"
"gokratos-base/common/mysql"
"gokratos-base/common/redis"
)
var (
confPath string
Conf = &Config{}
)
func init() {
flag.StringVar(&confPath, "conf", "config/config.yaml", "指定配置文件 eg: -conf config.yaml")
}
type Config struct {
Server *Server
Data *Data
Log *logz.Config
Consul *Consul
Jaeger *Jaeger
}
type Consul struct {
Addr string
}
type Jaeger struct {
Url string
}
type Data struct {
Mysql *mysql.Config
Redis *redis.Config
}
type Server struct {
Name string
Version string
Grpc struct {
Addr string
Timeout int
}
Http struct {
Addr string
Timeout int
}
}
func LoadConfig() {
conf.LoadFromYaml(confPath, &Conf)
}
package data
import (
"github.com/go-kratos/kratos/v2/log"
"github.com/google/wire"
"gokratos-base/app/service/demo/internal/conf"
"gokratos-base/common/mysql"
"gokratos-base/common/redis"
"gorm.io/gorm"
)
// ProviderSet is data providers.
var ProviderSet = wire.NewSet(NewData, NewDemoRepo)
// Data .
type Data struct {
// TODO wrapped database client
DB *gorm.DB
Redis *redis.WrapClient
}
// NewData .
func NewData(c *conf.Data) (*Data, func(), error) {
db := mysql.NewDB(c.Mysql)
rs := redis.NewRedis(c.Redis)
cleanup := func() {
d, _ := db.DB()
d.Close()
rs.Client.Close()
log.Debug("close data resources")
}
return &Data{
DB: db,
Redis: rs,
}, cleanup, nil
}
package data
import (
"context"
"gokratos-base/app/service/demo/internal/biz"
"gokratos-base/app/service/demo/internal/model"
"time"
)
type demoRepo struct {
data *Data
}
var _ biz.DemoRepo = &demoRepo{}
// NewDemoRepo .
func NewDemoRepo(data *Data) biz.DemoRepo {
return &demoRepo{
data: data,
}
}
func (r *demoRepo) Save(ctx context.Context, m *model.Demo) error {
err := r.data.DB.Model(&model.Demo{}).Create(m).Error
return err
}
func (r *demoRepo) Update(ctx context.Context, m *model.Demo) error {
err := r.data.DB.Where("id = ?", m.Id).Updates(&model.Demo{
Name: m.Name,
Value: m.Value,
UpdatedAt: int(time.Now().Unix()),
}).Error
return err
}
func (r *demoRepo) GetByID(ctx context.Context, id int) (res *model.Demo, err error) {
res = &model.Demo{}
err = r.data.DB.Model(&model.Demo{}).Where("id = ?", id).Take(res).Error
return
}
func (r *demoRepo) List(ctx context.Context, limit, offset int) (res []*model.Demo, err error) {
res = make([]*model.Demo, 0)
err = r.data.DB.Model(&model.Demo{}).Limit(limit).Offset(offset).Order("id desc").Find(&res).Error
return
}
func (r *demoRepo) DeleteByID(ctx context.Context, id int) (err error) {
err = r.data.DB.Model(&model.Demo{}).Where("id = ?", id).Delete(&model.Demo{}).Error
return
}
package model
type Demo struct {
Id int `json:"id"` // Id
Name string `json:"name"` // Name
Value string `json:"value"` // Value
CreatedAt int `json:"created_at"` // CreatedAt
UpdatedAt int `json:"updated_at"` // UpdatedAt
}
func (d *Demo) TableName() string {
return "demo"
}
package server
import (
"github.com/go-kratos/kratos/v2/log"
"github.com/go-kratos/kratos/v2/middleware/recovery"
"github.com/go-kratos/kratos/v2/middleware/tracing"
"github.com/go-kratos/kratos/v2/transport/grpc"
"gokratos-base/api/service/demo"
"gokratos-base/app/service/demo/internal/conf"
"gokratos-base/app/service/demo/internal/service"
"time"
)
// NewGRPCServer new a gRPC server.
func NewGRPCServer(c *conf.Server, svc *service.DemoService) *grpc.Server {
var opts = []grpc.ServerOption{
grpc.Middleware(
recovery.Recovery(),
tracing.Server(),
ValidateParams(),
),
}
if c.Grpc.Addr != "" {
opts = append(opts, grpc.Address(c.Grpc.Addr))
}
if c.Grpc.Timeout != 0 {
opts = append(opts, grpc.Timeout(time.Duration(c.Grpc.Timeout)*time.Second))
}
srv := grpc.NewServer(opts...)
demo.RegisterDemoServer(srv, svc)
logHelper = log.NewHelper(log.GetLogger())
return srv
}
package server
import (
"github.com/go-kratos/kratos/v2/middleware/auth/jwt"
"github.com/go-kratos/kratos/v2/middleware/recovery"
"github.com/go-kratos/kratos/v2/middleware/tracing"
"github.com/go-kratos/kratos/v2/transport/http"
jwtv4 "github.com/golang-jwt/jwt/v4"
"gokratos-base/api/service/demo"
"gokratos-base/app/service/demo/internal/conf"
"gokratos-base/app/service/demo/internal/service"
"gokratos-base/common/httpext"
"time"
)
// NewHTTPServer new a http server.
func NewHTTPServer(c *conf.Server, svc *service.DemoService) *http.Server {
var opts = []http.ServerOption{
http.Middleware(
recovery.Recovery(),
tracing.Server(),
jwt.Server(func(token *jwtv4.Token) (interface{}, error) {
return []byte("testkey"), nil
}),
ValidateParams(),
),
http.ResponseEncoder(httpext.Encoder),
http.ErrorEncoder(httpext.ErrorEncoder),
}
if c.Http.Addr != "" {
opts = append(opts, http.Address(c.Http.Addr))
}
if c.Http.Timeout != 0 {
opts = append(opts, http.Timeout(time.Duration(c.Grpc.Timeout)*time.Second))
}
srv := http.NewServer(opts...)
demo.RegisterDemoHTTPServer(srv, svc)
return srv
}
package server
import (
"context"
"github.com/go-kratos/kratos/v2/middleware"
"github.com/go-kratos/kratos/v2/transport"
"gokratos-base/common/errm"
"gokratos-base/common/validate"
)
// ValidateParams 验证参数
func ValidateParams() middleware.Middleware {
return func(handler middleware.Handler) middleware.Handler {
return func(ctx context.Context, req interface{}) (interface{}, error) {
if tr, ok := transport.FromServerContext(ctx); ok {
err := validate.Struct(req)
if err != nil {
logHelper.WithContext(ctx).Warnf("[%s]参数验证失败: %s", tr.Operation(), err.Error())
return nil, errm.ParamsError(err.Error())
}
}
return handler(ctx, req)
}
}
}
package server
import (
"github.com/go-kratos/kratos/v2/log"
"github.com/google/wire"
)
// ProviderSet is server providers.
var (
ProviderSet = wire.NewSet(NewHTTPServer, NewGRPCServer)
logHelper *log.Helper
)
package service
import (
"gokratos-base/api/service/demo"
"gokratos-base/app/service/demo/internal/model"
)
func DemoModelToRep(m *model.Demo) *demo.DemoModel {
return &demo.DemoModel{
Id: int64(m.Id),
Name: m.Name,
Value: m.Value,
CreatedAt: int64(m.CreatedAt),
UpdatedAt: int64(m.UpdatedAt),
}
}
package service
import (
"context"
"gokratos-base/api/service/demo"
"gokratos-base/app/service/demo/internal/biz"
"gokratos-base/app/service/demo/internal/model"
)
// DemoService is a demo service.
type DemoService struct {
demo.UnimplementedDemoServer
uc *biz.DemoUseCase
}
var _ demo.DemoServer = &DemoService{}
// NewDemoService new a demo service.
func NewDemoService(uc *biz.DemoUseCase) *DemoService {
return &DemoService{uc: uc}
}
// Hello implements DemoServer.
func (s *DemoService) Hello(ctx context.Context, req *demo.HelloReq) (*demo.HelloRep, error) {
return &demo.HelloRep{Message: "Hello " + req.Name}, nil
}
func (s *DemoService) Create(ctx context.Context, req *demo.CreateReq) (*demo.OKRep, error) {
err := s.uc.CreateDemo(ctx, &model.Demo{Name: req.Name, Value: req.Value})
if err != nil {
return nil, err
}
return nil, nil
}
func (s *DemoService) Update(ctx context.Context, req *demo.UpdateReq) (*demo.OKRep, error) {
err := s.uc.UpdateDemo(ctx, &model.Demo{Id: int(req.Id), Name: req.Name, Value: req.Value})
if err != nil {
return nil, err
}
return &demo.OKRep{}, nil
}
func (s *DemoService) Get(ctx context.Context, req *demo.IdReq) (*demo.DemoModel, error) {
res, err := s.uc.GetDemo(ctx, int(req.Id))
if err != nil {
return nil, err
}
return DemoModelToRep(res), nil
}
func (s *DemoService) List(ctx context.Context, req *demo.ListReq) (*demo.ListRep, error) {
list, err := s.uc.ListDemo(ctx, &biz.ListReq{
Page: int(req.Page),
Size: int(req.Size),
})
if err != nil {
return nil, err
}
rep := &demo.ListRep{}
rep.List = make([]*demo.DemoModel, 0, len(list))
for _, v := range list {
rep.List = append(rep.List, DemoModelToRep(v))
}
return rep, nil
}
func (s *DemoService) Delete(ctx context.Context, req *demo.IdReq) (*demo.OKRep, error) {
err := s.uc.DeleteDemo(ctx, int(req.Id))
if err != nil {
return nil, err
}
return &demo.OKRep{}, nil
}
package service
import "github.com/google/wire"
// ProviderSet is service providers.
var ProviderSet = wire.NewSet(NewDemoService)
# Generated with protoc-gen-openapi
# https://github.com/google/gnostic/tree/master/cmd/protoc-gen-openapi
openapi: 3.0.3
info:
title: Greeter API
description: The greeting service definition.
version: 0.0.1
paths:
/helloworld/{name}:
get:
tags:
- Greeter
- subgroup
description: Sends a greeting
operationId: Greeter_SayHello
parameters:
- name: name
in: path
required: true
schema:
type: string
responses:
"200":
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/helloworld.v1.HelloReply'
components:
schemas:
helloworld.v1.HelloReply:
type: object
properties:
message:
type: string
description: The response message containing the greetings
tags:
- name: Greeter
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment