【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 int
   Name 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 User
   found := false
   //类似于数据库的SQL查询
   for _, u := range users {
      if strings.EqualFold(id, strconv.Itoa(u.ID)) {
         user = u
         found = true
         break
      }
   }
   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 = index
         break
      }
   }
   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 = index
         break
      }
   }
   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.B
   return 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 int
   err = 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 rpc
      go 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 int
     err = 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 error
     conn, 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 语言在云原生中发挥了极大的优势。

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

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

相关文章

Hlang社区-前端社区宣传首页实现

文章目录 前言页面结构固定钉头部轮播JS特效完整代码总结前言 这里的话,博主其实也是今年参与考研的大军之一,所以的话,是抽空去完成这个项目的,当然这个项目的肯定是可以在较短的时间内完成的。 那么废话不多说,昨天也是干到1点多,把这个首页写出来了。先看看看效果吧:…

60页数字政府智慧政务大数据资源平台项目可研方案PPT

导读:原文《60页数字政府智慧政务大数据资源平台项目可研方案PPT》(获取来源见文尾),本文精选其中精华及架构部分,逻辑清晰、内容完整,为快速形成售前方案提供参考。 项目需求分析 项目建设原则和基本策略…

Ozon限制售卖品类 速速收藏

每个电商平台都会有自己的规则和政策,都会有一些限制销售的品类,根据不同地域和文化会有不同,本文来介绍一下老牌俄罗斯购物平台Ozon有哪些限制售卖的品类。选品的商家需要明确自己的品类有没有违规,违规商品平台会进行下架和限制…

【操作系统考点汇集】操作系统考点汇集

关于操作系统可能考察的知识点 操作系统基本原理 什么是操作系统? 操作系统是指控制和管理整个计算机系统的硬件和软件资源,并合理地组织调度计算机的工作和资源的分配,以提供给用户和它软件方便的接口和环境,是计算机系统中最基…

数据结构-->栈

💕休对故人思故国,且将新火试新茶,诗酒趁年华💕 作者:Mylvzi 文章主要内容:详解链表OJ题 前言: 前面已经学习过顺序表,链表。他们都是线性表,今天要学习的栈也是一种线…

I2S/PCM board-level 约束及同步(latencyskewbitsync)

I2S/PCM是典型的低速串口,在两个方向上分别有两组信号,我们已soc为视角分为soc-adif和外设audio-codec。 那么adif输入: sclk_i, ws_i, sdi 当然并不是三个输入信号同时有效,只有adif RX slave时,三个输入都会有效…

LeetCode[1122]数组的相对排序

难度:Easy 题目: 给你两个数组,arr1 和 arr2,arr2 中的元素各不相同,arr2 中的每个元素都出现在 arr1 中。 对 arr1 中的元素进行排序,使 arr1 中项的相对顺序和 arr2 中的相对顺序相同。未在 arr2 中出现…

比特币凌晨短线暴跌,17万多头爆仓近10亿美元!原因何在?

凌晨5:30AM左右,加密货币短线暴跌。比特币触及24715美元低点,随后回升至26000美元以上,日内跌幅一度扩大至7%以上。以太坊击穿1500美元,现已回调至1650以上,山寨币也出现集体下跌。 此次下跌使比特币市值自6月16日以来…

手机照片误删怎么办,电脑照片误删怎么办怎么才能找回,EasyRecovery来帮您

手机照片误删怎么办,电脑照片误删怎么办怎么才能找回,EasyRecovery 2023来帮您!!! EasyRecovery 2023是一款操作安全、价格便宜、用户自主操作的 数据恢复 方案,它支持从各种各样的 存储介质 恢复删除 或者…

k8s 自身原理 4

前面咱们分享了 mater 和 worker 节点里面都有哪些组件,他们又是各自主要负责的工作是什么,现在我们心里应该都有数了吧 master 节点: etcd 存储资源配置,ApiServer 提供 RESTful Api 用于交互,scheduler 用于调度 p…

Java IO流(一)IO基础

概述 IO流本质 I/O表示Input/Output,即数据传输过程中的输入/输出,并且输入和输出都是相对于内存来讲Java IO(输入/输出)流是Java用于处理数据读取和写入的关键组件常见的I|O介质包括 文件(输入|输出)网络(输入|输出)键盘(输出)显示器(输出)使用场景 文件拷贝(File&…

Layui列表复选框根据条件禁用

// 禁用客服回访id有值的复选框res.data.forEach(function (item, i) {if (item.feedbackEmpId) {let index res.data[i][LAY_TABLE_INDEX];$(".layui-table tr[data-index"index"] input[typecheckbox]").prop(disabled,true);$(".layui-table tr[d…

谈谈召回率(R值),准确率(P值)及F值

通俗解释机器学习中的召回率、精确率、准确率,一文让你一辈子忘不掉这两个词 赶时间的同学们看这里:提升精确率是为了不错报、提升召回率是为了不漏报 先说个题外话,暴击一下乱写博客的人,网络上很多地方分不清准确率和精确率&am…

SENet网络分析

文章目录 注意力机制:AttentionBiased Competition Theorybottom-up和top-down注意力 SE BlockSqueeze操作Excitation操作scale操作与原结构合并计算复杂度评估 实验与其他网络对比数据集实验内部参数对比实验进一步评估Squeezeexcitation Squuze-and-Excitation网络…

大数据课程I3——Kafka的消息流与索引机制

文章作者邮箱:yugongshiye@sina.cn 地址:广东惠州 ▲ 本章节目的 ⚪ 掌握Kafka的消息流处理; ⚪ 掌握Kafka的索引机制; ⚪ 掌握Kafka的消息系统语义; 一、Kafka消息流处理 1. Producer 写入消息 流程说明: 1. producer 要向Kafka生产消息,需要先通过…

修改el-table行悬停状态的背景颜色

.content:deep().el-table tr:hover>td {background-color: #f5f5f5 !important; /* 设置悬停时的背景颜色 */ }/*这一点很重要,否则可能会导致hover行时操作列还是原来的背景色*/ .content:deep().el-table__body tr.hover-row>td{background-color: #f5f5f5…

【Axure高保真原型】JS日期选择器筛选中继器表格

今天和大家分享JS日期选择器筛选中继器表格的原型模板,通过调用浏览器的日期选择器,所以可以获取真实的日历效果,具体包括哪一年二月份有29天,几号对应星期几,都是真实的,获取日期值后,通过交互…

GraphQL strawberry的使用回顾和体会

GraphQL vs RESTful 简单来说GraphQL 比起 RESTful 集成额外一些功能 出入参校验、序列化 (简化后端编程)自由可选的返回数据字段 (简化一些多余接口开发和沟通联调成本) 这些都是优点了。 开发效率在项目初期是很重要的,需要快速原型化。 但是后期稳定后&#…

使用维纳过滤器消除驾驶舱噪音(Matlab代码实现)

💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…

【NepCTF2023】复现

文章目录 【NepCTF2023】复现MISC与AI共舞的哈夫曼codesc语言获取环境变量 小叮弹钢琴陌生的语言你也喜欢三月七么Ez_BASIC_IImisc参考 WEBez_java_checkinPost Crad For You独步天下配置环境独步天下-镜花水月环境变量提权 独步天下-破除虚妄总结 独步天下-破除试炼_加冕成王知…