init code
This commit is contained in:
7
api_gateway/README.md
Normal file
7
api_gateway/README.md
Normal file
@@ -0,0 +1,7 @@
|
||||
sudo docker rm -f user_gateway_api && sudo docker run -itd --name user_gateway_api -v ./:/app -p 20000:80 golang:1.25.0-alpine3.22
|
||||
|
||||
----
|
||||
|
||||
# 格式:docker build -t 镜像名:标签 .
|
||||
sudo docker rmi -f user-gateway-api:1.0.0
|
||||
sudo docker build -t user-gateway-api:1.0.0 .
|
69
api_gateway/depend.py
Normal file
69
api_gateway/depend.py
Normal file
@@ -0,0 +1,69 @@
|
||||
import subprocess
|
||||
import os
|
||||
|
||||
def run_command(command, check=True, shell=True):
|
||||
"""执行shell命令并返回结果,包含错误处理"""
|
||||
try:
|
||||
print(f"执行命令: {command}")
|
||||
result = subprocess.run(
|
||||
command,
|
||||
shell=shell,
|
||||
check=check,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
text=True
|
||||
)
|
||||
print(f"命令输出: {result.stdout}")
|
||||
return result
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"命令执行失败: {e.stderr}")
|
||||
raise
|
||||
|
||||
def main():
|
||||
container_name = "user_gateway_api"
|
||||
script_dir = os.path.dirname(os.path.abspath(__file__)) # 获取当前脚本所在目录
|
||||
|
||||
try:
|
||||
# 1. 删除已存在的容器
|
||||
print("===== 步骤1: 删除已存在的容器 =====")
|
||||
run_command(f"sudo docker rm -f {container_name}", check=False)
|
||||
|
||||
# 2. 启动新容器
|
||||
print("\n===== 步骤2: 启动新容器 =====")
|
||||
run_command(
|
||||
f"sudo docker run -itd --name {container_name} "
|
||||
f"-v {script_dir}:/app -p 20000:80 golang:1.25.0-alpine3.22"
|
||||
)
|
||||
|
||||
# 3-5. 进入容器、进入app目录并执行go mod tidy(使用sh而非bash)
|
||||
print("\n===== 步骤3-5: 容器内操作 =====")
|
||||
exec_commands = (
|
||||
"cd /app && " # 进入app目录
|
||||
"echo '当前目录内容:' && ls -la && " # 新增目录检查
|
||||
"go version && " # 检查Go版本
|
||||
"go mod init user_gateway && " # 执行依赖整理
|
||||
"go mod tidy && " # 执行依赖整理
|
||||
"exit" # 退出容器
|
||||
)
|
||||
|
||||
# 使用sh代替bash执行命令
|
||||
run_command(
|
||||
f"sudo docker exec -it {container_name} sh -c '{exec_commands}'"
|
||||
)
|
||||
|
||||
# 7. 删除容器
|
||||
print("\n===== 步骤7: 删除容器 =====")
|
||||
run_command(f"sudo docker rm -f {container_name}")
|
||||
|
||||
# 8. 删除虚悬镜像
|
||||
print("\n===== 步骤8: 清理虚悬镜像 =====")
|
||||
run_command("sudo docker images -f 'dangling=true' -q | xargs -r sudo docker rmi")
|
||||
|
||||
print("\n===== 所有操作完成 =====")
|
||||
|
||||
except Exception as e:
|
||||
print(f"\n操作失败: {str(e)}")
|
||||
exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
11
api_gateway/docker-compose.gateway.yaml
Normal file
11
api_gateway/docker-compose.gateway.yaml
Normal file
@@ -0,0 +1,11 @@
|
||||
services:
|
||||
user_gateway:
|
||||
image: user-gateway-api:1.0.0
|
||||
container_name: api_user_gateway
|
||||
restart: always
|
||||
ports:
|
||||
- "20000:80"
|
||||
networks:
|
||||
- user-network
|
||||
environment:
|
||||
GATEWAY_PORT: ${GATEWAY_PORT} # 引用.env变量
|
38
api_gateway/dockerfile
Normal file
38
api_gateway/dockerfile
Normal file
@@ -0,0 +1,38 @@
|
||||
# ==================== 第一阶段:构建Go程序(构建阶段)====================
|
||||
# 使用官方Go镜像作为构建基础,选择与项目匹配的Go版本(示例用1.25.0,可根据实际调整)
|
||||
FROM golang:1.25.0-alpine3.22 AS builder
|
||||
|
||||
# 设置工作目录(容器内的目录,规范文件位置)
|
||||
WORKDIR /app
|
||||
|
||||
# 复制go.mod和go.sum(先复制依赖文件,利用Docker缓存机制,避免每次代码变动都重新下载依赖)
|
||||
COPY go.mod go.sum ./
|
||||
|
||||
# 下载项目依赖(仅当go.mod/go.sum变动时才会重新执行)
|
||||
RUN go mod download
|
||||
|
||||
# 复制整个项目代码到工作目录
|
||||
COPY . .
|
||||
|
||||
# 构建Go程序:
|
||||
# - CGO_ENABLED=0:禁用CGO,生成静态链接的二进制文件(避免依赖系统库,保证镜像兼容性)
|
||||
# - -o app:指定输出二进制文件名为app
|
||||
# - ./main.go:指定入口文件
|
||||
RUN CGO_ENABLED=0 GOOS=linux go build -o app ./main.go
|
||||
|
||||
|
||||
# ==================== 第二阶段:运行程序(运行阶段)====================
|
||||
# 使用轻量级的alpine镜像(仅5MB左右,大幅减小最终镜像体积)
|
||||
FROM alpine:3.19
|
||||
|
||||
# 设置工作目录
|
||||
WORKDIR /app
|
||||
|
||||
# 从构建阶段复制编译好的二进制文件到当前镜像(仅复制最终产物,减小体积)
|
||||
COPY --from=builder /app/app ./
|
||||
|
||||
# 暴露程序运行端口(与代码中一致)
|
||||
EXPOSE 8080
|
||||
|
||||
# 容器启动时执行的命令:运行二进制文件
|
||||
CMD ["./app"]
|
42
api_gateway/go.mod
Normal file
42
api_gateway/go.mod
Normal file
@@ -0,0 +1,42 @@
|
||||
module user_gateway
|
||||
|
||||
go 1.25.0
|
||||
|
||||
require (
|
||||
github.com/gin-contrib/cors v1.7.6
|
||||
github.com/gin-gonic/gin v1.11.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/bytedance/sonic v1.14.0 // indirect
|
||||
github.com/bytedance/sonic/loader v0.3.0 // indirect
|
||||
github.com/cloudwego/base64x v0.1.6 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.9 // indirect
|
||||
github.com/gin-contrib/sse v1.1.0 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.27.0 // indirect
|
||||
github.com/goccy/go-json v0.10.5 // indirect
|
||||
github.com/goccy/go-yaml v1.18.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||
github.com/quic-go/qpack v0.5.1 // indirect
|
||||
github.com/quic-go/quic-go v0.54.0 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.3.0 // indirect
|
||||
go.uber.org/mock v0.5.0 // indirect
|
||||
golang.org/x/arch v0.20.0 // indirect
|
||||
golang.org/x/crypto v0.40.0 // indirect
|
||||
golang.org/x/mod v0.25.0 // indirect
|
||||
golang.org/x/net v0.42.0 // indirect
|
||||
golang.org/x/sync v0.16.0 // indirect
|
||||
golang.org/x/sys v0.35.0 // indirect
|
||||
golang.org/x/text v0.27.0 // indirect
|
||||
golang.org/x/tools v0.34.0 // indirect
|
||||
google.golang.org/protobuf v1.36.9 // indirect
|
||||
)
|
137
api_gateway/main.go
Normal file
137
api_gateway/main.go
Normal file
@@ -0,0 +1,137 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/gin-contrib/cors"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
var serviceMap = map[string]string{
|
||||
"/user/login": "http://user_login:80",
|
||||
"/user/register": "http://user_register:80",
|
||||
"/user/delete": "http://user_delete:80",
|
||||
"/user/update/account": "http://user_update_account:80",
|
||||
"/user/update/password": "http://user_update_password:80",
|
||||
}
|
||||
|
||||
// 日志中间件(记录网关接收的请求)
|
||||
func loggerMiddleware() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
startTime := time.Now()
|
||||
c.Next()
|
||||
endTime := time.Now()
|
||||
|
||||
log.Printf(
|
||||
"[网关请求] %s %s | 状态码: %d | 耗时: %s | 客户端IP: %s",
|
||||
c.Request.Method,
|
||||
c.Request.URL.Path,
|
||||
c.Writer.Status(),
|
||||
endTime.Sub(startTime),
|
||||
c.ClientIP(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// 反向代理处理(添加详细转发日志)
|
||||
func reverseProxy(target string) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
// 解析目标服务地址
|
||||
targetURL, err := url.Parse(target)
|
||||
if err != nil {
|
||||
log.Printf("[代理错误] 目标服务地址解析失败: %s, 错误: %v", target, err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"success": false,
|
||||
"message": "服务配置错误",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 创建反向代理实例
|
||||
proxy := httputil.NewSingleHostReverseProxy(targetURL)
|
||||
|
||||
// 记录转发前的请求信息
|
||||
reqStart := time.Now()
|
||||
log.Printf(
|
||||
"[开始转发] 方法: %s | 原始路径: %s | 目标服务: %s | 客户端IP: %s",
|
||||
c.Request.Method,
|
||||
c.Request.URL.Path,
|
||||
targetURL.String(),
|
||||
c.ClientIP(),
|
||||
)
|
||||
|
||||
// 自定义请求转发逻辑(并记录请求头)
|
||||
originalDirector := proxy.Director
|
||||
proxy.Director = func(req *http.Request) {
|
||||
originalDirector(req)
|
||||
// 传递关键头信息
|
||||
req.Header.Set("Origin", c.Request.Header.Get("Origin"))
|
||||
req.Host = targetURL.Host
|
||||
req.Header.Set("X-Forwarded-By", "user-gateway")
|
||||
|
||||
// 打印转发的请求头(调试用,生产环境可注释)
|
||||
log.Printf("[转发请求头] 目标服务: %s | Origin: %s | Host: %s",
|
||||
targetURL.Host,
|
||||
req.Header.Get("Origin"),
|
||||
req.Host,
|
||||
)
|
||||
}
|
||||
|
||||
// 记录响应信息
|
||||
proxy.ModifyResponse = func(resp *http.Response) error {
|
||||
// 计算转发耗时
|
||||
proxyDuration := time.Since(reqStart)
|
||||
|
||||
// 打印响应状态
|
||||
log.Printf(
|
||||
"[转发响应] 目标服务: %s | 状态码: %d | 耗时: %s | 响应头Origin: %s",
|
||||
targetURL.Host,
|
||||
resp.StatusCode,
|
||||
proxyDuration,
|
||||
resp.Header.Get("Access-Control-Allow-Origin"),
|
||||
)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 执行代理转发
|
||||
proxy.ServeHTTP(c.Writer, c.Request)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
r := gin.Default()
|
||||
|
||||
// 注册日志中间件
|
||||
r.Use(loggerMiddleware())
|
||||
|
||||
// 跨域配置
|
||||
r.Use(cors.New(cors.Config{
|
||||
AllowOrigins: []string{"*"},
|
||||
AllowMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"},
|
||||
AllowHeaders: []string{"Origin", "Content-Type", "Accept", "Authorization"},
|
||||
ExposeHeaders: []string{"Content-Length"},
|
||||
AllowCredentials: true,
|
||||
MaxAge: 12 * time.Hour,
|
||||
}))
|
||||
|
||||
// 注册路由
|
||||
for path, target := range serviceMap {
|
||||
r.Any(path, reverseProxy(target))
|
||||
r.Any(path+"/*any", reverseProxy(target))
|
||||
}
|
||||
|
||||
// 启动服务
|
||||
port := os.Getenv("GATEWAY_PORT")
|
||||
if port == "" {
|
||||
port = "80"
|
||||
}
|
||||
log.Printf("网关服务启动在 %s 端口", port)
|
||||
if err := r.Run(":" + port); err != nil {
|
||||
log.Fatalf("服务启动失败: %v", err)
|
||||
}
|
||||
}
|
22
api_gateway/release.sh
Executable file
22
api_gateway/release.sh
Executable file
@@ -0,0 +1,22 @@
|
||||
#!/bin/bash
|
||||
set -e # 当任何命令失败时立即退出脚本
|
||||
|
||||
# 定义镜像名称和标签
|
||||
IMAGE_NAME="user-gateway-api"
|
||||
IMAGE_TAG="1.0.0"
|
||||
FULL_IMAGE="${IMAGE_NAME}:${IMAGE_TAG}"
|
||||
|
||||
echo "开始删除现有镜像 ${FULL_IMAGE}..."
|
||||
if sudo docker rmi -f "${FULL_IMAGE}" >/dev/null 2>&1; then
|
||||
echo "镜像 ${FULL_IMAGE} 删除成功"
|
||||
else
|
||||
echo "镜像 ${FULL_IMAGE} 不存在,跳过删除步骤"
|
||||
fi
|
||||
|
||||
echo "开始构建新镜像 ${FULL_IMAGE}..."
|
||||
if sudo docker build -t "${FULL_IMAGE}" .; then
|
||||
echo "镜像 ${FULL_IMAGE} 构建成功!"
|
||||
else
|
||||
echo "错误:镜像构建失败" >&2
|
||||
exit 1
|
||||
fi
|
Reference in New Issue
Block a user