golang gin入门

news2024/11/26 0:35:31

gin是个小而精的web开发框架
官方文档

安装

go get -u github.com/gin-gonic/gin
最简单的起手代码
package main

import (
	"net/http"

	"github.com/gin-gonic/gin"
)

func main() {
	r := gin.Default()
	r.GET("/ping", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{
			"message": "pong",
		})
	})
	r.Run() // listen and serve on 0.0.0.0:8080
	// r.Run(":8083") // listen and serve on 0.0.0.0:8080
}

get、post、put等http方法
package main

import (
	"net/http"

	"github.com/gin-gonic/gin"
)

func main() {
	router := gin.Default()
	router.GET("/someGet", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{
			"message": "pong",
		})
	})
	router.POST("/somePost", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{
			"message": "pong",
		})
	})
	router.PUT("/somePut", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{
			"message": "pong",
		})
	})
	router.DELETE("/someDelete", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{
			"message": "pong",
		})
	})
	router.PATCH("/somePatch", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{
			"message": "pong",
		})
	})
	router.HEAD("/someHead", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{
			"message": "pong",
		})
	})
	router.OPTIONS("/someOptions", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{
			"message": "pong",
		})
	})
	router.Run() // listen and serve on 0.0.0.0:8080
	// r.Run(":8083") // listen and serve on 0.0.0.0:8080
}

路由分组
package main

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

func goodList(c *gin.Context) {
	c.JSON(200, gin.H{
		"message": "goodList",
	})
}
func goodsDetail(c *gin.Context) {
	c.JSON(200, gin.H{
		"message": "goodsDetail",
	})
}
func createGoods(c *gin.Context) {
	c.JSON(200, gin.H{
		"message": "createGoods",
	})
}
func main() {
	router := gin.Default()
	goodsGroup := router.Group("/goods")
	{
		goodsGroup.GET("/list", goodList)
		goodsGroup.GET("/1", goodsDetail)
		goodsGroup.GET("/add", createGoods)
	}

	router.Run() // listen and serve on 0.0.0.0:8080
	// r.Run(":8083") // listen and serve on 0.0.0.0:8080
}

带参数的url
package main

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

func goodList(c *gin.Context) {
	c.JSON(200, gin.H{
		"message": "goodList",
	})
}
func goodsDetail(c *gin.Context) {
	id := c.Param("id")
	action := c.Param("action")
	c.JSON(200, gin.H{
		"message": "goodsDetail",
		"id":      id,
		"action":  action,
	})
}
func createGoods(c *gin.Context) {
	c.JSON(200, gin.H{
		"message": "createGoods",
	})
}
func main() {
	router := gin.Default()
	goodsGroup := router.Group("/goods")
	{
		goodsGroup.GET("", goodList)
		goodsGroup.GET("/:id/:action", goodsDetail)
		// goodsGroup.GET("/:id/*action", goodsDetail)
		goodsGroup.POST("", createGoods)
	}

	router.Run() // listen and serve on 0.0.0.0:8080
	// r.Run(":8083") // listen and serve on 0.0.0.0:8080
}

获取路由分组的参数
package main

import (
	"net/http"

	"github.com/gin-gonic/gin"
)

type Person struct {
	ID   string `uri:"id" binding:"required,uuid"`
	Name string `uri:"name" binding:"required"`
}

func main() {
	router := gin.Default()
	router.GET("/:name/:id", func(c *gin.Context) {
		var person Person
		if err := c.ShouldBindUri(&person); err != nil {
			c.Status(404)
			return
		}
		c.JSON(http.StatusOK, gin.H{
			"name": person.Name,
			"id":   person.ID,
		})
	})
	router.Run() // listen and serve on 0.0.0.0:8080
	// r.Run(":8083") // listen and serve on 0.0.0.0:8080
}

http://localhost:8080/sd1/8bebdad9-2829-4849-a011-c19128061822

获取get和post参数

get、post和get&post

package main

import (
	"fmt"
	"net/http"

	"github.com/gin-gonic/gin"
)

func welcome(c *gin.Context) {
	firstName := c.DefaultQuery("firstname", "bobby")
	lastName := c.Query("lastname")
	c.JSON(http.StatusOK, gin.H{
		"first_name": firstName,
		"last_name":  lastName,
	})
}

