当前位置: 首页 > news >正文

《Go 简易速速上手小册》第10章:微服务与云原生应用(2024 最新版)

在这里插入图片描述

文章目录

  • 10.1 构建微服务架构 - 探索 Go 语言的微观世界
    • 10.1.1 基础知识讲解
    • 10.1.2 重点案例:订单处理系统
      • 订单服务
      • 测试服务
    • 10.1.3 拓展案例 1:用户认证服务
      • 安装所需的包
      • 实现用户模型和存储
      • 实现 JWT 生成和验证
      • 实现认证服务
      • 测试服务
    • 10.1.4 拓展案例 2:商品推荐服务
      • 设计商品推荐服务
      • 实现简化的推荐服务
      • 集成推荐服务到订单系统
      • 测试服务
  • 10.2 容器化与 Go - 打包你的 Go 应用航向云端
    • 10.2.1 基础知识讲解
    • 10.2.2 重点案例:Go Web 服务的容器化
      • 步骤一:编写Go Web服务
      • 步骤二:编写 Dockerfile
      • 步骤三:构建和运行容器
      • 测试服务
    • 10.2.3 拓展案例 1:多阶段构建优化
      • 功能描述
      • 步骤一:优化 Dockerfile
      • 步骤二:构建和运行容器
      • 步骤三:验证镜像大小的优化
      • 测试服务
    • 10.2.3 拓展案例 2:为 Go 微服务创建 Docker Compose 环境
      • 功能描述
      • 步骤一:准备用户服务和产品服务
      • 步骤二:编写 Dockerfile
      • 步骤三:编写 Docker Compose 文件
      • 步骤四:启动服务
      • 测试服务
  • 10.3 云原生技术栈与 Go - Go 语言在云上的航行
    • 10.3.1 基础知识讲解
    • 10.3.2 重点案例:Go 微服务在 Kubernetes 上的部署
      • 步骤一:准备 Go 微服务
      • 步骤二:容器化 Go 微服务
      • 步骤三:编写 Kubernetes 部署配置
      • 步骤四:部署到 Kubernetes
      • 步骤五:访问微服务
    • 10.3.3 拓展案例 1:使用 Helm 管理 Go 应用的 Kubernetes 部署
      • 步骤一:创建 Helm Chart
      • 步骤二:定制化 Chart
      • 步骤三:打包和部署 Chart
      • 步骤四:验证部署
      • 步骤五:更新和升级
    • 10.3.4 拓展案例 2:实现 Go 微服务的自动扩展
      • 步骤一:准备 Go 微服务
      • 步骤二:为微服务启用资源请求和限制
      • 步骤三:创建 HPA
      • 步骤四:测试自动扩展
      • 步骤五:调整 HPA 策略(可选)

10.1 构建微服务架构 - 探索 Go 语言的微观世界

10.1.1 基础知识讲解

微服务架构是一种将单一应用程序划分成一组小服务的方法,每个服务运行在其独立的进程中,服务之间通过轻量级的通信机制(通常是HTTP资源API)相互协作、相互独立部署。这种架构允许快速、可靠和频繁地部署大型、复杂的应用程序。

微服务架构的核心特点包括:

  • 服务分离:每个微服务负责应用程序的一小部分功能,并可以独立更新和扩展。
  • 自治性:每个服务都是独立部署的,有自己的数据库和数据管理模型,减少了服务间的依赖。
  • 技术多样性:不同的服务可以使用不同的编程语言和数据存储技术开发,使得技术栈更加灵活。
  • 可扩展性:可以根据需要对特定功能进行扩展,而不必重新部署整个应用。

Go 在微服务中的应用

Go语言以其并发支持、高性能和简洁语法成为构建微服务的热门选择。Go的标准库提供了强大的网络和HTTP支持,使得开发RESTful API变得简单快捷。

10.1.2 重点案例:订单处理系统

在这个扩展案例中,我们将构建一个简单的订单处理系统,演示如何使用Go语言和gin框架开发微服务。这个系统将包含订单服务的基础实现,包括创建订单和查询订单详情的功能。

订单服务

安装gin框架

首先,确保安装了gin框架:

go get -u github.com/gin-gonic/gin

定义订单模型

在订单服务中,我们定义一个Order结构体来表示订单信息:

// models.gopackage maintype Order struct {ID    string  `json:"id"`Items []Item  `json:"items"`Total float64 `json:"total"`
}type Item struct {ProductID string `json:"product_id"`Quantity  int    `json:"quantity"`Price     float64 `json:"price"`
}

实现订单服务

我们使用gin来创建RESTful API,处理创建和查询订单的请求:

// order_service.gopackage mainimport ("github.com/gin-gonic/gin""net/http"
)var orders = []Order{{ID: "1", Items: []Item{{ProductID: "101", Quantity: 2, Price: 15.0}}, Total: 30.0},
}func main() {router := gin.Default()router.POST("/orders", createOrder)router.GET("/orders/:id", getOrder)router.Run(":8080")
}func createOrder(c *gin.Context) {var newOrder Orderif err := c.ShouldBindJSON(&newOrder); err != nil {c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})return}// 这里简化处理,直接添加到数组。在实际应用中,应保存到数据库。newOrder.ID = "2" // 假设生成的订单IDorders = append(orders, newOrder)c.JSON(http.StatusCreated, newOrder)
}func getOrder(c *gin.Context) {orderID := c.Param("id")for _, order := range orders {if order.ID == orderID {c.JSON(http.StatusOK, order)return}}c.JSON(http.StatusNotFound, gin.H{"error": "Order not found"})
}

在这个简单的示例中,我们实现了两个API端点:/orders用于创建新订单,/orders/:id用于查询指定ID的订单详情。为了简化,我们将创建的订单存储在内存中的一个切片中,并未使用数据库。

测试服务

  1. 创建订单:使用curl或Postman发送POST请求到http://localhost:8080/orders,请求体包含订单数据。
  2. 查询订单:发送GET请求到http://localhost:8080/orders/1,获取ID为1的订单详情。

通过这个案例,你已经学会了如何使用Go语言和gin框架开发简单的微服务。虽然这个订单处理系统非常基础,但它为你提供了微服务架构下开发复杂系统的起点。随着你进一步深入学习,你将能够添加更多服务,如支付服务和库存服务,使用消息队列处理服务间通信,甚至使用容器化和云原生技术来部署你的微服务。

10.1.3 拓展案例 1:用户认证服务

