<!-- go-zero微服务实战系列(三、API定义和表结构设计)-->

前两篇文章别离介绍了本系列文章的背景以及依据业务职能对商城零碎做了服务的拆分,其中每个服务又可分为如下三类:

  • api服务 - BFF层,对外提供HTTP接口
  • rpc服务 - 外部依赖的微服务,实现繁多的业务性能
  • rmq服务 - 负责流式工作的解决,如生产kafka等等
  • admin服务 - 对外部治理后盾提供HTTP接口,通常数据操作权限比拟高

如果没看过前两篇文章可通过如下传送门查看

go-zero 微服务实战系列(一、开篇)

go-zero微服务实战系列(二、服务拆分)

前两篇文章比拟偏实践,以至于文章收回去后有些同学感觉写得比拟水,十分了解大家迫切想要写代码的情绪,我也进行了粗浅的反思哈哈哈。所以从本篇开始就要进入万众期待的代码环节了。然而,所谓磨刀不误砍柴工,在真正的生产开发过程中,咱们个别都会花大量的工夫在需要的了解和协定的设计上,如果需要了解的不透彻或者协定设计的不合理就会大大增加咱们我的项目返工的可能,甚至还没上线就得重构。所以后期多投入一些工夫也齐全是值得的。当咱们把需要了解透彻,我的项目构造和协定定义清晰后,其实写代码就是因势利导的事件,速度那是大大滴快。闲言少叙,咱们开始明天的内容。

API定义

可能大家在工作中都遇到过这样的场景,就是代码更新了然而文档没有更新,从而产生一些问题导致一些扯皮事件的产生。这个问题的实质是服务和文档是割裂的。咱们冀望的是文档即协定,协定即服务,这个理念与go-zero的api定义不约而同。

咱们定义了BFF层,BFF是对外提供HTTP接口的对立进口,所以咱们这里API的定义次要是针对BFF服务的API的定义。

API的兼容性

咱们定义或批改API的时候肯定要思考向前兼容,如下几种状况是向前兼容的:

  • 减少新的API接口协议
  • 申请参数增加字段,须要保障新老客户端对该字段的解决形式不同
  • 响应后果增加字段,该字段信息只会在新版本客户端中展现

如下几种状况是向前不兼容的:

  • 删除或重命名服务、字段、办法等,从实质上说,如果客户端代码能够援用某些内容,那么删除或者重命名它都是不兼容的变动,这时必须批改major版本号
  • 批改字段类型,这会导致客户端库生成的代码发生变化,因而必须减少major版本号,对于编译型动态语言来说,可能会编译谬误
  • 批改现有申请的可见行为,客户端通常依赖于API行为和语义,即便这样的行为没有被明确反对或记录。因而,在大多数状况下,批改API数据的行为或语义将被消费者视为是破坏性的
  • 给资源音讯增加 读取/写入 字段
首页API定义

首页性能次要分为四个局部,搜寻、Banner图、限时抢购和举荐商品列表,点击搜寻框会跳转到搜寻页,举荐局部是分页展现的,用户通过一直地往上滑动能够加载下一页。通过剖析首页咱们大抵须要提供三个接口,别离是Banner接口,限时抢购接口和举荐接口。

这里须要留神的是举荐接口,举荐接口返回的数据是须要反对分页的,这里分页采纳游标的形式,Ps参数为每页返回数据条数,默认一页返回20条数据,留神在服务端肯定须要再次校验Ps值,避免Ps歹意值导致的性能问题,比方Ps传了10000,当为非法值的时候须要把Ps置为默认值,Cursor为游标值,游标为每页最初一条数据的RecommendTime。

返回值中Products定义了返回的商品列表,IsEnd示意是否是最初一页,客户端通过判断IsEnd是否为true决定是否终止申请,RecommendTime为本页返回数据最初一条数据的举荐工夫,推动列表依照举荐工夫倒序返回。

