集群开发学习(一)(安装GO和MySQL,K8S基础概念)

完成gin小任务

参考文档:
https://www.kancloud.cn/jiajunxi/ginweb100/1801414
https://github.com/hanjialeOK/going

最终代码地址:https://github.com/qinliangql/gin_mini_test.git

学习

1.安装go

wget https://dl.google.com/go/go1.20.2.linux-amd64.tar.gz
tar -C /usr/local -zxf go1.20.2.linux-amd64.tar.gz
export PATH=$PATH:/usr/local/go/bin
// All Tools installed by go are in $GOPATH.
export GOPATH=$PATH:/usr/local/go
export PATH=$PATH:$GOPATH/bin
// https://goproxy.cn is faster than aliyun.
export GOPROXY=https://goproxy.cn

验证安装是否成功

go version

在这里插入图片描述
hello world

mkdir hello && cd /hello
go mod init example/hello

创建一个hello.go

package main

import "fmt"

func main() {
    fmt.Println("Hello, World!")
}

在hello下运行

go run hello.go

做一个映射出来的
安装

go get github.com/gin-gonic/gin

我映射了端口8001

package main

import (
    "github.com/gin-gonic/gin"
    "net/http"
)

func main() {
    router := gin.Default()
    router.GET("/", func(c *gin.Context) {
        c.String(http.StatusOK, "Hello World")
    })
    router.Run(":8001") 
}

go run hello.go

在这里插入图片描述
在这里插入图片描述

2.安装MySQL

// For ubuntu 18.04
sudo apt install mysql-server
// start mysql
service mysql start

Change the password for mysql

// login
mysql -u root
// change the password for mysql
use mysql;
update user set authentication_string='' where user='root';
// newpassword: 123456
alter user 'root'@'localhost' identified with mysql_native_password by '123456';
// exit
quit
// restart mysql
service mysql restart

Create database and table.

mysql -u root -p
create database gobase;
use gobase;
CREATE TABLE `account` (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `name` varchar(20) NOT NULL,
    `email` varchar(50) NOT NULL,
    `password` varchar(30) NOT NULL,
    PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

查看数据库信息(先quit再重新打开mysql)

show databases;

在这里插入图片描述
查看table

use gobase;
\\ List Table info
show tables;
\\ Look structure of table
desc account;

在这里插入图片描述

Task

1.使用Gin创建一个API,允许用户通过提供姓名,电子邮件和密码来创建新账户。该API应验证所有字段是否存在以及电子邮件格式是否正确。

安装依赖

go get github.com/gin-gonic/gin
go get gorm.io/driver/mysql
go get gorm.io/gorm

代码:

package main

import (
	"fmt"
    "net/http"
	"net/mail"

    "github.com/gin-gonic/gin"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
    // "regexp"
)

type UserInfo struct {
	ID       int    `form:"id"`
	Name     string `form:"name" binding:"required"`
	Email    string `form:"email" binding:"required,email"`
	Password string `form:"password" binding:"required,min=6"`
}



func main() {
    // 创建一个默认的 Gin 路由器
    r := gin.Default()

    // 创建一个路由,允许用户提交新账户信息
    r.POST("/api/register", create)

    // 启动 Gin 服务器,默认监听在 8001 端口
    r.Run(":8001")
}

// isValidEmail 检查电子邮件格式是否正确
func isValidEmail(email string) bool {
    _, err := mail.ParseAddress(email)
	// 如果解析正确,err会变为nil
	return err == nil
}

func create(c *gin.Context) {
	// 绑定请求体到 UserInfo 结构体类型的form 
	var form UserInfo
	if err := c.ShouldBindJSON(&form); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	// 检查电子邮件格式是否正确
	if !isValidEmail(form.Email) {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid email format"})
		return
	}

	// 打开数据库,3306是MySQL默认端口
	dsn := "root:123456@tcp(127.0.0.1:3306)/gobase"
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	// check whether the name exists.
	var user UserInfo
	db.Table("account").Where("name = ?", form.Name).First(&user)
	if (user != UserInfo{}) {
		c.JSON(http.StatusBadRequest, gin.H{"error": "name exists!"})
		return
	}

	// check whether the email has been used.
	db.Table("account").Where("email = ?", form.Email).First(&user)
	if (user != UserInfo{}) {
		c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("email %s has been used!", form.Email)})
		return
	}

	// 保存用户到数据库(:= 对于没有声明的变量会自动声明)
	user = UserInfo{Name: form.Name, Email: form.Email, Password: form.Password}
	result := db.Table("account").Create(&user)
	if result.Error != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": result.Error})
		return
	}
	
	
	// 这里只是返回成功消息
	c.JSON(http.StatusOK, gin.H{"message": fmt.Sprintf("Account %s created successfully!",user.Name)})
}

