Day 60: 综合项目展示 - 构建微服务电商平台
1. 课程概述
项目内容 | 详细说明 | 预计完成时间 |
---|---|---|
项目介绍 | 理解整体架构和业务流程 | 30分钟 |
用户服务 | 实现用户注册、登录、认证 | 2小时 |
商品服务 | 实现商品CRUD、库存管理 | 2小时 |
订单服务 | 实现订单创建、支付流程 | 2小时 |
网关服务 | 实现API统一入口、限流 | 1.5小时 |
系统测试 | 完成单元测试和集成测试 | 1小时 |
项目部署 | 使用Docker完成服务部署 | 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 serviceimport ("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.DBredis *redis.Client
}func NewUserService(db *gorm.DB, redis *redis.Client) *UserService {return &UserService{db: db,redis: redis,}
}type RegisterRequest struct {Username stringEmail stringPassword string
}func (s *UserService) Register(ctx context.Context, req *RegisterRequest) error {// 检查用户是否已存在var existingUser model.Userif 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.Userif 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")}// 生成tokentoken := 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 serviceimport ("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.DBredis *redis.Client
}type ProductRequest struct {Name stringDescription stringPrice float64Stock 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.Productif err := json.Unmarshal(data, &product); err == nil {return &product, nil}}// 缓存未命中,从数据库获取var product model.Productif 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.Productif 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 -= quantityif 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 serviceimport ("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.DBproductSvc ProductClientrabbitmq *amqp.Channel
}type CreateOrderRequest struct {UserID uintProductID uintQuantity intAddress 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.Orderif 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 keyfalse, // mandatoryfalse, // immediateamqp.Publishing{ContentType: "application/json",Body: body,},)
}
3.5 API网关实现
// cmd/gateway/main.go
package mainimport ("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 UserClientproductClient ProductClientorderClient OrderClientredis *redis.Clientlimiter *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验证tokenuserID, 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 configtype 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 modelimport ("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/Dockerfileports:- "8080:8080"depends_on:- user-service- product-service- order-serviceenvironment:- SERVER_PORT=8080- REDIS_HOST=redis- USER_SERVICE_URL=user-service:9001- PRODUCT_SERVICE_URL=product-service:9002- ORDER_SERVICE_URL=order-service:9003user-service:build:context: .dockerfile: cmd/user/Dockerfiledepends_on:- mysql- redisenvironment:- SERVER_PORT=9001- DB_HOST=mysql- REDIS_HOST=redisproduct-service:build:context: .dockerfile: cmd/product/Dockerfiledepends_on:- mysql- redisenvironment:- SERVER_PORT=9002- DB_HOST=mysql- REDIS_HOST=redisorder-service:build:context: .dockerfile: cmd/order/Dockerfiledepends_on:- mysql- rabbitmqenvironment:- SERVER_PORT=9003- DB_HOST=mysql- RABBITMQ_URL=amqp://guest:guest@rabbitmq:5672/mysql:image: mysql:8.0environment:- MYSQL_ROOT_PASSWORD=password- MYSQL_DATABASE=microshopvolumes:- mysql-data:/var/lib/mysqlports:- "3306:3306"redis:image: redis:6.2ports:- "6379:6379"volumes:- redis-data:/datarabbitmq:image: rabbitmq:3.9-managementports:- "5672:5672"- "15672:15672"volumes:- rabbitmq-data:/var/lib/rabbitmqvolumes: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.goFROM 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 serviceimport ("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 stringreq *ProductRequestmock 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 serviceimport ("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 stringreq *CreateOrderRequestmock 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 integrationimport ("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 stringt.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 uintt.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 部署检查清单
检查项 | 说明 | 状态确认方法 |
---|---|---|
Docker环境 | 确保Docker和Docker Compose已安装 | docker -v && docker-compose -v |
配置文件 | 检查所有服务的配置文件是否正确 | 检查config目录下的配置文件 |
数据库初始化 | 确保数据库表结构已创建 | 执行go run cmd/migrate/main.go |
网络配置 | 检查服务间网络连接是否正常 | docker network ls |
服务状态 | 验证所有服务是否正常运行 | docker-compose ps |
日志检查 | 检查服务日志是否有错误 | docker-compose logs |
性能监控 | 配置Prometheus和Grafana | 访问监控面板 |
6. 项目总结
6.1 项目特点
-
微服务架构设计
- 服务解耦
- 独立部署
- 技术栈灵活
-
高可用性设计
- 服务注册与发现
- 负载均衡
- 熔断降级
-
性能优化
- 缓存策略
- 消息队列
- 数据库优化
-
安全性考虑
- 身份认证
- 访问控制
- 数据加密
6.2 可扩展性设计
-
水平扩展
- 服务实例动态扩展
- 数据库读写分离
- 缓存集群
-
垂直扩展
- 业务模块独立
- 功能模块可插拔
- 接口版本控制
6.3 后续优化方向
-
技术架构
- 服务网格整合
- 容器编排优化
- 监控告警完善
-
业务功能
- 支付系统集成
- 库存管理优化
- 订单流程完善
-
运维支持
- CI/CD流程优化
- 日志中心建设
- 监控体系完善
7. 系统监控和维护
7.1 监控系统架构
7.2 Prometheus监控配置
# prometheus.yml
global:scrape_interval: 15sevaluation_interval: 15salerting:alertmanagers:- static_configs:- targets:- alertmanager:9093rule_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 metricsimport ("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.Pathmethod := c.Request.Methodc.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_alertsrules:# API响应时间告警- alert: HighResponseTimeexpr: rate(http_request_duration_seconds_sum[5m]) / rate(http_request_duration_seconds_count[5m]) > 0.5for: 5mlabels:severity: warningannotations:summary: "High response time detected"description: "API response time is above 500ms for 5 minutes"# 错误率告警- alert: HighErrorRateexpr: sum(rate(http_requests_total{status=~"5.."}[5m])) / sum(rate(http_requests_total[5m])) > 0.05for: 5mlabels:severity: criticalannotations:summary: "High error rate detected"description: "Error rate is above 5% for 5 minutes"# 商品库存不足告警- alert: LowProductStockexpr: product_stock_level < 10for: 5mlabels:severity: warningannotations:summary: "Low product stock"description: "Product {{ $labels.product_id }} stock is below 10 units"# 系统错误数量告警- alert: HighSystemErrorsexpr: rate(system_errors_total[5m]) > 10for: 5mlabels:severity: criticalannotations:summary: "High system error rate"description: "Service {{ $labels.service }} is experiencing high error rate"# 服务实例存活告警- alert: ServiceDownexpr: up == 0for: 1mlabels:severity: criticalannotations:summary: "Service is down"description: "Service {{ $labels.instance }} has been down for more than 1 minute"# CPU使用率告警- alert: HighCPUUsageexpr: 100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 80for: 5mlabels:severity: warningannotations:summary: "High CPU usage"description: "CPU usage is above 80% for 5 minutes"# 内存使用率告警- alert: HighMemoryUsageexpr: (node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes) / node_memory_MemTotal_bytes * 100 > 85for: 5mlabels:severity: warningannotations:summary: "High memory usage"description: "Memory usage is above 85% for 5 minutes"# 磁盘使用率告警- alert: HighDiskUsageexpr: 100 - ((node_filesystem_avail_bytes / node_filesystem_size_bytes) * 100) > 85for: 5mlabels:severity: warningannotations:summary: "High disk usage"description: "Disk usage is above 85% for 5 minutes"
7.5 系统维护最佳实践
-
日常维护清单
- 日志检查和分析
- 数据库备份和优化
- 系统性能监控
- 安全漏洞扫描
- 配置文件版本管理
-
故障处理流程
- 故障发现和报告
- 影响评估
- 紧急响应
- 问题定位和解决
- 事后总结和改进
-
性能优化建议
- 定期进行性能测试
- 优化数据库查询
- 调整缓存策略
- 监控系统资源使用
- 代码优化和重构
-
安全维护措施
- 定期安全审计
- 更新安全补丁
- 访问权限管理
- 数据备份和恢复
- 安全事件响应
8. 项目扩展建议
-
功能扩展
- 用户评价系统
- 推荐系统
- 积分系统
- 优惠券系统
- 售后服务系统
-
技术升级
- 服务网格整合
- GraphQL API支持
- 实时数据分析
- AI辅助决策
- 区块链集成
-
运维优化
- 自动化部署
- 容灾备份
- 多区域部署
- 自动扩缩容
- 智能运维
这个综合项目展示了一个完整的微服务电商系统的设计、实现和维护过程。通过合理的架构设计、可靠的代码实现、完善的测试和监控体系,以及良好的运维实践,可以构建一个高可用、可扩展、易维护的现代化微服务系统。在实际项目中,可以根据具体需求和场景进行调整和优化。
怎么样今天的内容还满意吗?再次感谢观众老爷的观看,关注GZH:凡人的AI工具箱,回复666,送您价值199的AI大礼包。最后,祝您早日实现财务自由,还请给个赞,谢谢!