Day 60: 综合项目展示 - 构建微服务电商平台
1. 课程概述
2. 项目架构图
3. 核心代码实现
3.1 项目结构
microshop/
├── api/
│ └── proto/
│ ├── user.proto
│ ├── product.proto
│ └── order.proto
├── cmd/
│ ├── gateway/
│ ├── user/
│ ├── product/
│ └── order/
├── internal/
│ ├── auth/
│ ├── config/
│ ├── database/
│ └── middleware/
├── pkg/
│ ├── errors/
│ ├── logger/
│ └── utils/
├── docker-compose.yml
└── go.mod
3.2 用户服务实现
// internal/user/service/user.go
package service
import (
"context"
"crypto/sha256"
"encoding/hex"
"time"
"github.com/go-redis/redis/v8"
"gorm.io/gorm"
"github.com/your/microshop/internal/model"
"github.com/your/microshop/pkg/errors"
)
type UserService struct {
db *gorm.DB
redis *redis.Client
}
func NewUserService(db *gorm.DB, redis *redis.Client) *UserService {
return &UserService{
db: db,
redis: redis,
}
}
type RegisterRequest struct {
Username string
Email string
Password string
}
func (s *UserService) Register(ctx context.Context, req *RegisterRequest) error {
// 检查用户是否已存在
var existingUser model.User
if err := s.db.Where("email = ?", req.Email).First(&existingUser).Error; err != gorm.ErrRecordNotFound {
return errors.New("user already exists")
}
// 密码加密
hashedPassword := hashPassword(req.Password)
// 创建新用户
user := &model.User{
Username: req.Username,
Email: req.Email,
Password: hashedPassword,
Created: time.Now(),
Updated: time.Now(),
}
if err := s.db.Create(user).Error; err != nil {
return errors.Wrap(err, "failed to create user")
}
return nil
}
func (s *UserService) Login(ctx context.Context, email, password string) (string, error) {
var user model.User
if err := s.db.Where("email = ?", email).First(&user).Error; err != nil {
return "", errors.New("invalid credentials")
}
if hashPassword(password) != user.Password {
return "", errors.New("invalid credentials")
}
// 生成token
token := generateToken()
// 存储token到Redis,设置过期时间
err := s.redis.Set(ctx, token, user.ID, 24*time.Hour).Err()
if err != nil {
return "", errors.Wrap(err, "failed to store token")
}
return token, nil
}
func hashPassword(password string) string {
hash := sha256.New()
hash.Write([]byte(password))
return hex.EncodeToString(hash.Sum(nil))
}
func generateToken() string {
// 实际项目中应该使用更安全的token生成方式
return hex.EncodeToString([]byte(time.Now().String()))
}
3.3 商品服务实现
// internal/product/service/product.go
package service
import (
"context"
"encoding/json"
"time"
"github.com/go-redis/redis/v8"
"gorm.io/gorm"
"github.com/your/microshop/internal/model"
"github.com/your/microshop/pkg/errors"
)
type ProductService struct {
db *gorm.DB
redis *redis.Client
}
type ProductRequest struct {
Name string
Description string
Price float64
Stock int
}
func NewProductService(db *gorm.DB, redis *redis.Client) *ProductService {
return &ProductService{
db: db,
redis: redis,
}
}
func (s *ProductService) CreateProduct(ctx context.Context, req *ProductRequest) error {
product := &model.Product{
Name: req.Name,
Description: req.Description,
Price: req.Price,
Stock: req.Stock,
Created: time.Now(),
Updated: time.Now(),
}
if err := s.db.Create(product).Error; err != nil {
return errors.Wrap(err, "failed to create product")
}
// 更新缓存
return s.updateProductCache(ctx, product)
}
func (s *ProductService) GetProduct(ctx context.Context, id uint) (*model.Product, error) {
// 先从缓存获取
cacheKey := s.getProductCacheKey(id)
data, err := s.redis.Get(ctx, cacheKey).Bytes()
if err == nil {
var product model.Product
if err := json.Unmarshal(data, &product); err == nil {
return &product, nil
}
}
// 缓存未命中,从数据库获取
var product model.Product
if err := s.db.First(&product, id).Error; err != nil {
return nil, errors.Wrap(err, "product not found")
}
// 更新缓存
if err := s.updateProductCache(ctx, &product); err != nil {
return nil, err
}
return &product, nil
}
func (s *ProductService) UpdateStock(ctx context.Context, id uint, quantity int) error {
return s.db.Transaction(func(tx *gorm.DB) error {
var product model.Product
if err := tx.First(&product, id).Error; err != nil {
return errors.Wrap(err, "product not found")
}
if product.Stock < quantity {
return errors.New("insufficient stock")
}
product.Stock -= quantity
if err := tx.Save(&product).Error; err != nil {
return errors.Wrap(err, "failed to update stock")
}
// 更新缓存
return s.updateProductCache(ctx, &product)
})
}
func (s *ProductService) getProductCacheKey(id uint) string {
return fmt.Sprintf("product:%d", id)
}
func (s *ProductService) updateProductCache(ctx context.Context, product *model.Product) error {
data, err := json.Marshal(product)
if err != nil {
return errors.Wrap(err, "failed to marshal product")
}
cacheKey := s.getProductCacheKey(product.ID)
return s.redis.Set(ctx, cacheKey, data, 24*time.Hour).Err()
}
3.4 订单服务实现
// internal/order/service/order.go
package service
import (
"context"
"time"
"github.com/streadway/amqp"
"gorm.io/gorm"
"github.com/your/microshop/internal/model"
"github.com/your/microshop/pkg/errors"
)
type OrderService struct {
db *gorm.DB
productSvc ProductClient
rabbitmq *amqp.Channel
}
type CreateOrderRequest struct {
UserID uint
ProductID uint
Quantity int
Address string
}
func NewOrderService(db *gorm.DB, productSvc ProductClient, rabbitmq *amqp.Channel) *OrderService {
return &OrderService{
db: db,
productSvc: productSvc,
rabbitmq: rabbitmq,
}
}
func (s *OrderService) CreateOrder(ctx context.Context, req *CreateOrderRequest) error {
return s.db.Transaction(func(tx *gorm.DB) error {
// 检查并更新商品库存
if err := s.productSvc.UpdateStock(ctx, req.ProductID, req.Quantity); err != nil {
return errors.Wrap(err, "failed to update stock")
}
// 创建订单
order := &model.Order{
UserID: req.UserID,
ProductID: req.ProductID,
Quantity: req.Quantity,
Status: "pending",
Address: req.Address,
Created: time.Now(),
Updated: time.Now(),
}
if err := tx.Create(order).Error; err != nil {
return errors.Wrap(err, "failed to create order")
}
// 发送订单创建消息到消息队列
if err := s.publishOrderCreatedEvent(order); err != nil {
return errors.Wrap(err, "failed to publish order created event")
}
return nil
})
}
func (s *OrderService) GetOrder(ctx context.Context, id uint) (*model.Order, error) {
var order model.Order
if err := s.db.First(&order, id).Error; err != nil {
return nil, errors.Wrap(err, "order not found")
}
return &order, nil
}
func (s *OrderService) UpdateOrderStatus(ctx context.Context, id uint, status string) error {
result := s.db.Model(&model.Order{}).
Where("id = ?", id).
Updates(map[string]interface{}{
"status": status,
"updated": time.Now(),
})
if result.Error != nil {
return errors.Wrap(result.Error, "failed to update order status")
}
if result.RowsAffected == 0 {
return errors.New("order not found")
}
return nil
}
func (s *OrderService) publishOrderCreatedEvent(order *model.Order) error {
body, err := json.Marshal(map[string]interface{}{
"order_id": order.ID,
"user_id": order.UserID,
"product_id": order.ProductID,
"quantity": order.Quantity,
"status": order.Status,
"created_at": order.Created,
})
if err != nil {
return err
}
return s.rabbitmq.Publish(
"orders", // exchange
"created", // routing key
false, // mandatory
false, // immediate
amqp.Publishing{
ContentType: "application/json",
Body: body,
},
)
}
3.5 API网关实现
// cmd/gateway/main.go
package main
import (
"context"
"net/http"
"time"
"github.com/gin-gonic/gin"
"github.com/go-redis/redis/v8"
"golang.org/x/time/rate"
"github.com/your/microshop/internal/middleware"
"github.com/your/microshop/pkg/errors"
)
type Gateway struct {
userClient UserClient
productClient ProductClient
orderClient OrderClient
redis *redis.Client
limiter *rate.Limiter
}
func NewGateway(userClient UserClient, productClient ProductClient,
orderClient OrderClient, redis *redis.Client) *Gateway {
return &Gateway{
userClient: userClient,
productClient: productClient,
orderClient: orderClient,
redis: redis,
limiter: rate.NewLimiter(rate.Every(time.Second), 100), // 限制每秒100个请求
}
}
func (g *Gateway) SetupRouter() *gin.Engine {
router := gin.Default()
// 中间件
router.Use(middleware.Cors())
router.Use(g.RateLimit())
router.Use(g.RequestLogger())
// 用户相关路由
user := router.Group("/api/v1/users")
{
user.POST("/register", g.HandleUserRegister)
user.POST("/login", g.HandleUserLogin)
user.GET("/profile", g.AuthMiddleware(), g.HandleGetUserProfile)
}
// 商品相关路由
product := router.Group("/api/v1/products")
{
product.GET("", g.HandleListProducts)
product.GET("/:id", g.HandleGetProduct)
product.POST("", g.AuthMiddleware(), g.AdminRequired(), g.HandleCreateProduct)
product.PUT("/:id", g.AuthMiddleware(), g.AdminRequired(), g.HandleUpdateProduct)
}
// 订单相关路由
order := router.Group("/api/v1/orders")
{
order.POST("", g.AuthMiddleware(), g.HandleCreateOrder)
order.GET("", g.AuthMiddleware(), g.HandleListOrders)
order.GET("/:id", g.AuthMiddleware(), g.HandleGetOrder)
}
return router
}
// 限流中间件
func (g *Gateway) RateLimit() gin.HandlerFunc {
return func(c *gin.Context) {
if !g.limiter.Allow() {
c.JSON(http.StatusTooManyRequests, gin.H{"error": "too many requests"})
c.Abort()
return
}
c.Next()
}
}
// 认证中间件
func (g *Gateway) AuthMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
token := c.GetHeader("Authorization")
if token == "" {
c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"})
c.Abort()
return
}
// 从Redis验证token
userID, err := g.redis.Get(c, token).Uint64()
if err != nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid token"})
c.Abort()
return
}
c.Set("userID", uint(userID))
c.Next()
}
}
// 处理用户注册
func (g *Gateway) HandleUserRegister(c *gin.Context) {
var req struct {
Username string `json:"username" binding:"required"`
Email string `json:"email" binding:"required,email"`
Password string `json:"password" binding:"required,min=6"`
}
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
err := g.userClient.Register(c.Request.Context(), &RegisterRequest{
Username: req.Username,
Email: req.Email,
Password: req.Password,
})
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusCreated, gin.H{"message": "user registered successfully"})
}
// 处理创建订单
func (g *Gateway) HandleCreateOrder(c *gin.Context) {
var req struct {
ProductID uint `json:"product_id" binding:"required"`
Quantity int `json:"quantity" binding:"required,min=1"`
Address string `json:"address" binding:"required"`
}
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
userID := c.MustGet("userID").(uint)
err := g.orderClient.CreateOrder(c.Request.Context(), &CreateOrderRequest{
UserID: userID,
ProductID: req.ProductID,
Quantity: req.Quantity,
Address: req.Address,
})
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusCreated, gin.H{"message": "order created successfully"})
}
3.6 系统配置和数据库模型
// internal/config/config.go
package config
type Config struct {
Server struct {
Port int `yaml:"port"`
Mode string `yaml:"mode"`
} `yaml:"server"`
Database struct {
Host string `yaml:"host"`
Port int `yaml:"port"`
User string `yaml:"user"`
Password string `yaml:"password"`
DBName string `yaml:"dbname"`
} `yaml:"database"`
Redis struct {
Host string `yaml:"host"`
Port int `yaml:"port"`
Password string `yaml:"password"`
DB int `yaml:"db"`
} `yaml:"redis"`
RabbitMQ struct {
URL string `yaml:"url"`
} `yaml:"rabbitmq"`
}
// internal/model/models.go
package model
import (
"time"
)
type User struct {
ID uint `gorm:"primaryKey"`
Username string `gorm:"size:255;not null"`
Email string `gorm:"size:255;not null;unique"`
Password string `gorm:"size:255;not null"`
Role string `gorm:"size:20;default:'user'"`
Created time.Time `gorm:"not null"`
Updated time.Time `gorm:"not null"`
Orders []Order `gorm:"foreignKey:UserID"`
}
type Product struct {
ID uint `gorm:"primaryKey"`
Name string `gorm:"size:255;not null"`
Description string `gorm:"type:text"`
Price float64 `gorm:"not null"`
Stock int `gorm:"not null"`
Created time.Time `gorm:"not null"`
Updated time.Time `gorm:"not null"`
Orders []Order `gorm:"foreignKey:ProductID"`
}
type Order struct {
ID uint `gorm:"primaryKey"`
UserID uint `gorm:"not null"`
ProductID uint `gorm:"not null"`
Quantity int `gorm:"not null"`
Status string `gorm:"size:20;not null"`
Address string `gorm:"type:text;not null"`
Created time.Time `gorm:"not null"`
Updated time.Time `gorm:"not null"`
User User `gorm:"foreignKey:UserID"`
Product Product `gorm:"foreignKey:ProductID"`
}
3.7 Docker部署配置
# docker-compose.yml
version: '3.8'
services:
gateway:
build:
context: .
dockerfile: cmd/gateway/Dockerfile
ports:
- "8080:8080"
depends_on:
- user-service
- product-service
- order-service
environment:
- SERVER_PORT=8080
- REDIS_HOST=redis
- USER_SERVICE_URL=user-service:9001
- PRODUCT_SERVICE_URL=product-service:9002
- ORDER_SERVICE_URL=order-service:9003
user-service:
build:
context: .
dockerfile: cmd/user/Dockerfile
depends_on:
- mysql
- redis
environment:
- SERVER_PORT=9001
- DB_HOST=mysql
- REDIS_HOST=redis
product-service:
build:
context: .
dockerfile: cmd/product/Dockerfile
depends_on:
- mysql
- redis
environment:
- SERVER_PORT=9002
- DB_HOST=mysql
- REDIS_HOST=redis
order-service:
build:
context: .
dockerfile: cmd/order/Dockerfile
depends_on:
- mysql
- rabbitmq
environment:
- SERVER_PORT=9003
- DB_HOST=mysql
- RABBITMQ_URL=amqp://guest:guest@rabbitmq:5672/
mysql:
image: mysql:8.0
environment:
- MYSQL_ROOT_PASSWORD=password
- MYSQL_DATABASE=microshop
volumes:
- mysql-data:/var/lib/mysql
ports:
- "3306:3306"
redis:
image: redis:6.2
ports:
- "6379:6379"
volumes:
- redis-data:/data
rabbitmq:
image: rabbitmq:3.9-management
ports:
- "5672:5672"
- "15672:15672"
volumes:
- rabbitmq-data:/var/lib/rabbitmq
volumes:
mysql-data:
redis-data:
rabbitmq-data:
# cmd/gateway/Dockerfile
FROM golang:1.19-alpine AS builder
WORKDIR /app
COPY . .
RUN go build -o gateway cmd/gateway/main.go
FROM alpine:3.14
WORKDIR /app
COPY --from=builder /app/gateway .
COPY configs/ configs/
EXPOSE 8080
CMD ["./gateway"]
4. 系统测试流程
4.1 测试流程图
4.2 单元测试示例
// internal/product/service/product_test.go
package service
import (
"context"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/go-redis/redis/v8"
"gorm.io/gorm"
"github.com/your/microshop/internal/model"
)
type MockDB struct {
mock.Mock
}
func (m *MockDB) First(out interface{}, conditions ...interface{}) *gorm.DB {
args := m.Called(out, conditions)
return args.Get(0).(*gorm.DB)
}
func (m *MockDB) Create(value interface{}) *gorm.DB {
args := m.Called(value)
return args.Get(0).(*gorm.DB)
}
func TestProductService_CreateProduct(t *testing.T) {
// 初始化mock对象
mockDB := new(MockDB)
mockRedis := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
})
service := NewProductService(mockDB, mockRedis)
// 测试用例
tests := []struct {
name string
req *ProductRequest
mock func()
wantErr bool
}{
{
name: "successful creation",
req: &ProductRequest{
Name: "Test Product",
Description: "Test Description",
Price: 99.99,
Stock: 100,
},
mock: func() {
mockDB.On("Create", mock.AnythingOfType("*model.Product")).
Return(&gorm.DB{Error: nil})
},
wantErr: false,
},
{
name: "database error",
req: &ProductRequest{
Name: "Test Product",
Description: "Test Description",
Price: 99.99,
Stock: 100,
},
mock: func() {
mockDB.On("Create", mock.AnythingOfType("*model.Product")).
Return(&gorm.DB{Error: gorm.ErrInvalidTransaction})
},
wantErr: true,
},
}
// 执行测试用例
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tt.mock()
err := service.CreateProduct(context.Background(), tt.req)
if tt.wantErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
})
}
}
// internal/order/service/order_test.go
package service
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)
type MockProductClient struct {
mock.Mock
}
func (m *MockProductClient) UpdateStock(ctx context.Context, productID uint, quantity int) error {
args := m.Called(ctx, productID, quantity)
return args.Error(0)
}
func TestOrderService_CreateOrder(t *testing.T) {
// 初始化mock对象
mockDB := new(MockDB)
mockProductClient := new(MockProductClient)
service := NewOrderService(mockDB, mockProductClient, nil)
// 测试用例
tests := []struct {
name string
req *CreateOrderRequest
mock func()
wantErr bool
}{
{
name: "successful order creation",
req: &CreateOrderRequest{
UserID: 1,
ProductID: 1,
Quantity: 2,
Address: "Test Address",
},
mock: func() {
mockProductClient.On("UpdateStock", mock.Anything, uint(1), 2).
Return(nil)
mockDB.On("Create", mock.AnythingOfType("*model.Order")).
Return(&gorm.DB{Error: nil})
},
wantErr: false,
},
{
name: "insufficient stock",
req: &CreateOrderRequest{
UserID: 1,
ProductID: 1,
Quantity: 100,
Address: "Test Address",
},
mock: func() {
mockProductClient.On("UpdateStock", mock.Anything, uint(1), 100).
Return(errors.New("insufficient stock"))
},
wantErr: true,
},
}
// 执行测试用例
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tt.mock()
err := service.CreateOrder(context.Background(), tt.req)
if tt.wantErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
})
}
}
4.3 集成测试示例
// tests/integration/api_test.go
package integration
import (
"bytes"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert"
"github.com/your/microshop/internal/gateway"
)
func TestAPIIntegration(t *testing.T) {
// 设置测试环境
gin.SetMode(gin.TestMode)
router := setupTestRouter()
// 用户注册测试
t.Run("user registration", func(t *testing.T) {
reqBody := map[string]interface{}{
"username": "testuser",
"email": "test@example.com",
"password": "password123",
}
jsonBody, _ := json.Marshal(reqBody)
req := httptest.NewRequest("POST", "/api/v1/users/register", bytes.NewBuffer(jsonBody))
req.Header.Set("Content-Type", "application/json")
resp := httptest.NewRecorder()
router.ServeHTTP(resp, req)
assert.Equal(t, http.StatusCreated, resp.Code)
var response map[string]interface{}
err := json.Unmarshal(resp.Body.Bytes(), &response)
assert.NoError(t, err)
assert.Contains(t, response, "message")
})
// 用户登录测试
var token string
t.Run("user login", func(t *testing.T) {
reqBody := map[string]interface{}{
"email": "test@example.com",
"password": "password123",
}
jsonBody, _ := json.Marshal(reqBody)
req := httptest.NewRequest("POST", "/api/v1/users/login", bytes.NewBuffer(jsonBody))
req.Header.Set("Content-Type", "application/json")
resp := httptest.NewRecorder()
router.ServeHTTP(resp, req)
assert.Equal(t, http.StatusOK, resp.Code)
var response map[string]interface{}
err := json.Unmarshal(resp.Body.Bytes(), &response)
assert.NoError(t, err)
assert.Contains(t, response, "token")
token = response["token"].(string)
})
// 创建商品测试
var productID uint
t.Run("create product", func(t *testing.T) {
reqBody := map[string]interface{}{
"name": "Test Product",
"description": "Test Description",
"price": 99.99,
"stock": 100,
}
jsonBody, _ := json.Marshal(reqBody)
req := httptest.NewRequest("POST", "/api/v1/products", bytes.NewBuffer(jsonBody))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", token)
resp := httptest.NewRecorder()
router.ServeHTTP(resp, req)
assert.Equal(t, http.StatusCreated, resp.Code)
var response map[string]interface{}
err := json.Unmarshal(resp.Body.Bytes(), &response)
assert.NoError(t, err)
assert.Contains(t, response, "product_id")
productID = uint(response["product_id"].(float64))
})
// 创建订单测试
t.Run("create order", func(t *testing.T) {
reqBody := map[string]interface{}{
"product_id": productID,
"quantity": 2,
"address": "Test Address",
}
jsonBody, _ := json.Marshal(reqBody)
req := httptest.NewRequest("POST", "/api/v1/orders", bytes.NewBuffer(jsonBody))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", token)
resp := httptest.NewRecorder()
router.ServeHTTP(resp, req)
assert.Equal(t, http.StatusCreated, resp.Code)
var response map[string]interface{}
err := json.Unmarshal(resp.Body.Bytes(), &response)
assert.NoError(t, err)
assert.Contains(t, response, "message")
})
}
func setupTestRouter() *gin.Engine {
// 初始化测试环境的路由和依赖
router := gin.New()
router.Use(gin.Recovery())
// 配置测试环境的服务依赖
gateway := gateway.NewGateway(
newMockUserClient(),
newMockProductClient(),
newMockOrderClient(),
setupTestRedis(),
)
gateway.SetupRouter(router)
return router
}
4.4 性能测试工具
# 使用 Apache Benchmark 进行基本性能测试
ab -n 1000 -c 100 http://localhost:8080/api/v1/products
# 使用 hey 进行并发测试
hey -n 10000 -c 100 http://localhost:8080/api/v1/products
# 使用 wrk 进行压力测试
wrk -t12 -c400 -d30s http://localhost:8080/api/v1/products
5. 项目部署流程
5.1 部署流程图
5.2 部署检查清单
6. 项目总结
6.1 项目特点
-
微服务架构设计
- 服务解耦
- 独立部署
- 技术栈灵活
-
高可用性设计
- 服务注册与发现
- 负载均衡
- 熔断降级
-
性能优化
- 缓存策略
- 消息队列
- 数据库优化
-
安全性考虑
- 身份认证
- 访问控制
- 数据加密
6.2 可扩展性设计
-
水平扩展
- 服务实例动态扩展
- 数据库读写分离
- 缓存集群
-
垂直扩展
- 业务模块独立
- 功能模块可插拔
- 接口版本控制
6.3 后续优化方向
-
技术架构
- 服务网格整合
- 容器编排优化
- 监控告警完善
-
业务功能
- 支付系统集成
- 库存管理优化
- 订单流程完善
-
运维支持
- CI/CD流程优化
- 日志中心建设
- 监控体系完善
7. 系统监控和维护
7.1 监控系统架构
7.2 Prometheus监控配置
# prometheus.yml
global:
scrape_interval: 15s
evaluation_interval: 15s
alerting:
alertmanagers:
- static_configs:
- targets:
- alertmanager:9093
rule_files:
- "rules/*.yml"
scrape_configs:
- job_name: 'microshop-gateway'
static_configs:
- targets: ['gateway:8080']
labels:
service: 'gateway'
- job_name: 'microshop-user'
static_configs:
- targets: ['user-service:9001']
labels:
service: 'user'
- job_name: 'microshop-product'
static_configs:
- targets: ['product-service:9002']
labels:
service: 'product'
- job_name: 'microshop-order'
static_configs:
- targets: ['order-service:9003']
labels:
service: 'order'
- job_name: 'node-exporter'
static_configs:
- targets: ['node-exporter:9100']
- job_name: 'mysql-exporter'
static_configs:
- targets: ['mysql-exporter:9104']
- job_name: 'redis-exporter'
static_configs:
- targets: ['redis-exporter:9121']
- job_name: 'rabbitmq-exporter'
static_configs:
- targets: ['rabbitmq-exporter:9419']
7.3 服务监控指标实现
// internal/metrics/metrics.go
package metrics
import (
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
)
var (
// HTTP请求总数
HttpRequestsTotal = promauto.NewCounterVec(
prometheus.CounterOpts{
Name: "http_requests_total",
Help: "Total number of HTTP requests",
},
[]string{"method", "endpoint", "status"},
)
// HTTP请求处理时间
HttpRequestDuration = promauto.NewHistogramVec(
prometheus.HistogramOpts{
Name: "http_request_duration_seconds",
Help: "HTTP request duration in seconds",
Buckets: prometheus.DefBuckets,
},
[]string{"method", "endpoint"},
)
// 活跃用户数
ActiveUsers = promauto.NewGauge(
prometheus.GaugeOpts{
Name: "active_users",
Help: "Number of active users",
},
)
// 订单处理总数
OrderProcessedTotal = promauto.NewCounterVec(
prometheus.CounterOpts{
Name: "orders_processed_total",
Help: "Total number of processed orders",
},
[]string{"status"},
)
// 商品库存水平
ProductStock = promauto.NewGaugeVec(
prometheus.GaugeOpts{
Name: "product_stock_level",
Help: "Current stock level of products",
},
[]string{"product_id"},
)
// 系统错误总数
ErrorsTotal = promauto.NewCounterVec(
prometheus.CounterOpts{
Name: "system_errors_total",
Help: "Total number of system errors",
},
[]string{"service", "type"},
)
)
// 中间件:记录HTTP请求指标
func MetricsMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
start := time.Now()
path := c.Request.URL.Path
method := c.Request.Method
c.Next()
status := strconv.Itoa(c.Writer.Status())
duration := time.Since(start).Seconds()
HttpRequestsTotal.WithLabelValues(method, path, status).Inc()
HttpRequestDuration.WithLabelValues(method, path).Observe(duration)
}
}
// 更新商品库存指标
func UpdateProductStock(productID string, stock float64) {
ProductStock.WithLabelValues(productID).Set(stock)
}
// 记录订单处理
func RecordOrderProcessed(status string) {
OrderProcessedTotal.WithLabelValues(status).Inc()
}
// 记录系统错误
func RecordError(service, errorType string) {
ErrorsTotal.WithLabelValues(service, errorType).Inc()
}
7.4 告警规则配置
# rules/alert_rules.yml
groups:
- name: microshop_alerts
rules:
# API响应时间告警
- alert: HighResponseTime
expr: rate(http_request_duration_seconds_sum[5m]) / rate(http_request_duration_seconds_count[5m]) > 0.5
for: 5m
labels:
severity: warning
annotations:
summary: "High response time detected"
description: "API response time is above 500ms for 5 minutes"
# 错误率告警
- alert: HighErrorRate
expr: sum(rate(http_requests_total{status=~"5.."}[5m])) / sum(rate(http_requests_total[5m])) > 0.05
for: 5m
labels:
severity: critical
annotations:
summary: "High error rate detected"
description: "Error rate is above 5% for 5 minutes"
# 商品库存不足告警
- alert: LowProductStock
expr: product_stock_level < 10
for: 5m
labels:
severity: warning
annotations:
summary: "Low product stock"
description: "Product {{ $labels.product_id }} stock is below 10 units"
# 系统错误数量告警
- alert: HighSystemErrors
expr: rate(system_errors_total[5m]) > 10
for: 5m
labels:
severity: critical
annotations:
summary: "High system error rate"
description: "Service {{ $labels.service }} is experiencing high error rate"
# 服务实例存活告警
- alert: ServiceDown
expr: up == 0
for: 1m
labels:
severity: critical
annotations:
summary: "Service is down"
description: "Service {{ $labels.instance }} has been down for more than 1 minute"
# CPU使用率告警
- alert: HighCPUUsage
expr: 100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 80
for: 5m
labels:
severity: warning
annotations:
summary: "High CPU usage"
description: "CPU usage is above 80% for 5 minutes"
# 内存使用率告警
- alert: HighMemoryUsage
expr: (node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes) / node_memory_MemTotal_bytes * 100 > 85
for: 5m
labels:
severity: warning
annotations:
summary: "High memory usage"
description: "Memory usage is above 85% for 5 minutes"
# 磁盘使用率告警
- alert: HighDiskUsage
expr: 100 - ((node_filesystem_avail_bytes / node_filesystem_size_bytes) * 100) > 85
for: 5m
labels:
severity: warning
annotations:
summary: "High disk usage"
description: "Disk usage is above 85% for 5 minutes"
7.5 系统维护最佳实践
-
日常维护清单
- 日志检查和分析
- 数据库备份和优化
- 系统性能监控
- 安全漏洞扫描
- 配置文件版本管理
-
故障处理流程
- 故障发现和报告
- 影响评估
- 紧急响应
- 问题定位和解决
- 事后总结和改进
-
性能优化建议
- 定期进行性能测试
- 优化数据库查询
- 调整缓存策略
- 监控系统资源使用
- 代码优化和重构
-
安全维护措施
- 定期安全审计
- 更新安全补丁
- 访问权限管理
- 数据备份和恢复
- 安全事件响应
8. 项目扩展建议
-
功能扩展
- 用户评价系统
- 推荐系统
- 积分系统
- 优惠券系统
- 售后服务系统
-
技术升级
- 服务网格整合
- GraphQL API支持
- 实时数据分析
- AI辅助决策
- 区块链集成
-
运维优化
- 自动化部署
- 容灾备份
- 多区域部署
- 自动扩缩容
- 智能运维
这个综合项目展示了一个完整的微服务电商系统的设计、实现和维护过程。通过合理的架构设计、可靠的代码实现、完善的测试和监控体系,以及良好的运维实践,可以构建一个高可用、可扩展、易维护的现代化微服务系统。在实际项目中,可以根据具体需求和场景进行调整和优化。
怎么样今天的内容还满意吗?再次感谢观众老爷的观看,关注GZH:凡人的AI工具箱,回复666,送您价值199的AI大礼包。最后,祝您早日实现财务自由,还请给个赞,谢谢!