在这个扩展案例中,我们将为订单处理系统添加一个用户认证服务,使用JSON Web Tokens (JWT)进行安全认证。这个服务将负责用户的注册、登录,并在成功登录后发放JWT,以便用户在访问受保护的订单服务时进行身份验证。

安装所需的包

首先,安装gin框架和JWT相关的Go包:

go get -u github.com/gin-gonic/gin
go get -u github.com/golang-jwt/jwt/v4

实现用户模型和存储

为简化,我们使用内存存储来保存用户信息和模拟数据库操作:

// models.gopackage maintype User struct {Username stringPassword string // 注意:实际应用中应存储密码的哈希值
}var userStore = map[string]string{"user1": "password1", // 用户名:密码,实际应用中应存储密码的哈希值
}

实现 JWT 生成和验证

定义一个简单的JWT管理器,用于生成和验证JWT:

// jwt_manager.gopackage mainimport ("fmt""time""github.com/golang-jwt/jwt/v4"
)var jwtKey = []byte("my_secret_key") // 保持安全type Claims struct {Username string `json:"username"`jwt.RegisteredClaims
}func GenerateJWT(username string) (string, error) {expirationTime := time.Now().Add(1 * time.Hour)claims := &Claims{Username: username,RegisteredClaims: jwt.RegisteredClaims{ExpiresAt: jwt.NewNumericDate(expirationTime),},}token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)tokenString, err := token.SignedString(jwtKey)return tokenString, err
}func ValidateToken(tokenString string) (*Claims, bool) {claims := &Claims{}token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) {return jwtKey, nil})if err != nil {return nil, false}return claims, token.Valid
}

实现认证服务

使用gin框架实现用户注册、登录以及JWT验证的中间件:

// auth_service.gopackage mainimport ("github.com/gin-gonic/gin""net/http"
)func main() {router := gin.Default()router.POST("/login", login)router.GET("/orders", authenticateJWT(), getOrder) // 使用JWT中间件保护订单服务router.Run(":8080")
}func login(c *gin.Context) {var user Userif err := c.ShouldBindJSON(&user); err != nil {c.JSON(http.StatusBadRequest, gin.H{"error": "Bad request"})return}// 模拟用户认证expectedPassword, ok := userStore[user.Username]if !ok || expectedPassword != user.Password {c.JSON(http.StatusUnauthorized, gin.H{"error": "Authentication failed"})return}// 生成JWTtoken, err := GenerateJWT(user.Username)if err != nil {c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to generate token"})return}c.JSON(http.StatusOK, gin.H{"token": token})
}func authenticateJWT() gin.HandlerFunc {return func(c *gin.Context) {const Bearer_schema = "Bearer "header := c.GetHeader("Authorization")if header == "" {c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Authorization header missing"})return}tokenString := header[len(Bearer_schema):]claims, valid := ValidateToken(tokenString)if !valid {c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Invalid token"})return}// Token is valid, add username to the contextc.Set("username", claims.Username)c.Next()}
}func getOrder(c *gin.Context) {// 获取订单逻辑c.JSON(http.StatusOK, gin.H{"message": "Order details"})
}

在这个示例中,login函数处理用户登录请求,成功认证后生成JWT。authenticateJWT是一个中间件,用于保护需要认证的路由,它验证请求中的JWT并提取用户名。

测试服务

  1. 登录:发送POST请求到http://localhost:8080/login,包含用户名和密码,以获取JWT。
  2. 访问受保护的路由:使用获取的JWT作为Authorization头发送GET请求到http://localhost:8080/orders

通过这个拓展案例,你已经学会了如何在Go语言中添加用户认证服务,并使用JWT进行安全认证。这是构建现代Web应用和微服务的关键组成部分,确保了数据的安全访问和服务的安全调用。随着你对Go和微服务架构的深入理解,你将能够构建更加安全、可靠的应用。

10.1.4 拓展案例 2:商品推荐服务

在这个拓展案例中,我们将为订单处理系统添加一个商品推荐服务,根据用户的购买历史和浏览行为来推荐商品。此服务将独立于订单处理系统,通过事件驱动方式接收用户行为数据,并使用简单的算法来生成推荐。

设计商品推荐服务

核心概念

  • 用户行为数据:包括用户的购买记录和浏览历史,用于分析用户偏好。
  • 推荐算法:基于用户行为数据,推算出可能感兴趣的商品。
  • 事件驱动:该服务通过监听用户行为事件(如购买或浏览商品)来触发推荐算法。

实现简化的推荐服务

为了保持示例的简洁性,我们将使用一个静态的商品列表来模拟商品数据库,并实现一个基于用户最近购买商品的简单推荐算法。

模拟商品数据库

// products.gopackage mainvar products = []string{"Go Programming Book","Rubber Duck","Pirate Hat","Gin Framework Guide","Kubernetes Deployment Handbook",
}

实现推荐逻辑

// recommendation_service.gopackage mainimport ("fmt""math/rand""time"
)func recommendProduct(boughtProduct string) string {rand.Seed(time.Now().UnixNano())recommendedIndex := rand.Intn(len(products))// 简单地从产品列表中随机选择一个产品作为推荐,确保推荐的产品不是刚买的产品for products[recommendedIndex] == boughtProduct {recommendedIndex = rand.Intn(len(products))}return products[recommendedIndex]
}

集成推荐服务到订单系统

我们将模拟用户购买商品后接收推荐的过程。在订单服务中,每当用户购买商品,我们将调用推荐服务来推荐另一个商品。

