【go语言学习笔记】05 Go 语言实战

文章目录

    • 一、 RESTful API 服务
      • 1. RESTful API 定义
        • 1.1 HTTP Method
        • 1.2 RESTful API 规范
      • 2. RESTful API 风格示例
      • 3. RESTful JSON API
      • 4. Gin 框架
        • 4.1 导入 Gin 框架
        • 4.2 使用 Gin 框架
          • 4.2.1 获取特定的用户(GET)
          • 4.2.2 新增一个用户(POST)
          • 4.2.3 删除一个用户(DELETE)
          • 4.2.4 修改一个用户的名字(PATCH)
    • 二、 RPC 跨平台服务
      • 1. 定义
      • 2. Go语言中的RPC
        • 2.1 服务端
        • 2.2 客户端
      • 3. 基于 HTTP 的RPC
      • 3. JSON RPC 跨平台通信
        • 3.1 基于 TCP 的 JSON RPC
        • 3.2 基于 HTTP的JSON RPC
    • 三、Go 语言的发展前景

一、 RESTful API 服务

在做项目开发的时候,要善于借助已经有的轮子,让自己的开发更有效率,也更容易实现。

1. RESTful API 定义

RESTful API 是一套规范,它可以规范如何对服务器上的资源进行操作。和 RESTful API 和密不可分的是 HTTP Method。

1.1 HTTP Method

HTTP Method最常见的就是POST和GET,其实最早在 HTTP 0.9 版本中,只有一个GET方法,该方法是一个幂等方法,用于获取服务器上的资源。

在 HTTP 1.0 版本中又增加了HEAD和POST方法,其中常用的是 POST 方法,一般用于给服务端提交一个资源,导致服务器的资源发生变化。

随着网络越来越复杂,在 HTTP1.1 版本的时候,支持的方法增加到了 9 个,新增的方法有 HEAD、OPTIONS、PUT、DELETE、TRACE、PATCH 和 CONNECT。下面是它们各自的作用:

  1. GET 方法可请求一个指定资源的表示形式,使用 GET 的请求应该只被用于获取数据。
  2. HEAD 方法用于请求一个与 GET 请求的响应相同的响应,但没有响应体。
  3. POST 方法用于将实体提交到指定的资源,通常导致服务器上的状态变化或副作用。
  4. PUT 方法用于请求有效载荷替换目标资源的所有当前表示。
  5. DELETE 方法用于删除指定的资源。
  6. CONNECT 方法用于建立一个到由目标资源标识的服务器的隧道。
  7. OPTIONS 方法用于描述目标资源的通信选项。
  8. TRACE 方法用于沿着到目标资源的路径执行一个消息环回测试。
  9. PATCH 方法用于对资源应用部分修改。

HTTP 规范针对每个方法都给出了明确的定义,所以使用的时候也要尽可能地遵循这些定义,这样在开发中才可以更好地协作。

1.2 RESTful API 规范

RESTful API 规范就是基于 HTTP Method 规范对服务器资源的操作,同时规范了 URL 的样式和 HTTP Status Code。

在 RESTful API 中,使用的主要是以下五种 HTTP 方法:

  1. GET,表示读取服务器上的资源;
  2. POST,表示在服务器上创建资源;
  3. PUT,表示更新或者替换服务器上的资源;
  4. DELETE,表示删除服务器上的资源;
  5. PATCH,表示更新 / 修改资源的一部分。

以上 HTTP 方法在 RESTful API 规范中是一个操作,操作的就是服务器的资源,服务器的资源通过特定的 URL 表示。

(1)GET 方法的示例

HTTP GET https://www.flysnow.org/users
HTTP GET https://www.flysnow.org/users/123

上例中

  • 第一个表示获取所有用户的信息;
  • 第二个表示获取 ID 为 123 用户的信息。

(2) POST 方法的示例

HTTP POST https://www.flysnow.org/users

该示例表示创建一个用户,通过 POST 方法给服务器提供创建这个用户所需的全部信息。

这里 users 是个复数。