测试代码:
-X 指定发送的请求类型为 POST 请求
-H 设置请求头,指定请求的内容类型为 JSON 格式

curl -X POST -H "Content-Type: application/json" -d '{"name":"QL","email":"qinl@example.com","password":"password"}' http://localhost:8001/api/register

查看数据库当前情况

service mysql start
mysql -u root -p
show databases;
use gobase;
show tables;
SELECT * FROM account;
quit

在这里插入图片描述

2.使用Gin实现一个API,允许用户通过提供电子邮件和密码来检索其帐户信息。该API应验证电子邮件和密码是否正确,然后返回用户信息。

新加代码:

// 新增路由,允许用户通过提供电子邮件和密码来检索其帐户信息,相当于登陆
r.POST("/api/login", login)

/*
// 处理登录请求的函数 (这种必须要求输入完整的UserInfo类型信息才行,所以放弃)
func login(c *gin.Context) {
	// 绑定请求体到 UserInfo 结构体类型的 form
	var form UserInfo
	if err := c.ShouldBindJSON(&form); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	// 打开数据库
	dsn := "root:123456@tcp(127.0.0.1:3306)/gobase"
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	// 查询数据库中是否存在匹配的用户记录
	var user UserInfo
	result := db.Table("account").Where("email = ? AND password = ?", form.Email, form.Password).First(&user)
	if result.Error != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid email or password"})
		return
	}

	// 返回匹配的用户信息
	c.JSON(http.StatusOK, gin.H{"message": fmt.Sprintf("Welcom user %s !", user.Name)})
}
*/

func login(c *gin.Context) {
	// 绑定请求体到 UserInfo 结构体类型的 form
	email := c.Query("email")
	password := c.Query("password")

	// 打开数据库
	dsn := "root:123456@tcp(127.0.0.1:3306)/gobase"
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	// 查询数据库中是否存在匹配的用户记录
	var user UserInfo
	result := db.Table("account").Where("email = ? AND password = ?", email, password).First(&user)
	if result.Error != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("Invalid email:%s or password:%s",email,password)})
		return
	}

	// 返回匹配的用户信息
	c.JSON(http.StatusOK, gin.H{"message": fmt.Sprintf("Welcom user %s !", user.Name)})
}

测试代码:

# 正确案例
curl -X POST "http://localhost:8001/api/login?email=qinl@example.com&password=password"
# 错误案例
curl -X POST "http://localhost:8001/api/login?email=qinl@example.com&password=654321"

3.添加一个API,使用Gin允许用户更新其帐户信息。该API应验证用户是否已通过身份验证并且所有字段是否存在,然后将用户信息更新到数据库中。

安装依赖

go get github.com/golang-jwt/jwt/v5

更新的代码:

package main

import (
	"fmt"
    "net/http"
	"net/mail"
	"time"

    "github.com/gin-gonic/gin"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
    "github.com/golang-jwt/jwt/v5"
)

// 创建一个 JWT(JSON Web Token)字符串。
// JWT 是一种用于在网络应用之间安全传递声明的开放标准。

var jwtKey = []byte("my_secret_key")	// 用于签名和验证 JWT 的密钥

// input: email || output:string,error
func createToken(email string) (string, error) {
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
		"email": email,
		"exp":   time.Now().Add(time.Minute * 10).Unix(), // 有效时长10分钟
	})

	return token.SignedString(jwtKey)
}