// order_service.gopackage mainimport ("github.com/gin-gonic/gin""net/http"
)func main() {router := gin.Default()router.POST("/purchase", purchaseProduct)router.Run(":8080")
}func purchaseProduct(c *gin.Context) {type Purchase struct {ProductName string `json:"product_name"`}var purchase Purchaseif err := c.ShouldBindJSON(&purchase); err != nil {c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request"})return}recommendedProduct := recommendProduct(purchase.ProductName)c.JSON(http.StatusOK, gin.H{"message": "Purchase successful", "recommended_product": recommendedProduct})
}

在这个简单的示例中,当用户通过POST请求到/purchase端点购买一个商品时,系统将返回一个推荐的商品。这个推荐是基于一个简单的随机选择算法,实际应用中,推荐算法会更加复杂,可能会考虑用户的购买历史、商品相似度、用户评分等因素。

测试服务

  • 购买商品:发送 POST 请求到http://localhost:8080/purchase,包含要购买的商品名称,查看返回的推荐商品。

通过这个拓展案例,你了解了如何为现有的订单处理系统添加一个简单的商品推荐服务。虽然这里使用的推荐算法非常基础,但它展示了如何基于用户行为数据来增加额外的服务和功能,为用户提供个性化体验。随着技术的深入,你可以探索更高级的算法和技术,如机器学习,来进一步提升推荐系统的准确性和效率。

10.2 容器化与 Go - 打包你的 Go 应用航向云端

10.2.1 基础知识讲解

容器化是一种轻量级、可移植的软件打包技术,它允许开发者将应用及其全部依赖一起打包成一个容器镜像。这种方法确保了应用在不同环境中的一致性和可靠性。Docker是目前最流行的容器化平台,提供了一个标准化的方法来打包、分发和运行容器化应用。

为什么 Go 适合容器化?

  • 高效的二进制文件:Go编译后的应用是单个二进制文件,包含了所有依赖,非常适合放入轻量级的容器中。
  • 跨平台编译:Go支持交叉编译,可以轻松为不同平台生成二进制文件,进一步增强了容器的可移植性。
  • 快速启动时间:Go应用启动速度快,非常适合在容器环境中快速扩展和部署。

容器化 Go 应用的基本步骤

  1. 编写Dockerfile:定义如何在容器中构建和运行Go应用。
  2. 构建容器镜像:使用Dockerfile和源代码构建可部署的容器镜像。
  3. 运行容器:从镜像启动容器,运行你的Go应用。

10.2.2 重点案例:Go Web 服务的容器化

让我们通过一个实际的示例来演示如何将一个简单的Go编写的Web服务容器化,从而可以在任何支持Docker的环境中运行。

步骤一:编写Go Web服务

首先,我们需要创建一个简单的HTTP服务。以下是服务的代码,它会在根路径/上响应带有欢迎信息的HTTP请求。

main.go:

package mainimport ("fmt""net/http"
)func main() {http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {fmt.Fprintf(w, "Hello, Dockerized World!")})fmt.Println("Server is running on port 8080...")http.ListenAndServe(":8080", nil)
}

步骤二:编写 Dockerfile

接下来,我们需要为我们的Go应用编写一个Dockerfile。这个Dockerfile使用了多阶段构建,第一阶段用于构建应用,第二阶段运行应用。

Dockerfile:

# 第一阶段:构建环境
FROM golang:1.16-alpine AS buildWORKDIR /app# 复制模块文件并下载依赖
COPY go.mod go.sum ./
RUN go mod download# 复制源代码
COPY *.go ./# 编译应用
RUN CGO_ENABLED=0 GOOS=linux go build -o /go-web-app# 第二阶段:运行环境
FROM scratch# 从构建阶段复制编译好的二进制文件
COPY --from=build /go-web-app /go-web-app# 暴露端口
EXPOSE 8080# 定义入口点
ENTRYPOINT ["/go-web-app"]

步骤三:构建和运行容器

使用以下命令构建Docker镜像,并运行容器:

docker build -t go-web-app .
docker run -d -p 8080:8080 go-web-app
  • -t go-web-app:给镜像命名为go-web-app
  • -d:后台运行容器。
  • -p 8080:8080:将容器的8080端口映射到宿主机的8080端口。

测试服务

在浏览器或使用命令行工具(如curl)访问http://localhost:8080,你应该会看到“Hello, Dockerized World!”的欢迎信息。

例如,使用curl测试:

curl http://localhost:8080

输出应为:

Hello, Dockerized World!

通过这个案例,你已经学会了如何将一个简单的Go Web服务容器化。这个过程涉及到编写应用代码、创建Dockerfile以及使用Docker命令构建和运行容器。容器化不仅使得部署变得简单快捷,而且提高了应用的可移植性和一致性,为在云环境中运行提供了便利。随着你对Docker和容器化技术的进一步探索,你将能够更有效地开发、部署和管理Go应用。

10.2.3 拓展案例 1:多阶段构建优化

在Docker容器化的上下文中,多阶段构建是一种优化技术,它允许在一个Dockerfile中使用多个构建阶段,但最终只将必要的文件复制到最终镜像中。这样做的好处是可以显著减小最终镜像的大小,同时保持构建过程的清晰和高效。

功能描述

为了展示多阶段构建优化,我们将使用前面创建的Go Web服务案例,并优化其Dockerfile,以减小最终产生的Docker镜像的大小。

步骤一:优化 Dockerfile

以下是针对Go Web服务的多阶段构建优化后的Dockerfile:

# 第一阶段:构建环境
FROM golang:1.16-alpine AS builderWORKDIR /app# 复制Go模块和依赖文件
COPY go.mod go.sum ./
RUN go mod download# 复制源代码
COPY . .# 编译Go应用为静态链接的二进制文件
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o go-web-app .# 第二阶段:运行环境
FROM alpine:latest  RUN apk --no-cache add ca-certificatesWORKDIR /root/# 从构建阶段复制编译好的二进制文件
COPY --from=builder /app/go-web-app .# 暴露端口
EXPOSE 8080# 定义入口点
CMD ["./go-web-app"]

在这个优化后的Dockerfile中,我们在第二阶段使用了alpine:latest作为基础镜像,而不是scratch。这是因为alpine镜像虽然相对较小,但包含了运行大多数应用所需的最小系统和库,包括ca-certificates,这对于执行HTTPS请求非常重要。同时,通过使用CGO_ENABLED=0编译Go应用,我们确保生成的二进制文件是静态链接的,没有依赖于C库,这让它可以在几乎任何Linux环境下运行。

步骤二:构建和运行容器

使用优化后的Dockerfile,按照之前的步骤构建并运行容器:

docker build -t go-web-app-optimized .
docker run -d -p 8080:8080 go-web-app-optimized

步骤三:验证镜像大小的优化

你可以使用以下命令来比较优化前后镜像的大小,看到多阶段构建优化带来的效果:

docker images | grep go-web-app

你应该会注意到,使用多阶段构建优化后的镜像大小要比原始镜像小得多。

测试服务

确保服务正常运行,通过访问http://localhost:8080或使用curl命令测试:

curl http://localhost:8080

应返回“Hello, Dockerized World!”的欢迎信息。

通过这个拓展案例,你学会了如何通过多阶段构建来优化Go应用的Docker镜像大小,使其更适合生产环境部署。这种优化不仅减少了资源消耗,还加快了镜像的传输和部署速度,是容器化应用部署中的一个重要实践。随着你深入探索Docker和容器技术,你将能够构建更高效、更安全的容器化应用。

10.2.3 拓展案例 2:为 Go 微服务创建 Docker Compose 环境