func formPost(c *gin.Context) {
	message := c.PostForm("message")
	nick := c.DefaultPostForm("nick", "anonymous")
	c.JSON(http.StatusOK, gin.H{
		"message": message,
		"nick":    nick,
	})
}

func main() {
	router := gin.Default()
	//get请求
	router.GET("/welcome", welcome)
	//post请求
	router.POST("/form", formPost)
	//post & get
	router.POST("/postsd", func(c *gin.Context) {
		id := c.Query("id")
		page := c.DefaultQuery("page", "0")
		name := c.PostForm("name")
		message := c.PostForm("message")

		fmt.Printf("id: %s,page: %s,name: %s,message: %s", id, page, name, message)
	})
	router.Run() // listen and serve on 0.0.0.0:8080
	// r.Run(":8083") // listen and serve on 0.0.0.0:8080
}

在这里插入图片描述

http://localhost:8080/welcome?firstname=dlkjf&lastname=sdfljk

JSON、ProtoBuf 渲染

json渲染、Prootobuf渲染和purejson渲染

package main

import (
	"net/http"

	"GolangStudy/Introduction/proto"

	"github.com/gin-gonic/gin"
)

func moreJSON(c *gin.Context) {
	var msg struct {
		Name    string `json:"user"`
		Message string
		Number  int
	}
	msg.Name = "bobby"
	msg.Message = "测试json"
	msg.Number = 20
	c.JSON(http.StatusOK, msg)
}
func returnProto(c *gin.Context) {
	user := &proto.Teacher{
		Name:   "bobby",
		Course: []string{"python", "go", "java"},
	}
	c.ProtoBuf(http.StatusOK, user)
}
func main() {
	router := gin.Default()
	router.GET("/moreJSON", moreJSON)
	router.GET("/someProtoBuf", returnProto)
	router.GET("/pureJSON", func(c *gin.Context) {
		c.PureJSON(200, gin.H{
			"html": "<b>Hello, world!</b>",
		})
	})
	router.Run()
}

表单验证

Gin使用 go-playground/validator 验证参数
文档

package main

import (
	"fmt"
	"net/http"

	"github.com/gin-gonic/gin"
)

// 绑定为json
type LoginForm struct {
	User     string `form:"user" json:"user" xml:"user"  binding:"required,min=3,max=10"` //min最短长度,max最大长度
	Password string `form:"password" json:"password" xml:"password" binding:"required"`
}

func main() {
	router := gin.Default()
	router.POST("/loginJSON", func(c *gin.Context) {
		var loginForm LoginForm
		if err := c.ShouldBind(&loginForm); err != nil {
			fmt.Println(err.Error())
			c.JSON(http.StatusBadRequest, gin.H{
				"error": err.Error(),
			})
			return
		}
		c.JSON(http.StatusOK, gin.H{
			"msg": "登录成功",
		})
	})
	router.Run()
}

在这里插入图片描述

注册表单的验证
package main

import (
	"fmt"
	"net/http"

	"github.com/gin-gonic/gin"
)

// 绑定为json
type LoginForm struct {
	User     string `form:"user" json:"user" xml:"user"  binding:"required,min=3,max=10"` //min最短长度,max最大长度
	Password string `form:"password" json:"password" xml:"password" binding:"required"`
}

type SignUpForm struct {
	Age        uint8  `json:"age" binding:"gte=1,lte=130"`
	Name       string `json:"name" binding:"required,min=3"`
	Email      string `json:"email" binding:"required,email"`
	Password   string `json:"password" binding:"required"`
	RePassword string `json:"re_password" binding:"required,eqfield=Password"`
}

func main() {
	router := gin.Default()
	router.POST("/loginJSON", func(c *gin.Context) {
		var loginForm LoginForm
		if err := c.ShouldBind(&loginForm); err != nil {
			fmt.Println(err.Error())
			c.JSON(http.StatusBadRequest, gin.H{
				"error": err.Error(),
			})
			return
		}
		c.JSON(http.StatusOK, gin.H{
			"msg": "登录成功",
		})
	})
	router.POST("/signup", func(c *gin.Context) {
		var signForm SignUpForm
		if err := c.ShouldBind(&signForm); err != nil {
			fmt.Println(err.Error())
			c.JSON(http.StatusBadRequest, gin.H{
				"error": err.Error(),
			})
			return
		}
		c.JSON(http.StatusBadRequest, gin.H{
			"msg": "注册成功",
		})
	})
	router.Run()
}