(3)PUT 方法的示例

HTTP PUT https://www.flysnow.org/users/123

该示例表示要更新 / 替换 ID 为 123 的这个用户,在更新的时候,会通过 PUT 方法提供更新这个用户需要的全部用户信息。这里 PUT 方法和 POST 方法不太一样的是,从 URL 上看,PUT 方法操作的是单个资源,比如这里 ID 为 123 的用户。

如果要更新一个用户的部分信息,使用 PATCH 方法更恰当。

(4)DELETE 方法的示例

HTTP DELETE https://www.flysnow.org/users/123

DELETE 方法的使用和 PUT 方法一样,也是操作单个资源,这里是删除 ID 为 123 的这个用户。

2. RESTful API 风格示例

Go 语言的一个很大的优势,就是可以很容易地开发出网络后台服务,而且性能快、效率高。Golang 提供了内置的 net/http 包处理 HTTP 请求,让开发者可以比较方便地开发一个 HTTP 服务。

一个简单的 HTTP 服务的 Go 语言实现代码如下所示:

func main() {http.HandleFunc("/users",handleUsers)http.ListenAndServe(":8080", nil)
}
func handleUsers(w http.ResponseWriter, r *http.Request){fmt.Fprintln(w,"ID:1,Name:张三")fmt.Fprintln(w,"ID:2,Name:李四")fmt.Fprintln(w,"ID:3,Name:王五")
}

这个示例运行后,在浏览器中输入 http://localhost:8080/users, 就可以看到如下内容信息:

ID:1,Name:张三
ID:2,Name:李四
ID:3,Name:王五

这并不是一个 RESTful API,因为使用者不仅可以通过 HTTP GET 方法获得所有的用户信息,还可以通过 POST、DELETE、PUT 等 HTTP 方法获得所有的用户信息,这显然不符合 RESTful API 的规范。

对以上示例进行修改,使它符合 RESTful API 的规范,修改后的示例代码如下所示:

func handleUsers(w http.ResponseWriter, r *http.Request){switch r.Method {case "GET":w.WriteHeader(http.StatusOK)fmt.Fprintln(w,"ID:1,Name:张三")fmt.Fprintln(w,"ID:2,Name:李四")fmt.Fprintln(w,"ID:3,Name:王五")default:w.WriteHeader(http.StatusNotFound)fmt.Fprintln(w,"not found")}
}

该示例修改了 handleUsers 函数,在该函数中增加了只在使用 GET 方法时,才获得所有用户的信息的判断,其他情况返回 not found。

3. RESTful JSON API

在项目中最常见的是使用 JSON 格式传输信息,也就是提供的 RESTful API 要返回 JSON 内容给使用者。

用上面的示例改造成可以返回 JSON 内容的方式,示例代码如下所示:

//数据源,类似MySQL中的数据
var users = []User{{ID: 1,Name: "张三"},{ID: 2,Name: "李四"},{ID: 3,Name: "王五"},
}
func handleUsers(w http.ResponseWriter, r *http.Request){switch r.Method {case "GET":users,err:=json.Marshal(users)if err!=nil {w.WriteHeader(http.StatusInternalServerError)fmt.Fprint(w,"{\"message\": \""+err.Error()+"\"}")}else {w.WriteHeader(http.StatusOK)w.Write(users)}default:w.WriteHeader(http.StatusNotFound)fmt.Fprint(w,"{\"message\": \"not found\"}")}
}
//用户
type User struct {ID intName string
}

从以上代码可以看到,这次的改造主要是新建了一个 User 结构体,并且使用 users 这个切片存储所有的用户,然后在 handleUsers 函数中把它转化为一个 JSON 数组返回。这样,就实现了基于 JSON 数据格式的 RESTful API。

运行这个示例,在浏览器中输入 http://localhost:8080/users,可以看到如下信息:

[{"ID":1,"Name":"张三"},{"ID":2,"Name":"李四"},{"ID":3,"Name":"王五"}]

4. Gin 框架