在微服务架构中,通常需要同时管理多个服务。Docker Compose是一个用于定义和运行多容器Docker应用程序的工具。通过使用Docker Compose,你可以使用YAML文件来配置应用的服务,并且通过一个简单的命令来启动和停止所有服务。

功能描述

假设我们有两个Go微服务:一个是用户服务,用于处理用户的注册和登录请求;另一个是产品服务,用于管理产品信息。我们将使用Docker Compose来定义这两个服务,并确保它们可以在同一网络中相互通信。

步骤一:准备用户服务和产品服务

为了简化,我们将为用户服务和产品服务各自创建一个简单的HTTP服务器。每个服务都监听不同的端口,并提供基本的RESTful API。

用户服务(UserService):

// userService/main.go
package mainimport ("fmt""net/http"
)func main() {http.HandleFunc("/users", func(w http.ResponseWriter, r *http.Request) {fmt.Fprintf(w, "User service is up!")})fmt.Println("User service listening on port 8081...")http.ListenAndServe(":8081", nil)
}

产品服务(ProductService):

// productService/main.go
package mainimport ("fmt""net/http"
)func main() {http.HandleFunc("/products", func(w http.ResponseWriter, r *http.Request) {fmt.Fprintf(w, "Product service is up!")})fmt.Println("Product service listening on port 8082...")http.ListenAndServe(":8082", nil)
}

步骤二:编写 Dockerfile

为每个服务编写一个Dockerfile。由于这两个服务结构类似,Dockerfile也会非常相似。

# Dockerfile
FROM golang:1.16-alpineWORKDIR /appCOPY go.mod ./
COPY go.sum ./
RUN go mod downloadCOPY *.go ./RUN go build -o serviceEXPOSE 8081 # 对于用户服务
# EXPOSE 8082 # 对于产品服务CMD ["./service"]

请根据实际服务调整EXPOSE行。

步骤三:编写 Docker Compose 文件

创建docker-compose.yml文件来定义用户服务和产品服务。

version: '3.8'
services:user-service:build:context: ./userServicedockerfile: Dockerfileports:- "8081:8081"product-service:build:context: ./productServicedockerfile: Dockerfileports:- "8082:8082"

这个docker-compose.yml文件定义了两个服务:user-serviceproduct-service。它们分别映射了对应的端口到宿主机,以便你可以从宿主机访问这些服务。

步骤四:启动服务

在包含docker-compose.yml文件的目录中运行以下命令来构建和启动服务:

docker-compose up --build

这将根据每个服务的Dockerfile构建镜像,然后启动容器。--build选项确保在启动服务之前构建或重新构建镜像。

测试服务

一旦服务启动,你可以通过访问http://localhost:8081/usershttp://localhost:8082/products来测试用户服务和产品服务是否正常运行。

通过这个拓展案例,你已经学会了如何使用Docker Compose来定义和管理多个Go微服务。Docker Compose不仅简化了多容器应用的开发和测试流程,还提供了一种在生产环境中部署和扩展服务的有效方法。随着你对Docker Compose的进一步探索,你将能够更加灵活和高效地部署复杂的微服务架构。

10.3 云原生技术栈与 Go - Go 语言在云上的航行

10.3.1 基础知识讲解

云原生技术是指那些为开发者提供构建和运行可扩展应用程序在现代动态环境中(如公有云、私有云和混合云)的技术集合。这些技术使得应用更加灵活、可维护,并易于扩展。

云原生技术栈的关键组件包括:

  • 容器化:容器提供了一种轻量级的、一致的软件打包方式,使应用在不同的计算环境中运行得更加可靠。
  • 微服务架构:通过将应用拆分为一组小服务,微服务架构使得应用更容易开发和扩展。
  • 声明式自动化:使用Kubernetes等工具自动管理容器化应用,实现自我修复、自动扩展和滚动更新等。
  • DevOps和持续交付:云原生鼓励更快的迭代速度和更高的部署频率,通过自动化的构建、测试和部署来实现。

Go 在云原生中的角色

Go语言因其简单、高效和强大的并发支持,在云原生生态系统中占据了重要地位。许多关键的云原生项目,如Kubernetes、Docker和Istio,都是用Go编写的。Go的这些特性使其成为开发高性能、可扩展的云原生应用的理想选择。

10.3.2 重点案例:Go 微服务在 Kubernetes 上的部署

让我们通过一个具体的示例来演示如何将Go编写的微服务容器化并部署到Kubernetes集群上。这个过程涵盖了应用的容器化、创建Docker镜像、推送到镜像仓库,以及编写和应用Kubernetes部署配置。

步骤一:准备 Go 微服务

首先,我们复用之前创建的简单HTTP服务器代码,该服务监听8080端口并返回欢迎消息。

main.go:

package mainimport ("fmt""net/http"
)func handler(w http.ResponseWriter, r *http.Request) {fmt.Fprintf(w, "Hello, Kubernetes World!")
}func main() {http.HandleFunc("/", handler)fmt.Println("Starting server on port 8080...")http.ListenAndServe(":8080", nil)
}

步骤二:容器化 Go 微服务

为微服务创建一个Dockerfile:

# 使用Go官方镜像作为构建环境
FROM golang:1.16-alpine AS build# 设置工作目录
WORKDIR /app# 复制并下载依赖
COPY go.mod ./
COPY go.sum ./
RUN go mod download# 复制源代码并编译
COPY *.go ./
RUN CGO_ENABLED=0 GOOS=linux go build -o webapp .# 使用scratch作为运行环境
FROM scratch
COPY --from=build /app/webapp /webapp
EXPOSE 8080
ENTRYPOINT ["/webapp"]

构建并推送镜像到Docker Hub或其他容器镜像仓库:

docker build -t yourusername/go-webapp-k8s .
docker push yourusername/go-webapp-k8s

请确保替换yourusername为你的Docker Hub用户名。

步骤三:编写 Kubernetes 部署配置

创建webapp-deployment.yaml文件,定义微服务的部署和服务对象:

apiVersion: apps/v1
kind: Deployment
metadata:name: go-webapp
spec:replicas: 2selector:matchLabels:app: go-webapptemplate:metadata:labels:app: go-webappspec:containers:- name: go-webappimage: yourusername/go-webapp-k8sports:- containerPort: 8080---
apiVersion: v1
kind: Service
metadata:name: go-webapp-service
spec:type: LoadBalancerports:- port: 8080targetPort: 8080selector:app: go-webapp

替换image字段中的yourusername/go-webapp-k8s为你的镜像名称。

步骤四:部署到 Kubernetes