func validateToken(tokenString string) (*jwt.Token, error) {
	token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
			return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
		}
		return jwtKey, nil
	})

	if err != nil {
		return nil, err
	}

	if _, ok := token.Claims.(jwt.MapClaims); !ok || !token.Valid {
		return nil, fmt.Errorf("bad token.")
	}

	return token, nil
}

func validateCookie(c *gin.Context) (*jwt.Token, error) {
	tokenString, err := c.Cookie("gin_cookie")
	if err != nil {
		return nil, err
	}
	token, err := validateToken(tokenString)
	if err != nil {
		return nil, err
	}
	return token, nil
}

func main() {
    // 创建一个默认的 Gin 路由器
    r := gin.Default()

    // 创建一个路由,允许用户提交新账户信息
    r.POST("/api/register", create)  // 新增路由,允许用户通过提供电子邮件和密码来检索其帐户信息,相当于登陆
	r.POST("/api/login", login)
	r.POST("/api/update", update) // 新增的路由,用于更新用户信息

    // 启动 Gin 服务器,默认监听在 8001 端口
    r.Run(":8001")
}


func login(c *gin.Context) {
	// ...

	// Create the JWT string.
	token, err := createToken(user.Email)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create token"})
		return
	}
	// tell the user it's ok. 3600 是最大存活时间1h
	c.SetCookie("gin_cookie", token, 3600, "/", "", false, true)

	// 返回匹配的用户信息
	c.JSON(http.StatusOK, gin.H{"message": fmt.Sprintf("Welcom user %s !", user.Name)})
}

func update(c *gin.Context) {
    token, err := validateCookie(c)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	// get email from cookie
	email := token.Claims.(jwt.MapClaims)["email"]
	// new password
	password := c.Query("password")
	name := c.Query("name")
	// update
	dsn := "root:123456@tcp(127.0.0.1:3306)/gobase"
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	// retrieve the user by email.
	var user UserInfo
	db.Table("account").Where("email = ?", email).First(&user)
	if (user == UserInfo{}) {
		c.JSON(http.StatusBadRequest, gin.H{"error": "this email has not been registered."})
		return
	}

    // 更新用户信息
	if name != "" {
		user.Name = name
	}
	if password != "" {
		user.Password = password
	}

    // 保存更新后的用户信息到数据库
    if err := db.Table("account").Save(&user).Error; err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update user"})
        return
    }

    // 返回更新成功的消息
    c.JSON(http.StatusOK, gin.H{"message": "User information updated successfully"})
}

测试代码,用 -i 可以更细致的看到信息,目前还是在curl里面传递cookie的

curl -i -X POST "http://localhost:8001/api/login?email=qinl@example.com&password=password"
curl -i -X POST "http://localhost:8001/api/update?password=newpassword" --cookie "gin_cookie=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InFpbmxAZXhhbXBsZS5jb20iLCJleHAiOjE3MTI4MDU0NTl9.VTLihEYTy-kWZjZhLrwpwIMuPgimpm-DVnYwvhoKnNs"

修改后的数据库
在这里插入图片描述

4. 使用Gin实现一个API,允许用户通过提供电子邮件和密码来删除其帐户。该API应验证电子邮件和密码是否正确,然后从数据库中删除用户帐户。

新加代码:

r.POST("/api/delete",delete)	// 删除用户信息

func delete(c *gin.Context) {
	email := c.Query("email")
	password := c.Query("password")

	// 打开数据库
    dsn := "root:123456@tcp(127.0.0.1:3306)/gobase"
    db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
    if err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        return
    }

    // 查询数据库中是否存在匹配的用户记录
    var user UserInfo
    result := db.Table("account").Where("email = ? AND password = ?", email, password).First(&user)
    if result.Error != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid email or password"})
        return
    }

    // 删除用户账户
    if err := db.Table("account").Delete(&user).Error; err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to delete account"})
        return
    }

    // 返回删除成功的消息
    c.JSON(http.StatusOK, gin.H{"message": fmt.Sprintf("Account %s deleted successfully",user.Name)})
}

测试代码:

curl -X POST "http://localhost:8001/api/delete?email=qinl@example.com&password=newpassword"

在这里插入图片描述

5. 添加一个API,使用Gin允许用户检索数据库中所有帐户的列表。