在这里插入图片描述

表单验证错误翻译成中文
package main

import (
	"fmt"
	"net/http"
	"reflect"
	"strings"

	"github.com/gin-gonic/gin"
	"github.com/gin-gonic/gin/binding"
	"github.com/go-playground/locales/en"
	"github.com/go-playground/locales/zh"
	ut "github.com/go-playground/universal-translator"
	"github.com/go-playground/validator/v10"
	en_translations "github.com/go-playground/validator/v10/translations/en"
	zh_translations "github.com/go-playground/validator/v10/translations/zh"
)

// 绑定为json
type LoginForm struct {
	User     string `form:"user" json:"user" xml:"user"  binding:"required,min=3,max=10"` //min最短长度,max最大长度
	Password string `form:"password" json:"password" xml:"password" binding:"required"`
}

type SignUpForm struct {
	Age        uint8  `json:"age" binding:"gte=1,lte=130"`
	Name       string `json:"name" binding:"required,min=3"`
	Email      string `json:"email" binding:"required,email"`
	Password   string `json:"password" binding:"required"`
	RePassword string `json:"re_password" binding:"required,eqfield=Password"`
}

func removeTopStruct(fileds map[string]string) map[string]string {
	rsp := map[string]string{}
	for field, err := range fileds {
		rsp[field[strings.Index(field, ".")+1:]] = err
	}
	return rsp
}
func InitTrans(locale string) (err error) {
	//修改gin的validator引擎属性,实现定制
	if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
		//注册一个获取json的tag的自定义方法
		v.RegisterTagNameFunc(func(fld reflect.StructField) string {
			name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0]
			if name == "-" {
				return ""
			}
			return name
		})
		zhT := zh.New() //中文翻译器
		enT := en.New() //英文翻译器
		//第一个是备用的,后面的是支持的语言环境
		uni := ut.New(enT, zhT, enT)
		trans, ok = uni.GetTranslator(locale)
		if !ok {
			return fmt.Errorf("uni.GetTranslator(%s)", locale)
		}
		switch locale {
		case "en":
			en_translations.RegisterDefaultTranslations(v, trans)
		case "zh":
			zh_translations.RegisterDefaultTranslations(v, trans)
		default:
			en_translations.RegisterDefaultTranslations(v, trans)
		}
		return
	}
	return
}

var trans ut.Translator

func main() {
	if err := InitTrans("zh"); err != nil {
		fmt.Println("初始化翻译器错误")
		return
	}
	router := gin.Default()
	router.POST("/loginJSON", func(c *gin.Context) {
		var loginForm LoginForm
		if err := c.ShouldBind(&loginForm); err != nil {
			errs, ok := err.(validator.ValidationErrors)
			if !ok {
				c.JSON(http.StatusOK, gin.H{
					"msg": err.Error(),
				})
			} else {
				c.JSON(http.StatusBadRequest, gin.H{
					"error": removeTopStruct(errs.Translate(trans)),
				})
			}
			fmt.Println(err.Error())
			return
		}
		c.JSON(http.StatusOK, gin.H{
			"msg": "登录成功",
		})
	})
	router.POST("/signup", func(c *gin.Context) {
		var signForm SignUpForm
		if err := c.ShouldBind(&signForm); err != nil {
			errs, ok := err.(validator.ValidationErrors)
			if !ok {
				c.JSON(http.StatusOK, gin.H{
					"msg": err.Error(),
				})
			} else {
				c.JSON(http.StatusBadRequest, gin.H{
					"error": removeTopStruct(errs.Translate(trans)),
				})
			}
			fmt.Println(err.Error())
			return
		}
		c.JSON(http.StatusBadRequest, gin.H{
			"msg": "注册成功",
		})
	})
	router.Run()
}

在这里插入图片描述

中间件

gin.New()可以添加中间件;gin.Default()默认启动方式,包含 Logger、Recovery 中间件
添加中间件

package main

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

	"github.com/gin-gonic/gin"
)