虽然 Go 语言自带的 net/http 包,可以比较容易地创建 HTTP 服务,但是它也有很多不足:

  • 不能单独地对请求方法(POST、GET 等)注册特定的处理函数;
  • 不支持 Path 变量参数;
  • 不能自动对 Path 进行校准;
  • 性能一般;
  • 扩展性不足;
  • ……

基于以上这些不足,出现了很多 Golang Web 框架,如 Mux,Gin、Fiber 等,其中使用最多是 Gin 框架。

4.1 导入 Gin 框架

Gin 框架是一个在 Github 上开源的 Web 框架,封装了很多 Web 开发需要的通用功能,并且性能也非常高,可以很容易地写出 RESTful API。

Gin 框架其实是一个模块,也就是 Go Mod,所以采用 Go Mod 的方法引入即可。

安装代码如下:

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

导入代码如下:

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

4.2 使用 Gin 框架

用 Gin 框架重写上面的示例,修改的代码如下所示:

func main() {r:=gin.Default()r.GET("/users", listUser)r.Run(":8080")
}
func listUser(c *gin.Context)  {c.JSON(200,users)
}

相比 net/http 包,Gin 框架的代码非常简单,通过它的 GET 方法就可以创建一个只处理 HTTP GET 方法的服务,而且输出 JSON 格式的数据也非常简单,使用 c.JSON 方法即可。

最后通过 Run 方法启动 HTTP 服务,监听在 8080 端口。运行这个示例,在浏览器中输入 http://localhost:8080/users,看到的信息和通过 net/http 包实现的效果是一样的。

4.2.1 获取特定的用户(GET)

如果要获得特定用户的信息,需要使用的是 GET 方法,并且 URL 格式如下所示:

http://localhost:8080/users/2

以上示例中的 2 是用户的 ID,也就是通过 ID 来获取特定的用户。

通过 Gin 框架 Path 路径参数可以实现这个功能,示例代码如下:

func main() {//省略没有改动的代码r.GET("/users/:id", getUser)
}
func getUser(c *gin.Context) {id := c.Param("id")var user Userfound := false//类似于数据库的SQL查询for _, u := range users {if strings.EqualFold(id, strconv.Itoa(u.ID)) {user = ufound = truebreak}}if found {c.JSON(200, user)} else {c.JSON(404, gin.H{"message": "用户不存在",})}
}

在 Gin 框架中,路径中使用冒号表示 Path 路径参数,比如示例中的 :id,然后在 getUser 函数中可以通过 c.Param(“id”) 获取需要查询用户的 ID 值。

Param 方法的参数要和 Path 路径参数中的一致,比如示例中都是 ID。

运行这个示例,通过浏览器访问 http://localhost:8080/users/2,就可以获得 ID 为 2 的用户,输出信息如下所示:

{"ID":2,"Name":"李四"}

假如我们访问一个不存在的 ID,得到的结果如下所示:

curl http://localhost:8080/users/99
{"message":"用户不存在"}%
4.2.2 新增一个用户(POST)

根据 RESTful API 规范,实现新增使用的是 POST 方法,并且 URL 的格式为 http://localhost:8080/users ,向这个 URL 发送数据,就可以新增一个用户,然后返回创建的用户信息。

使用 Gin 框架实现新增一个用户,示例代码如下:

func main() {//省略没有改动的代码r.POST("/users", createUser)
}
func createUser(c *gin.Context) {name := c.DefaultPostForm("name", "")if name != "" {u := User{ID: len(users) + 1, Name: name}users = append(users, u)c.JSON(http.StatusCreated,u)} else {c.JSON(http.StatusOK, gin.H{"message": "请输入用户名称",})}
}

以上新增用户的主要逻辑是获取客户端上传的 name 值,然后生成一个 User 用户,最后把它存储到 users 集合中,达到新增用户的目的。

在这个示例中,使用 POST 方法来新增用户,所以只能通过 POST 方法才能新增用户成功。

运行这个示例,通过如下命令发送一个新增用户的请求,查看结果:

curl -X POST -d 'name=小明' http://localhost:8080/users
{"ID":4,"Name":"小明"}
4.2.3 删除一个用户(DELETE)

删除一个用户比较简单,它的 API 格式和获取一个用户一样,但是 HTTP 方法换成了DELETE。示例代码如下所示:

func main() {//省略没有修改的代码r.DELETE("/users/:id", deleteUser)
}
func deleteUser(c *gin.Context) {id := c.Param("id")i := -1//类似于数据库的SQL查询for index, u := range users {if strings.EqualFold(id, strconv.Itoa(u.ID)) {i = indexbreak}}if i >= 0 {users = append(users[:i], users[i+1:]...)c.JSON(http.StatusNoContent, "")} else {c.JSON(http.StatusNotFound, gin.H{"message": "用户不存在",})}
}

这个示例的逻辑就是注册 DELETE 方法,达到删除用户的目的。删除用户的逻辑是通过ID 查询:

  • 如果可以找到要删除的用户,记录索引并跳出循环,然后根据索引删除该用户;
  • 如果找不到要删除的用户,则返回 404。
4.2.4 修改一个用户的名字(PATCH)

修改和删除一个用户非常像,实现代码如下所示:

func main() {//省略没有修改的代码r.PATCH("/users/:id",updateUserName)
}
func updateUserName(c *gin.Context) {id := c.Param("id")i := -1//类似于数据库的SQL查询for index, u := range users {if strings.EqualFold(id, strconv.Itoa(u.ID)) {i = indexbreak}}if i >= 0 {users[i].Name = c.DefaultPostForm("name",users[i].Name)c.JSON(http.StatusOK, users[i])} else {c.JSON(http.StatusNotFound, gin.H{"message": "用户不存在",})}
}

逻辑和删除的差不多的,只不过这里使用的是 PATCH方法。

二、 RPC 跨平台服务

1. 定义

RPC,也就是远程过程调用,是分布式系统中不同节点调用的方式(进程间通信),属于 C/S 模式。RPC 由客户端发起,调用服务端的方法进行通信,然后服务端把结果返回给客户端。

RPC的核心有两个:通信协议序列化。在 HTTP 2 之前,一般采用自定义 TCP 协议的方式进行通信,HTTP 2 出来后,也有采用该协议的,比如流行的gRPC。

序列化反序列化是一种把传输内容编码和解码的方式,常见的编解码方式有 JSON、Protobuf 等。

在大多数 RPC的架构设计中,都有ClientClient StubServerServer Stub这四个组件,Client 和 Server 之间通过 Socket 进行通信。RPC 架构如下图所示:
在这里插入图片描述
RPC 调用的流程:

  1. 客户端(Client)调用客户端存根(Client Stub),同时把参数传给客户端存根;
  2. 客户端存根将参数打包编码,并通过系统调用发送到服务端;
  3. 客户端本地系统发送信息到服务器;
  4. 服务器系统将信息发送到服务端存根(Server Stub);
  5. 服务端存根解析信息,也就是解码;
  6. 服务端存根调用真正的服务端程序(Sever);
  7. 服务端(Server)处理后,通过同样的方式,把结果再返回给客户端(Client)。

RPC 调用常用于大型项目,也就是常说的微服务,而且还会包含服务注册、治理、监控等功能,是一套完整的体系。

2. Go语言中的RPC

在 Go SDK 中,已经内置了 net/rpc 包来帮助开发者实现 RPC。简单来说,net/rpc 包提供了通过网络访问服务端对象方法的能力。

在实际的项目开发中,使用Go 语言自带的 RPC 框架并不多,比较常用的是Google的gRPC 框架,它是通过Protobuf 序列化的,是基于 HTTP/2 协议的二进制传输,并且支持很多编程语言,效率也比较高。

2.1 服务端

一个 RPC 示例的服务端代码如下所示:

package server
type MathService struct {
}
type Args struct {A, B int
}
func (m *MathService) Add(args Args, reply *int) error {*reply = args.A + args.Breturn nil
}