新加代码:

func show_all(c *gin.Context) {
	if _, err := validateCookie(c); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	// 打开数据库
    dsn := "root:123456@tcp(127.0.0.1:3306)/gobase"
    db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
    if err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
        return
    }

    // 查询数据库,获取所有帐户列表
    var accountNames []string
    if err := db.Table("account").Pluck("name", &accountNames).Error; err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to retrieve accounts"})
        return
    }

    // 返回帐户列表给客户端
    c.JSON(http.StatusOK, accountNames)
}

测试代码:

# 先加两个用户
curl -X POST -H "Content-Type: application/json" -d '{"name":"QL","email":"qinl@example.com","password":"password"}' http://localhost:8001/api/register
curl -X POST -H "Content-Type: application/json" -d '{"name":"ZS","email":"zs@example.com","password":"password"}' http://localhost:8001/api/register
# 登录一个用户获取token
curl -i -X POST "http://localhost:8001/api/login?email=qinl@example.com&password=password"
# 用token,获取用户信息
curl -X POST "http://localhost:8001/api/show_all" --cookie "gin_cookie=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InFpbmxAZXhhbXBsZS5jb20iLCJleHAiOjE3MTI4MDc0OTB9.hoB7TY0TorAlmdtm-jyqb8U5nSAehmP7d2n5EjSE1r8"

[“QL”,“ZS”]

6. 调研了解目前主流的登录鉴权方式,选择一个在你的项目中进行实现。

主流鉴权方式:

  • 基于Token的认证(Token-based Authentication):用户在登录成功后会收到一个令牌(Token),之后每次请求都需要在请求头中携带这个令牌进行验证。常见的Token包括JWT(JSON Web Token)和OAuth2 Token。

  • OAuth2认证(OAuth2 Authentication):OAuth2是一种开放标准,允许用户授权第三方应用访问其资源。用户可以通过第三方认证提供者(如Google、Facebook等)进行登录,然后授权给应用访问其资源的权限。

  • 基于Session的认证(Session-based Authentication):用户在登录成功后,服务器会为其创建一个会话(Session),并将会话ID存储在Cookie中,用户在后续的请求中都会携带这个会话ID。服务器通过会话ID来验证用户的身份和权限。

原本的是基于token做的,这里改为基于session做

安装依赖:

go get github.com/gorilla/sessions

新代码:

// 定义一个全局的会话存储对象
var store = sessions.NewCookieStore([]byte("my-secret"))

// 创建会话并将用户ID存储在会话中
session, _ := store.Get(c.Request, "session-name")
session.Values["userID"] = user.ID
session.Save(c.Request, c.Writer)

// 获取会话中的用户ID
   session, _ := store.Get(c.Request, "session-name")
   _, ok := session.Values["userID"].(int)
   if !ok {
       c.JSON(http.StatusBadRequest, gin.H{"error": "User not authenticated"})
       return
   }

测试代码:

# 登录一个用户
curl -i -X POST "http://localhost:8001/api/login?email=qinl@example.com&password=password"
# 用token,获取用户信息
curl -X POST "http://localhost:8001/api/show_all" --cookie "session-name=MTcxMjgyNjI1NHxEdi1CQkFFQ180SUFBUkFCRUFBQUhmLUNBQUVHYzNSeWFXNW5EQWdBQm5WelpYSkpSQU5wYm5RRUFnQUV8hXB2Vl_OaBCvi71u6KLgrh96uhu-Irt-CAzSbxFBMeY="

K8S 概念理解

