Go 语言编程高级:微服务架构实战

Go 语言编程高级:微服务架构实战


随着应用的规模不断扩大,单体应用的局限性逐渐显现,越来越多的开发者开始采用微服务架构来提升系统的可扩展性和灵活性。本部分将带你构建一个简单的微服务架构,学习如何使用 Go 语言开发和管理多个微服务。

1. 微服务架构概述

1.1 什么是微服务架构

微服务架构是一种将应用分解为多个小型、独立服务的架构模式,每个服务都运行在自己的进程中,服务之间通过轻量级协议(如 HTTP/REST、gRPC)进行通信。

1.2 微服务的特点

  • 松耦合:服务之间相互独立,降低了服务之间的耦合度。
  • 独立部署:每个服务可以独立部署、扩展和管理。
  • 技术多样性:每个服务可以采用不同的技术栈,适合各自的需求。

2. 微服务案例:用户服务与订单服务

我们将构建一个简单的微服务架构,包含以下两个服务:

  • 用户服务:管理用户信息,提供用户的增删改查 API。
  • 订单服务:管理用户订单信息,提供订单相关 API,并通过用户服务获取用户信息。

2.1 技术栈

  • 通信协议:使用 HTTP/REST 实现服务之间的通信。
  • 数据库:使用 GORM 和 SQLite 存储数据。
  • 日志记录:使用 Logrus 记录日志。
  • 配置管理:使用 Viper 管理配置。

3. 项目结构

微服务架构中的每个服务都是独立的项目,因此我们会为用户服务和订单服务创建各自的项目结构。

microservices/
├── user-service/
│   ├── cmd/
│   │   └── main.go         # 用户服务入口
│   ├── internal/
│   │   ├── database/
│   │   │   └── db.go       # 数据库操作
│   │   ├── models/
│   │   │   └── user.go     # 用户模型
│   │   └── routes/
│   │       └── user_routes.go  # 用户 API 路由
│   ├── config/
│   │   └── config.yaml     # 配置文件
│   └── logs/
│       └── app.log         # 日志文件
├── order-service/
│   ├── cmd/
│   │   └── main.go         # 订单服务入口
│   ├── internal/
│   │   ├── database/
│   │   │   └── db.go       # 数据库操作
│   │   ├── models/
│   │   │   └── order.go    # 订单模型
│   │   └── routes/
│   │       └── order_routes.go  # 订单 API 路由
│   ├── config/
│   │   └── config.yaml     # 配置文件
│   └── logs/
│       └── app.log         # 日志文件
└── go.mod                  # Go 模块依赖管理

4. 用户服务实现

4.1 用户服务配置文件

user-service/config/config.yaml 中定义用户服务的配置:

app:
  port: ":8081"
database:
  path: "./users.db"
logging:
  file: "./logs/app.log"

4.2 用户模型

user-service/internal/models/user.go 中定义用户模型:

package models

type User struct {
    ID    uint   `gorm:"primaryKey"`
    Name  string `json:"name"`
    Email string `json:"email"`
}

4.3 数据库初始化

user-service/internal/database/db.go 中初始化数据库:

package database

import (
    "gorm.io/driver/sqlite"
    "gorm.io/gorm"
    "user-service/internal/models"
)

var DB *gorm.DB

func InitDatabase(path string) {
    var err error
    DB, err = gorm.Open(sqlite.Open(path), &gorm.Config{})
    if err != nil {
        panic("failed to connect to database")
    }
    DB.AutoMigrate(&models.User{})
}

4.4 用户 API 路由

user-service/internal/routes/user_routes.go 中定义用户的 API 路由:

package routes

import (
    "net/http"
    "user-service/internal/database"
    "user-service/internal/models"
    "github.com/gin-gonic/gin"
)

// 获取用户列表
func GetUsers(c *gin.Context) {
    var users []models.User
    database.DB.Find(&users)
    c.JSON(http.StatusOK, users)
}

// 创建用户
func CreateUser(c *gin.Context) {
    var user models.User
    if err := c.ShouldBindJSON(&user); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        return
    }
    database.DB.Create(&user)
    c.JSON(http.StatusOK, user)
}

4.5 用户服务入口

user-service/cmd/main.go 中启动用户服务:

package main

import (
    "user-service/internal/database"
    "user-service/internal/routes"
    "github.com/gin-gonic/gin"
    "github.com/sirupsen/logrus"
    "github.com/spf13/viper"
    "os"
)

func initConfig() {
    viper.SetConfigName("config")
    viper.SetConfigType("yaml")
    viper.AddConfigPath("./config")
    if err := viper.ReadInConfig(); err != nil {
        panic("failed to read config file")
    }
}