使用kubectl应用部署配置,将应用部署到Kubernetes集群:

kubectl apply -f webapp-deployment.yaml

查看部署状态和服务:

kubectl get deployments
kubectl get services

步骤五:访问微服务

如果你在本地使用Minikube,使用以下命令找到服务的URL:

minikube service go-webapp-service --url

在浏览器中访问该URL,或使用curl命令,你应该能够看到“Hello, Kubernetes World!”的消息。

通过这个案例,你已经学会了如何将Go微服务容器化并在Kubernetes上部署。这不仅展示了从代码到部署的完整流程,还体现了云原生应用开发中的关键实践,包括容器化、微服务架构和声明式自动化部署。随着你深入探索Kubernetes和云原生技术栈,你将能够构建和管理更加复杂和强大的应用。

10.3.3 拓展案例 1:使用 Helm 管理 Go 应用的 Kubernetes 部署

Helm是Kubernetes的包管理器,它使得定义、安装和升级Kubernetes应用变得简单。通过Helm,我们可以将应用及其依赖打包到一个chart中,然后通过简单的命令来部署和管理这个chart。这个案例将展示如何使用Helm来管理之前创建的Go微服务的部署。

步骤一:创建 Helm Chart

首先,确保你已经安装了Helm。然后在命令行中执行以下命令来创建一个新的Helm chart:

helm create go-webapp-chart

这将在当前目录下创建一个名为go-webapp-chart的文件夹,里面包含了chart的初始文件和文件夹结构。

步骤二:定制化 Chart

定制化你的Helm chart来适配Go微服务。修改go-webapp-chart/values.yaml文件来定义一些默认配置,比如镜像的仓库和标签:

# values.yamlreplicaCount: 2image:repository: yourusername/go-webapp-k8spullPolicy: IfNotPresent# tag: "If you have a specific version"service:type: LoadBalancerport: 8080

确保将image.repository的值替换为你的容器镜像地址。

接下来,修改go-webapp-chart/templates/deployment.yaml文件,确保它使用values.yaml中定义的值:

# deployment.yaml 中的部分内容
spec:replicas: {{ .Values.replicaCount }}template:spec:containers:- name: go-webappimage: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"ports:- containerPort: {{ .Values.service.port }}

步骤三:打包和部署 Chart

在chart目录(go-webapp-chart)中,打包你的chart:

helm package .

然后,使用Helm安装你的chart到Kubernetes集群:

helm install go-webapp-release ./go-webapp-chart-0.1.0.tgz

这里go-webapp-release是release的名字,你可以根据需要自定义。

步骤四:验证部署

使用以下命令来检查release的状态:

helm list
kubectl get services

找到你的服务的外部IP或端口(如果你在本地如Minikube上测试,使用minikube service go-webapp-service --url获取URL),然后在浏览器中访问或使用curl命令来验证服务是否正常运行。

步骤五:更新和升级

如果需要更新应用配置,你可以修改values.yaml文件,然后使用以下命令更新部署:

helm upgrade go-webapp-release ./go-webapp-chart

通过这个案例,你学会了如何使用Helm来管理Go应用的Kubernetes部署。Helm不仅简化了Kubernetes应用的部署流程,还提供了版本控制、回滚等功能,极大地提高了云原生应用管理的效率和可靠性。随着你对Helm的深入学习,你将能够更加高效地管理复杂的Kubernetes应用。

10.3.4 拓展案例 2:实现 Go 微服务的自动扩展

Kubernetes的水平自动扩展(HPA,Horizontal Pod Autoscaler)允许根据监测到的CPU使用率或其他选定的度量自动增加或减少Pod的数量。这个案例将演示如何为Go编写的微服务实现自动扩展功能,以确保应用能够根据负载自动调整其运行实例的数量。

步骤一:准备 Go 微服务

假设我们已经有一个Go微服务,它已经被容器化并部署到Kubernetes上,如之前的“Go微服务在Kubernetes上的部署”案例所示。

步骤二:为微服务启用资源请求和限制

为了使HPA能够根据CPU使用情况自动扩展Pod,首先需要在微服务的Deployment配置中指定每个容器的资源请求和限制。编辑你的deployment.yaml文件,为containers部分添加resources字段:

apiVersion: apps/v1
kind: Deployment
metadata:name: go-webapp
spec:...template:...spec:containers:- name: go-webappimage: yourusername/go-webapp-k8sresources:requests:cpu: "100m"memory: "100Mi"limits:cpu: "200m"memory: "200Mi"ports:- containerPort: 8080

这里的requests字段指定了每个Pod启动时的最小资源需求,而limits字段则指定了Pod可以消耗的最大资源量。

步骤三:创建 HPA

接下来,使用kubectl命令创建HPA,以自动扩展你的Go微服务。以下命令创建一个HPA,它将根据目标Pod的平均CPU使用率自动调整Pod的数量。当CPU使用率超过50%时,Kubernetes会尝试增加Pod的数量,直到最多10个Pod。

kubectl autoscale deployment go-webapp --cpu-percent=50 --min=1 --max=10

步骤四:测试自动扩展

为了测试HPA,你可以通过增加向微服务发送的请求来人为增加负载。这可以通过编写简单的脚本不断请求你的服务来实现。

监控HPA和Pod的状态,以查看是否根据CPU负载自动调整了Pod的数量:

kubectl get hpa
kubectl get pods

步骤五:调整 HPA 策略(可选)

根据应用的具体需求,你可能需要调整HPA的行为。这可以通过编辑HPA的配置来实现:

kubectl edit hpa go-webapp

在编辑器中,你可以修改例如--cpu-percent--min/--max参数等HPA的配置项。

通过这个案例,你学会了如何为Go编写的微服务实现Kubernetes的自动扩展功能。利用HPA,你的应用可以根据实时负载自动调整资源使用,从而保证应用的性能和响应速度。这是构建高可用云原生应用的关键技术之一,随着你对Kubernetes和云原生技术栈的深入学习,你将能够构建更加灵活和强大的应用系统。

相关文章:

《Go 简易速速上手小册》第10章:微服务与云原生应用(2024 最新版)

文章目录 10.1 构建微服务架构 - 探索 Go 语言的微观世界10.1.1 基础知识讲解10.1.2 重点案例:订单处理系统订单服务测试服务 10.1.3 拓展案例 1:用户认证服务安装所需的包实现用户模型和存储实现 JWT 生成和验证实现认证服务测试服务 10.1.4 拓展案例 2…...

代码随想录算法训练营第34天| Leetcode 860.柠檬水找零、406.根据身高重建队列、452. 用最少数量的箭引爆气球