func MyLogger() gin.HandlerFunc {
	return func(c *gin.Context) {
		t := time.Now()
		c.Set("example", "123456")
		//让原本该执行的逻辑继续执行
		c.Next()
		end := time.Since(t)
		fmt.Printf("耗时:%V\n", end)
		status := c.Writer.Status()
		fmt.Println("状态", status)
	}
}
func main() {
	router := gin.New()
	//使用logger和recovery中间件
	router.Use(MyLogger())
	router.GET("/ping", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{
			"message": "pong",
		})
	})
	router.Run()
}

通过c.abort()实现中止中间件的执行(通过return是不能终止的,因为中间件都存储在中间件队列中,当前中间的中止会继续调用后续的中间件,直到调用完成。c.abort()会将中间件队列的索引值改为math.MaxInt8/2实现不调用后续中间件)

package main

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

	"github.com/gin-gonic/gin"
)

func MyLogger() gin.HandlerFunc {
	return func(c *gin.Context) {
		t := time.Now()
		c.Set("example", "123456")
		//让原本该执行的逻辑继续执行
		c.Next()
		end := time.Since(t)
		fmt.Printf("耗时:%V\n", end)
		status := c.Writer.Status()
		fmt.Println("状态", status)
	}
}
func TokenRequired() gin.HandlerFunc {
	return func(c *gin.Context) {
		var token string
		for k, v := range c.Request.Header {
			if k == "X-Token" {
				token = v[0]
			}
			fmt.Println(k, v, token)
		}
		if token != "bobby" {
			c.JSON(http.StatusUnauthorized, gin.H{
				"msg": "未登录",
			})
			c.Abort()
		}
		c.Next()
	}
}
func main() {
	router := gin.New()
	//使用logger和recovery中间件
	router.Use(TokenRequired())
	router.GET("/ping", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{
			"message": "pong",
		})
	})
	router.Run()
}

gin加载html文件

在这里插入图片描述
html路径可以使用绝对路径和相对路径(推荐),使用相对路径需要先go build后运行,主要是因为直接通过编译器执行生成的可执行文件放置于系统的临时文件夹中并不抱存在当前项目的路径下,所以会出现找不到html文件。

package main

import (
	"net/http"

	"github.com/gin-gonic/gin"
)

func main() {
	router := gin.Default()
	//制定加载好相对路径
	router.LoadHTMLFiles("templates/index.tmpl")
	router.GET("/index", func(c *gin.Context) {
		c.HTML(http.StatusOK, "index.tmpl", gin.H{
			"title": "mooc",
		})
	})
	router.Run()
}

加载多html文件

package main

import (
	"net/http"

	"github.com/gin-gonic/gin"
)

func main() {
	router := gin.Default()
	//制定加载好相对路径
	//加载二级目录
	router.LoadHTMLGlob("templates/**/*")
	// router.LoadHTMLFiles("templates/index.tmpl", "templates/goods.html")
	router.GET("/index", func(c *gin.Context) {
		c.HTML(http.StatusOK, "index.tmpl", gin.H{
			"title": "mooc",
		})
	})
	router.GET("/goods/list", func(c *gin.Context) {
		c.HTML(http.StatusOK, "goods/list.html", gin.H{
			"name": "微服务开发",
		})
	})
	router.GET("/users/list", func(c *gin.Context) {
		c.HTML(http.StatusOK, "users/list.html", gin.H{
			"name": "微服务开发",
		})
	})
	router.Run()
}

优雅退出

自定义监听结束信号,方便完成关闭服务器等后续操作

package main

import (
	"fmt"
	"net/http"
	"os"
	"os/signal"
	"syscall"

	"github.com/gin-gonic/gin"
)

func main() {
	//优雅推出,当我们关闭程序后需要做的处理
	//微服务 启动之前或者启动之后必须做一件事:将当前的ip地址和端口号注册到注册中心
	//我们当前的服务停止之后并没有告知注册中心
	router := gin.Default()
	router.GET("/", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{
			"msg": "pong",
		})
	})
	go func() {
		router.Run()
	}()
	//接受信号
	quit := make(chan os.Signal)
	//syscall.SIGINT ctrl+c

	signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
	<-quit
	//处理后续的逻辑
	fmt.Println("关闭server中。。。")
	fmt.Println("注册服务。。。")
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【自用】王道文件管理强化笔记

