preface
We will show you one in detail through a series of articles go-zero Microservice example , The whole series is divided into ten articles , The directory structure is as follows :
- Environment building
- Service split
- Customer service
- Product service
- Order service
- Payment services
- RPC service Auth verification
- Service monitoring
- Link tracking
- Distributed transactions ( this paper )
I hope this series will take you to use it locally Docker Environmental utilization go-zero Quickly develop a mall system , Let you get started with micro services quickly .
Complete sample code :https://github.com/nivin-studio/go-zero-mall
First , Let's take a look at the overall service split diagram :

10.1 DTM Introduce
DTM Is a golang Developed distributed transaction manager , It solves the problem of cross database 、 Cross Service 、 Consistency of updating data across language stacks .
The vast majority of order system transactions will cross Services , Therefore, there is a need to update data consistency , Both can pass DTM Greatly simplify the architecture , Form an elegant solution .
and DTM Already in-depth cooperation , Native support go-zero Distributed transactions in , Here's how to use it in detail DTM To help our order system solve the consistency problem
10.2 go-zero Use DTM
First, let's review The fifth chapter Order service in order rpc In service Create Interface processing logic . The method judges the legitimacy of users and products , And whether the product inventory is sufficient , Finally through OrderModel Created a new order , And call product rpc service Update The interface updates the inventory of products .
func (l *CreateLogic) Create(in *order.CreateRequest) (*order.CreateResponse, error) {
// Query whether the user exists
_, err := l.svcCtx.UserRpc.UserInfo(l.ctx, &user.UserInfoRequest{
Id: in.Uid,
})
if err != nil {
return nil, err
}
// Check whether the product exists
productRes, err := l.svcCtx.ProductRpc.Detail(l.ctx, &product.DetailRequest{
Id: in.Pid,
})
if err != nil {
return nil, err
}
// Judge whether the product inventory is sufficient
if productRes.Stock <= 0 {
return nil, status.Error(500, " Insufficient product inventory ")
}
newOrder := model.Order{
Uid: in.Uid,
Pid: in.Pid,
Amount: in.Amount,
Status: 0,
}
res, err := l.svcCtx.OrderModel.Insert(&newOrder)
if err != nil {
return nil, status.Error(500, err.Error())
}
newOrder.Id, err = res.LastInsertId()
if err != nil {
return nil, status.Error(500, err.Error())
}
_, err = l.svcCtx.ProductRpc.Update(l.ctx, &product.UpdateRequest{
Id: productRes.Id,
Name: productRes.Name,
Desc: productRes.Desc,
Stock: productRes.Stock - 1,
Amount: productRes.Amount,
Status: productRes.Status,
})
if err != nil {
return nil, err
}
return &order.CreateResponse{
Id: newOrder.Id,
}, nil
}
We said before , There is a problem of data consistency in the processing logic , It is possible that the order was created successfully , However, failure may occur when updating the product inventory , At this time, the order will be created successfully , There is no reduction in product inventory .
Because the product inventory update here is a cross service operation , There is no way to use local transactions to handle , So we need to use distributed transactions to deal with it . We need help here DTM Of SAGA Protocol to realize the cross service distributed transaction operation of order creation and product inventory update .
You can move to DTM I'll take the following documents first SAGA Transaction mode .
10.2.1 add to DTM Service configuration
See Chapter one Environment building , modify dtm->config.yml The configuration file . We just need to modify MicroService Medium Target,EndPoint The configuration can be , take dtm Sign up to etcd in .
# ......
# Microservices
MicroService:
Driver: 'dtm-driver-gozero' # To process registration / The name of the driver found
Target: 'etcd://etcd:2379/dtmservice' # register dtm Service etcd Address
EndPoint: 'dtm:36790'
# ......
10.2.2 add to dtm_barrier Data sheet
Microservice is a distributed system , Therefore, various abnormalities may occur , For example, network jitter leads to repeated requests , Such exceptions can complicate business processing . and DTM in , Pioneered Sub transaction barrier technology , Use the technology , It can be very convenient to solve abnormal problems , It greatly reduces the use threshold of distributed transactions .
Use DTM The provided sub transaction barrier technology needs to create sub transaction barrier related tables in the business database , Build the predicative sentence as follows :
create database if not exists dtm_barrier
/*!40100 DEFAULT CHARACTER SET utf8mb4 */
;
drop table if exists dtm_barrier.barrier;
create table if not exists dtm_barrier.barrier(
id bigint(22) PRIMARY KEY AUTO_INCREMENT,
trans_type varchar(45) default '',
gid varchar(128) default '',
branch_id varchar(128) default '',
op varchar(45) default '',
barrier_id varchar(45) default '',
reason varchar(45) default '' comment 'the branch type who insert this record',
create_time datetime DEFAULT now(),
update_time datetime DEFAULT now(),
key(create_time),
key(update_time),
UNIQUE key(gid, branch_id, op, barrier_id)
);
Be careful : Please do not modify the database name and table name , If you customize the table name , Please call before use.
dtmcli.SetBarrierTableName.
10.2.3 modify OrderModel and ProductModel
In each sub transaction , Many operational logic , Need to use local transactions , So let's add some model Method compatible DTM Sub transaction barrier
$ vim mall/service/order/model/ordermodel.go
package model
......
type (
OrderModel interface {
TxInsert(tx *sql.Tx, data *Order) (sql.Result, error)
TxUpdate(tx *sql.Tx, data *Order) error
FindOneByUid(uid int64) (*Order, error)
}
)
......
func (m *defaultOrderModel) TxInsert(tx *sql.Tx, data *Order) (sql.Result, error) {
query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?)", m.table, orderRowsExpectAutoSet)
ret, err := tx.Exec(query, data.Uid, data.Pid, data.Amount, data.Status)
return ret, err
}
func (m *defaultOrderModel) TxUpdate(tx *sql.Tx, data *Order) error {
productIdKey := fmt.Sprintf("%s%v", cacheOrderIdPrefix, data.Id)
_, err := m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) {
query := fmt.Sprintf("update %s set %s where `id` = ?", m.table, orderRowsWithPlaceHolder)
return tx.Exec(query, data.Uid, data.Pid, data.Amount, data.Status, data.Id)
}, productIdKey)
return err
}
func (m *defaultOrderModel) FindOneByUid(uid int64) (*Order, error) {
var resp Order
query := fmt.Sprintf("select %s from %s where `uid` = ? order by create_time desc limit 1", orderRows, m.table)
err := m.QueryRowNoCache(&resp, query, uid)
switch err {
case nil:
return &resp, nil
case sqlc.ErrNotFound:
return nil, ErrNotFound
default:
return nil, err
}
}
$ vim mall/service/product/model/productmodel.go
package model
......
type (
ProductModel interface {
TxAdjustStock(tx *sql.Tx, id int64, delta int) (sql.Result, error)
}
)
......
func (m *defaultProductModel) TxAdjustStock(tx *sql.Tx, id int64, delta int) (sql.Result, error) {
productIdKey := fmt.Sprintf("%s%v", cacheProductIdPrefix, id)
return m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) {
query := fmt.Sprintf("update %s set stock=stock+? where stock >= -? and id=?", m.table)
return tx.Exec(query, delta, delta, id)
}, productIdKey)
}
10.2.4 modify product rpc service
add to
DecrStock,DecrStockRevertInterface methodWe need to
product rpcService addDecrStock、DecrStockRevertTwo interface methods , Used for product inventory update respectively and Compensation for product inventory update .
$ vim mall/service/product/rpc/product.proto
syntax = "proto3";
package productclient;
option go_package = "product";
......
// Less product inventory
message DecrStockRequest {
int64 id = 1;
int64 num = 2;
}
message DecrStockResponse {
}
// Less product inventory
service Product {
......
rpc DecrStock(DecrStockRequest) returns(DecrStockResponse);
rpc DecrStockRevert(DecrStockRequest) returns(DecrStockResponse);
}
Tips : Use... After modification goctl The tool regenerates the next code .
Realization
DecrStockInterface methodHere, only when inventory is insufficient , We don't need to try again , Direct rollback .
$ vim mall/service/product/rpc/internal/logic/decrstocklogic.go
package logic
import (
"context"
"database/sql"
"mall/service/product/rpc/internal/svc"
"mall/service/product/rpc/product"
"github.com/dtm-labs/dtmcli"
"github.com/dtm-labs/dtmgrpc"
"github.com/tal-tech/go-zero/core/logx"
"github.com/tal-tech/go-zero/core/stores/sqlx"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
type DecrStockLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewDecrStockLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DecrStockLogic {
return &DecrStockLogic{
ctx: ctx,
svcCtx: svcCtx,
Logger: logx.WithContext(ctx),
}
}
func (l *DecrStockLogic) DecrStock(in *product.DecrStockRequest) (*product.DecrStockResponse, error) {
// obtain RawDB
db, err := sqlx.NewMysql(l.svcCtx.Config.Mysql.DataSource).RawDB()
if err != nil {
return nil, status.Error(500, err.Error())
}
// Get the sub transaction barrier object
barrier, err := dtmgrpc.BarrierFromGrpc(l.ctx)
if err != nil {
return nil, status.Error(500, err.Error())
}
// Open sub transaction barrier
err = barrier.CallWithDB(db, func(tx *sql.Tx) error {
// Update product inventory
result, err := l.svcCtx.ProductModel.TxAdjustStock(tx, in.Id, -1)
if err != nil {
return err
}
affected, err := result.RowsAffected()
// Insufficient inventory , Failed to return sub transaction
if err == nil && affected == 0 {
return dtmcli.ErrFailure
}
return err
})
// In this case, the inventory is insufficient , No longer try again , Go rollback
if err == dtmcli.ErrFailure {
return nil, status.Error(codes.Aborted, dtmcli.ResultFailure)
}
if err != nil {
return nil, err
}
return &product.DecrStockResponse{}, nil
}
Realization
DecrStockRevertInterface methodstay
DecrStockIn the interface method , Product inventory is minus the specified quantity , Here we add it back . In this way, the product inventory will return toDecrStockInterface methods minus the previous number .
$ vim mall/service/product/rpc/internal/logic/decrstockrevertlogic.go
package logic
import (
"context"
"database/sql"
"mall/service/product/rpc/internal/svc"
"mall/service/product/rpc/product"
"github.com/dtm-labs/dtmcli"
"github.com/dtm-labs/dtmgrpc"
"github.com/tal-tech/go-zero/core/logx"
"github.com/tal-tech/go-zero/core/stores/sqlx"
"google.golang.org/grpc/status"
)
type DecrStockRevertLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewDecrStockRevertLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DecrStockRevertLogic {
return &DecrStockRevertLogic{
ctx: ctx,
svcCtx: svcCtx,
Logger: logx.WithContext(ctx),
}
}
func (l *DecrStockRevertLogic) DecrStockRevert(in *product.DecrStockRequest) (*product.DecrStockResponse, error) {
// obtain RawDB
db, err := sqlx.NewMysql(l.svcCtx.Config.Mysql.DataSource).RawDB()
if err != nil {
return nil, status.Error(500, err.Error())
}
// Get the sub transaction barrier object
barrier, err := dtmgrpc.BarrierFromGrpc(l.ctx)
if err != nil {
return nil, status.Error(500, err.Error())
}
// Open sub transaction barrier
err = barrier.CallWithDB(db, func(tx *sql.Tx) error {
// Update product inventory
_, err := l.svcCtx.ProductModel.TxAdjustStock(tx, in.Id, 1)
return err
})
if err != nil {
return nil, err
}
return &product.DecrStockResponse{}, nil
}
10.2.5 modify order rpc service
add to
CreateRevertInterface methodorder rpcAlready in serviceCreateInterface method 、 We need to create its compensation interface methodCreateRevert.
$ vim mall/service/order/rpc/order.proto
syntax = "proto3";
package orderclient;
option go_package = "order";
......
service Order {
rpc Create(CreateRequest) returns(CreateResponse);
rpc CreateRevert(CreateRequest) returns(CreateResponse);
......
}
Tips : Use... After modification goctl The tool regenerates the next code .
modify
CreateInterface methodoriginal
CreateProduct inventory judgment and update in the interface method , We've beenproduct rpcDecrStockInterface method , So we just need to create an order here .
$ vim mall/service/order/rpc/internal/logic/createlogic.go
package logic
import (
"context"
"database/sql"
"fmt"
"mall/service/order/model"
"mall/service/order/rpc/internal/svc"
"mall/service/order/rpc/order"
"mall/service/user/rpc/user"
"github.com/dtm-labs/dtmgrpc"
"github.com/tal-tech/go-zero/core/logx"
"github.com/tal-tech/go-zero/core/stores/sqlx"
"google.golang.org/grpc/status"
)
type CreateLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewCreateLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateLogic {
return &CreateLogic{
ctx: ctx,
svcCtx: svcCtx,
Logger: logx.WithContext(ctx),
}
}
func (l *CreateLogic) Create(in *order.CreateRequest) (*order.CreateResponse, error) {
// obtain RawDB
db, err := sqlx.NewMysql(l.svcCtx.Config.Mysql.DataSource).RawDB()
if err != nil {
return nil, status.Error(500, err.Error())
}
// Get the sub transaction barrier object
barrier, err := dtmgrpc.BarrierFromGrpc(l.ctx)
if err != nil {
return nil, status.Error(500, err.Error())
}
// Open sub transaction barrier
if err := barrier.CallWithDB(db, func(tx *sql.Tx) error {
// Query whether the user exists
_, err := l.svcCtx.UserRpc.UserInfo(l.ctx, &user.UserInfoRequest{
Id: in.Uid,
})
if err != nil {
return fmt.Errorf(" The user doesn't exist ")
}
newOrder := model.Order{
Uid: in.Uid,
Pid: in.Pid,
Amount: in.Amount,
Status: 0,
}
// Create order
_, err = l.svcCtx.OrderModel.TxInsert(tx, &newOrder)
if err != nil {
return fmt.Errorf(" Order creation failed ")
}
return nil
}); err != nil {
return nil, status.Error(500, err.Error())
}
return &order.CreateResponse{}, nil
}
Realization
CreateRevertInterface methodIn this interface, we query the order just created by the user , Change the status of the order to
9( Invalid state ).
$ vim mall/service/order/rpc/internal/logic/createrevertlogic.go
package logic
import (
"context"
"database/sql"
"fmt"
"mall/service/order/rpc/internal/svc"
"mall/service/order/rpc/order"
"mall/service/user/rpc/user"
"github.com/dtm-labs/dtmgrpc"
"github.com/tal-tech/go-zero/core/logx"
"github.com/tal-tech/go-zero/core/stores/sqlx"
"google.golang.org/grpc/status"
)
type CreateRevertLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewCreateRevertLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateRevertLogic {
return &CreateRevertLogic{
ctx: ctx,
svcCtx: svcCtx,
Logger: logx.WithContext(ctx),
}
}
func (l *CreateRevertLogic) CreateRevert(in *order.CreateRequest) (*order.CreateResponse, error) {
// obtain RawDB
db, err := sqlx.NewMysql(l.svcCtx.Config.Mysql.DataSource).RawDB()
if err != nil {
return nil, status.Error(500, err.Error())
}
// Get the sub transaction barrier object
barrier, err := dtmgrpc.BarrierFromGrpc(l.ctx)
if err != nil {
return nil, status.Error(500, err.Error())
}
// Open sub transaction barrier
if err := barrier.CallWithDB(db, func(tx *sql.Tx) error {
// Query whether the user exists
_, err := l.svcCtx.UserRpc.UserInfo(l.ctx, &user.UserInfoRequest{
Id: in.Uid,
})
if err != nil {
return fmt.Errorf(" The user doesn't exist ")
}
// Query the latest order created by the user
resOrder, err := l.svcCtx.OrderModel.FindOneByUid(in.Uid)
if err != nil {
return fmt.Errorf(" The order does not exist ")
}
// Modify order status 9, Identify that the order has expired , And update the order
resOrder.Status = 9
err = l.svcCtx.OrderModel.TxUpdate(tx, resOrder)
if err != nil {
return fmt.Errorf(" Order update failed ")
}
return nil
}); err != nil {
return nil, status.Error(500, err.Error())
}
return &order.CreateResponse{}, nil
}
10.2.6 modify order api service
We put order rpc service Create、CreateRevert Interface method ,product rpc service DecrStock、DecrStockRevert Interface method , mention order api Make one in the service SAGA Transaction mode Distributed transaction operations .
- add to
pproduct rpcDepend on the configuration
$ vim mall/service/order/api/etc/order.yaml
Name: Order
Host: 0.0.0.0
Port: 8002
......
OrderRpc:
Etcd:
Hosts:
- etcd:2379
Key: order.rpc
ProductRpc:
Etcd:
Hosts:
- etcd:2379
Key: product.rpc
- add to
pproduct rpcInstantiation of service configuration
$ vim mall/service/order/api/internal/config/config.go
package config
import (
"github.com/tal-tech/go-zero/rest"
"github.com/tal-tech/go-zero/zrpc"
)
type Config struct {
rest.RestConf
Auth struct {
AccessSecret string
AccessExpire int64
}
OrderRpc zrpc.RpcClientConf
ProductRpc zrpc.RpcClientConf
}
- Register service context
pproduct rpcDependence
$ vim mall/service/order/api/internal/svc/servicecontext.go
package svc
import (
"mall/service/order/api/internal/config"
"mall/service/order/rpc/orderclient"
"mall/service/product/rpc/productclient"
"github.com/tal-tech/go-zero/zrpc"
)
type ServiceContext struct {
Config config.Config
OrderRpc orderclient.Order
ProductRpc productclient.Product
}
func NewServiceContext(c config.Config) *ServiceContext {
return &ServiceContext{
Config: c,
OrderRpc: orderclient.NewOrder(zrpc.MustNewClient(c.OrderRpc)),
ProductRpc: productclient.NewProduct(zrpc.MustNewClient(c.ProductRpc)),
}
}
- Add import
gozeroOfdtmdrive
$ vim mall/service/order/api/order.go
package main
import (
......
_ "github.com/dtm-labs/driver-gozero" // Add import `gozero` Of `dtm` drive
)
var configFile = flag.String("f", "etc/order.yaml", "the config file")
func main() {
......
}
- modify
order apiCreateInterface method
$ vim mall/service/order/api/internal/logic/createlogic.go
package logic
import (
"context"
"mall/service/order/api/internal/svc"
"mall/service/order/api/internal/types"
"mall/service/order/rpc/order"
"mall/service/product/rpc/product"
"github.com/dtm-labs/dtmgrpc"
"github.com/tal-tech/go-zero/core/logx"
"google.golang.org/grpc/status"
)
type CreateLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewCreateLogic(ctx context.Context, svcCtx *svc.ServiceContext) CreateLogic {
return CreateLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *CreateLogic) Create(req types.CreateRequest) (resp *types.CreateResponse, err error) {
// obtain OrderRpc BuildTarget
orderRpcBusiServer, err := l.svcCtx.Config.OrderRpc.BuildTarget()
if err != nil {
return nil, status.Error(100, " Order creation exception ")
}
// obtain ProductRpc BuildTarget
productRpcBusiServer, err := l.svcCtx.Config.ProductRpc.BuildTarget()
if err != nil {
return nil, status.Error(100, " Order creation exception ")
}
// dtm Service etcd Registered address
var dtmServer = "etcd://etcd:2379/dtmservice"
// Create a gid
gid := dtmgrpc.MustGenGid(dtmServer)
// Create a saga The transaction of the agreement
saga := dtmgrpc.NewSagaGrpc(dtmServer, gid).
Add(orderRpcBusiServer+"/orderclient.Order/Create", orderRpcBusiServer+"/orderclient.Order/CreateRevert", &order.CreateRequest{
Uid: req.Uid,
Pid: req.Pid,
Amount: req.Amount,
Status: 0,
}).
Add(productRpcBusiServer+"/productclient.Product/DecrStock", productRpcBusiServer+"/productclient.Product/DecrStockRevert", &product.DecrStockRequest{
Id: req.Pid,
Num: 1,
})
// Transaction submission
err = saga.Submit()
if err != nil {
return nil, status.Error(500, err.Error())
}
return &types.CreateResponse{}, nil
}
Tips :
SagaGrpc.AddMethod first parameteractionIt's microservicegrpcAccess method path , This method path needs to be found in the following files .mall/service/order/rpc/order/order.pb.gomall/service/product/rpc/product/product.pb.go
By keywordInvokeSearch to find .

10.3 test go-zero + DTM
10.3.1 Test the normal flow of distributed transactions
- Use
postmancall/api/product/createInterface , Create a product , stockstockby1.


- Use
postmancall/api/order/createInterface , Create an order , product IDpidby1.


- We can see that , The inventory of products has changed from the original
1Has become0.

- Let's look at the sub transaction barrier table
barrierThe data in , We can see that the operations of both services have been completed .

10.3.2 Test distributed transaction failed process 1
- Then the above test results , Products at this time ID by
1Our inventory is already0了 , Usepostmancall/api/order/createInterface , Create another order .

- Let's see there's one in the order data sheet ID by
2product ID by1The data of , Its order data status is9.

- Let's look at the sub transaction barrier table
barrierThe data in , We can see that(gid = fqYS8CbYbK8GkL8SCuTRUF)First service(branch_id = 01)The sub transaction barrier is normal operation , The second service(branch_id = 02)Sub transaction barrier operation failed , Ask for compensation . Therefore, the operation records of compensation have occurred in both services .

The operation flow of this distributed transaction
- First
DTMService meetingorder rpcCreateInterface to create order processing . - After the order is created
DTMService re adjustmentproduct rpcDecrStockInterface , The of this interface is throughpidUpdate product inventory , Due to insufficient product inventory , Throw transaction failed . DTMService initiation compensation mechanism , transferorder rpcCreateRevertThe interface is used to process the compensation of the order .DTMService initiation compensation mechanism , transferproduct rpcDecrStockRevertInterface for compensation processing of product inventory update . But because inproduct rpcDecrStockWithin the sub transaction barrier of the interface , Business processing was not successful . So inDecrStockRevertThe business logic in the sub transaction barrier will not be executed in the interface .
- First
10.3.3 Test distributed transaction failed process 2
- We manually put the product in the database ID by
1The inventory is modified to 100, And then inproduct rpcDecrStockThe interface method is outside the transaction barrier , Man made abnormal failure .

- Use
postmancall/api/order/createInterface , Create another order , product IDpidby1.

- Let's look at the order data sheet and product data sheet respectively , Order data sheet ID by
3The order of , Its order data status is9. Product data sheet ID by1Products , Its inventory is still100And the data update time has also changed .


- Let's look at the sub transaction barrier table
barrierThe data in , We can see that(gid = ZbjYHv2jNra7RMwyWjB5Lc)First service(branch_id = 01)The sub transaction barrier is normal operation , The second service(branch_id = 02)The sub transaction barrier operation is also normal . Because inproduct rpcDecrStockThe interface method is outside the transaction barrier , We artificially create abnormal failures , Therefore, the operation records of compensation occurred in the two services .

You can compare Test distributed transaction failed process 1 And Test distributed transaction failed process 2 The difference , Can you find and experience DTM The strength of this sub transaction barrier technology .
The sub transaction barrier will automatically identify whether the forward operation has been performed , Failure process 1 Business operation not performed , So when compensation , It will not perform compensation business operations ; Failure process 2 Performed business operations , So when compensation , It will also perform compensation business operations .
Project address
https://github.com/zeromicro/go-zero
Welcome to use go-zero and star Support us !
WeChat ac group
Focus on 『 Microservice practice 』 Official account and click Communication group Get community group QR code .
Take you ten days to easily finish Go The grand finale of microservices ( Distributed transactions ) More articles about
- Take you ten days to easily finish Go Micro service series ( One )
This article begins , We will publish a series of articles to show you one in detail go-zero Microservice example , The whole series is divided into ten articles , The directory structure is as follows : Environment building ( this paper ) Service split Customer service Product service Order service Payment services RPC service Au ...
- Take you ten days to easily finish Go Micro service series ( Two )
The last article begins with , We will show you one in detail through a series of articles go-zero Microservice example , The whole series is divided into ten articles , The directory structure is as follows : Environment building Service split ( this paper ) Customer service Product service Order service Payment services RPC service ...
- Take you ten days to easily finish Go Micro service series ( 3、 ... and )
preface We will show you one in detail through a series of articles go-zero Microservice example , The whole series is divided into ten articles , The directory structure is as follows : Environment building Service split Customer service ( this paper ) Product service Order service Payment services RPC service Auth ...
- Take you ten days to easily finish Go Micro service series ( 5、 ... and )
preface We will show you one in detail through a series of articles go-zero Microservice example , The whole series is divided into ten articles , The directory structure is as follows : Environment building Service split Customer service Product service Order service ( this paper ) Payment services RPC service Auth ...
- Take you ten days to easily finish Go Micro service series ( 6、 ... and )
preface We will show you one in detail through a series of articles go-zero Microservice example , The whole series is divided into ten articles , The directory structure is as follows : Environment building Service split Customer service Product service Order service Payment services ( this paper ) RPC service Auth ...
- Take you ten days to easily finish Go Micro service series ( 7、 ... and )
preface We will show you one in detail through a series of articles go-zero Microservice example , The whole series is divided into ten articles , The directory structure is as follows : Environment building Service split Customer service Product service Order service Payment services RPC service Auth verification ( ...
- Take you ten days to easily finish Go Micro service series ( 8、 ... and 、 Service monitoring )
preface We will show you one in detail through a series of articles go-zero Microservice example , The whole series is divided into ten articles , The directory structure is as follows : Environment building Service split Customer service Product service Order service Payment services RPC service Auth verification ...
- 【 Microservices 】 The second : Starting from scratch , Easy to handle SpringCloud Micro service series -- Registry Center ( One )
Micro service system , Effectively solve the huge project . The problem of interdependence . at present SpringCloud The system has a powerful set of solutions for micro services . In this paper , This paper focuses on the service discovery registry in the micro service system . The registry in this article , use Netf ...
- 【 Microservices 】 Of the seven : Easy to handle SpringCloud Microservices -API Access control
Access control , It is an important function of a system . Zhang San can only access the specific functions of the input Zhang San , Li Si can't access the specific menu belonging to Zhao Liu . This requires a perfect authority control system for the whole system . The system should be able to distinguish users . jurisdiction . Roles and other necessary functions ...
- 【 Microservices 】 A third : Starting from scratch , Easy to handle SpringCloud Microservices - Configuration center
In the whole microservice system , In addition to the importance of a registry , There's also a registry . Registry serves as an important carrier to manage the configuration files and dynamic parameters of the whole project group .Spring Cloud In the subprojects of the system ,Spring Clou ...
Random recommendation
- shell test usage
1) Judging expressions if test ( Expression is true ) if test ! Expression is false test expression 1 –a expression 2 Both expressions are true test expression 1 –o expression ...
- Java Object access Static variable of class
Java Static variables of a class can be accessed by both object and class name , Generally use the class name , But what if you use objects to access static variables , What effect does it have ? Test it : package JavaTest; public class test{ public s ...
- no-jquery 03 Ajax
Ajax Requests GETting var xhr = new XMLHttpRequest(); xhr.open('GET', encodeURI('myservice/username? ...
- [Stephen]Android Of adb Can't start
1. Operating income in the program cmd, open dos Command window , Run successively in the window abd kill-server and adb start-server Try to restart adb service 2. If the boot still fails dos Type... In the command window ...
- python Based on learning ( Twelve )
modular There is a brief introduction to how to use import Get functions from external modules and use them for your own programs : >>> import math >>> math.sin(0) #sin Is a sine function ...
- SharpMap stay web Application on
Recently, the company uses SharpMap Made a desktop program , It's open source Gis project , The function is OK , The biggest feature is simple and easy to use , Here is how to web Next use : This time we are based on demo First learn how show A map . This is the most basic ...
- elasticsearch Dynamic template settings
Custom dynamic mapping If you want to add new fields at runtime , You may enable dynamic mapping . However , occasionally , Dynamic mapping The rules May not be very intelligent . Fortunately, , We can customize these rules by setting , In order to better apply to your data . Date of inspection When E ...
- Android Title head sliding gradient ,Titlebar Sliding gradient , Is Mimi Tuan hungry ;
The principle is to change transparency while sliding : Core code : rv.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public vo ...
- Android Of API Version and name correspondence
Android Version name and API Level The full name of the relationship Android Version of Android Version name Code name Android Of API level Android 1.0 (API level 1) ...
- verilog Implementation of millisecond timer
verilog Implementation of millisecond timer Overall circuit diagram Experimental state diagram Stop It means there's no time ,Start It's time to start ,Inc It stands for timer plus 1,Trap representative inc When the button is pressed down, the chattering state is eliminated . Status code table Experimental design ideas The clock is ten minutes ...






![[QT] Qt development environment installation (QT version 5.14.2 | QT download | QT installation)](/img/18/f0c9ef6250a717f8e66c95da4de08c.jpg)
![[visual studio 2019] create and import cmake project](/img/51/6c2575030c5103aee6c02bec8d5e77.jpg)

