go语言项目--实例化(图书管理)--005
v5: 图书微服务 — 分布式架构一、版本概述v5 将 v4 的单体应用拆分为两个独立微服务book-service 和 borrow-service通过 gRPC 通信。引入 MySQL 替代内存存储Redis 实现缓存和分布式锁。相比 v4 的核心变化微服务拆分book-service(:8081/:9081) borrow-service(:8082)MySQL 持久化GORM ORMAutoMigrate 自动建表Redis 缓存Cache-Aside 模式5分钟过期分布式锁Redis SetNX 实现跨实例互斥gRPC 通信borrow-service 通过 gRPC 调用 book-serviceProtobuf 定义独立 proto/book.proto 定义服务契约Docker 支持Dockerfile docker-compose.yml项目结构v5/ ├── book-service/ │ ├── main.go # HTTP(:8081) gRPC(:9081) │ ├── model/book.go # GORM Book 模型 │ ├── repository/ │ │ ├── db.go # MySQL 初始化 │ │ └── book_repo.go # GORM CRUD 行锁 │ ├── cache/redis.go # Redis 缓存 分布式锁 │ ├── service/book_service.go # 缓存锁业务 │ ├── handler/ │ │ ├── http_handler.go # HTTP API │ │ └── grpc_handler.go # gRPC 服务端 │ └── Dockerfile ├── borrow-service/ │ ├── main.go # HTTP(:8082) gRPC Client │ ├── model/borrow_record.go │ ├── repository/ │ │ ├── db.go │ │ └── borrow_repo.go │ ├── client/grpc_client.go # gRPC 客户端封装 │ ├── service/borrow_service.go │ ├── handler/http_handler.go │ └── Dockerfile ├── proto/ │ ├── book.proto # Protobuf 服务定义 │ └── book/ # 生成的 Go 代码 └── docker-compose.yml二、核心代码解读proto/book.proto — 服务契约service BookService { rpc GetBook(GetBookRequest) returns (BookResponse); rpc DecreaseStock(DecreaseStockRequest) returns (StockResponse); rpc IncreaseStock(IncreaseStockRequest) returns (StockResponse); }设计要点只暴露 borrow-service 需要调用的方法不暴露 CRUDDecreaseStock/IncreaseStock原子操作库存而非先 GetBook 再修改cache/redis.go — Cache-Aside 分布式锁// Cache-Aside 读缓存funcGetBookCache(ctx context.Context,idint64)(*model.Book,error){key:fmt.Sprintf(book:%d,id)data,err:rdb.Get(ctx,key).Bytes()iferr!nil{returnnil,err// 缓存未命中}varbook model.Book json.Unmarshal(data,book)returnbook,nil}// Cache-Aside 写缓存funcSetBookCache(ctx context.Context,book*model.Book){key:fmt.Sprintf(book:%d,book.ID)data,_:json.Marshal(book)rdb.Set(ctx,key,data,5*time.Minute)}// 分布式锁funcAcquireLock(ctx context.Context,keystring)(bool,error){lockKey:fmt.Sprintf(lock:%s,key)returnrdb.SetNX(ctx,lockKey,1,5*time.Second).Result()}设计要点Cache-Aside读时先查缓存 → miss 查 DB → 写回缓存写时更新 DB → 删除缓存分布式锁SetNX不存在才设置 过期时间防止死锁比 v4 的 Mutex 支持多实例repository/book_repo.go — GORM 行锁func(r*BookRepository)DecreaseStock(bookIDint64)(int,error){tx:r.db.Begin()varbook model.Book// SELECT ... FOR UPDATE 行锁iferr:tx.Set(gorm:query_option,FOR UPDATE).First(book,bookID).Error;err!nil{tx.Rollback()return0,ErrBookNotFound}ifbook.Stock0{tx.Rollback()return0,ErrStockNotEnough}book.Stock-1tx.Save(book)tx.Commit()returnbook.Stock,nil}设计要点行锁 FOR UPDATE在事务内锁定该行其他事务等待防止数据库层面超卖事务Begin → 操作 → Commit/Rollback保证原子性双重保护分布式锁应用层 行锁数据库层borrow-service/client/grpc_client.go — gRPC 调用typeBookServiceClientstruct{conn*grpc.ClientConn client pb.BookServiceClient}func(c*BookServiceClient)DecreaseStock(ctx context.Context,bookIDint64)(int,error){resp,err:c.client.DecreaseStock(ctx,pb.DecreaseStockRequest{BookId:bookID})iferr!nil{return0,err}returnint(resp.Available),nil}borrow_service.go — 跨服务事务func(s*borrowService)BorrowBook(ctx context.Context,...)(*model.BorrowRecord,error){// 1. 通过 gRPC 调用 book-service 减库存if_,err:s.grpcClient.DecreaseStock(ctx,bookID);err!nil{returnnil,ErrBookNotAvailable}// 2. 本地创建借阅记录record,err:s.repo.Create(bookID,bookTitle,userName)iferr!nil{// 3. 创建失败 → 回滚库存补偿事务s.grpcClient.IncreaseStock(ctx,bookID)returnnil,err}returnrecord,nil}设计要点Saga 模式跨服务没有强一致性事务使用补偿事务保证最终一致性gRPC 同步调用borrow-service 先调 book-service 扣库存成功后再创建本地记录三、与 v4 的对比特性v4v5架构单体微服务拆分存储内存JSON文件MySQL Redis并发控制进程内 Mutex分布式锁 DB行锁服务通信函数调用gRPC缓存无Redis Cache-Aside部署单进程Docker Compose跨服务事务不需要补偿事务(Saga)四、设计思想思想体现微服务拆分按业务领域拆分独立部署和扩缩容服务契约Protobuf 定义接口强类型、向后兼容Cache-Aside缓存与DB分离先更新DB再删缓存分布式锁Redis SetNX 实现跨实例互斥双重保护应用层锁 数据库行锁防御性编程补偿事务跨服务无强一致性通过回滚保证最终一致五、为什么需要 v6v5 实现了微服务架构但仍存在不足没有用户体系借阅没有登录认证任何人可以借任何书没有搜索功能只能遍历查询不支持关键词搜索没有前端界面只能通过 curl/API 工具操作gRPC 通信不完整borrow-service 的 gRPC 客户端存在 placeholder缺少 CORS前端无法跨域访问 APIv6 的改进方向新增 user-service JWT 认证 Bleve 全文搜索 前端页面 完善 gRPC 调用。