文章目录 Leetcode 860.柠檬水找零Leetcode 406.根据身高重建队列Leetcode 452. 用最少数量的箭引爆气球 Leetcode 860.柠檬水找零 题目链接:Leetcode 860.柠檬水找零 题目描述: 在柠檬水摊上,每一杯柠檬水的售价为 5 美元。顾客排队购买你的…...

数据结构~二叉树(基础知识)

上一篇博客我们对树有了初步了解与学习,这篇我将初步学习二叉树!!(新年快乐!) 目录 二叉树 1、定义: 2、特点: 3、基本形态: 4、二叉树的种类: &…...

AI大模型学习笔记之四:生成式人工智能(AIGC)是如何工作的?

OpenAI 发布 ChatGPT 已经1年多了,生成式人工智能(AIGC)也已经广为人知,我们常常津津乐道于 ChatGPT 和 Claude 这样的人工智能系统能够神奇地生成文本与我们对话,并且能够记忆上下文情境。 Midjunery和DALLE 这样的AI…...

bat脚本 创建计划任务 一分钟设置ntp同步周期为60s

要在Windows中使用批处理脚本(.bat)创建一个计划任务来每分钟同步一次NTP时间,你可以使用schtasks命令来创建计划任务。下面是一个示例脚本,展示了如何创建这样一个计划任务: echo off set "taskNameSyncNTP"…...

python数据分析numpy基础之mean用法和示例

1 python数据分析numpy基础之mean用法和示例 python的numpy库的mean()函数&#xff0c;用于计算沿指定轴(一个轴或多个轴)的算术平均值。 用法 numpy.mean(a, axisNone, dtypeNone, outNone, keepdims<no value>, *, where<no value>)描述 返回数组元素的平均值…...

微服务学习 | Springboot整合Dubbo+Nacos实现RPC调用

&#x1f3f7;️个人主页&#xff1a;鼠鼠我捏&#xff0c;要死了捏的主页 &#x1f3f7;️系列专栏&#xff1a;Golang全栈-专栏 &#x1f3f7;️个人学习笔记&#xff0c;若有缺误&#xff0c;欢迎评论区指正 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&…...

只允许访问固定网址,如何让电脑只能上指定的网站

在企业管理中&#xff0c;确保员工在工作时能够专注于指定的任务和资源至关重要。为了实现这一目标&#xff0c;许多企业选择限制员工电脑的访问权限&#xff0c;只允许他们访问固定的网址或网站。 这种策略不仅有助于提高工作效率&#xff0c;还能减少因不当上网行为带来的安全…...

作业帮 x TiDB丨多元化海量数据业务的支撑

导读 作业帮是一家成立于 2015 年的在线教育品牌&#xff0c;致力于用科技手段助力教育普惠。经过近十年的积累&#xff0c;作业帮运用人工智能、大数据等技术&#xff0c;为学生、老师、家长提供学习、教育解决方案&#xff0c;智能硬件产品等。随着公司产品和业务场景越来越…...

文生图提示词:天气条件

天气和气候 --天气条件 Weather Conditions 涵盖了从基本的天气类型到复杂的气象现象&#xff0c;为描述不同的天气和气候条件提供了丰富的词汇。 Sunny 晴朗 Cloudy 多云 Overcast 阴天 Partly Cloudy 局部多云 Clear 清晰 Foggy 雾 Misty 薄雾 Hazy 朦胧 Rainy 下雨 Showers …...

【nginx实践连载-3】发布VSTO应用

要使用 Nginx 发布 VSTO 应用程序&#xff0c;需要将 ClickOnce 发布文件夹部署到 Nginx 服务器上。以下是一些步骤&#xff1a; 将 ClickOnce 发布文件夹复制到 Nginx 服务器上。确认 Nginx 配置文件中有一个指向 ClickOnce 发布文件夹的位置块。确保Nginx 配置文件中启用了 …...

【前端工程化面试题】使用 webpack 来优化前端性能/ webpack的功能

这个题目实际上就是来回答 webpack 是干啥的&#xff0c;你对webpack的理解&#xff0c;都是一个问题。 &#xff08;1&#xff09;对 webpack 的理解 webpack 为啥提出 webpack 是啥 webpack 的主要功能 前端开发通常是基于模块化的&#xff0c;为了提高开发效率&#xff0…...

思迈特再获国家权威认证:代码自主率98.78%

日前&#xff0c;思迈特软件自主研发的商业智能与数据分析软件&#xff08;Smartbi Insight&#xff09;通过中国赛宝实验室&#xff08;工业和信息化部电子第五研究所&#xff09;代码扫描测试&#xff0c;Smartbi Insight V11版本扫描测得代码自主率为98.78%的好成绩&#xf…...

JavaScript排序

直接看代码 <table border"1" cellspacing"0"><thead class"tou"><tr><td>选择按钮</td><td>汽车编号</td><td>汽车图片</td><td>汽车系列名称</td><td>汽车能源</…...

【读书笔记】ICS设备及应用攻击(一)

工控系统通常是由互联设备所构成的大型复杂系统&#xff0c;这些设备包括类似于人机界面&#xff08;HMI&#xff09;、PLC、传感器、执行器以及其他使用协商好的协议进行相互通信的设备。所有交互背后的驱动力都是软件&#xff0c;软件为工控系统中几乎所有部分的运行提供支撑…...

网络原理(HTTP篇)

网络原理HTTP 前言HTTPHTTP的工作流程抓包工具抓取HTTP报文HTTP报文格式 请求报文具体细节首行URLURL的基本格式URL encode 方法 报头(header)HostContent-Length 和 Content-TypeUser-Agent&#xff08;UA&#xff09;RefererCookie&#xff08;重要&#xff09; 前言 如图&a…...

关于油封密封件你了解多少?

油封也称为轴封或旋转轴封&#xff0c;旨在防止设备中的润滑剂泄漏&#xff0c;并防止外部污染物进入机械。它们通常用于泵和电机等旋转设备&#xff0c;在固定部件和移动部件之间提供密封界面。 油封的有效性很大程度上取决于其材料。不同的材料具有不同程度的耐热性、耐压性…...

Leetcode 72 编辑距离

题意理解&#xff1a; 给你两个单词 word1 和 word2&#xff0c; 请返回将 word1 转换成 word2 所使用的最少操作数 。 你可以对一个单词进行如下三种操作&#xff1a; 插入一个字符 删除一个字符 替换一个字符 将word1转换为word2,可以进行三种操作&#xff1a;增、删、改&am…...

