Ce guide présente comment utiliser l'outil goctl pour générer et structurer des services basés sur le framework go-zero, en couvrant à la fois les API REST et les services RPC.
Prérequis
Assurez-vous d'avoir installé protoc, protoc-gen-go, et goctl.
Développement d'API REST
-
Initialisation du projet et configuration Go Modules :
mkdir zeroService && cd zeroService && go mod init zeroService -
Gestion des versions de dépendances (exemple avec gRPC) : Ajoutez la directive
replacedansgo.modpour spécifier une version degoogle.golang.org/grpc.module zeroService go 1.15 replace google.golang.org/grpc => google.golang.org/grpc v1.29.1 -
Organisation des répertoires pour les API et les RPC :
mkdir rpc && mkdir api -
Définition de l'API avec la syntaxe spécifique à go-zero (fichier
api/user.api) :syntax = "v1" info( "user-api" ) type ( RegisterRequest { Username string `json:"username"` Password string `json:"password"` Nickname string `json:"nickname"` Age int `json:"age"` } RegisterResponse { UserID int64 `json:"userId"` } UserInfoRequest { UserID int64 `json:"userId"` } UserInfoResponse { UserID int64 `json:"userId"` Username string `json:"username"` Nickname string `json:"nickname"` Age int `json:"age"` } ) @server( group: user ) service User { @doc "Enregistrement d'un nouvel utilisateur" @handler RegisterHandler post /users/register (RegisterRequest) returns (RegisterResponse) @doc "Récupération des informations utilisateur" @handler UserInfoHandler get /users/:userId (UserInfoRequest) returns (UserInfoResponse) } -
Génération du code de l'API REST avec
goctl:goctl api go -api api/user.api -dir .Cela crée une structure de projet avec les répertoires
api/etc,api/internal, etc. -
Configuration de l'application (fichier
api/etc/user-api.yaml) :name: user-api host: 0.0.0.0 port: 8888 database: source: "user:password@tcp(127.0.0.1:3306)/gouserdb" cache: - host: 127.0.0.1:6379 log: level: "console"Mettez à jour la structure de configuration dans
api/internal/config/config.gopour inclure les nouvelles propriétés. -
Conception et génération du modèle de base de données :
Exemple de schéma SQL pour la table
users:CREATE TABLE `users` ( `id` BIGINT AUTO_INCREMENT PRIMARY KEY, `username` VARCHAR(100) NOT NULL UNIQUE, `nickname` VARCHAR(100) NOT NULL, `password` VARCHAR(255) NOT NULL, `age` INT DEFAULT 0, `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, `updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP );Génération du modèle de données avec
goctl:goctl model mysql datasource -url="user:password@tcp(127.0.0.1:3306)/gouserdb" -table="users" -dir="./api/model"Intégration du modèle dans le contexte de service (fichier
api/internal/svc/servicecontext.go) :package svc import ( "gouserdb/api/internal/config" "gouserdb/api/model" // Assurez-vous que le chemin est correct "github.com/zeromicro/go-zero/core/stores/sqlx" ) type ServiceContext struct { Config config.Config UserModel model.UsersModel } func NewServiceContext(c config.Config) *ServiceContext { mysqlConn := sqlx.NewMysql(c.Database.Source) return &ServiceContext{ Config: c, UserModel: model.NewUsersModel(mysqlConn), } } -
Implémentation de la logique métier pour l'API (exemple pour
UserInfoHandlerdansapi/internal/logic/userinfologic.go) :package logic import ( "context" "fmt" "gouserdb/api/internal/svc" "gouserdb/api/internal/types" "github.com/zeromicro/go-zero/core/logx" ) type UserInfoLogic struct { logx.Logger ctx context.Context svcCtx *svc.ServiceContext } func NewUserInfoLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UserInfoLogic { return &UserInfoLogic{ Logger: logx.WithContext(ctx), ctx: ctx, svcCtx: svcCtx, } } func (l *UserInfoLogic) GetUserInfo(req *types.UserInfoRequest) (*types.UserInfoResponse, error) { user, err := l.svcCtx.UserModel.FindOne(l.ctx, req.UserID) if err != nil { if err == model.ErrNotFound { l.Logger.Errorf("User not found: %d", req.UserID) return nil, fmt.Errorf("user not found") } l.Logger.Errorf("Error fetching user: %v", err) return nil, err } return &types.UserInfoResponse{ UserID: user.ID, Username: user.Username, Nickname: user.Nickname, Age: int(user.Age), }, nil } -
Démarrage du service API :
go run api/user.goTester l'API avec
curl.
Développement de Services RPC
-
Définition du contrat de service RPC avec Protocol Buffers (fichier
rpc/user_service.proto) :syntax = "proto3"; package userService; option go_package = "./userService"; message UserRegisterRequest { string username = 1; string nickname = 2; string password = 3; int64 age = 4; } message UserRegisterResponse { int64 userId = 1; } message GetUserRequest { int64 userId = 1; } message GetUserResponse { int64 userId = 1; string username = 2; string nickname = 3; int64 age = 4; } service UserService { rpc Register (UserRegisterRequest) returns (UserRegisterResponse); rpc GetUser (GetUserRequest) returns (GetUserResponse); } -
Génération du code RPC à partir du fichier
.proto:goctl rpc proto -src rpc/user_service.proto -dir .Cela génère les fichiers dans
rpc/userServiceet les fichiers de configuration dansrpc/etc. -
Configuration du service RPC (fichier
rpc/etc/user_service.yaml) :name: user-rpc-service listenOn: 0.0.0.0:9000 etcd: hosts: - 127.0.0.1:2379 key: user-rpc-service database: source: "user:password@tcp(127.0.0.1:3306)/gouserdb" cache: - host: 127.0.0.1:6379 log: level: "console"Mettez à jour la configuration dans
rpc/internal/config/config.go. -
Intégration du modèle de données dans le contexte du service RPC (fichier
rpc/internal/svc/servicecontext.go) :package svc import ( "gouserdb/rpc/internal/config" "gouserdb/rpc/model" // Assurez-vous que le chemin est correct "github.com/zeromicro/go-zero/core/stores/sqlx" "github.com/zeromicro/go-zero/zrpc" ) type ServiceContext struct { Config config.Config UserModel model.UsersModel } func NewServiceContext(c config.Config) *ServiceContext { mysqlConn := sqlx.NewMysql(c.Database.Source) return &ServiceContext{ Config: c, UserModel: model.NewUsersModel(mysqlConn), } } -
Implémentation de la logique métier pour le service RPC (fichier
rpc/internal/logic/registerlogic.go) :package logic import ( "context" "time" "gouserdb/rpc/internal/svc" "gouserdb/rpc/userService" "gouserdb/rpc/model" "github.com/zeromicro/go-zero/core/logx" ) type RegisterLogic struct { logx.Logger ctx context.Context svcCtx *svc.ServiceContext } func NewRegisterLogic(ctx context.Context, svcCtx *svc.ServiceContext) *RegisterLogic { return &RegisterLogic{ Logger: logx.WithContext(ctx), ctx: ctx, svcCtx: svcCtx, } } func (l *RegisterLogic) RegisterUser(in *userService.UserRegisterRequest) (*userService.UserRegisterResponse, error) { newUser := model.Users{ Username: in.Username, Nickname: in.Nickname, Password: in.Password, Age: in.Age, // created_at et updated_at seront gérés par la base de données ou par défaut } result, err := l.svcCtx.UserModel.Insert(l.ctx, newUser) if err != nil { l.Logger.Errorf("Failed to insert user: %v", err) return nil, err } userId, err := result.LastInsertId() if err != nil { l.Logger.Errorf("Failed to get last insert ID: %v", err) return nil, err } return &userService.UserRegisterResponse{UserId: userId}, nil } -
Configuration de l'API pour utiliser le client RPC :
Ajoutez la configuration du client RPC dans
api/etc/user-api.yamlsous la sectionrpcClient.Mettez à jour
api/internal/config/config.gopour inclure la nouvelle structure de configuration du client RPC.Dans
api/internal/svc/servicecontext.go, ajoutez l'instance du client RPC et initialisez-la dansNewServiceContext. -
Implémentation de la logique de l'API pour appeler le service RPC (dans
api/internal/logic/registerlogic.go) :package logic import ( "context" "fmt" "gouserdb/api/internal/svc" "gouserdb/api/internal/types" userpb "gouserdb/rpc/userService" // Alias pour éviter les conflits "github.com/zeromicro/go-zero/core/logx" "github.com/zeromicro/go-zero/zrpc" ) type RegisterLogic struct { logx.Logger ctx context.Context svcCtx *svc.ServiceContext } func NewRegisterLogic(ctx context.Context, svcCtx *svc.ServiceContext) *RegisterLogic { return &RegisterLogic{ Logger: logx.WithContext(ctx), ctx: ctx, svcCtx: svcCtx, } } func (l *RegisterLogic) Register(req types.RegisterRequest) (*types.RegisterResponse, error) { // Créer la requête pour le service RPC rpcReq := &userpb.UserRegisterRequest{ Username: req.Username, Nickname: req.Nickname, Password: req.Password, Age: int64(req.Age), } // Appeler le service RPC rpcResp, err := l.svcCtx.UserServiceRpcClient.Register(l.ctx, rpcReq) if err != nil { l.Logger.Errorf("RPC call to Register failed: %v", err) return nil, fmt.Errorf("failed to register user via RPC") } // Mapper la réponse RPC vers la réponse de l'API return &types.RegisterResponse{UserID: int(rpcResp.UserId)}, nil } -
Démarrage des services API et RPC :
# Démarrer le service RPC en arrière-plan go run rpc/user_service.go & # Démarrer le service API en arrière-plan go run api/user.go &Tester les points de terminaison de l'API.
Déploiement avec Docker
Utilisez goctl docker pour générer des Dockerfile pour vos services.
goctl docker -go rpc/user_service.go -port 9000
goctl docker -go api/user.go -port 8888
Construisez les images Docker et lancez les conteneurs.
Résumé
goctlest un outil puissant pour générer rapidement du code boilerplate.- Une attention particulière doit être portée à la gestion des dépendances et aux configurations spécifiuqes.
- La génération de modèles avec support de cache automatique est une fonctionnalité notable pour optimiser les accès à la base de données.