文章目录 操作系统引导:磁盘初始化文件打开过程角度1文件的打开过程角度2 内存映射的文件访问 操作系统引导: ①CPU从一个特定主存地址开始&#xff0c;取指令&#xff0c;执行ROM中的引导程序(先进行硬件自检&#xff0c;再开机) ②)将磁盘的第一块–主引导记录读入内存&…

【机器学习】深度学习、强化学习和深度强化学习?

深度学习、强化学习和深度强化学习是机器学习的三个重要子领域。它们有着各自独特的应用场景和研究目标&#xff0c;虽然都属于机器学习的范畴&#xff0c;但各自的实现方式和侧重点有所不同。 1. 深度学习&#xff08;Deep Learning&#xff09; 深度学习是一种基于神经网络的…

2024 年在线翻译谁称霸?论文翻译场景大揭秘!

现在这世界&#xff0c;语言就是把我们连在一起的绳子&#xff0c;挺关键的。不管搞研究、做生意还是传文化&#xff0c;翻译得又快又准。2024年&#xff0c;翻译这行竞争挺猛的&#xff0c;各种在线翻译工具都挺拼的。咱们今天就聊聊论文翻译&#xff0c;瞅瞅谁能在这场翻译比…

Meta 发布 Quest 3S 头显及 AR 眼镜原型:开启未来交互新视界

简介 在科技的浪潮中&#xff0c;Meta 始终站在创新的前沿&#xff0c;不断为我们带来令人惊叹的虚拟现实和增强现实体验。2024 年 10 月 6 日&#xff0c;让我们一同聚焦 Meta 最新发布的 Quest 3S 头显及 AR 眼镜原型&#xff08;Orion&#xff09;&#xff0c;探索这两款产品…

【Blender Python】5.Blender场景中的集合

概述 这里的“集合”是指Blender场景中的集合。你可以在“大纲视图”面板中看到 图标的&#xff0c;就是集合&#xff0c;可以看做是文件夹&#xff0c;用于分类和整理场景中的对象。 获取场景的集合 >>> C.scene bpy.data.scenes[Scene]>>> C.scene.coll…

nodejs 构建高性能服务器的关键技术

nodejs 构建高性能服务器的关键技术 演示地址 演示地址 源码地址 源码地址 获取更多 获取更多 在现代 Web 开发中&#xff0c;Node.js 已成为构建高性能、可扩展网络应用的首选平台之一。它的非阻塞 I/O 模型与事件驱动架构使其能够在处理大量并发请求时表现出色&#xff0…

环形缓冲区(Ring Buffer)在STM32 HAL库中的应用:防止按键丢失

环形缓冲区&#xff08;Ring Buffer&#xff09;又称为循环缓冲区或圆形队列&#xff0c;是一种数据结构&#xff0c;它用于管理固定大小的数据存储空间。环形缓冲区本质是一个一维数组&#xff0c;不过是收尾相连的&#xff0c;类比一条蛇咬自己尾巴。 环形缓冲区重要性&…

危机四伏|盘点紧盯我国的五大APT组织

毒云藤&#xff08;APT-C-01&#xff09; 命名&#xff1a;该组织是 360 独立发现的&#xff0c;并率先披露了该组织的部分相关信息符合 360 对 APT 组织就行独立命名的条件。 360 威胁情报中心将 APT-C-01 组织命名为“毒云藤”&#xff0c;主要是考虑了以下几方面的因素&…

L1415 【哈工大_操作系统】CPU调度策略一个实际的schedule函数

L2.7 CPU调度策略 1、调度的策略 周转时间&#xff1a;任务进入到任务结束&#xff08;后台任务更关注&#xff09;响应时间&#xff1a;操作发生到响应时&#xff08;前台任务更关注&#xff09;吞吐量&#xff1a;CPU完成的任务量 响应时间小 -> 切换次数多 -> 系统…

curses函数库简介及使用

curses函数库简介及使用 导语curses简介屏幕输出读取清除移动字符 键盘键盘模式输入 窗口WINDOW常用函数屏幕刷新优化 子窗口keypad彩色显示pad总结参考文献 导语 curses函数库主要用来实现对屏幕和光标的操作&#xff0c;它的功能定位处于简单文本行程序和完全图形化界面之间…

【重学 MySQL】五十八、文本字符串(包括 enum set)类型