羊大师揭秘,如何挑选出好牧场的奶羊,该怎么看

羊大师揭秘&#xff0c;如何挑选出好牧场的奶羊&#xff0c;该怎么看 了解牧场的管理和环境&#xff1a;好的牧场应该有规范的管理制度&#xff0c;环境整洁&#xff0c;草场茂盛&#xff0c;为奶羊提供了充足的食物和良好的生活环境。在这样的牧场中&#xff0c;奶羊能够得到…...

MySQL数据库基础(八):DML数据操作语言

文章目录 DML数据操作语言 一、DML包括哪些SQL语句 二、数据的增删改&#xff08;重点&#xff09; 1、数据的增加操作 2、数据的修改操作 3、数据的删除操作 DML数据操作语言 一、DML包括哪些SQL语句 insert插入、update更新、delete删除 二、数据的增删改&#xff08…...

(09)Hive——CTE 公共表达式

目录 1.语法 2. 使用场景 select语句 chaining CTEs 链式 union语句 insert into 语句 create table as 语句 前言 Common Table Expressions&#xff08;CTE&#xff09;&#xff1a;公共表达式是一个临时的结果集&#xff0c;该结果集是从with子句中指定的查询派生而来…...

Spring 用法学习总结(四)之 JdbcTemplate 连接数据库

&#x1f409;目录 9 JdbcTemplate 9 JdbcTemplate Spring 框架对 JDBC 进行了封装&#xff0c;使用 JdbcTemplate 方便实现对数据库操作 相关包&#xff1a; 百度网盘链接https://pan.baidu.com/s/1Gw1l6VKc-p4gdqDyD626cg?pwd6666 创建properties配置文件 &#x1f4a5;注意…...

第 385 场 LeetCode 周赛题解