RecommendRequest {        Cursor int64 `json:"cursor"`        Ps     int64 `form:"ps,default=20"` // 每页大小}RecommendResponse {        Products      []*Product `json:"products"`        IsEnd         bool       `json:"is_end"`         // 是否最初一页        RecommendTime int64      `json:"recommend_time"` // 商品列表最初一个商品的举荐工夫}Product {        ID          int64   `json:"id"`          // 商品ID        Name        string  `json:"name"`        // 产品名称        Description string  `json:"description"` // 商品形容        Price       float64 `json:"price"`       // 商品价格        Stock       int64   `json:"stock"`       // 库存        Category    string  `json:"category"`    // 分类        Status      int64   `json:"status"`      // 状态:1-失常,2-下架        CreateTime  int64   `json:"create_time"` // 创立工夫        UpdateTime  int64   `json:"update_time"` // 更新工夫}

抢购有一个倒计时的性能,咱们这里返回抢购开始工夫,客户端计算剩余时间进行倒计时。

FlashSaleResponse {        StartTime int64      `json:"start_time"` // 抢购开始工夫        Products  []*Product `json:"products"`}
分类API定义

分类列表中能够切换不同的tab来抉择不同的分类,同时在每一种分类上面又能够依照不同的维度进行排序,且反对分页。

分类商品列表和举荐接口的分页形式一样,都是采纳游标的形式,同时分类商品列表须要依据不同的分类和排序属性进行排序,此类须要排序的列表咱们个别会通过redis的sorted set来实现,score为须要排序的属性,比方销量,member为对应商品的id。

CategoryListRequest {        Cursor   int64  `form:"cursor"`        // 分页游标        Ps       int64  `form:"ps,default=20"` // 每页大小        Category string `form:"category"`      // 分类        Sort     string `form:"sort"`          // 排序}CategoryListResponse {        Products []*Product `json:"products"`        IsEnd    bool       `json:"is_end"`        LastVal  int64      `json:"last_val"`}

提到sorted set在这里说一个笔者应用sorted set已经踩过的一个坑。咱们应用缓存的罕用姿态是cache aside模式,即先读缓存,如果缓存命中则间接从缓存中返回数据,如果读取缓存miss了,则回源到DB中读数据,且为了前面更快的读取数据,从DB中读取的数据会回塞到缓存中,且会给缓存设置一个过期工夫。

而为了保障缓存和数据库数据的一致性,当咱们新增数据的时候须要把这条数据也写到缓存中从而保障缓存和数据库数据统一,个别代码会这么写,先通过Exists判断缓存对应的key是否存在,如果存在就往sorted set中减少一条数据,如果不存在则不解决,期待下次来读取列表的时候从新加载列表数据到缓存中。咱们发现有时候缓存中列表数据会变成一条,然而数据其实是有多条的,过后感觉是很诡异的,通过排查最终定位到问题,原来是Exists操作和Zadd两个操作不是原子的操作导致的,也就是在Exists的时候缓存的Key还没有过期,然而在Exists后和进行Zadd前这个key过期了,而后再执行Zadd就导致缓存列表中就只有本次新增的这条数据了。解决这个问题的方法也很简略,不应用Exists判断key是否存在,而是通过Expire给这个key续期,如果key不存在则Expire返回0,key存在则Expire返回1,续期胜利。缓存的应用咱们还踩过很多坑,特地是在高并发的场景下,这个后续文章再具体介绍。

购物车API定义

在这里咱们对购物车的数量做一下限度,咱们限度购物车最多只能加200个商品,这样做是为了在全选的时候下单不会导致过高的写放大,因为加了200条的限度,所以购物车列表不须要分页。

购物车列表申请和返回定义如下:

CartListRequest {        UID int64 `form:"uid"`    }    CartListResponse {        Products []*CartProduct `json:"products"`    }    CartProduct {        Product *Product `json:"product"`        Count   int64    `json:"count"` // 购买数量    }
商品评估API定义

商品评估的性能同样也是须要反对分页的,采纳游标的形式进行分页,同时依照评论工夫进行倒序

评论列表定义如下:

ProductCommentRequest {        ProductID int64 `form:"product_id"`        Cursor    int64 `form:"cursor"`        Ps        int64 `form:"ps,default=20"`    }    ProductCommentResponse {        Comments    []*Comment `json:"comments"`        IsEnd       bool       `json:"is_end"`       // 是否最初一页        CommentTime int64      `json:"comment_time"` // 评论列表最初一个评论的工夫    }    Comment {        ID         int64    `json:"id"`          // 评论ID        ProductID  int64    `json:"product_id"`  // 商品ID        Content    string   `json:"content"`     // 评论内容        Images     []*Image `json:"images"`      // 评论图片        User       *User    `json:"user"`        // 用户信息        CreateTime int64    `json:"create_time"` // 评论工夫        UpdateTime int64    `json:"update_time"` // 更新工夫    }    User {        ID     int64  `json:"id"`     // 用户ID        Name   string `json:"name"`   // 用户名        Avatar string `json:"avatar"` // 头像    }    Image {        ID  int64  `json:"id"`        URL string `json:"url"`    }

以上列出了一些外围的API的定义,商城的性能点十分多,很难短时间内全副定义完,笔者会在工作之余一直的欠缺。定义接口返回数据的时候咱们要尽量的收敛只返回必要的数据。

定义好api后,咱们应用如下命令从新生成我的项目代码,输入如下信息表明生成胜利

$ goctl api go -api api.api -dir .etc/api-api.yaml exists, ignored generationinternal/config/config.go exists, ignored generationapi.go exists, ignored generationinternal/svc/servicecontext.go exists, ignored generationinternal/handler/homebannerhandler.go exists, ignored generationinternal/handler/flashsalehandler.go exists, ignored generationinternal/handler/recommendhandler.go exists, ignored generationinternal/handler/categorylisthandler.go exists, ignored generationinternal/handler/cartlisthandler.go exists, ignored generationinternal/handler/productcommenthandler.go exists, ignored generationinternal/logic/homebannerlogic.go exists, ignored generationinternal/logic/flashsalelogic.go exists, ignored generationinternal/logic/recommendlogic.go exists, ignored generationinternal/logic/categorylistlogic.go exists, ignored generationinternal/logic/cartlistlogic.go exists, ignored generationinternal/logic/productcommentlogic.go exists, ignored generationDone.
RPC定义

因为BFF只负责数据的组装工作,数据真正的起源是各个微服务通过RPC接口提供,接下来咱们来定义各个微服务的proto。如下展现的订单列表页面由两局部数据组成,别离是订单数据和商品数据,也就是咱们的BFF须要依赖order-rpc和product-rpc来实现该页面数据的组装,上面咱们别离来定义order-rpc和product-rpc

order.proto定义如下,service名字为Order,增加了Orders获取订单列表rpc接口。

syntax = "proto3";package order;option go_package="./order";service Order {  rpc Orders(OrdersRequest) returns(OrdersResponse);}message OrdersRequest {  int64 user_id = 1;  int32 status = 2;  int64 cursor = 3;  int32 ps = 4;}message OrdersResponse {  repeated OrderItem orders = 1;  bool is_end = 2;  string create_time = 3;}message OrderItem {  string order_id = 1;  int64 quantity = 2;  float payment = 3;  int64 product_id = 4;  int64 user_id = 5;  int64 create_time = 6;}

应用如下命令从新生成代码,留神这里须要依赖protoc-gen-goprotoc-gen-go-grpc两个插件,木有装置的话执行上面命令会报错

$ goctl rpc protoc order.proto --go_out=. --go-grpc_out=. --zrpc_out=.

生成好后而后启动order-rpc服务,输入如下:

$ go run order.goStarting rpc server at 127.0.0.1:8080...{"level":"warn","ts":"2022-06-09T15:42:21.680+0800","logger":"etcd-client","caller":"v3@v3.5.4/retry_interceptor.go:62","msg":"retrying of unary invoker failed","target":"etcd-endpoints://0xc000029c00/127.0.0.1:2379","attempt":0,"error":"rpc error: code = DeadlineExceeded desc = latest balancer error: last connection error: connection error: desc = \"transport: Error while dialing dial tcp 127.0.0.1:2379: connect: connection refused\""}{"@timestamp":"2022-06-09T15:42:21.682+08:00","caller":"zrpc/server.go:90","content":"context deadline exceeded","level":"error"}panic: context deadline exceeded

什么状况?居然报错了,还好日志输入的比拟具体,通过日志能够看进去如同是本地的etcd没有启动,那咱们就把本地的etcd启动,启动后再次运行order rpc服务,曾经侦听在默认的8080端口上

$ go run order.goStarting rpc server at 127.0.0.1:8080...

product.proto定义如下

syntax = "proto3";package product;option go_package="./product";service Product {  rpc Products(ProductRequest) returns(ProductResponse);}message ProductRequest {  string product_ids = 1;}message ProductResponse {  repeated ProductItem products = 1;}message ProductItem {  int64 product_id = 1;  string name = 2;  string description = 3;  string image_url = 4;}

执行如下命令生成product rpc的代码

$ goctl rpc protoc product.proto --go_out=. --go-grpc_out=. --zrpc_out=.

留神,goctl生成的rpc服务默认侦听在8080端口,因为咱们当初是在本地测试,所以把product rpc默认的端口改为8081,而后启动服务。

Name: product.rpcListenOn: 127.0.0.1:8081Etcd:  Hosts:  - 127.0.0.1:2379  Key: product.rpc
$ go run product.goStarting rpc server at 127.0.0.1:8081...

因为咱们的BFF须要依赖order.rpc和product.rpc,咱们须要先增加配置文件,如下:

Name: api-apiHost: 0.0.0.0Port: 8888OrderRPC:    Etcd:        Hosts:          - 127.0.0.1:2379        Key: order.rpcProductRPC:  Etcd:    Hosts:      - 127.0.0.1:2379    Key: product.rpc

而后在ServiceContext中增加RPC的客户端,如下:

type ServiceContext struct {    Config config.Config    OrderRPC order.Order    ProductRPC product.Product}func NewServiceContext(c config.Config) *ServiceContext {    return &ServiceContext{        Config: c,        OrderRPC: order.NewOrder(zrpc.MustNewClient(c.OrderRPC)),        ProductRPC: product.NewProduct(zrpc.MustNewClient(c.ProductRPC)),    }}

最初只有在订单接口的logic办法中增加逻辑就能够啦,这里只是演示,所以会比较简单:

func (l *OrderListLogic) OrderList(req *types.OrderListRequest) (resp *types.OrderListResponse, err error) {    orderRet, err := l.svcCtx.OrderRPC.Orders(l.ctx, &order.OrdersRequest{UserId: req.UID})    if err != nil {        return nil, err    }    var pids []string    for _, o := range orderRet.Orders {        pids = append(pids, strconv.Itoa(int(o.ProductId)))    }    productRet, err := l.svcCtx.ProductRPC.Products(l.ctx, &product.ProductRequest{ProductIds: strings.Join(pids, ",")})    if err != nil {        return nil, err    }    var orders []*types.Order    for _, o := range orderRet.Orders {        if p, ok := productRet.Products[o.ProductId]; ok {            orders = append(orders, &types.Order{                OrderID: o.OrderId,                ProductName: p.Name,            })        }    }    return &types.OrderListResponse{Orders: orders}, nil}

而后在浏览器中申请订单接口,就能够看到输入了如下的数据,阐明从BFF到RPC的链路曾经买通:

http://127.0.0.1:8888/v1/order/list?uid=123{  "orders": [    {      "order_id": "20220609123456",      "status": 0,      "quantity": 0,      "payment": 0,      "total_price": 0,      "create_time": 0,      "product_id": 0,      "product_name": "测试商品名称",      "product_image": "",      "product_description": ""    }  ],  "is_end": false,  "order_time": 0}

表构造定义

不同的微服务间须要做数据的隔离,每个微服务独占数据库资源,通过RPC调用来获取数据依赖,整体架构如下图所示:

通过以上对API的定义咱们大抵理解了须要哪些数据字段,上面开始进行数据表的设计,建表语句放在我的项目根目录下data.sql文件中,该文件会不断更新,次要波及的库和表定义如下:

用户表次要保留用户信息,在user库中后续可能还会扩大比方用户积分,用户等级等性能

CREATE DATABASE user;USE user;CREATE TABLE `user` (    `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '用户ID',    `username` varchar(50) NOT NULL DEFAULT '' COMMENT '用户名',    `password` varchar(50) NOT NULL DEFAULT '' COMMENT '用户明码,MD5加密',    `phone` varchar(20) NOT NULL DEFAULT '' COMMENT '手机号',    `question` varchar(100) NOT NULL DEFAULT '' COMMENT '找回明码问题',    `answer` varchar(100) NOT NULL DEFAULT '' COMMENT '找回明码答案',    `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创立工夫',    `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新工夫',    PRIMARY KEY (`id`),    KEY `ix_update_time` (`update_time`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';

商品库中次要波及商品表和商品分类表:

CREATE DATABASE product;USE product;CREATE TABLE `product` (    `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '商品id',    `cateid` smallint(6) UNSIGNED NOT NULL DEFAULT 0 COMMENT '类别Id',    `name` varchar(100) NOT NULL DEFAULT '' COMMENT '商品名称',    `subtitle` varchar(200) DEFAULT NULL DEFAULT '' COMMENT '商品副标题',    `images` text COMMENT '图片地址,json格局,扩大用',    `detail` text COMMENT '商品详情',    `price` decimal(20,2) NOT NULL DEFAULT 0 COMMENT '价格,单位-元保留两位小数',    `stock` int(11) NOT NULL DEFAULT 0 COMMENT '库存数量',    `status` int(6) NOT NULL DEFAULT 1 COMMENT '商品状态.1-在售 2-下架 3-删除',    `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创立工夫',    `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新工夫',    PRIMARY KEY (`id`),    KEY `ix_cateid` (`cateid`),    KEY `ix_update_time` (`update_time`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品表';CREATE TABLE `category` (    `id` smallint(6) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '分类id',    `parentid` smallint(6) NOT NULL DEFAULT 0 COMMENT '父类别id当id=0时阐明是根节点,一级类别',    `name` varchar(50) NOT NULL DEFAULT '' COMMENT '类别名称',    `status` tinyint(4) NOT NULL DEFAULT 1 COMMENT '类别状态1-失常,2-已废除',    `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创立工夫',    `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新工夫',    PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品类别表';

购物车

CREATE DATABASE cart;USE cart;CREATE TABLE `cart` (    `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '购物车id',    `userid` bigint(20) UNSIGNED NOT NULL DEFAULT 0 COMMENT '用户id',    `proid` bigint(20) UNSIGNED NOT NULL DEFAULT 0 COMMENT '商品id',    `quantity` int(11) NOT NULL DEFAULT 0 COMMENT '数量',    `checked` int(11) NOT NULL DEFAULT 0 COMMENT '是否抉择,1=已勾选,0=未勾选',    `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创立工夫',    `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新工夫',    PRIMARY KEY (`id`),    KEY `ix_userid` (`userid`),    KEY `ix_proid` (`proid`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='购物车表';

订单相干:

CREATE DATABASE order;USE order;CREATE TABLE `orders` (    `id` varchar(64) NOT NULL DEFAULT '' COMMENT '订单id',    `userid` bigint(20) UNSIGNED NOT NULL DEFAULT 0 COMMENT '用户id',    `shoppingid` bigint(20) NOT NUMBER DEFAULT 0 COMMENT '收货信息表id',    `payment` decimal(20,2) DEFAULT NULL DEFAULT 0 COMMENT '理论付款金额,单位是元,保留两位小数',    `paymenttype` tinyint(4) NOT NULL DEFAULT 1 COMMENT '领取类型,1-在线领取',    `postage` int(10)  NOT NULL DEFAULT 0 COMMENT '运费,单位是元',    `status` smallint(6) NOT NULL DEFAULT 10 COMMENT '订单状态:0-已勾销-10-未付款,20-已付款,30-待发货 40-待收货,50-交易胜利,60-交易敞开',    `payment_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '领取工夫',    `send_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '发货工夫',    `end_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '交易实现工夫',    `close_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '交易敞开工夫',    `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创立工夫',    `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新工夫',    PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单表';CREATE TABLE `orderitem` (     `id` bigint(20) UNSIGNED NOT NULL COMMENT '订单子表id',     `orderid` varchar(64) NOT NULL DEFAULT '' COMMENT '订单id',     `userid` bigint(20) UNSIGNED NOT NULL DEFAULT 0 COMMENT '用户id',     `proid` bigint(20) UNSIGNED NOT NULL DEFAULT 0 COMMENT '商品id',     `proname` varchar(100) NOT NULL DEFAULT '' COMMENT '商品名称',     `proimage` varchar(500) NOT NULL DEFAULT '' COMMENT '商品图片地址',     `currentunitprice` decimal(20,2) NOT NULL DEFAULT 0 COMMENT '生成订单时的商品单价,单位是元,保留两位小数',     `quantity` int(10) NOT NULL DEFAULT 0 COMMENT '商品数量',     `totalprice` decimal(20,2) NOT NULL DEFAULT 0 COMMENT '商品总价,单位是元,保留两位小数',     `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创立工夫',     `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新工夫',     PRIMARY KEY (`id`),     KEY `ix_orderid` (`orderid`),     KEY `ix_userid` (`userid`),     KEY `ix_proid` (`proid`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单明细表';CREATE TABLE `shopping` (    `id` bigint(20) UNSIGNED NOT NULL COMMENT '收货信息表id',    `orderid` varchar(64) NOT NULL DEFAULT '' COMMENT '订单id',    `userid` bigint(20) UNSIGNED NOT NULL DEFAULT 0 COMMENT '用户id',    `receiver_name` varchar(20) NOT NULL DEFAULT '' COMMENT '收货姓名',    `receiver_phone` varchar(20) NOT NULL DEFAULT '' COMMENT '收货固定电话',    `receiver_mobile` varchar(20) NOT NULL DEFAULT '' COMMENT '收货移动电话',    `receiver_province` varchar(20) NOT NULL DEFAULT '' COMMENT '省份',    `receiver_city` varchar(20) NOT NULL DEFAULT '' COMMENT '城市',    `receiver_district` varchar(20) NOT NULL DEFAULT '' COMMENT '区/县',    `receiver_address` varchar(200) NOT NULL DEFAULT '' COMMENT '具体地址',    `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创立工夫',    `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新工夫',    PRIMARY KEY (`id`),    KEY `ix_orderid` (`orderid`),    KEY `ix_userid` (`userid`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='收货信息表';

领取相干:

CREATE DATABASE pay;USE pay;CREATE TABLE `payinfo` (    `id` bigint(20) UNSIGNED NOT NULL COMMENT '领取信息表id',    `orderid` varchar(64) NOT NULL DEFAULT '' COMMENT '订单id',    `userid` bigint(20) UNSIGNED NOT NULL DEFAULT 0 COMMENT '用户id',    `payplatform` tinyint(4) NOT NULL DEFAULT 0 COMMENT '领取平台:1-支付宝,2-微信',    `platformnumber` varchar(200) NOT NULL DEFAULT '' COMMENT '领取流水号',    `platformstatus` varchar(20) NOT NULL DEFAULT '' COMMENT '领取状态',    `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创立工夫',    `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新工夫',    PRIMARY KEY (`id`),    KEY `ix_orderid` (`orderid`),    KEY `ix_userid` (`userid`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='领取信息表';

结束语

本篇文章介绍了如何定义API,并依据定义好的api文件通过 goctl 生成服务代码,整个我的项目波及的api十分多,没方法一次性定义完,后续还会一直的补充。

接着演示了如何在BFF服务中调用RPC服务,把整个调用链路买通,这里只是为了演示所以写死了代码,前面RPC返回的数据会从缓存或者数据库中获取。

最初定义了整个我的项目次要波及的库和表,咱们采纳了微服务的架构,服务间数据做了隔离,每个服务独享了数据库。

到这里后期的筹备工作根本实现了,前面次要就是依照需要实现业务性能,和应答高并发来做优化。

因为笔者程度无限,难免会呈现了解有误的中央,如果你发现有能够改良的中央,心愿可能失去你贵重的意见。

另外,如果你感兴趣,十分欢送你退出,咱们一起来实现这个我的项目,为社区献出本人的一份力。

心愿本篇文章对你有所帮忙,谢谢。

每周一、周四更新

代码仓库 https://github.com/zhoushugua...

我的项目地址

https://github.com/zeromicro/go-zero

欢送应用 go-zerostar 反对咱们!

微信交换群

关注『微服务实际』公众号并点击 交换群 获取社区群二维码。