gin
简介
Gin 是一个用 Go (Golang) 编写的 Web 框架
特性
快速
基于 Radix 树的路由,小内存占用。没有反射。可预测的 API 性能。
支持中间件
传入的 HTTP 请求可以由一系列中间件和最终操作来处理。 例如:Logger,Authorization,GZIP,最终操作 DB。
Crash 处理
Gin 可以 catch 一个发生在 HTTP 请求中的 panic 并 recover 它。这样,你的服务器将始终可用。例如,你可以向 Sentry 报告这个 panic!
JSON 验证
Gin 可以解析并验证请求的 JSON,例如检查所需值的存在。
路由组
更好地组织路由。是否需要授权,不同的 API 版本…… 此外,这些组可以无限制地嵌套而不会降低性能。
错误管理
Gin 提供了一种方便的方法来收集 HTTP 请求期间发生的所有错误。最终,中间件可以将它们写入日志文件,数据库并通过网络发送。
内置渲染
Gin 为 JSON,XML 和 HTML 渲染提供了易于使用的 API。
可扩展性
新建一个中间件非常简单
安装
要安装 Gin 软件包,需要先安装 Go 并设置 Go 工作区。
1.下载并安装 gin:
$ go get -u github.com/gin-gonic/gin
或 install
$ go install github.com/gin-gonic/gin@latest
示例
package main
import "github.com/gin-gonic/gin"
func main() {
router := gin.Default()
router.GET("/ping", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "pong",
})
})
router.Run() // 监听并在 0.0.0.0:8080 上启动服务
}
路由存储
在 Gin 框架里,采用前缀树(Trie 树),也就是压缩字段树来存储路由信息。前缀树是一种树形数据结构,它能高效地存储和查找字符串
原理概述
- 节点结构:前缀树的每个节点代表一个字符,从根节点到某个节点的路径上经过的字符连接起来,就构成了一个字符串。
- 压缩机制:为了节省空间和提高查找效率,Gin 对前缀树进行了压缩,将连续的字符压缩成一个节点,而不是每个字符一个节点。
- 路由匹配:在进行路由匹配时,从根节点开始,根据请求的路径依次匹配节点,直到找到匹配的路由或者匹配失败。
示例
假设我们有以下几个路由
/user
/user/info
/user/settings
/article
前缀树结构
下面是对应的前缀树结构:
解释
- 根节点:这是前缀树的起始点,不包含任何字符。
- 第一层节点:有两个节点
user
和article
,分别对应/user
和/article
这两个路由。 - 第二层节点:在
user
节点下,有info
和settings
两个节点,分别对应/user/info
和/user/settings
这两个路由。
代码示例
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func main() {
r := gin.Default()
// 定义 /user 路由
r.GET("/user", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "这是 /user 路由",
})
})
// 定义 /user/info 路由
r.GET("/user/info", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "这是 /user/info 路由",
})
})
// 定义 /user/settings 路由
r.GET("/user/settings", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "这是 /user/settings 路由",
})
})
// 定义 /article 路由
r.GET("/article", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "这是 /article 路由",
})
})
r.Run(":8080")
}
文档
https://gin-gonic.com/zh-cn/docs/
协议
http1.1
package main
import (
"github.com/gin-gonic/gin"
)
func main() {
// 创建一个默认的 Gin 引擎
r := gin.Default()
// 定义一个简单的路由
r.GET("/hello", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "Hello, HTTP 1.1!",
})
})
// 启动服务器,默认使用 HTTP 1.1 协议
r.Run(":8080")
}
http2.0
package main
import (
"github.com/gin-gonic/gin"
)
func main() {
// 创建一个默认的 Gin 引擎
r := gin.Default()
// 定义一个简单的路由
r.GET("/hello", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "Hello, HTTP 2.0!",
})
})
// 启动使用 HTTP 2.0 协议的服务器,需要提供证书和私钥文件
err := r.RunTLS(":8443", "cert.pem", "key.pem")
if err != nil {
panic(err)
}
}
websocket
代码解释
- WebSocket 升级器:
upgrader
用于将 HTTP 连接升级为 WebSocket 连接,CheckOrigin
函数用于允许跨域请求。 - WebSocket 处理函数:
wsHandler
函数将接收到的 HTTP 请求升级为 WebSocket 连接,然后在一个无限循环中读取客户端发送的消息,并发送响应消息。 - 路由注册:在
main
函数中,使用r.GET("/ws", wsHandler)
注册 WebSocket 路由。 - 启动服务器:使用
r.Run(":8080")
启动一个监听在8080
端口的 HTTP 1.1 服务器。
package main
import (
"github.com/gin-gonic/gin"
"github.com/gorilla/websocket"
"log"
"net/http"
)
// 定义一个 WebSocket 升级器
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
CheckOrigin: func(r *http.Request) bool {
return true
},
}
// WebSocket 处理函数
func wsHandler(c *gin.Context) {
// 将 HTTP 连接升级为 WebSocket 连接
conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
if err != nil {
log.Println("WebSocket 升级失败:", err)
return
}
defer conn.Close()
for {
// 读取消息
messageType, message, err := conn.ReadMessage()
if err != nil {
log.Println("读取消息失败:", err)
break
}
// 打印接收到的消息
log.Printf("接收到消息: %s", string(message))
// 发送响应消息
err = conn.WriteMessage(messageType, []byte("已收到消息: "+string(message)))
if err != nil {
log.Println("发送消息失败:", err)
break
}
}
}
func main() {
// 创建一个默认的 Gin 引擎
r := gin.Default()
// 注册 WebSocket 路由
r.GET("/ws", wsHandler)
// 启动服务器,默认使用 HTTP 1.1 协议
r.Run(":8080")
}
中间件
package main
import (
"github.com/gin-gonic/gin"
"log"
"time"
)
// LoggerMiddleware 是一个自定义的中间件,用于记录请求的处理时间
func LoggerMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
// 记录请求开始时间
start := time.Now()
// 打印请求的基本信息
log.Printf("开始处理请求: %s %s", c.Request.Method, c.Request.URL.Path)
// 调用 Next() 方法,将请求传递给后续的处理函数或中间件
c.Next()
// 记录请求结束时间
end := time.Now()
// 计算请求处理时间
elapsed := end.Sub(start)
// 打印请求处理结果和处理时间
log.Printf("请求处理完成: %s %s,状态码: %d,处理时间: %v", c.Request.Method, c.Request.URL.Path, c.Writer.Status(), elapsed)
}
}
func main() {
// 创建一个默认的 Gin 引擎
r := gin.Default()
// 全局使用 LoggerMiddleware 中间件
r.Use(LoggerMiddleware())
// 定义一个简单的路由处理函数
r.GET("/hello", func(c *gin.Context) {
// 模拟一些处理操作
time.Sleep(200 * time.Millisecond)
c.JSON(200, gin.H{
"message": "Hello, World!",
})
})
// 启动服务器
r.Run(":8080")
}
r.use()
后置拦截
在中间件函数中 next()表示
前面我们讲的都是到达我们定义的HTTP处理方法前进行拦截, 其实, 如果在中间件中调用gin.Context的Next()方法, 则可以请求到达并完成业务处理后, 再经过中间件后置拦截处理.
在中间件调用Next()方法, Next()方法之前的代码会在到达请求方法前执行, Next()方法之后的代码则在请求方法处理后执行.
grpc
简介
gRPC 是一个高性能、开源的远程过程调用(RPC)框架,其由Google主要面向移动应用开发并基于HTTP/2协议标准 而设计,基于ProtoBuf(Protocol Buffers)序列化协议开发,且支持众多开发语言。它可以使不同平台和语言之间的服务相互通信。它的优点包括:高效性、跨平台、异步流处理、支持多种语言、安全、易于使用和开源。
Go 语言:gRPC 服务端默认会为每个请求创建一个新的 goroutine 来处理,利用 goroutine 的轻量级特性高效处理并发请求
// greeter.proto
syntax = "proto3";
package greeter;
// 请求消息
message HelloRequest {
string name = 1;
}
// 响应消息
message HelloResponse {
string message = 1;
}
// 服务定义
service Greeter {
// 定义 SayHello 方法
rpc SayHello (HelloRequest) returns (HelloResponse);
}
protoc --go_out=. --go-grpc_out=. greeter.proto
package main
import (
"context"
"log"
"net"
"google.golang.org/grpc"
pb "your_package_path" // 替换为实际生成的包路径
)
// server 实现了 greeter.Greeter 服务
type server struct {
pb.UnimplementedGreeterServer
}
// SayHello 实现了 Greeter 服务的 SayHello 方法
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloResponse, error) {
log.Printf("Received: %v", in.GetName())
return &pb.HelloResponse{Message: "Hello " + in.GetName()}, nil
}
func main() {
lis, err := net.Listen("tcp", ":50051")
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer()
pb.RegisterGreeterServer(s, &server{})
log.Printf("server listening at %v", lis.Addr())
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
package main
import (
"context"
"log"
"os"
"time"
"google.golang.org/grpc"
pb "your_package_path" // 替换为实际生成的包路径
)
func main() {
// 连接到服务端
conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure(), grpc.WithBlock())
if err != nil {
log.Fatalf("did not connect: %v", err)
}
defer conn.Close()
c := pb.NewGreeterClient(conn)
// 创建上下文
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
// 创建请求消息
name := "World"
if len(os.Args) > 1 {
name = os.Args[1]
}
in := &pb.HelloRequest{Name: name}
// 调用服务方法
resp, err := c.SayHello(ctx, in)
if err != nil {
log.Fatalf("could not greet: %v", err)
}
log.Printf("Greeting: %s", resp.GetMessage())
}
gnet
简介
gnet
是一个基于事件驱动的高性能和轻量级网络框架。它直接使用 epoll 和 kqueue 系统调用而非标准 Golang 网络包:net 来构建网络应用,它的工作原理类似两个开源的网络库:netty 和 libuv。
这个项目存在的价值是提供一个在网络包处理方面能和 Redis、Haproxy 这两个项目具有相近性能的 Go 语言网络服务器框架。
gnet
的亮点在于它是一个高性能、轻量级、非阻塞的纯 Go 实现的传输层(TCP/UDP/Unix-Socket)网络框架,开发者可以使用 gnet
来实现自己的应用层网络协议(HTTP、RPC、Redis、WebSocket 等等),从而构建出自己的应用层网络应用:比如在 gnet
上实现 HTTP 协议就可以创建出一个 HTTP 服务器 或者 Web 开发框架,实现 Redis 协议就可以创建出自己的 Redis 服务器等等。
多线程/Go 程网络模型
主从多 Reactors
I/O 事件
gnet
目前支持的 I/O 事件如下:
EventHandler.OnInitComplete
当 server 初始化完成之后调用。EventHandler.OnOpened
当连接被打开的时候调用。EventHandler.OnClosed
当连接被关闭的之后调用。EventHandler.React
当 server 端接收到从 client 端发送来的数据的时候调用。(你的核心业务代码一般是写在这个方法里)EventHandler.Tick
服务器启动的时候会调用一次,之后就以给定的时间间隔定时调用一次,是一个定时器方法。EventHandler.PreWrite
预先写数据方法,在 server 端写数据回 client 端之前调用
github
https://github.com/panjf2000/gnet
引用
gnet: 一个轻量级且高性能的 Go 网络框架_golang gnet-CSDN博客
tars
简介
TARS 是 Linux 基金会的开源项目,它是基于名字服务使用 TARS 协议的高性能 RPC 开发框架,配套一体化的运营管理平台,并通过伸缩调度,实现运维半托管服务。
TARS 是腾讯从 2008 年到今天一直在使用的后台逻辑层的统一应用框架,覆盖腾讯 100 多个产品。目前支持 C++,Java,PHP,Nodejs,Go 语言。该框架为用户提供了涉及到开发、运维、以及测试的一整套解决方案,帮助一个产品或者服务快速开发、部署、测试、上线。 它集可扩展协议编解码、高性能 RPC 通信框架、名字路由与发现、发布监控、日志统计、配置管理等于一体,通过它可以快速用微服务的方式构建自己的稳定可靠的分布式应用,并实现完整有效的服务治理。