在以上代码中:

  • 定义了MathService,用于表示一个远程服务对象;
  • Args 结构体用于表示参数;
  • Add 这个方法实现了加法的功能,加法的结果通过 replay这个指针变量返回。

定义好服务对象就可以把它注册到暴露的服务列表中,以供其他客户端使用了。在Go 语言中,要注册一个RPC 服务对象可以通过 RegisterName 方法,示例代码如下所示:

package main
import ("server""log""net""net/rpc"
)
func main()  {rpc.RegisterName("MathService",new(server.MathService))l, e := net.Listen("tcp", ":1234")if e != nil {log.Fatal("listen error:", e)}rpc.Accept(l)
}

以上示例代码中,通过 RegisterName 函数注册了一个服务对象,该函数接收两个参数:

  1. 服务名称(MathService);
  2. 具体的服务对象,也就是刚刚定义好的MathService 这个结构体。

然后通过 net.Listen 函数建立一个TCP 链接,在 1234 端口进行监听,最后通过 rpc.Accept 函数在该 TCP 链接上提供 MathService 这个 RPC 服务。现在客户端就可以看到MathService这个服务以及它的Add 方法了。

在 net/rpc 这个RPC框架时,要想把一个对象注册为 RPC 服务,可以让客户端远程访问,那么该对象(类型)的方法必须满足如下条件:

  • 方法的类型是可导出的(公开的);
  • 方法本身也是可导出的;
  • 方法必须有 2 个参数,并且参数类型是可导出或者内建的;
  • 方法必须返回一个 error 类型。

总结来说该方法的格式如下所示:

func (t *T) MethodName(argType T1, replyType *T2) error

这里面的 T1、T2都是可以被 encoding/gob 序列化的。

  • 第一个参数 argType 是调用者(客户端)提供的;
  • 第二个参数 replyType是返回给调用者结果,必须是指针类型。

2.2 客户端

代码如下所示:

package main
import ("fmt""server""log""net/rpc"
)
func main()  {client, err := rpc.Dial("tcp",  "localhost:1234")if err != nil {log.Fatal("dialing:", err)}args := server.Args{A:7,B:8}var reply interr = client.Call("MathService.Add", args, &reply)if err != nil {log.Fatal("MathService.Add error:", err)}fmt.Printf("MathService.Add: %d+%d=%d", args.A, args.B, reply)
}

在以上实例代码中,首先通过 rpc.Dial 函数建立 TCP 链接。TCP 链接建立成功后,就需要准备远程方法需要的参数,也就是示例中的args 和 reply。参数准备好之后,就可以通过 Call 方法调用远程的RPC 服务了。Call 方法有 3 个参数,它们的作用分别如下所示:

  1. 调用的远程方法的名字,这里是MathService.Add,点前面的部分是注册的服务的名称,点后面的部分是该服务的方法;
  2. 客户端为了调用远程方法提供的参数,示例中是args;
  3. 为了接收远程方法返回的结果,必须是一个指针,也就是示例中的 &reply,这样客户端就可以获得服务端返回的结果了。

3. 基于 HTTP 的RPC

RPC 除了可以通过 TCP 协议调用之外,还可以通过HTTP 协议进行调用,而且内置的net/rpc 包已经支持,修改以上示例代码支持 HTTP 协议的调用,服务端代码如下所示:

func main() {rpc.RegisterName("MathService", new(server.MathService))rpc.HandleHTTP()//新增的l, e := net.Listen("tcp", ":1234")if e != nil {log.Fatal("listen error:", e)}http.Serve(l, nil)//换成http的服务
}

客户端修改的代码如下所示:

func main()  {client, err := rpc.DialHTTP("tcp",  "localhost:1234")//省略了其他没有修改的代码
}

可以看到,只需要把建立链接的方法从 Dial 换成 DialHTTP 即可。

Go 语言 net/rpc 包提供的 HTTP 协议的 RPC 还有一个调试的 URL,运行服务端代码后,在浏览器中输入 http://localhost:1234/debug/rpc 回车,即可看到服务端注册的RPC 服务,以及每个服务的方法,如下图所示:
在这里插入图片描述
如上图所示,注册的 RPC 服务、方法的签名、已经被调用的次数都可以看到。