A 统计前后缀下标对 I 模拟 class Solution { public:int countPrefixSuffixPairs(vector<string> &words) {int n words.size();int res 0;for (int i 0; i < n; i)for (int j i 1; j < n; j)if (words[i].size() < words[j].size()) {int li words[…...

什么是RabbitMQ?

一、引言 RabbitMQ是一个开源的消息代理软件&#xff0c;用于在分布式系统中传递消息。它实现了高级消息队列协议&#xff08;AMQP&#xff09;&#xff0c;提供了一种可靠的、强大的、灵活的消息传递机制&#xff0c;使得不同应用程序或组件之间可以轻松地进行通信。 二、概念…...

JWT登录验证前后端设计与实现笔记

设计内容 前端 配置全局前置路由守卫axios拦截器登录页面和主页 后端 JWT的封装登录接口中间件放行mysql数据库的连接 详细设计 路由设计 配置全局前置守卫&#xff0c;如果访问的是登录页面则放行&#xff0c;不是则进入判断是否有token&#xff0c;没有则拦截回到登录…...

自定义类型详解 ----结构体,位段,枚举,联合

目录 结构体 1.不完全声明 2.结构体的自引用 3.定义与初始化 4.结构体内存对齐与结构体类型的大小 结构体嵌套问题 位段 1.什么是位段&#xff1f; 2.位段的内存分配 枚举 1.枚举类型的定义 2.枚举的优点 联合&#xff08;共同体&#xff09; 1.联合体类型的声明以…...

VueCLI核心知识综合案例TodoList

目录 1 拿到一个功能模块首先需要拆分组件&#xff1a; 2 使用组件实现静态页面的效果 3 分析数据保存在哪个组件 4 实现添加数据 5 实现复选框勾选 6 实现数据的删除 7 实现底部组件中数据的统计 8 实现勾选全部的小复选框来实现大复选框的勾选 9 实现勾选大复选框来…...

关于cuda路径问题

问题&#xff1a;Could not load dynamic library ‘libcudart.so.11.0’ 原因&#xff1a;调用系统环境下的cuda但系统环境没有装cuda 解决&#xff1a; 1.在系统环境装cuda&#xff0c;但如果每权限就不好操作&#xff1b; 2.用虚拟环境装好的cuda路径丢给环境变量 暂时性&am…...

六、Spring/Spring Boot整合ActiveMQ

Spring/Spring Boot整合ActiveMQ 一、Spring整合ActiveMQ1.pom.xml2.Queue - 队列2.1 applicationContext.xml2.2 生产者2.3 消费者 3.Topic - 主题3.1 applicationContext.xml3.2 生产者3.3 消费者 4.消费者 - 监听器4.1 编写监听器类4.2 配置监听器4.3 生产者消费者一体 二、…...

树莓派4B(Raspberry Pi 4B)使用docker搭建springBoot/springCloud服务

树莓派4B&#xff08;Raspberry Pi 4B&#xff09;使用docker搭建springBoot/springCloud服务 前提&#xff1a;本文基于Ubuntu&#xff0c;Java8&#xff0c;SpringBoot 2.6.13讲解 准备工作 准备SpringBoot/SpringCloud项目jar包 用 maven 打包springBoot/springCloud项目&…...

数据库设计、JDBC、数据库连接池

数据库设计 数据库设计概念 数据库设计就是根据业务 系统的具体需求&#xff0c;结合我们所选用的DBMS,为这个业务系统构造出最优的数据存储模型。建立数据库中的表结构以及表与表之间的关联关系的过程。有哪些表?表里有哪些字段?表和表之间有什么关系? 数据库设计的步骤…...

SpringBoot实现OneDrive文件上传

SpringBoot实现OneDrive文件上传 源码 OneDriveUpload: SpringBoot实现OneDrive文件上传 获取accessToken步骤 参考文档&#xff1a;针对 OneDrive API 的 Microsoft 帐户授权 - OneDrive dev center | Microsoft Learn 1.访问Azure创建应用Microsoft Azure&#xff0c;使…...

C++初阶:容器适配器介绍、stack和queue常用接口详解及模拟实现

介绍完了list类的相关内容后&#xff1a;C初阶&#xff1a;适合新手的手撕list&#xff08;模拟实现list&#xff09; 接下来进入新的篇章&#xff0c;stack和queue的介绍以及模拟&#xff1a; 文章目录 1.stack的初步介绍2.stack的使用3.queue的初步介绍4.queue的使用5.容器适…...

GRUB and the Boot Process on UEFI-based x86 Systems

background info : BIOS and UEFI-CSDN博客 The UEFI-based platform reads the partition table on the system storage and mounts the EFI System Partition (ESP), a VFAT partition labeled with a particular globally unique identifier (GUID). The ESP contains EFI a…...

2.C语言——输入输出

1.字符输入输出函数 1.输入:getchar() 字面意思&#xff0c;接收单个字符&#xff0c;使用方法 char a; a getchar();实际上效果等同于char a; scanf("%c",&a);2.输出:putchar() 2.格式化输入输出函数 1.输入:scanf() 格式&#xff1a; scanf(“格式控制…...

MySQL篇之SQL优化

一、表的设计优化 表的设计优化&#xff08;参考阿里开发手册《嵩山版》&#xff09;&#xff1a; 1. 比如设置合适的数值&#xff08;tinyint int bigint&#xff09;&#xff0c;要根据实际情况选择。 2. 比如设置合适的字符串类型&#xff08;char和varchar&#xff09…...

QGis —— 1、Windows10下载安装QGis及插件

QGis官网 QGIS&#xff08;自由开源的地理信息系统&#xff09;是一个专业的GIS应用程序&#xff0c;它建立在免费和开源软件&#xff08;FOSS&#xff09;之上&#xff0c;并为此而自豪。QGIS 是一个方便使用的开源地理信息系统 (GIS)&#xff0c;根据 GNU 通用公共许可授权。…...

【打工日常】使用docker部署Dashdot工具箱

一、Dashdot介绍 dashdot是一个简洁清晰的服务器数据仪表板&#xff0c;基于React实现 &#xff0c;主要是显示操作系统、进程、存储、内存、网络这五个的数据。 二、本次实践介绍 1. 本次实践简介 本次实践部署环境为个人测试环境 2. 本地环境规划 本次实践环境规划&#xf…...

使用client-only 解决组件不兼容SSR问题

目录 前言 一、解决方案 1.基于Nuxt 框架的SSR应用 2.基于vue2框架的应用 3.基于vue3框架的应用 二、总结 往期回顾 前言 最近在我的单页面SSR应用上开发JSON编辑器功能&#xff0c;在引入组件后直接客户端跳转OK&#xff0c;但是在直接加载服务端渲染的时候一直报这…...

基于Java SSM框架实现网上报名系统项目【项目源码+论文说明】

基于java的SSM框架实现网上报名系统演示 摘要 随着互联网时代的到来&#xff0c;同时计算机网络技术高速发展&#xff0c;网络管理运用也变得越来越广泛。因此&#xff0c;建立一个B/S结构的网上报名系统&#xff0c;会使网上报名系统工作系统化、规范化&#xff0c;也会提高网…...

7.1 Qt 中输入行与按钮

目录 前言&#xff1a; 技能&#xff1a; 内容&#xff1a; 参考&#xff1a; 前言&#xff1a; line edit 与pushbotton的一点联动 当输入行有内容时&#xff0c;按钮才能使用&#xff0c;并能读出输入行的内容 技能&#xff1a; pushButton->setEnabled(false) 按钮不…...

云计算基础-网络虚拟化

虚拟交换机 什么是虚拟交换机 虚拟交换机是一种运行在虚拟化环境中的网络设备&#xff0c;其运行在宿主机的内存中&#xff0c;通过软件方式在宿主机内部实现了部分物理交换机的功能&#xff0c;如 VLAN 划分、流量控制、QoS 支持和安全功能等网络管理特性 虚拟交换机在云平…...

166基于matlab的通过峭度指标与互相关系数筛选IMF进行SVD分解去噪

基于matlab的通过峭度指标与互相关系数筛选IMF进行SVD分解去噪&#xff0c;分辨虚假imf&#xff0c;提取最大峭度imf图。输出去噪前后时域及其包络谱结果。程序已调通&#xff0c;可直接运行。 166 matlab SVD去噪 IMF筛选 包络谱 (xiaohongshu.com)...

第六十三天 服务攻防-框架安全CVE复现DjangoFlaskNode.JSJQuery

第六十三天 服务攻防-框架安全&CVE复现&Django&Flask&Node.JS&JQuery 知识点&#xff1a; 中间件及框架列表&#xff1a; IIS,Apache,Nginx,Tomcat,Docker,K8s,Weblogic.JBoos,WebSphere, Jenkins,GlassFish,Jetty,Jira,Struts2,Laravel,Solr,Shiro,Thin…...

最大子序和+旅行问题——单调队列

一、最大子序和 输入一个长度为 n 的整数序列&#xff0c;从中找出一段长度不超过 m 的连续子序列&#xff0c;使得子序列中所有数的和最大。 注意&#xff1a; 子序列的长度至少是 1。 输入 第一行输入两个整数 n,m (1 ≤ n,m ≤ 300000)。 第二行输入 n 个数&#xff0c;代…...

Unity设备分级策略

Unity设备分级策略 前言 之前自己做的设备分级策略&#xff0c;在此做一个简单的记录和思路分享。希望能给大家带来帮助。 分级策略 根据拟定的评分标准&#xff0c;预生成部分已知机型的分级信息&#xff0c;且保存在包内&#xff1b;如果设备没有被评级过&#xff0c;则优…...

自己在开发AI应用的过程总结的 Prompt - 持续更新

自己在开发AI应用的过程总结的 Prompt - 持续更新 0. 引言1. 让模型以"中文"进行回复2. 控制模型仅输出"hi"3. 让模型"提供简单、清晰而具体的回答"4. 让模型"在最后说谢谢" 0. 引言 我想&#xff0c;我们多半有着相似的经历&#xf…...

STM32——OLED菜单

文章目录 一.补充二. 二级菜单代码 简介&#xff1a;首先在我的51 I2C里面有OLED详细讲解&#xff0c;本期代码从51OLED基础上移植过来的&#xff0c;可以先看完那篇文章&#xff0c;在看这个&#xff0c;然后按键我是用的定时器扫描不会堵塞程序,可以翻开我的文章有单独的定时…...

Open CASCADE学习|布尔运算后消除内部拓扑

在CAD建模中&#xff0c;布尔运算是一种逻辑运算方法&#xff0c;通过这种方法&#xff0c;可以创建、修改或组合几何对象。布尔运算主要包括并集&#xff08;UNION&#xff09;、交集&#xff08;INTERSECT&#xff09;和差集&#xff08;SUBTRACT&#xff09;三种运算。 并集…...

【数据仓库】主题域和数据域

数据域与主题域区别 https://www.cnblogs.com/datadance/p/16898254.html 数据域是自下而上&#xff0c;以业务数据视角来划分数据&#xff0c;一般进行完业务系统数据调研之后就可以进行数据域的划分。针对公共明细层&#xff08;DWD&#xff09;进行主题划分。主题域则自上而…...