Go-kratos 框架商城微服务实战之用户服务 (四)
这篇主要编写 HTTP API 端的服务,跟前几篇写的用户服务对接上,主要还是项目的初始化准备工作。写的不清晰的地方可看GitHub 源码 , 也感谢您指出不足之处。
注:竖排 … 代码省略,为了保持文章的篇幅简洁,我会将一些不必要的代码使用竖排的 . 来代替,你在复制本文代码块的时候,切记不要将 . 也一同复制进去。这里所有 import 引入的包都没有特殊说明,自己写的时候要注意包的引入。
shop Api 准备工作
new 一个新的 kratos 项目
在 kratos-shop 目录下新建一个 kratos new shop
shop 项目
// 整体的项目 目录结构如下
|-- kratos-shop
|-- service
|-- user // 原先的用户服务 grpc
|-- shop // 刚刚通过 kratos new shop 新增的项目代码
- 进入新建的 shop 目录下
- 删除目录下的所有文件
rm -rf api/helloworld
- 复制
service/user/api/user/v1/user.proto
文件到新建的shop/api/service/user/v1
目录下 - 执行命令
kratos proto add api/shop/v1/user.proto
创建 api user.proto - 执行
kratos proto server api/user/v1/user.proto -t internal/service
命令生成对应的 service 文件。 - 删除不需要的 service 文件
rm internal/service/greeter.go
- 完整执行命令如下:
cd kratos-shop
kratos new shop
cd shop
rm -rf api/helloworld
rm internal/service/greeter.go
kratos proto add api/shop/v1/user.proto
kratos proto server api/shop/v1/user.proto -t internal/service
mkdir -p api/service/user/v1
cp ../service/user/api/user/v1/user.proto api/service/user/v1
proto 的目录结构如下:
├── api
│ ├── service
│ │ └── user
│ │ └── v1
│ │ └── user.proto
│ └── shop
│ └── v1
│ └── user.proto
修改 shop 下的 user.proto
这里提供对外访问的接口,会聚合从不同的服务之间获取数据,接口路由通过 Protobuf IDL 定义对应的 REST API 和 gRPC API,
参数校验使用 Validate 中间件,使用 proto-gen-validate 生成
在使用 validate 之前首先需要安装 proto-gen-validate。
syntax = "proto3";
package shop.shop.v1;
// 这里可以把 proto 文件下载下来,放到项目的 third_party 目录下
import "google/api/annotations.proto";
import "google/protobuf/empty.proto";
import "validate/validate.proto";
option go_package = "shop/api/shop/v1;v1";
// The Shop service definition.
service Shop {
rpc Register (RegisterReq) returns (RegisterReply) {
option (google.api.http) = {
post: "/api/users/register",
body: "*",
};
}
rpc Login (LoginReq) returns (RegisterReply) {
option (google.api.http) = {
post: "/api/users/login",
body: "*",
};
}
rpc Captcha (google.protobuf.Empty) returns (CaptchaReply) {
option (google.api.http) = {
get: "/api/users/captcha",
};
}
rpc Detail (google.protobuf.Empty) returns (UserDetailResponse) {
option (google.api.http) = {
get: "/api/users/detail",
};
}
}
// Data returned by registration and login
message RegisterReply {
int64 id = 1;
string mobile = 3;
string username = 4;
string token = 5;
int64 expiredAt = 6;
}
message RegisterReq {
string mobile = 1 [(validate.rules).string.len = 11];
string username = 2 [(validate.rules).string = {min_len: 3, max_len: 15}];
string password = 3 [(validate.rules).string = {min_len: 8}];
}
message LoginReq {
string mobile = 1 [(validate.rules).string.len = 11];
string password = 2 [(validate.rules).string = {min_len: 8}];
string captcha = 3 [(validate.rules).string = {min_len: 5,max_len:5}];
string captchaId = 4 [(validate.rules).string ={min_len: 1}];
}
// user Detail returned
message UserDetailResponse{
int64 id = 1;
string mobile = 2;
string nickName = 3;
int64 birthday = 4;
string gender = 5;
int32 role = 6;
}
message CaptchaReply{
string captchaId = 1;
string picPath = 2;
}
这里一共定义了 4 个 rpc 方法,其中三个是需要跟之前写的用户服务交互的,还有个获取图片验证码的接口,自己内部实现。
- shop 根目录执行
make api
,生成对应的*pb.go
文件
修改配置文件
- 修改
shop/configs/config.yaml
文件
项目中引入了 consul 配置需要把相关的配置设置好,service 就是 consul 用来服务发现的。这里的 trace 并没有用到呢先在这里定义了,之后会专门拿一篇来说说。auth 是用来 jwt 验证。
name: shop.api
server:
http:
addr: 0.0.0.0:8097
timeout: 1s
grpc:
addr: 0.0.0.0:9001
timeout: 1s
data:
database:
driver: mysql
source: root:root@tcp(127.0.0.1:3306)/test
redis:
addr: 127.0.0.1:6379
read_timeout: 0.2s
write_timeout: 0.2s
trace:
endpoint: http://127.0.0.1:14268/api/traces
auth:
jwt_key: hqFr%3ddt32DGlSTOI5cO6@TH#fFwYnP$S
service:
user:
endpoint: discovery:///shop.user.service
goods:
endpoint: discovery:///shop.goods.service
- 新增
shop/configs/registry.yaml
文件
consul:
address: 127.0.0.1:8500
scheme: http
- 修改
internal/conf/conf.proto
文件:
syntax = "proto3";
package shop.api;
option go_package = "shop/internal/conf;conf";
import "google/protobuf/duration.proto";
message Bootstrap {
Server server = 1;
Data data = 2;
Trace trace = 3; // 链路追踪
Auth auth = 4; // 认证鉴权
Service service = 5; // 服务注册与发现
}
message Server {
message HTTP {
string network = 1;
string addr = 2;
google.protobuf.Duration timeout = 3;
}
message GRPC {
string network = 1;
string addr = 2;
google.protobuf.Duration timeout = 3;
}
HTTP http = 1;
GRPC grpc = 2;
}
message Data {
message Database {
string driver = 1;
string source = 2;
}
message Redis {
string network = 1;
string addr = 2;
google.protobuf.Duration read_timeout = 3;
google.protobuf.Duration write_timeout = 4;
}
Database database = 1;
Redis redis = 2;
}
message Service {
message User { // 用户服务
string endpoint = 1;
}
message Goods { // 商品服务
string endpoint = 1;
}
User user = 1;
Goods goods = 2;
}
message Trace {
string endpoint = 1;
}
message Registry {
message Consul {
string address = 1;
string scheme = 2;
}
Consul consul = 1;
}
message Auth {
string jwt_key = 1;
}
- 生成新的配置文件
shop 根目录执行
make config
生成 shop/internal/conf/conf.pb.go 文件
修改 HTTP 服务
- 修改
internal/server/http.go
文件
这里用到的一些 middleware 中间件都是 kratos 官方支持的,jwt、validate、tracing,具体使用方式可参考 kratos 的middleware 文档
package server
import (
"context"
"github.com/go-kratos/kratos/v2/log"
"github.com/go-kratos/kratos/v2/middleware/auth/jwt"
"github.com/go-kratos/kratos/v2/middleware/logging"
"github.com/go-kratos/kratos/v2/middleware/recovery"
"github.com/go-kratos/kratos/v2/middleware/selector"
"github.com/go-kratos/kratos/v2/middleware/validate"
"github.com/go-kratos/kratos/v2/transport/http"
jwt2 "github.com/golang-jwt/jwt/v4"
"github.com/gorilla/handlers"
v1 "shop/api/shop/v1"
"shop/internal/conf"
"shop/internal/service"
)
// NewHTTPServer new an HTTP server.
func NewHTTPServer(c *conf.Server, ac *conf.Auth, s *service.ShopService, logger log.Logger) *http.Server {
var opts = []http.ServerOption{
http.Middleware(
recovery.Recovery(),
validate.Validator(), // 接口访问的参数校验
selector.Server( // jwt 验证
jwt.Server(func(token *jwt2.Token) (interface{}, error) {
return []byte(ac.JwtKey), nil
}, jwt.WithSigningMethod(jwt2.SigningMethodHS256)),
).Match(NewWhiteListMatcher()).Build(),
logging.Server(logger),
),
http.Filter(handlers.CORS( // 浏览器跨域
handlers.AllowedHeaders([]string{"X-Requested-With", "Content-Type", "Authorization"}),
handlers.AllowedMethods([]string{"GET", "POST", "PUT", "HEAD", "OPTIONS"}),
handlers.AllowedOrigins([]string{"*"}),
)),
}
if c.Http.Network != "" {
opts = append(opts, http.Network(c.Http.Network))
}
if c.Http.Addr != "" {
opts = append(opts, http.Address(c.Http.Addr))
}
if c.Http.Timeout != nil {
opts = append(opts, http.Timeout(c.Http.Timeout.AsDuration()))
}
srv := http.NewServer(opts...)
v1.RegisterShopHTTPServer(srv, s)
return srv
}
// NewWhiteListMatcher 设置白名单,不需要 token 验证的接口
func NewWhiteListMatcher() selector.MatchFunc {
whiteList := make(map[string]struct{})
whiteList["/shop.shop.v1.Shop/Captcha"] = struct{}{}
whiteList["/shop.shop.v1.Shop/Login"] = struct{}{}
whiteList["/shop.shop.v1.Shop/Register"] = struct{}{}
return func(ctx context.Context, operation string) bool {
if _, ok := whiteList[operation]; ok {
return false
}
return true
}
}
- 修改
internal/server/server.go
由于此服务只对外提供 http 服务,所以同目录下 grpc 文件可以删除,这样子注册服务的时候也把 grpc 服务去掉。
package server
import (
"github.com/google/wire"
)
// ProviderSet is server providers.
var ProviderSet = wire.NewSet(NewHTTPServer)
实现接口
- 修改
shop/internal/service/service.go
文件
package service
import (
"github.com/go-kratos/kratos/v2/log"
"github.com/google/wire"
v1 "shop/api/shop/v1"
"shop/internal/biz"
)
// ProviderSet is service providers.
var ProviderSet = wire.NewSet(NewShopService)
// ShopService is a shop service.
type ShopService struct {
v1.UnimplementedShopServer
uc *biz.UserUsecase
log *log.Helper
}
// NewShopService new a shop service.
func NewShopService(uc *biz.UserUsecase, logger log.Logger) *ShopService {
return &ShopService{
uc: uc,
log: log.NewHelper(log.With(logger, "module", "service/shop")),
}
}
- 修改
shop/internal/service/user.go
这里的 ShopService 的 usecase 还没实现,编辑器可能会有错误提示,先忽略
package service
import (
"context"
"google.golang.org/protobuf/types/known/emptypb"
v1 "shop/api/shop/v1"
)
func (s *ShopService) Register(ctx context.Context, req *v1.RegisterReq) (*v1.RegisterReply, error) {
return s.uc.CreateUser(ctx, req)
}
func (s *ShopService) Login(ctx context.Context, req *v1.LoginReq) (*v1.RegisterReply, error) {
return s.uc.PassWordLogin(ctx, req)
}
func (s *ShopService) Captcha(ctx context.Context, r *emptypb.Empty) (*v1.CaptchaReply, error) {
return s.uc.GetCaptcha(ctx)
}
func (s *ShopService) Detail(ctx context.Context, r *emptypb.Empty) (*v1.UserDetailResponse, error) {
return s.uc.UserDetailByID(ctx)
}
新增 jwt 验证的 Middleware
- 新建文件
internal/pkg/middleware/auth/auth.go
package auth
import (
"errors"
"github.com/golang-jwt/jwt/v4"
)
type CustomClaims struct {
ID int64
NickName string
AuthorityId int
jwt.StandardClaims
}
// CreateToken generate token
func CreateToken(c CustomClaims, key string) (string, error) {
claims := jwt.NewWithClaims(jwt.SigningMethodHS256, c)
signedString, err := claims.SignedString([]byte(key))
if err != nil {
return "", errors.New("generate token failed" + err.Error())
}
return signedString, nil
}
新增生成验证码的文件
- 新建文件
internal/pkg/captcha/captcha.go
package captcha
import (
"context"
"github.com/mojocn/base64Captcha"
)
var Store = base64Captcha.DefaultMemStore
type CaptchaInfo struct {
CaptchaId string
PicPath string
}
// GetCaptcha 生成验证码
func GetCaptcha(ctx context.Context) (*CaptchaInfo, error) {
driver := base64Captcha.NewDriverDigit(80, 250, 5, 0.7, 80)
cp := base64Captcha.NewCaptcha(driver, Store)
id, b64s, err := cp.Generate()
if err != nil {
return nil, err
}
return &CaptchaInfo{
CaptchaId: id,
PicPath: b64s,
}, nil
}
- 修改
shop/internal/biz/user.go
package biz
import (
"context"
"errors"
"github.com/go-kratos/kratos/v2/log"
"github.com/go-kratos/kratos/v2/middleware/auth/jwt"
jwt2 "github.com/golang-jwt/jwt/v4"
v1 "shop/api/shop/v1"
"shop/internal/conf"
"shop/internal/pkg/captcha"
"shop/internal/pkg/middleware/auth"
"time"
)
// 定义错误信息
var (
ErrPasswordInvalid = errors.New("password invalid")
ErrUsernameInvalid = errors.New("username invalid")
ErrCaptchaInvalid = errors.New("verification code error")
ErrMobileInvalid = errors.New("mobile invalid")
ErrUserNotFound = errors.New("user not found")
ErrLoginFailed = errors.New("login failed")
ErrGenerateTokenFailed = errors.New("generate token failed")
ErrAuthFailed = errors.New("authentication failed")
)
// 定义返回的数据的结构体
type User struct {
ID int64
Mobile string
NickName string
Birthday int64
Gender string
Role int
CreatedAt time.Time
}
type UserRepo interface {
CreateUser(c context.Context, u *User) (*User, error)
UserByMobile(ctx context.Context, mobile string) (*User, error)
UserById(ctx context.Context, Id int64) (*User, error)
CheckPassword(ctx context.Context, password, encryptedPassword string) (bool, error)
}
type UserUsecase struct {
uRepo UserRepo
log *log.Helper
signingKey string // 这里是为了生存 token 的时候可以直接取配置文件里面的配置
}
func NewUserUsecase(repo UserRepo, logger log.Logger, conf *conf.Auth) *UserUsecase {
helper := log.NewHelper(log.With(logger, "module", "usecase/shop"))
return &UserUsecase{uRepo: repo, log: helper, signingKey: conf.JwtKey}
}
// GetCaptcha 验证码
func (uc *UserUsecase) GetCaptcha(ctx context.Context) (*v1.CaptchaReply, error) {
captchaInfo, err := captcha.GetCaptcha(ctx)
if err != nil {
return nil, err
}
return &v1.CaptchaReply{
CaptchaId: captchaInfo.CaptchaId,
PicPath: captchaInfo.PicPath,
}, nil
}
func (uc *UserUsecase) UserDetailByID(ctx context.Context) (*v1.UserDetailResponse, error) {
// 在上下文 context 中取出 claims 对象
var uId int64
if claims, ok := jwt.FromContext(ctx); ok {
c := claims.(jwt2.MapClaims)
if c["ID"] == nil {
return nil, ErrAuthFailed
}
uId = int64(c["ID"].(float64))
}
user, err := uc.uRepo.UserById(ctx, uId)
if err != nil {
return nil, err
}
return &v1.UserDetailResponse{
Id: user.ID,
NickName: user.NickName,
Mobile: user.Mobile,
}, nil
}
func (uc *UserUsecase) PassWordLogin(ctx context.Context, req *v1.LoginReq) (*v1.RegisterReply, error) {
// 表单验证
if len(req.Mobile) <= 0 {
return nil, ErrMobileInvalid
}
if len(req.Password) <= 0 {
return nil, ErrUsernameInvalid
}
// 验证验证码是否正确
if !captcha.Store.Verify(req.CaptchaId, req.Captcha, true) {
return nil, ErrCaptchaInvalid
}
if user, err := uc.uRepo.UserByMobile(ctx, req.Mobile); err != nil {
return nil, ErrUserNotFound
} else {
// 用户存在检查密码
if passRsp, pasErr := uc.uRepo.CheckPassword(ctx, req.Password, user.Password); pasErr != nil {
return nil, ErrPasswordInvalid
} else {
if passRsp {
claims := auth.CustomClaims{
ID: user.ID,
NickName: user.NickName,
AuthorityId: user.Role,
StandardClaims: jwt2.StandardClaims{
NotBefore: time.Now().Unix(), // 签名的生效时间
ExpiresAt: time.Now().Unix() + 60*60*24*30, // 30天过期
Issuer: "Gyl",
},
}
token, err := auth.CreateToken(claims, uc.signingKey)
if err != nil {
return nil, ErrGenerateTokenFailed
}
return &v1.RegisterReply{
Id: user.ID,
Mobile: user.Mobile,
Username: user.NickName,
Token: token,
ExpiredAt: time.Now().Unix() + 60*60*24*30,
}, nil
} else {
return nil, ErrLoginFailed
}
}
}
}
func (uc *UserUsecase) CreateUser(ctx context.Context, req *v1.RegisterReq) (*v1.RegisterReply, error) {
newUser, err := NewUser(req.Mobile, req.Username, req.Password)
if err != nil {
return nil, err
}
createUser, err := uc.uRepo.CreateUser(ctx, &newUser)
if err != nil {
return nil, err
}
claims := auth.CustomClaims{
ID: createUser.ID,
NickName: createUser.NickName,
AuthorityId: createUser.Role,
StandardClaims: jwt2.StandardClaims{
NotBefore: time.Now().Unix(), // 签名的生效时间
ExpiresAt: time.Now().Unix() + 60*60*24*30, // 30天过期
Issuer: "Gyl",
},
}
token, err := auth.CreateToken(claims, uc.signingKey)
if err != nil {
return nil, err
}
return &v1.RegisterReply{
Id: createUser.ID,
Mobile: createUser.Mobile,
Username: createUser.NickName,
Token: token,
ExpiredAt: time.Now().Unix() + 60*60*24*30,
}, nil
}
func NewUser(mobile, username, password string) (User, error) {
// check mobile
if len(mobile) <= 0 {
return User{}, ErrMobileInvalid
}
// check username
if len(username) <= 0 {
return User{}, ErrUsernameInvalid
}
// check password
if len(password) <= 0 {
return User{}, ErrPasswordInvalid
}
return User{
Mobile: mobile,
NickName: username,
Password: password,
}, nil
}
- 修改
shop/internal/biz/biz.go
文件
package biz
import "github.com/google/wire"
// ProviderSet is biz providers.
var ProviderSet = wire.NewSet(NewUserUsecase)
- 修改
internal/data/data.go
这里比较重要,data 不是直接链接本机器配置的数据库的,而是链接各个服务,通过服务提供的 rpc 接口去获取数据的。
package data
import (
"context"
consul "github.com/go-kratos/kratos/contrib/registry/consul/v2"
"github.com/go-kratos/kratos/v2/log"
"github.com/go-kratos/kratos/v2/middleware/recovery"
"github.com/go-kratos/kratos/v2/registry"
"github.com/go-kratos/kratos/v2/transport/grpc"
"github.com/google/wire"
consulAPI "github.com/hashicorp/consul/api"
grpcx "google.golang.org/grpc"
userV1 "shop/api/service/user/v1"
"shop/internal/conf"
"time"
)
// ProviderSet is data providers.
var ProviderSet = wire.NewSet(NewData, NewUserRepo, NewUserServiceClient, NewRegistrar, NewDiscovery)
// Data .
type Data struct {
log *log.Helper
uc userV1.UserClient // 用户服务的客户端
}
// NewData .
func NewData(c *conf.Data, uc userV1.UserClient, logger log.Logger) (*Data, error) {
l := log.NewHelper(log.With(logger, "module", "data"))
return &Data{log: l, uc: uc}, nil
}
// NewUserServiceClient 链接用户服务
func NewUserServiceClient(ac *conf.Auth, sr *conf.Service, rr registry.Discovery) userV1.UserClient {
conn, err := grpc.DialInsecure(
context.Background(),
grpc.WithEndpoint(sr.User.Endpoint),// consul
grpc.WithDiscovery(rr),// consul
grpc.WithMiddleware(
recovery.Recovery(),
),
grpc.WithTimeout(2*time.Second),
)
if err != nil {
panic(err)
}
c := userV1.NewUserClient(conn)
return c
}
// NewRegistrar add consul
func NewRegistrar(conf *conf.Registry) registry.Registrar {
c := consulAPI.DefaultConfig()
c.Address = conf.Consul.Address
c.Scheme = conf.Consul.Scheme
cli, err := consulAPI.NewClient(c)
if err != nil {
panic(err)
}
r := consul.New(cli, consul.WithHealthCheck(false))
return r
}
func NewDiscovery(conf *conf.Registry) registry.Discovery {
c := consulAPI.DefaultConfig()
c.Address = conf.Consul.Address
c.Scheme = conf.Consul.Scheme
cli, err := consulAPI.NewClient(c)
if err != nil {
panic(err)
}
r := consul.New(cli, consul.WithHealthCheck(false))
return r
}
- 修改
shop/internal/data/user.go
package data
import (
"context"
"github.com/go-kratos/kratos/v2/log"
userService "shop/api/service/user/v1"
"shop/internal/biz"
)
type userRepo struct {
data *Data
log *log.Helper
}
// NewUserRepo .
func NewUserRepo(data *Data, logger log.Logger) biz.UserRepo {
return &userRepo{
data: data,
log: log.NewHelper(log.With(logger, "module", "repo/user")),
}
}
func (u *userRepo) CreateUser(c context.Context, user *biz.User) (*biz.User, error) {
createUser, err := u.data.uc.CreateUser(c, &userService.CreateUserInfo{
NickName: user.NickName,
Password: user.Password,
Mobile: user.Mobile,
})
if err != nil {
return nil, err
}
return &biz.User{
ID: createUser.Id,
Mobile: createUser.Mobile,
NickName: createUser.NickName,
}, nil
}
func (u *userRepo) UserByMobile(c context.Context, mobile string) (*biz.User, error) {
byMobile, err := u.data.uc.GetUserByMobile(c, &userService.MobileRequest{Mobile: mobile})
if err != nil {
return nil, err
}
return &biz.User{
Mobile: byMobile.Mobile,
ID: byMobile.Id,
NickName: byMobile.NickName,
}, nil
}
func (u *userRepo) CheckPassword(c context.Context, password, encryptedPassword string) (bool, error) {
if byMobile, err := u.data.uc.CheckPassword(c, &userService.PasswordCheckInfo{Password: password, EncryptedPassword: encryptedPassword}); err != nil {
return false, err
} else {
return byMobile.Success, nil
}
}
func (u *userRepo) UserById(c context.Context, id int64) (*biz.User, error) {
user, err := u.data.uc.GetUserById(c, &userService.IdRequest{Id: id})
if err != nil {
return nil, err
}
return &biz.User{
ID: user.Id,
Mobile: user.Mobile,
NickName: user.NickName,
Gender: user.Gender,
Role: int(user.Role),
}, nil
}
修改启动服务
- 修改
wire.go
一定要注意这里的注入的参数,多了少了都会报错的
package main
...
func initApp(*conf.Server, *conf.Data, *conf.Auth, *conf.Service, *conf.Registry, log.Logger) (*kratos.App, func(), error) {
panic(wire.Build(server.ProviderSet, data.ProviderSet, biz.ProviderSet, service.ProviderSet, newApp))
}
- 重新生成依赖注入关系
根目录执行
make wire
- 修改入口文件
main.go
package main
import (
"flag"
"os"
"github.com/go-kratos/kratos/v2"
"github.com/go-kratos/kratos/v2/config"
"github.com/go-kratos/kratos/v2/config/file"
"github.com/go-kratos/kratos/v2/log"
"github.com/go-kratos/kratos/v2/registry"
"github.com/go-kratos/kratos/v2/transport/grpc"
"github.com/go-kratos/kratos/v2/transport/http"
"shop/internal/conf"
)
// go build -ldflags "-X main.Version=x.y.z"
var (
// Name is the name of the compiled software.
Name = "shop.api"
// Version is the version of the compiled software.
Version = "shop.api.v1"
// flagconf is the config flag.
flagconf string
id, _ = os.Hostname()
)
func init() {
flag.StringVar(&flagconf, "conf", "../../configs", "config path, eg: -conf config.yaml")
}
func newApp(logger log.Logger, hs *http.Server, gs *grpc.Server, rr registry.Registrar) *kratos.App {
return kratos.New(
kratos.ID(id+"shop.api"),
kratos.Name(Name),
kratos.Version(Version),
kratos.Metadata(map[string]string{}),
kratos.Logger(logger),
kratos.Server(
hs,
),
kratos.Registrar(rr),
)
}
func main() {
flag.Parse()
logger := log.With(log.NewStdLogger(os.Stdout),
"ts", log.DefaultTimestamp,
"caller", log.DefaultCaller,
"service.id", id,
"service.name", Name,
"service.version", Version,
"trace_id", tracing.TraceID(),
"span_id", tracing.SpanID(),
)
c := config.New(
config.WithSource(
file.NewSource(flagconf),
),
)
defer c.Close()
if err := c.Load(); err != nil {
panic(err)
}
var bc conf.Bootstrap
if err := c.Scan(&bc); err != nil {
panic(err)
}
var rc conf.Registry
if err := c.Scan(&rc); err != nil {
panic(err)
}
app, cleanup, err := initApp(bc.Server, bc.Data, bc.Auth, bc.Service, &rc, logger)
if err != nil {
panic(err)
}
defer cleanup()
// start and wait for stop signal
if err := app.Run(); err != nil {
panic(err)
}
}
结束语
这一篇主要是前期的准备工作,下一篇开始测试接口、测试服务注册与发现、加入链路追踪并测试。
感谢您的耐心阅读,动动手指点个赞吧。