go语言开发文档gin实战文档简单实用
ztj100 2025-05-16 18:04 79 浏览 0 评论
1 gin的简单使用
package main
import "github.com/gin-gonic/gin"
func main() {
// Default方法的主要作用是实例化一个带有日志、故障恢复中间件的引擎。
r := gin.Default() //实例化一个gin对象
// 定义请求
//定义一个GET请求的路由,参数一是路由地址,也就是在浏览器访问的相对路径,
// 参数二是一个匿名函数,函数内部用于业务逻辑处理。
r.GET("/login", func(c *gin.Context) {
c.JSON(200, gin.H{ //JSON内容可以通过gin提供的H方法来构建,非常方便。
"msg": "login", //调用JSON方法返回数据。JSON的操作非常简单,参数一是状态码,参数二是JSON的内容。
})
})
// Run方法最终会调用内置http库的ListenAndServe方法来监听端口,如果不传参数默认监听80端口,
// 也可以通过参数来变更地址和端口。
r.Run(":12005")
}
2 RESTful API
转自:
https://www.jianshu.com/p/474bf6d5403e
参考:go语言中文文档:www.topgoer.com
RESTful 是网络应用程序的一种设计格和开发方式,每一个URI代表一种资源,客户端通过 POST 、 DELETE 、 PUT 、 GET 四种请求方式来对资源做增删改查的操作。
同样的,Gin框架给我们提供的除这4种动词外,还有 PATCH 、 OPTION 、 HEAD 等,详细内容可以查看 rentergroup.go 文件的IRoutes接口
// IRoutes defines all router handle interface.
type IRoutes interface {
Use(...HandlerFunc) IRoutes
Handle(string, string, ...HandlerFunc) IRoutes
Any(string, ...HandlerFunc) IRoutes
GET(string, ...HandlerFunc) IRoutes
POST(string, ...HandlerFunc) IRoutes
DELETE(string, ...HandlerFunc) IRoutes
PATCH(string, ...HandlerFunc) IRoutes
PUT(string, ...HandlerFunc) IRoutes
OPTIONS(string, ...HandlerFunc) IRoutes
HEAD(string, ...HandlerFunc) IRoutes
StaticFile(string, string) IRoutes
Static(string, string) IRoutes
StaticFS(string, http.FileSystem) IRoutes
}
例如接口:
func main() {
router := gin.Default()
// 请求动词的第一个参数是请求路径,第二个参数是用于逻辑处理的函数
router.POST("/article", func(c *gin.Context) {
c.String(200, "article post")
})
router.DELETE("/article", func(c *gin.Context) {
c.String(200, "article delete")
})
router.GET("/article/:id/:action", func(c *gin.Context) {
id := c.Param("id")
action := c.Param("action")
fmt.Printf("2 /article/:id->%s, action:%s\n", id, action)
c.String(200, id+" "+action)
})
router.Run(":8080")
}
- 通过web访问url
- 使用curl命令来访问url/ 测试方法 // curl -X PUT http://localhost:8080/article // curl -X POST http://localhost:8080/article // curl -X GET http://localhost:8080/article // curl -X DELETE http://localhost:8080/article
路由参数
:路由
这种匹配模式是精确匹配的,只能匹配一个
访问:http://localhost:8080/users/123
输出:123
func main() {
r := gin.Default()
r.GET("/users/:id", func(c *gin.Context) {
id := c.Param("id")
c.String(200, "The user id is %s", id)
})
r.Run(":8080")
}
*路由
还有一种不常用的就是 * 号类型的参数,表示匹配所有,结果是一个 / 开头的路径字符串
访问:http://localhost:8080/users/123
输出:/123
func main() {
r := gin.Default()
r.GET("/users/*id", func(c *gin.Context) {
id := c.Param("id")
c.String(200, "The user id is %s", id)
})
r.Run(":8080")
}
特别说明一点
访问 http://localhost:8080/users时候,会被重定向到 http://localhost:8080/users/,根本原因在于 /users 没有匹配的路由,但是有匹配 /users/ 的路由,所以就会被重定向 到 /users/ ,如下:
func main() {
r := gin.Default()
r.GET("/users/*id", func(c *gin.Context) {
id := c.Param("id")
c.String(200, "The user id is %s", id)
})
}
禁止重定向
r.RedirectTrailingSlash = false
加上如上设置之后,访问 http://localhost:8080/users,是访问不成功的,因为没有服务器去处理这个url
3 Gin获取查询参数
例如:
http://127.0.0.1:8080/users?k1=v1&k2=v2
以 ? 为起点,后面的 k=v&k1=v1&k2=v2 这样的字符串就是查询参数
上述案例中有2个参数键值对,通过&来连接:
k1=v1
k2=v2
可以使用gin框架中的如下接口来获取实际的参数值
// 3-2-url-param.go url参数获取
package main
import (
"fmt"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.GET("/", func(c *gin.Context) {
c.DefaultQuery("id", "0")
value, ok := c.GetQuery("id") // 适合用来判断是否存在该参数
if ok {
fmt.Println("id:", value)
} else {
fmt.Println("id: nil")
}
c.String(200, c.DefaultQuery("wechat", "default baidu_org"))
})
r.Run(":8080")
}
实际GetQuery具体实现:
func (c *Context) GetQuery(key string) (string, bool) {
if values, ok := c.GetQueryArray(key); ok {
return values[0], ok
}
return "", false
}
DefaultQuery的具体实现也是调用GetQuery:
func (c *Context) DefaultQuery(key, defaultValue string) string {
if value, ok := c.GetQuery(key); ok {
return value
}
return defaultValue
}
GetQuery 和 Query的区别
GetQuery中传入key值,会返回value,ok 若ok为true ,则value 有值
Query是直接返回字符串
可以用 GetQuery 来代替 Query 方法。 GetQuery 方法的底层实现其实是 c.Request.URL.Query().Get(key) ,通过 url.URL.Query() 来获取所有的参数键值对
仔细看GetQuery的具体使用方式
//本质上是调用的GetQueryArray,取的数组中第一个值
func (c *Context) GetQuery(key string) (string, bool) {
if values, ok := c.GetQueryArray(key); ok {
return values[0], ok
}
return "", false
}
func (c *Context) GetQueryArray(key string) ([]string, bool) {
c.getQueryCache() //得到缓存,这一点很关键,缓存所有的键值对
if values, ok := c.queryCache[key]; ok && len(values) > 0 {
return values, true
}
return []string{}, false
}
func (c *Context) getQueryCache() {
if c.queryCache == nil {
c.queryCache = c.Request.URL.Query()
}
}
其中 c.Request.URL.Query() 这个方法就是把 ?k=v&k1=v1&k2=v2 这类查询键值对转换为
map[string][]string ,所以还是很耗性能的,这里 Gin 采用了缓存的做法提高了性能挺好,这也是 Gin 成为性能最快的Golang Web 框架的原因之一。
4 接收数组和 Map
QueryArray
例如实际业务中,URL大概是这样的 ?a=b&a=c&a=d , key 值都一 样,但是对应的 value 不一样。
这类URL查询参数,就是一个数组,那么在Gin中我们如何获取它们呢?
// 在浏览器里访问http://localhost:8080/?media=blog&media=wechat 会看到如下信息:
// ["blog","wechat"]
func main() {
r := gin.Default()
r.GET("/", func(c *gin.Context) {
fmt.Println("media:", c.QueryArray("media"))
c.JSON(200, c.QueryArray("media"))
})
r.Run(":8080")
}
QueryArray 方法也有对应的 GetQueryArray 方法,区别在于返回对应的 key 是否存在
QueryMap
把满足一定格式的URL查询参数,转换为一个 map
例如:访问:http://localhost:8080/?ids[0]=a&ids[1]=b&ids[2]=c
输出:{"0":"a","1":"b","2":"c"}
func main() {
r := gin.Default()
r.GET("/", func(c *gin.Context) {
fmt.Println("map:", c.QueryMap("ids"))
c.JSON(200, c.QueryMap("ids"))
})
r.Run(":8080")
}
其中 QueryMap 的原理和具体源码实现:
// QueryMap returns a map for a given query key.
func (c *Context) QueryMap(key string) map[string]string {
dicts, _ := c.GetQueryMap(key)
return dicts
}
// GetQueryMap returns a map for a given query key, plus a boolean value
// whether at least one value exists for the given key.
func (c *Context) GetQueryMap(key string) (map[string]string, bool) {
c.getQueryCache()
return c.get(c.queryCache, key)
}
// get is an internal method and returns a map which satisfy conditions.
func (c *Context) get(m map[string][]string, key string) (map[string]string, bool) {
dicts := make(map[string]string)
exist := false
for k, v := range m {
if i := strings.IndexByte(k, '['); i >= 1 && k[0:i] == key {
if j := strings.IndexByte(k[i+1:], ']'); j >= 1 {
exist = true
dicts[k[i+1:][:j]] = v[0]
}
}
}
return dicts, exist
}
5 Form 表单
待补充
6 上传文件
上传单个文件 FormFile
test目录下的html文件源码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
<form action="http://127.0.0.1:8080/upload" method="post" enctype="multipart/form-data">
头像:
<input type="file" name="file">
<br>
<input type="submit" value="提交">
</form>
</body>
</html>
func main() {
// 1创建路由,默认使用了两个中间件Logger(),Recovery()
r := gin.Default()
// 给表单限制上传大小 (默认 32 MiB)
r.MaxMultipartMemory = 8 << 20 // 8 MiB
r.Static("/", "./test")
// 2绑定路由规则,
// gin.Context,封装了request和respose
r.POST("/upload", func(c *gin.Context) {
file, _ := c.FormFile("file")
log.Println("file:", file.Filename)
c.SaveUploadedFile(file, "./"+"test/"+file.Filename) // 上传文件到指定的路径
c.String(200, fmt.Sprintf("%s upload file!", file.Filename))
})
// 3监听端口,默认8080
r.Run(":8080")
}
上传多个文件,就是在上传单个文件的基础上 循环遍历文件列表而已
public 下的html文件为
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Multiple file upload</title>
</head>
<body>
<h1>Upload multiple files with fields</h1>
<form action="/upload" method="post" enctype="multipart/form-data">
Name: <input type="text" name="name"><br>
Email: <input type="email" name="email"><br>
Files: <input type="file" name="files" multiple><br><br>
<input type="submit" value="Submit">
</form>
</body>
</html>
func main() {
router := gin.Default()
// Set a lower memory limit for multipart forms (default is 32 MiB)
router.MaxMultipartMemory = 8 << 20 // 8 MiB
router.Static("/", "./public")
router.POST("/upload", func(c *gin.Context) {
name := c.PostForm("name")
email := c.PostForm("email")
// Multipart form
form, err := c.MultipartForm()
if err != nil {
c.String(http.StatusBadRequest, fmt.Sprintf("get form err: %s", err.Error()))
return
}
files := form.File["files"]
for _, file := range files {
log.Println("file:", file.Filename)
filename := filepath.Base(file.Filename)
if err := c.SaveUploadedFile(file, filename); err != nil {
c.String(http.StatusBadRequest, fmt.Sprintf("upload file err: %s", err.Error()))
return
}
}
c.String(http.StatusOK, fmt.Sprintf("Uploaded successfully %d files with fields name=%s and email=%s.", len(files), name, email))
})
router.Run(":8080")
}
7 分组路由
比如基于模块化,把同样模块的放在一起,比如 基于版本,把相同版本的API放一起,便于使用。在有的框架中,分组路由也被称之为命名空间
url分组,可以是分版本 等等
func main() {
r := gin.Default()
//路由组注册中间件方法1:
xx1Group := r.Group("/xx1", func(c *gin.Context) { fmt.Println("/xx1中间件") })
{
xx1Group.GET("/index", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"msg": "xx1Group"})
})
xx1Group.GET("/index2", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"msg": "2222xx1Group"})
})
}
//路由组注册中间件方法2:
xx2Group := r.Group("/xx2")
xx2Group.Use(authMiddleware(true))
{
xx2Group.GET("/index", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"msg": "xx2Group"})
})
}
r.Run(":8080")
}
路由中间件
通过 Group 方法的定义,我们可以看到,它是可以接收两个参数的:
func (group *RouterGroup) Group(relativePath string, handlers ...HandlerFunc) *RouterGroup
第一个就是我们注册的分组路由(命名空间);第二个是一个 ...HandlerFunc ,可以把它理解为这个 分组路由的中间件,所以这个分组路由下的子路由在执行的时候,都会调用它
如上述代码,访问xx1/index2 或者 xx1/index 都会打印出 /xx1中间件
分组路由嵌套
和上述分组的做法是一致
原理解析
以get为例
注意第一个参数 relativePath ,这是一个相对路径,也就是我们传给 Gin 的是一个相对路径,那么是 相对谁的呢?
func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes {
return group.handle(http.MethodGet, relativePath, handlers)
}
通过这句 absolutePath := group.calculateAbsolutePath(relativePath) 代码,我们可以 看出是相对当前的这个 group (方法接收者)的。 现在 calculateAbsolutePath 方法的源代码我们暂时不看,回过头来看 Group 这个生成分组路由的 方法。
func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes {
absolutePath := group.calculateAbsolutePath(relativePath)
handlers = group.combineHandlers(handlers)
group.engine.addRoute(httpMethod, absolutePath, handlers)
return group.returnObj()
}
func (group *RouterGroup) Group(relativePath string, handlers ...HandlerFunc) *RouterGroup {
return &RouterGroup{
Handlers: group.combineHandlers(handlers),
basePath: group.calculateAbsolutePath(relativePath),
engine: group.engine,
}
}
这里要注意的是,我们通过 gin.Default() 生成的 gin.Engine 其实包含一个 RouterGroup (嵌套组 合),所以它可以用 RouterGroup 的方法。 Group 方法又生成了一个 *RouterGroup ,这里最重要的就是 basePath ,它的值是 group.calculateAbsolutePath(relativePath) ,和我们刚刚暂停的分析的方法一样,既然这 样,就来看看这个方法吧。
func (group *RouterGroup) calculateAbsolutePath(relativePath string) string {
return joinPaths(group.basePath, relativePath)
}
GIn中间件
Gin框架允许开发者在处理请求的过程中,加入用户自己的钩子(Hook)函数。这个钩子函数就叫中间件,中间件适合处理一些公共的业务逻辑,比如登录认证、权限校验、数据分、记录日志、耗时统计等
在Gin中,我们可以通过Gin提供的默认函数,来构建一个自带默认中间件的 *Engine 。
r := gin.Default()
Default 函数会默认绑定两个已经准备好的中间件,它们就是Logger 和 Recovery,帮助我们打印日志 输出和 painc 处理。
func Default() *Engine {
debugPrintWARNINGDefault()
engine := New()
engine.Use(Logger(), Recovery())
return engine
}
从中我们可以看到,Gin的中间件是通过 Use 方法设置的,它接收一个可变参数,所以我们同时可以设置 多个中间件。
func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes {
engine.RouterGroup.Use(middleware...)
engine.rebuild404Handlers()
engine.rebuild405Handlers()
return engine
}
其实就是Gin定义的一个 HandlerFunc ,而它在我 们Gin中经常使用
r.GET("/", func(c *gin.Context) {
fmt.Println("HandlerFunc")
c.JSON(200, "HandlerFunc")
})
后面的 func(c *gin.Context) 这部分其实就是一个 HandlerFunc
中间件实现HTTP Basic Authorization
HTTP Basic Authorization 是HTTP常用的认证方案,它通过Authorization 请求消息头含有服务器用于 验证用户代理身份的凭证,格式为:
Authorization: Basic <credentials>
如果认证不成功,服务器返回401 Unauthorized 状态码以及WWW-Authenticate 消息头,让客户端输入
用户名和密码进一步认证。
在Gin中,为我们提供了 gin.BasicAuth 帮我们生成基本认证的中间件,方便我们的开发。
基本认证的中间件可以用在分组路由中,在特定的url下进行认证
func main() {
r := gin.Default()
r.Use(gin.BasicAuth(gin.Accounts{
"admin": "123456",
}))
r.GET("/", func(c *gin.Context) {
body, _ := ioutil.ReadAll(c.Request.Body)
fmt.Println("---body--- \r\n " + string(body))
fmt.Println("---header--- \r\n")
for k, v := range c.Request.Header {
fmt.Println(k, v)
}
fmt.Println("进入主页")
c.JSON(200, "首页")
})
r.Run(":8080")
}
中间件注意事项
gin.Default()
gin.Default()默认使用了Logger和Recovery中间件,其中:Logger中间件将日志写入 gin.DefaultWriter,即使配置GIN_MODE=release。Recovery中间件会recover任何panic。如果有 panic的话,会写入500响应码。如果不想使用上面两个默认的中间件,可以使用gin.New()新建一个没有 任何默认中间件的路由。
gin中间件中使用goroutine
当在中间件或handler中启动新的goroutine时,不能使用原始的上下文(c *gin.Context),必须使 用其只读副本(c.Copy())
gin框架中间件c.Next()理解
func main() {
router := gin.New()
mid1 := func(c *gin.Context) {
fmt.Println("mid1 start")
c.Next()
fmt.Println("mid1 end")
}
mid2 := func(c *gin.Context) {
fmt.Println("mid2 start")
c.Next()
fmt.Println("mid2 end")
}
mid3 := func(c *gin.Context) {
fmt.Println("mid3 start")
c.Next()
fmt.Println("mid3 end")
}
router.Use(mid1, mid2)
router.Use(mid3)
router.GET("/index", func(c *gin.Context) {
fmt.Println("process get request")
c.JSON(http.StatusOK, "hello")
fmt.Println("JSON after") //
// c.Next() // 这里加是没有用
})
router.Run(":8080")
}
- 正常写next是如下打印 ,类似于递归,洋葱模型mid1 start mid2 start mid3 start process get request JSON after mid3 end mid2 end mid1 end
- 如果注释掉3个中间件中的c.Next(),则执行情况如下,顺序调用每一个中间件mid1 start mid1 end mid2 start mid2 end mid3 start mid3 end process get request JSON after
- 只在m1中写入c.Next()mid1 start mid2 start mid2 end mid3 start mid3 end process get request JSON after mid1 end
总结:
最后的get路由处理函数可以理解为最后的中间件,在不是调用c.Abort()的情况下,所有的中间件 都会被执行到。当某个中间件调用了c.Next(),则整个过程会产生嵌套关系。如果某个中间件调用了 c.Abort(),则此中间件结束后会直接返回,后面的中间件均不会调用。
8 json、struct、xml、yaml、protobuf渲染
各种数据格式的响应
func main() {
r := gin.Default()
//1. json响应
r.GET("/someJSON", func(c *gin.Context) {
c.JSON(200, gin.H{"message": "someJSON", "status": 200})
})
//2. 结构体响应
r.GET("/someStruct", func(c *gin.Context) {
var msg struct {
Name string
Message string
Number int
}
msg.Name = "root"
msg.Message = "message"
msg.Number = 123
c.JSON(200, msg)
})
//3. XML
r.GET("/someXML", func(c *gin.Context) {
c.XML(200, gin.H{"message": "abc"})
})
//4. YAML响应
r.GET("/someYAML", func(c *gin.Context) {
c.YAML(200, gin.H{"name": "you"})
})
//5.Protobuf格式,谷歌开发的高效存储读取的工具
r.GET("/someProtoBuf", func(c *gin.Context) {
reps := []int64{int64(1), int64(2)}
//定义数据
label := "label"
//传protobuf格式数据
data := &protoexample.Test{
Label: &label,
Reps: reps,
}
c.ProtoBuf(200, data)
})
r.Run(":8080")
}
9 HTML模板渲染
- gin支持加载HTML模板,然后根据模板参数进行配置并返回响应的数据,本质上就是字符串替换
- LoadHTMLGlob()方法可以加载模板文件
正常渲染html模板
func main() {
r := gin.Default()
r.LoadHTMLGlob("view/*")
r.GET("/index", func(c *gin.Context) {
c.HTML(http.StatusOK, "index.html", gin.H{"title": "我是gin", "name": "you"})
})
r.GET("/", func(c *gin.Context) {
c.HTML(http.StatusOK, "index.html", gin.H{"title": "我是gin", "name": "you"})
})
r.Run(":8080")
}
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{{.title}}</title>
</head>
<body bgcolor="#E6E600">
<h1>{{.title}}</h1>
name : {{.name}}
</body>
</html>
将html文件头尾分离
func main() {
r := gin.Default()
r.LoadHTMLGlob("view2/**/*")
r.GET("/index", func(c *gin.Context) {
c.HTML(http.StatusOK, "user/index.html", gin.H{"title": "我是gin", "name": "you2"})
})
r.Run()
}
index.html
{{ define "user/index.html" }}
{{template "public/header" .}}
name: {{.name}}
{{template "public/footer" .}}
{{ end }}
header.html
{{define "public/header"}}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{{.title}}</title>
</head>
<body>
{{end}}
footer.html
{{define "public/footer"}}
</body>
</html>
{{end}}
url重定向
访问http://127.0.0.1:8080/ 会 自动重定向到 http://127.0.0.1:8080/index
func main() {
r := gin.Default()
r.LoadHTMLGlob("view/*")
r.GET("/index", func(c *gin.Context) {
c.HTML(http.StatusOK, "index.html", gin.H{"title": "我是gin", "name": "you"})
})
r.GET("/", func(c *gin.Context) {
c.Redirect(http.StatusMovedPermanently, "/index") // 重定向
})
r.Run(":8080")
}
静态文件目录
需要引入静态文件可以定义一个静态文件目录
r.Static("/assets", "./assets")
10 异步协程
- goroutine机制可以方便地实现异步处理
- 另外,在启动新的goroutine时,不应该使用原始上下文,必须使用它的只读副本。
func main() {
r := gin.Default()
//1. 异步
r.GET("/long_async", func(c *gin.Context) {
//需要搞一个副本
copyContext := c.Copy()
//异步处理
go func() {
time.Sleep(3 * time.Second)
log.Println("异步执行:" + copyContext.Request.URL.Path)
// copyContext.JSON(200, gin.H{"message": "someJSON", "status": 200})
}()
})
//2. 同步
r.GET("/long_sync", func(c *gin.Context) {
time.Sleep(3 * time.Second)
log.Println("同步执行:" + c.Request.URL.Path)
})
r.Run()
}
作者:小魔童哪吒
- 上一篇:Go语言核心36讲(新年彩蛋)--学习笔记
- 下一篇:gin 快速入门
相关推荐
- Linux集群自动化监控系统Zabbix集群搭建到实战
-
自动化监控系统...
- systemd是什么如何使用_systemd/system
-
systemd是什么如何使用简介Systemd是一个在现代Linux发行版中广泛使用的系统和服务管理器。它负责启动系统并管理系统中运行的服务和进程。使用管理服务systemd可以用来启动、停止、...
- Linux服务器日常巡检脚本分享_linux服务器监控脚本
-
Linux系统日常巡检脚本,巡检内容包含了,磁盘,...
- 7,MySQL管理员用户管理_mysql 管理员用户
-
一、首次设置密码1.初始化时设置(推荐)mysqld--initialize--user=mysql--datadir=/data/3306/data--basedir=/usr/local...
- Python数据库编程教程:第 1 章 数据库基础与 Python 连接入门
-
1.1数据库的核心概念在开始Python数据库编程之前,我们需要先理解几个核心概念。数据库(Database)是按照数据结构来组织、存储和管理数据的仓库,它就像一个电子化的文件柜,能让我们高效...
- Linux自定义开机自启动服务脚本_linux添加开机自启动脚本
-
设置WGCloud开机自动启动服务init.d目录下新建脚本在/etc/rc.d/init.d新建启动脚本wgcloudstart.sh,内容如下...
- linux系统启动流程和服务管理,带你进去系统的世界
-
Linux启动流程Rhel6启动过程:开机自检bios-->MBR引导-->GRUB菜单-->加载内核-->init进程初始化Rhel7启动过程:开机自检BIOS-->M...
- CentOS7系统如何修改主机名_centos更改主机名称
-
请关注本头条号,每天坚持更新原创干货技术文章。如需学习视频,请在微信搜索公众号“智传网优”直接开始自助视频学习1.前言本文将讲解CentOS7系统如何修改主机名。...
- 前端工程师需要熟悉的Linux服务器(SSH 终端操作)指令
-
在Linux服务器管理中,SSH(SecureShell)是远程操作的核心工具。以下是SSH终端操作的常用命令和技巧,涵盖连接、文件操作、系统管理等场景:一、SSH连接服务器1.基本连接...
- Linux开机自启服务完全指南:3步搞定系统服务管理器配置
-
为什么需要配置开机自启?想象一下:电商服务器重启后,MySQL和Nginx没自动启动,整个网站瘫痪!这就是为什么开机自启是Linux运维的必备技能。自启服务能确保核心程序在系统启动时自动运行,避免人工...
- Kubernetes 高可用(HA)集群部署指南
-
Kubernetes高可用(HA)集群部署指南本指南涵盖从概念理解、架构选择,到kubeadm高可用部署、生产优化、监控备份和运维的全流程,适用于希望搭建稳定、生产级Kubernetes集群...
- Linux项目开发,你必须了解Systemd服务!
-
1.Systemd简介...
- Linux系统systemd服务管理工具使用技巧
-
简介:在Linux系统里,systemd就像是所有进程的“源头”,它可是系统中PID值为1的进程哟。systemd其实是一堆工具的组合,它的作用可不止是启动操作系统这么简单,像后台服务...
- Linux下NetworkManager和network的和平共处
-
简介我们在使用CentoOS系统时偶尔会遇到配置都正确但network启动不了的问题,这问题经常是由NetworkManager引起的,关闭NetworkManage并取消开机启动network就能正...
你 发表评论:
欢迎- 一周热门
-
-
MySQL中这14个小玩意,让人眼前一亮!
-
旗舰机新标杆 OPPO Find X2系列正式发布 售价5499元起
-
面试官:使用int类型做加减操作,是线程安全吗
-
C++编程知识:ToString()字符串转换你用正确了吗?
-
【Spring Boot】WebSocket 的 6 种集成方式
-
PyTorch 深度学习实战(26):多目标强化学习Multi-Objective RL
-
pytorch中的 scatter_()函数使用和详解
-
与 Java 17 相比,Java 21 究竟有多快?
-
基于TensorRT_LLM的大模型推理加速与OpenAI兼容服务优化
-
这一次,彻底搞懂Java并发包中的Atomic原子类
-
- 最近发表
-
- Linux集群自动化监控系统Zabbix集群搭建到实战
- systemd是什么如何使用_systemd/system
- Linux服务器日常巡检脚本分享_linux服务器监控脚本
- 7,MySQL管理员用户管理_mysql 管理员用户
- Python数据库编程教程:第 1 章 数据库基础与 Python 连接入门
- Linux自定义开机自启动服务脚本_linux添加开机自启动脚本
- linux系统启动流程和服务管理,带你进去系统的世界
- CentOS7系统如何修改主机名_centos更改主机名称
- 前端工程师需要熟悉的Linux服务器(SSH 终端操作)指令
- Linux开机自启服务完全指南:3步搞定系统服务管理器配置
- 标签列表
-
- idea eval reset (50)
- vue dispatch (70)
- update canceled (42)
- order by asc (53)
- spring gateway (67)
- 简单代码编程 贪吃蛇 (40)
- transforms.resize (33)
- redisson trylock (35)
- 卸载node (35)
- np.reshape (33)
- torch.arange (34)
- npm 源 (35)
- vue3 deep (35)
- win10 ssh (35)
- vue foreach (34)
- idea设置编码为utf8 (35)
- vue 数组添加元素 (34)
- std find (34)
- tablefield注解用途 (35)
- python str转json (34)
- java websocket客户端 (34)
- tensor.view (34)
- java jackson (34)
- vmware17pro最新密钥 (34)
- mysql单表最大数据量 (35)