核心

  1. Namespace(命名空间)

    • Namespace是Kubernetes中用于将集群中的资源划分为不同逻辑组的一种机制。
    • 它允许用户在同一集群中创建多个虚拟集群。
    • 命名空间提供了一种在不同组织或项目之间隔离资源的方法,以及控制资源访问和使用的方法。
    • 默认情况下,Kubernetes提供了一些预定义的命名空间,如"default"用于用户创建的资源,以及"kube-system"用于Kubernetes系统组件。
  2. Pod(Pod)

    • Pod是Kubernetes中最小的部署和管理单位。
    • 它是一个或多个相关容器的组合,共享网络和存储资源,并在同一宿主机上运行。
    • Pod可以包含单个容器(单容器Pod)或多个容器(多容器Pod),这些容器可以共享网络命名空间和存储卷。
    • Pod可以用来运行应用程序、批处理作业或其他需要共享资源的任务。
  3. Deployment(部署)

    • Deployment是Kubernetes中用于管理Pod副本的控制器。
    • 它定义了应用程序或服务的期望状态,并确保集群中运行的Pod数量符合该状态。
    • Deployment允许用户轻松地创建、更新和扩展Pod副本,以及执行滚动升级和回滚操作。
    • 通过Deployment,用户可以指定应用程序的副本数量、容器镜像和更新策略等参数。
  4. Service(服务)

    • Service是Kubernetes中用于将Pod公开为网络服务的抽象。
    • 它为Pod提供了一个稳定的网络端点,允许其他应用程序或服务通过网络访问它们。
    • Service可以通过标签选择器将一组Pod组合成一个逻辑集,并将流量负载均衡到这些Pod之间。
    • Kubernetes支持多种类型的Service,包括ClusterIP、NodePort、LoadBalancer和ExternalName等。

综上所述,Namespace用于组织和隔离资源,Pod是最小的部署单元,Deployment用于管理Pod副本,Service提供网络访问入口,使应用程序或服务可以在集群内部或外部可达。这些概念共同构成了Kubernetes的核心功能,为容器化应用程序的部署、管理和扩展提供了强大的支持。

了解

  1. Node(节点)

    • Node是Kubernetes集群中的工作节点,用于运行应用程序和服务的实际工作负载。
    • 每个Node都是一个单独的物理机器或虚拟机,它们一起组成了Kubernetes集群。
    • Node由Kubernetes Master进行管理和监控,并承载了容器运行时引擎(如Docker或containerd)来运行Pod。
  2. Cluster(集群)

    • Cluster是由一组Node组成的Kubernetes环境,用于运行和管理容器化应用程序和服务。
    • 它包括一个Master节点和一组工作节点,所有这些节点共同协作以提供高可用性、自动扩展和自我修复的特性。
  3. Resource Quota(资源配额)

    • Resource Quota是Kubernetes中的一种资源管理机制,用于限制Namespace中资源的使用量。
    • 它允许集群管理员为每个Namespace设置配额,以控制Pod、CPU、内存、存储等资源的使用量,防止资源耗尽和过度使用。
  4. ConfigMap(配置映射)

    • ConfigMap是Kubernetes中用于存储配置数据的API对象。
    • 它允许将配置数据(如环境变量、配置文件等)从应用程序中解耦,并将其存储在集群中的ConfigMap对象中。
    • ConfigMap可以在Pod中作为卷或环境变量挂载,使应用程序可以动态地读取配置数据。
  5. Role(角色)RoleBinding(角色绑定)

    • Role和RoleBinding是Kubernetes中用于授权和访问控制的两个重要对象。
    • Role定义了一组权限规则,允许用户或服务账户对指定的资源执行特定的操作。
    • RoleBinding将Role绑定到用户、组或ServiceAccount,并为其分配相应的权限,以控制对资源的访问。
  6. Service Account(服务账户)

    • Service Account是Kubernetes中用于身份验证和授权的一种机制。
    • 它为Pod中运行的应用程序提供了一个身份标识,并允许它们与Kubernetes API进行交互。
    • 每个Namespace都有一个默认的Service Account,可以为Pod分配具有不同权限的Service Account。
  7. PersistentVolume(持久化卷)PersistentVolumeClaim(持久化卷声明)

    • PersistentVolume(PV)是Kubernetes中用于存储数据的持久化存储资源。
    • PersistentVolumeClaim(PVC)是用户向Kubernetes请求PV的一种机制,它定义了用户对存储资源的需求和要求。
    • PVC允许用户独立于底层存储实现和配置,并根据需要动态地请求和释放PV。

配置.kube文件