【重学 MySQL】五十八、文本字符串&#xff08;包括 enum set&#xff09;类型 CHAR 和 VARCHARTEXT 系列ENUMSET示例注意事项 在 MySQL 中&#xff0c;文本字符串类型用于存储字符数据。这些类型包括 CHAR、VARCHAR、TEXT 系列&#xff08;如 TINYTEXT、TEXT、MEDIUMTEXT 和 L…

鸿蒙开发(NEXT/API 12)【管理应用与Wear Engine服务的连接状态】手机侧应用开发

监测应用与Wear Engine服务的连接状态 华为运动健康App在后台停止服务&#xff08;如功耗过高&#xff09;&#xff0c;从而导致应用与Wear Engine服务的连接状态发生变化。对于类似这种不确定的断开情况&#xff0c;开发者可以通过本功能特性了解当前应用和Wear Engine的连接…

NatGo我的世界联机篇

书接上回 这里的TCP是JAVA&#xff0c;UDP是BE&#xff0c;选自适合你的映射类型 内网端口就填下面图片在你游戏同一个地方的数字!!! 就是我填12345的地方&#xff0c;mod-自定义局域网联机 默认 25565&#xff0c;如果出现无法创建本地游戏&#xff0c;那可能是端口被占用或…

卷积层是如何学习到图像特征的?

你好啊&#xff0c;我是董董灿。 想搞懂这个问题&#xff0c;需要先了解我们所说的特征指的是什么&#xff1f;然后再了解卷积核是如何学到的特征。 我们一步步来。 1、我们先来理解图像的特征 对于一张原始图像而言&#xff0c;说原始图像是相对于经过卷积处理而言的。 对…

【文心智能体】旅游攻略版,手把手教你调用插件实现智能体,绝对的干货满满!

1.灵感来源 需要创建一个智能体&#xff0c;首先当然是需要一个创作灵感啦&#xff0c;那么恰逢国庆假期&#xff0c;但是网上各种各样的旅游资料使我头晕目眩&#xff0c;刚好呢百度的文心智能体平台给我们提供了各种各样的插件以及知识库&#xff0c;可以供我们随心所欲地调…

嵌入式硬件设计中EDA布局与布线实现

大家好,今天主要给大家分享一下,如何使用立创EDA进行布局和布线,具体实现过程如下: 第一:PCB概念介绍 在介绍PCB的时候,先来说明一下,电子管的发明史。 贝尔在1876年发明了电话,爱迪生1879年发明了白炽灯、特斯拉于1888年发明了电动机,所有这些,都为电子学的诞生准…

2024 热门的4大电脑剪辑软件大盘点。

在电脑上进行视频剪辑能够让我们更好的发挥创意&#xff0c;制作出精彩的视频作品。同时也需要依赖一些比较专业的视频剪辑工具。这几款视频剪辑软件&#xff0c;无论是制作个人视频、商业广告还是电影预告片&#xff0c;都是非常不错的选择。 1、福昕电脑剪辑 直达链接&#…

vSAN05:vSAN延伸集群简介与创建、资源要求与计算、高级功能配置、维护、故障处理

目录 vSAN延伸集群延伸集群创建延伸集群的建议网络配置vSAN延伸集群的端口见证主机的资源要求vSAN延伸集群中见证节点带宽占用vSAN延伸集群的允许故障数vSAN延伸集群不同配置下的空间占用 vSAN延伸集群的HA配置vSAN延伸集群的DRS配置vSAN存储策略以及虚拟机/主机策略的互操作vS…

系统架构设计师③:数据块系统

系统架构设计师③&#xff1a;数据块系统 数据库模式 数据库模式是指数据库的结构和组织方式&#xff0c;它描述了数据库中数据的逻辑结构和组织方式&#xff0c;是数据库设计的核心组成部分。以下是关于数据库模式的详细解析&#xff1a; 一、定义与组成 定义&#xff1a;…

4款专业电脑数据恢复软件,帮你保障数据安全。

电脑里面会出现的数据丢失场景有很多&#xff0c;像硬盘故障、回收站清空、电脑格式化、系统崩溃、病毒入侵等等&#xff1b;如果发现数据丢失后&#xff0c;建议应停止使用电脑&#xff0c;避免新的数据写入覆盖丢失的数据。然后再尝试进行数据找回&#xff0c;如果想自己进行…