3. JSON RPC 跨平台通信

以上实现的RPC 服务是基于 gob 编码的,这种编码在跨语言调用的时候比较困难,当前在微服务架构中,RPC 服务的实现者和调用者都可能是不同的编程语言,因此实现的 RPC 服务要支持多语言的调用。

3.1 基于 TCP 的 JSON RPC

实现跨语言 RPC 服务的核心在于选择一个通用的编码,这样大多数语言都支持,比如常用的JSON。在 Go 语言中,实现一个 JSON RPC 服务非常简单,只需要使用 net/rpc/jsonrpc 包即可。

以上面的示例为例,改造成支持 JSON的RPC 服务,服务端代码如下所示:

func main() {rpc.RegisterName("MathService", new(server.MathService))l, e := net.Listen("tcp", ":1234")if e != nil {log.Fatal("listen error:", e)}for {conn, err := l.Accept()if err != nil {log.Println("jsonrpc.Serve: accept:", err.Error())return}//json rpcgo jsonrpc.ServeConn(conn)}
}

从以上代码可以看到,相比 gob 编码的RPC 服务,JSON 的 RPC 服务是把链接交给了jsonrpc.ServeConn这个函数处理,达到了基于 JSON 进行 RPC 调用的目的。

JSON RPC 的客户端代码修改的部分如下所示:

func main()  {client, err := jsonrpc.Dial("tcp",  "localhost:1234")//省略了其他没有修改的代码
}

从以上代码可以看到,只需要把建立链接的 Dial方法换成 jsonrpc 包中的即可。

3.2 基于 HTTP的JSON RPC

Go 语言内置的jsonrpc 并没有实现基于 HTTP的传输,需要自己实现,这里参考 gob 编码的HTTP RPC 实现方式,来实现基于 HTTP的JSON RPC 服务。

RPC 服务端代码如下所示:

func main() {rpc.RegisterName("MathService", new(server.MathService))//注册一个path,用于提供基于http的json rpc服务http.HandleFunc(rpc.DefaultRPCPath, func(rw http.ResponseWriter, r *http.Request) {conn, _, err := rw.(http.Hijacker).Hijack()if err != nil {log.Print("rpc hijacking ", r.RemoteAddr, ": ", err.Error())return}var connected = "200 Connected to JSON RPC"io.WriteString(conn, "HTTP/1.0 "+connected+"\n\n")jsonrpc.ServeConn(conn)})l, e := net.Listen("tcp", ":1234")if e != nil {log.Fatal("listen error:", e)}http.Serve(l, nil)//换成http的服务
}

以上代码的实现基于 HTTP 协议的核心,即使用 http.HandleFunc 注册了一个 path,对外提供基于 HTTP 的 JSON RPC 服务。在这个 HTTP 服务的实现中,通过Hijack方法劫持链接,然后转交给 jsonrpc 处理,这样就实现了基于 HTTP 协议的 JSON RPC 服务。

客户端调用的代码如下所示:

func main()  {client, err := DialHTTP("tcp",  "localhost:1234")if err != nil {log.Fatal("dialing:", err)}args := server.Args{A:7,B:8}var reply interr = client.Call("MathService.Add", args, &reply)if err != nil {log.Fatal("MathService.Add error:", err)}fmt.Printf("MathService.Add: %d+%d=%d", args.A, args.B, reply)}// DialHTTP connects to an HTTP RPC server at the specified network address// listening on the default HTTP RPC path.func DialHTTP(network, address string) (*rpc.Client, error) {return DialHTTPPath(network, address, rpc.DefaultRPCPath)}// DialHTTPPath connects to an HTTP RPC server// at the specified network address and path.func DialHTTPPath(network, address, path string) (*rpc.Client, error) {var err errorconn, err := net.Dial(network, address)if err != nil {return nil, err}io.WriteString(conn, "GET "+path+" HTTP/1.0\n\n")// Require successful HTTP response// before switching to RPC protocol.resp, err := http.ReadResponse(bufio.NewReader(conn), &http.Request{Method: "GET"})connected := "200 Connected to JSON RPC"if err == nil && resp.Status == connected {return jsonrpc.NewClient(conn), nil}if err == nil {err = errors.New("unexpected HTTP response: " + resp.Status)}conn.Close()return nil, &net.OpError{Op:   "dial-http",Net:  network + " " + address,Addr: nil,Err:  err,}}

以上这段代码的核心在于通过建立好的TCP 链接,发送 HTTP 请求调用远程的HTTP JSON RPC 服务,这里使用的是 HTTP GET 方法。

三、Go 语言的发展前景

Go 语言就是为云而生的编程语言,所以在云原生的时代,它就具备了天生的优势:易于学习、天然的并发、高效的网络支持、跨平台的二进制文件编译等。

CNCF(云原生计算基金会)对云原生的定义是:

  • 应用容器化;
  • 面向微服务架构;
  • 应用支持容器的编排调度。

对于这三点有代表性的 Docker、K8s 以及 istio 都是采用 Go 语言编写的,所以 Go 语言在云原生中发挥了极大的优势。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ElasticSearch安装与介绍

Elastic Stack简介 如果没有听说过Elastic Stack,那你一定听说过ELK,实际上ELK是三款软件的简称,分别是Elasticsearch、 Logstash、Kibana组成,在发展的过程中,又有新成员Beats的加入,所以就形成了Elastic…

9月大理,Move HackerHouse,成为全球数字游民的第一站

🚀世界各地的 hacker 们!即日起,我们正式向您发出 co-buiding & co-living 的邀请! 9.3日至9.24日,为期3周的 Move 主题Antalpha HackerHouse 将坐落于大理,邀请所有 Web3 开发者一起探索 Move 生态发…

基于Selenium模块实现无界面模式 执行JS脚本

此篇文章主要介绍如何使用 Selenium 模块实现 无界面模式 & 执行JS脚本(把滚动条拉到底部),并以具体的示例进行展示。 1、Selenium 设置无界面模式 创建浏览器对象之前,创建 options 功能对象 :options webdriver.ChromeOptions() 添加…

微服务系列(2)--注册中心

在博文:微服务系列(1)里我们提到过注册中心的概念,简单来说微服务注册中心是一个用于存储和管理微服务实例信息的组件,它提供了服务注册、服务发现、服务健康检查等功能,以确保微服务之间的稳定通信。在微服务架构中,各…

Python 图形界面框架TkInter(第八篇:理解pack布局)

前言 tkinter图形用户界面框架提供了3种布局方式,分别是 1、pack 2、grid 3、place 介绍下pack布局方式,这是我们最常用的布局方式,理解了pack布局,绝大多数需求都能满足。 第一次使用pack() import …

6. CSS(三)

目录 一、盒子模型 (一)网页布局的本质 (二)盒子模型组成 (三)边框(border) (四)表格的细线边框 (五)内边距(padding…

Android多屏幕支持-Android12

Android多屏幕支持-Android12 1、概览及相关文章2、屏幕窗口配置2.1 配置xml文件2.2 DisplayInfo#uniqueId 屏幕标识2.3 adb查看信息 3、配置文件解析3.1 xml字段读取3.2 简要时序图 4、每屏幕焦点 android12-release 1、概览及相关文章 AOSP > 文档 > 心主题 > 多屏…

【数据结构】栈与队列

1 栈 1.1 栈的概念及结构 栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出 LIFO (Last In First Out) 的原则。 压栈:栈…

【Git】

Git 简介下载安装验证安装 简介 Git 是一个分布式版本控制系统,用于跟踪和管理软件开发项目的变化。它可以有效地记录文件的修改历史、协调多人协作开发、解决代码冲突,并提供了分支管理、版本回滚等功能,使团队能够更好地合作开发软件项目。…

Android实现超出固定行数折叠文字“查看全文“、“收起全文“

先上效果图 分析问题 网上有很多关于这个的代码,实现都过于复杂了,github上甚至还看到一篇文章600多行代码,结果一跑起来全是bug。还是自己写吧!!! 如果我们需要换行的"查看全文"、"收起全…

8.14 作业 ARM

.text .globl _gcd_gcd:mov r0,#9mov r1,#15cmp r0,r1 比较r0和r1寄存器中的值beq stopsubhi r0,r0,r1subcc r1,r1,r0stop:b stop .end用for循环实现1~100之间和: .text .globl _start_start:mov r0,#0 总和mov r1,#1 从1开始mov r2,#100 到100结束bl add_loopa…

安装elasticsearch

一、docker安装elasticsearch 1、下载镜像 docker pull elasticsearch:6.5.4 2、启动容器 docker run -p 9200:9200 -p 9300:9300 --name elasticsearch \ -e "discovery.typesingle-node" \ -e "cluster.nameelasticsearch" \ -e "ES_JAVA_OPTS-Xm…

软件测试基础篇——Docker

1、docker技术概述 docker描述:docker是一项虚拟化的容器技术(类似于虚拟机),docker技术给使用者提供一个平台,在该平台上可以利用提供的容器,对每一个应用程序进行单独的封装隔离,每一个应用程…

IC人必看| 模拟IC方向面试常考问题及答案汇总(二)

有不少小伙伴说还想要更多模拟IC方向的面试题目,这不就来了!(文末可领全部面试题目) 1. Bandgap 里有几种反馈?原理是? 正反馈和负反馈。 2. 负反馈种类?负反馈的优点? 种类&am…

【深度学习】【风格迁移】Zero-shot Image-to-Image Translation

论文:https://arxiv.org/abs/2302.03027 代码:https://github.com/pix2pixzero/pix2pix-zero/tree/main 文章目录 Abstract1. Introduction相关工作3. Method Abstract 大规模文本到图像生成模型展示了它们合成多样且高质量图像的显著能力。然而&#x…

代码质量检查工具SonarQube

Devops流水线之SonarQube 文章目录 Devops流水线之SonarQube1. 软件功能介绍及用途2. 软件环境搭建与使用2.1 使用方法2.2 SonarQube相关属性说明2.3 Sonar配置文件内容说明 3. 使用环节4. 检查方法 1. 软件功能介绍及用途 SonarQube是一个用于代码质量管理的开源平台&#xf…

网络安全进阶学习第十五课——Oracle SQL注入

文章目录 一、Oracle数据库介绍二、Oracle和MySQL的语法差异:三、Oracle的数据库结构四、Oracle的重点系统表五、Oracle权限分类1、系统权限2、实体权限3、管理角色 六、oracle常用信息查询方法七、联合查询注入1、order by 猜字段数量2、查数据库版本和用户名3、查…

项目知识点记录

1.使用druid连接池 使用properties配置文件: driverClassName com.mysql.cj.jdbc.Driver url jdbc:mysql://localhost:3306/book?useSSLtrue&setUnicodetrue&charsetEncodingUTF-8&serverTimezoneGMT%2B8 username root password 123456 #初始化链接数…

Syncfusion Essential Edit for WPF Crack

Syncfusion Essential Edit for WPF Crack 在任何WPF应用程序中启用语法高亮显示。 Syncfusion Essential Edit for WPF是一款具有所有基本功能的编辑器,如文本编辑、剪切、复制和粘贴。它允许用户从各种文件格式打开文件并将其保存为各种文件格式。Syncfusion Esse…

Streamlit项目: 轻松搭建部署个人博客网站

文章目录 1 前言1.1 探索 Streamlit:轻松创建交互式应用1.2 最全 Streamlit 教程专栏 2 我的个人博客网站已上线!2.1 一个集成了智能中医舌诊-中e诊专栏的博客网站2.2 前期准备2.3 使用 Streamlit Cloud 运行 3 知识点讲解3.1 实现多页面:两种…