mkdir ~/.kube
# 需要管理员权限才能复制配置文件
sudo cp /etc/kubernetes/admin.conf ~/.kube/config
# 把文件权限改为自己,xxx就是自己的用户名
sudo chown -R qinl:qinl ~/.kube/
# 修改为自己的命名空间,vim编辑~/.kube/config文件,在context一栏加上一行,然后保存并退出即可:
nano ~/.kube/config
- context:
    cluster: kubernetes
    user: kubernetes-admin
    namespace: xxx # 这里加上自己的命名空间xxx,一般用自己的用户名
  name: kubernetes-admin@kubernetes
# 创建自己的命名空间,和上面的xxx对应
kubectl create ns qinl

测试

kubectl get po

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/536223.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

玩机进阶教程------手机定制机 定制系统 解除系统安装软件限制的一些步骤解析

定制机 在于各工作室与商家合作定制rom中有一些定制机。限制用户私自安装第三方软件。或者限制解锁 。无法如正常机登陆账号等等。定制机一般用于固定行业或者一些部门。专机专用。例如很多巴枪扫描机型等等。或者一些小牌机型。对于没有官方包的机型首先要导出各个分区来制作…

【OpenVINO™】使用 OpenVINO™ C# API 部署 YOLOv9 目标检测和实例分割模型(上篇)

YOLOv9模型是YOLO系列实时目标检测算法中的最新版本,代表着该系列在准确性、速度和效率方面的又一次重大飞跃。它通过引入先进的深度学习技术和创新的架构设计,如通用ELAN(GELAN)和可编程梯度信息(PGI)&…

复合数据类型

在C语言中,复合数据类型是指那些可以包含多个简单数据类型的数据类型。以下是一些常见的C语言复合数据类型以及相关的例子: 1. 数组(Arrays): 数组是一种可以存储多个相同类型数据的数据结构。例如: #in…

从像素游戏到 3A 大作的游戏引擎/框架

Bevy —— Rust 构建的游戏引擎 Bevy 是一款由 Rust 语言构建且简单明了的数据驱动的游戏引擎,并将永远保持开源且免费。 Mach —— Zig 游戏引擎和图形工具包 Mach 是一个 Zig 游戏引擎和图形工具包,用于构建高性能、真正跨平台、健壮且模块化的游戏&…

日程安排组件DHTMLX Scheduler v7.0新版亮点 - 拥有多种全新的主题

DHTMLX Scheduler是一个类似于Google日历的JavaScript日程安排控件,日历事件通过Ajax动态加载,支持通过拖放功能调整事件日期和时间,事件可以按天、周、月三个种视图显示。 备受关注的DHTMLX Scheduler 7.0版本日前正式发布了,如…

JS原生DOM操作 - 获得元素/网页大小/元素宽高

文章目录 获得元素的方法获取页面元素位置宽高概念方法获得网页/元素宽高clientHeight和clientWidth:scrollHeight和scrollWidth:window.innerWidth:element.style.width: offsetXXX 获得网页元素的宽高和相对父元素位置&#xff…

有道词典网页版接口分析与爬虫研究

说明:仅供学习使用,请勿用于非法用途,若有侵权,请联系博主删除 作者:zhu6201976 一、目标站点 有道词典网页版:网易有道 二、目标接口 url:https://dict.youdao.com/jsonapi_s?doctypejson&…

通过8种加锁情况来弄懂加锁对于线程执行顺序的影响

1个资源类对象,2个线程,2个同步方法,第二个线程等待1s后开启。 //资源类 public class Example {//2个同步方法public synchronized void method1(){System.out.println("线程1正在执行...");}public synchronized void method2()…

(2022级)成都工业学院数据库原理及应用实验三:数据定义语言DDL

唉,用爱发电连赞都没几个,博主感觉没有动力了 想要完整版的sql文件的同学们,点赞评论截图,发送到2923612607qq,com,我就会把sql文件以及如何导入sql文件到navicat的使用教程发给你的 基本上是无脑教程了,…

Banana Pi BPI-M7 RK3588开发板运行RKLLM软件堆AI大模型部署

关于Banana Pi BPI-M7 Banana Pi BPI-M7 采用Rockchip RK3588,板载8/16/32G RAM内存和 64/128G eMMC存储,支持无线wifi6和蓝牙5.2。2x2.5G网络端口,1个HDMIout标准 输出口,2x USB3.0,2xTYPE-C,2x MIPI CSI…