func setupLogger() *logrus.Logger {
    log := logrus.New()
    logFile, err := os.OpenFile(viper.GetString("logging.file"), os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
    if err == nil {
        log.Out = logFile
    } else {
        log.Info("Failed to log to file, using default stderr")
    }
    return log
}

func main() {
    initConfig()
    database.InitDatabase(viper.GetString("database.path"))
    log := setupLogger()

    router := gin.Default()
    router.GET("/users", routes.GetUsers)
    router.POST("/users", routes.CreateUser)

    log.Info("Starting user service at", viper.GetString("app.port"))
    router.Run(viper.GetString("app.port"))
}

5. 订单服务实现

5.1 订单服务配置文件

order-service/config/config.yaml 中定义订单服务的配置:

app:
  port: ":8082"
database:
  path: "./orders.db"
logging:
  file: "./logs/app.log"
user_service:
  url: "http://localhost:8081"

5.2 订单模型

order-service/internal/models/order.go 中定义订单模型:

package models

type Order struct {
    ID     uint   `gorm:"primaryKey"`
    UserID uint   `json:"user_id"`
    Item   string `json:"item"`
    Price  float64 `json:"price"`
}

5.3 数据库初始化

order-service/internal/database/db.go 中初始化数据库:

package database

import (
    "gorm.io/driver/sqlite"
    "gorm.io/gorm"
    "order-service/internal/models"
)

var DB *gorm.DB

func InitDatabase(path string) {
    var err error
    DB, err = gorm.Open(sqlite.Open(path), &gorm.Config{})
    if err != nil {
        panic("failed to connect to database")
    }
    DB.AutoMigrate(&models.Order{})
}

5.4 订单 API 路由

order-service/internal/routes/order_routes.go 中定义订单的 API 路由:

package routes

import (
    "fmt"
    "net/http"
    "order-service/internal/database"
    "order-service/internal/models"
    "github.com/gin-gonic/gin"
    "github.com/spf13/viper"
    "net/http"
    "io/ioutil"
)

// 获取订单列表
func GetOrders(c *gin.Context) {
    var orders []models.Order
    database.DB.Find(&orders)
    c.JSON(http.StatusOK, orders)
}

// 创建订单
func CreateOrder(c *gin.Context) {
    var order models.Order
    if err := c.ShouldBindJSON(&order); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        return
    }

    // 获取用户信息
    userID := order.UserID
    userServiceURL := fmt.Sprintf("%s/users/%d", viper.GetString("user_service.url"), userID)
    resp, err := http.Get(userServiceURL)
    if err != nil || resp.StatusCode != http.StatusOK {
        c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid user"})
        return
    }
    defer resp.Body.Close()
    body, _

 := ioutil.ReadAll(resp.Body)

    fmt.Println("User data:", string(body))

    database.DB.Create(&order)
    c.JSON(http.StatusOK, order)
}

5.5 订单服务入口

order-service/cmd/main.go 中启动订单服务:

package main

import (
    "order-service/internal/database"
    "order-service/internal/routes"
    "github.com/gin-gonic/gin"
    "github.com/sirupsen/logrus"
    "github.com/spf13/viper"
    "os"
)

func initConfig() {
    viper.SetConfigName("config")
    viper.SetConfigType("yaml")
    viper.AddConfigPath("./config")
    if err := viper.ReadInConfig(); err != nil {
        panic("failed to read config file")
    }
}

func setupLogger() *logrus.Logger {
    log := logrus.New()
    logFile, err := os.OpenFile(viper.GetString("logging.file"), os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
    if err == nil {
        log.Out = logFile
    } else {
        log.Info("Failed to log to file, using default stderr")
    }
    return log
}

func main() {
    initConfig()
    database.InitDatabase(viper.GetString("database.path"))
    log := setupLogger()

    router := gin.Default()
    router.GET("/orders", routes.GetOrders)
    router.POST("/orders", routes.CreateOrder)

    log.Info("Starting order service at", viper.GetString("app.port"))
    router.Run(viper.GetString("app.port"))
}

6. 服务通信与测试

6.1 启动用户服务

user-service 目录下运行以下命令:

go run cmd/main.go

6.2 启动订单服务

order-service 目录下运行以下命令:

go run cmd/main.go

6.3 测试用户服务和订单服务的交互

  1. 创建用户
curl -X POST http://localhost:8081/users -H "Content-Type: application/json" -d '{"name": "John Doe", "email": "john@example.com"}'
  1. 创建订单
curl -X POST http://localhost:8082/orders -H "Content-Type: application/json" -d '{"user_id": 1, "item": "Laptop", "price": 1200.50}'

7. 小结

  • 用户服务与订单服务:我们构建了两个独立的微服务,分别负责用户和订单的管理。
  • 服务之间的通信:订单服务通过 HTTP 请求与用户服务进行通信,获取用户信息。
  • 配置管理:使用 Viper 管理服务的配置,包括服务端口、数据库路径等。
  • 日志记录:通过 Logrus 记录服务的运行日志。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

悟生啊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值