Day96:云上攻防-云原生篇Docker安全系统内核版本漏洞CDK自动利用容器逃逸

目录 云原生-Docker安全-容器逃逸&系统内核漏洞 云原生-Docker安全-容器逃逸&docker版本漏洞 CVE-2019-5736 runC容器逃逸(需要管理员配合触发) CVE-2020-15257 containerd逃逸(启动容器时有前提参数) 云原生-Docker安全-容器逃逸&CDK自动化 知识点&#xff1…

Vue3基础语法

在这个章节中&#xff0c;简单的看下Vue3的基础语法&#xff0c;有了这些基础后&#xff0c;对写vue3单页也就没有什么问题了。 模板语法 在写html时&#xff0c;我们希望在某个节点绑定一个动态值时&#xff0c;是使用dom操作执行的&#xff0c;如下&#xff1a; <!DOCT…

(Java)数据结构——排序(第一节)堆排序+PTA L2-012 关于堆的判断

前言 本博客是博主用于复习数据结构以及算法的博客&#xff0c;如果疏忽出现错误&#xff0c;还望各位指正。 堆排序&#xff08;Heap Sort&#xff09;概念 堆排序是一种基于堆数据结构的排序算法&#xff0c;其核心思想是将待排序的序列构建成一个最大堆&#xff08;或最小…

大模型+交通治理,高德地图“评诊治”系统迎来全新升级

近日&#xff0c;由中国道路交通安全协会主办的第十四届中国国际道路交通安全产品博览会暨公安交警警用装备展(以下简称交博会)在厦门国际会展中心开幕&#xff0c;会上高德地图发布了全新升级的城市交通“评诊治”智能决策SaaS系统&#xff0c;以助力城市交通的可持续、精细化…

spring boot 集成rocketMq + 基本使用

1. RocketMq基本概念 1. NameServer 每个NameServer结点之间是相互独立&#xff0c;彼此没有任何信息交互 启动NameServer。NameServer启动后监听端口&#xff0c;等待Broker、Producer、Consumer连接&#xff0c; 相当于一个路由控制中心。主要是用来保存topic路由信息&#…

知识图谱与人工智能:携手共进

知识图谱与人工智能&#xff1a;携手共进 一、引言&#xff1a;知识图谱与人工智能的融合 在这个数据驱动的时代&#xff0c;知识图谱与人工智能&#xff08;AI&#xff09;之间的融合不仅是技术发展的必然趋势&#xff0c;也是推动各行各业创新的关键。知识图谱&#xff0c;作…

windows下pycharm中配置conda虚拟环境

目录 一&#xff1a;背景 二&#xff1a;安装conda环境 三&#xff1a;pycharm配置环境 四&#xff1a;注意问题 一&#xff1a;背景 在使用python的过程中&#xff0c;我们可能需要在一个windows环境中创建多个版本的python和安装不同的库去做一些开发任务。 使用conda&a…

TQ15EG开发板教程:在MPSOC上运行ADRV9371

首先需要在github上下载两个文件&#xff0c;本例程用到的文件以及最终文件我都会放在网盘里面&#xff0c; 地址放在本文最后。首先在github搜索hdl选择第一个&#xff0c;如下图所示 GitHub网址&#xff1a;https://github.com/analogdevicesinc/hdl/releases 点击releases…

Docker入门实战教程

文章目录 Docker引擎的安装Docker比vm虚拟机快 Docker常用命令帮助启动类命令镜像命令docker imagesdocker searchdocker pulldocker system dfdocker rmi 容器命令redis前台交互式启动redis后台守护式启动Nginx容器运行ubuntu交互式运行tomcat交互式运行对外暴露访问端口 Dock…

Java基础07--多线程-网络编程-Java高级

一、多线程 1.认识多线程 ①线程 ②多线程 2.创建线程方式 ①方式一&#xff1a;继承Thread类 1.让子类继承Thread线程类 2.重写run方法&#xff0c;就是这个线程执行会执行的操作。 3.创建继承Thread的子类对象就代表一个线程 4.启动线程:.start()-自动执行run方法 注意&am…