10 | 基于 Gin 实现 HTTP 服务器

news2025/3/17 0:19:21

提示:

  • 所有体系课见专栏:Go 项目开发极速入门实战课;
  • 欢迎加入 云原生 AI 实战 星球,12+ 高质量体系课、20+ 高质量实战项目助你在 AI 时代建立技术竞争力(聚焦于 Go、云原生、AI Infra);
  • 本节课最终源码位于 fastgo 项目的 feature/s07 分支;
  • 更详细的课程版本见:Go 项目开发中级实战课:20 | Web 服务器实现:如何基于 Gin 实现一个 HTTP 服务器?

在 Go 项目开发中,开发场景最多的是开发一个 Web 服务器,Web 服务器种类有很多,例如:HTTP 服务器、RPC 服务器、WebSocket 服务器等。

其中,HTTP 服务器是最常需要开发的服务器类型。HTTP 服务器,其实就是一个对外提供 API 接口的 Web 服务器。API 接口其实是有规范的,当前用的最多的 REST 规范。

所以,本节课,就来看下如何快速开发一个 REST API 服务器。

REST Web 框架选择

要编写一个 RESTful 风格的 API 服务器,你可以自己调用 net/http包手动实现一个,但这样耗时而且效果也不好。所以企业开发中,通常会使用 Web 框架来开发一个 REST 服务器。

Web 框架有很多,例如:Gin、Hertz、Echo、Eiber 等。当前使用最多的是 Gin 框架。Gin 框架很轻量,并且具有以下优点:高性能、扩展性强、稳定性强、相对而言比较简洁(查看 性能对比)。关于 Gin 的更多介绍可以参考 Gin 官网。

开发一个简单的 REST 服务器

使用 Gin 框架开发一个 REST 服务器分为以下几步:

  1. 配置 REST 服务器(配置监听端口);
  2. 创建 Gin 引擎;
  3. 设置 Gin 路由;
  4. 启动 Gin 服务器。

步骤 1:配置 REST 服务器

修改 cmd/fg-apiserver/app/options/options.go 文件,给 ServerOptions 结构体添加 Addr 配置项。代码变更如下:

package options

import (
    "fmt"
    "net"
    "strconv"
    ...
)

type ServerOptions struct {
    ...
    Addr         string                       `json:"addr" mapstructure:"addr"`
}

// NewServerOptions 创建带有默认值的 ServerOptions 实例.
func NewServerOptions() *ServerOptions {
    return &ServerOptions{
        ...
        Addr:         "0.0.0.0:6666",
    }
}

// Validate 校验 ServerOptions 中的选项是否合法.
// 提示:Validate 方法中的具体校验逻辑可以由 Claude、DeepSeek、GPT 等 LLM 自动生成。
func (o *ServerOptions) Validate() error {
    ...
    // 验证服务器地址
    if o.Addr == "" {
        return fmt.Errorf("server address cannot be empty")
    }

    // 检查地址格式是否为host:port
    _, portStr, err := net.SplitHostPort(o.Addr)
    if err != nil {
        return fmt.Errorf("invalid server address format '%s': %w", o.Addr, err)
    }

    // 验证端口是否为数字且在有效范围内
    port, err := strconv.Atoi(portStr)
    if err != nil || port < 1 || port > 65535 {
        return fmt.Errorf("invalid server port: %s", portStr)
    }

    return nil
}

// Config 基于 ServerOptions 构建 apiserver.Config.
func (o *ServerOptions) Config() (*apiserver.Config, error) {
    return &apiserver.Config{
        ...
        Addr:         o.Addr,
    }, nil
}

上面的代码给 ServerOptions 结构体添加了 Addr 字段,用来保存 Web 服务器的监听地址,默认地址设置为:0.0.0.0:6666

在 Validate方法中,添加了对 Addr 字段的校验,检查了 Addr字段的格式是否合法,端口的范围是否正确。在实际开发中,你可以根据实际需要添加更多的校验。

在 Config 方法中,需要将应用的 Addr 配置字段赋值给运行时配置。所以还要在运行时配置中添加 Addr 字段。修改 internal/apiserver/server.go 文件中的 Config 结构体添加 Addr 字段:

type Config struct {
    ...
    Addr         string
}

步骤 2:创建 Gin 引擎

修改 internal/apiserver/server.go 文件,在 Server 结构体中新增 *http.Server 类型的字段 srv,并在 NewServer方法中实例化 srv,变更代码如代码清单 10-1 所示:

代码清单 10-1

package apiserver

import (
    ...
    "net/http"
    ...
    "github.com/gin-gonic/gin"
    ...
)
...
// Server 定义一个服务器结构体类型.
type Server struct {
    ...
    srv *http.Server
}

// NewServer 根据配置创建服务器.
func (cfg *Config) NewServer() (*Server, error) {
    // 创建 Gin 引擎
    engine := gin.New()

    // 注册 404 Handler.
    engine.NoRoute(func(c *gin.Context) {
        c.JSON(http.StatusNotFound, gin.H{"code": "PageNotFound", "message": "Page not found."})
    })

    // 注册 /healthz handler.
    engine.GET("/healthz", func(c *gin.Context) {
        c.JSON(http.StatusOK, gin.H{"status": "ok"})
    })

    // 创建 HTTP Server 实例
    httpsrv := &http.Server{Addr: cfg.Addr, Handler: engine}

    return &Server{cfg: cfg, srv: httpsrv}, nil
}

NewServer 方法中,通过 gin.New() 创建了一个 Gin 引擎实例 engine。并通过 engine 中的方法给 engine 设置了 REST 路由和中间件。

代码中的 engine.NoRoute 方法调用,设置了当 Gin 找不到匹配的请求路径时返回的信息:

{"code":"PageNotFound","message":"Page not found."}

NewServer 方法的最后部分,创建了标准库的 http.Server 实例,并将配置好的 Gin 引擎设置为其Handler,随后将配置对象和 HTTP 服务器实例注入到新创建的 Server 结构体中并返回。

步骤 3:设置 Gin 路由

有时候服务器进程起来不代表服务器可以正常对外提供 API,我就曾经就遇到过这种问题:服务器进程存在,但是访问 API 确实失败的。

因此在启动服务器时,最好加一个健康检查接口。可以在健康检查接口中,检查任何我们觉得会影响服务器健康状态的项目。

在代码清单 10-1 中,通过以下方法调用给 engine 实例添加了一条健康检查 HTTP 路由:

    // 注册 /healthz handler.
    engine.GET("/healthz", func(c *gin.Context) {
        c.JSON(http.StatusOK, gin.H{"status": "ok"})
    })

engine 实例提供了 GETPOSTDELETEPATCHPUTOPTIONSHEADAny 方法,来给 engine 实例添加对应的 HTTP 路由。

上述代码添加了一条 HTTP 路由:

  • 请求方法:GET
  • 请求路径:/healthz
  • 请求返回:{"status":"ok"}

步骤 4:启动 Gin 服务器

NewServer 方法中创建了 Server 类型的实例,接下来就可以在 Server 实例的 Run 方法中启动 HTTP 服务器。代码如下:

// Run 运行应用.
func (s *Server) Run() error {
    // 运行 HTTP 服务器
    // 打印一条日志,用来提示 HTTP 服务已经起来,方便排障
    slog.Info("Start to listening the incoming requests on http address", "addr", s.cfg.Addr)
    if err := s.srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
        return err
    }

    return nil
}

上述代码,打印了一条日志,日志中输出了 HTTP 服务器的请求端口。将请求端口打印出来,可以用来帮助开发者了解 HTTP 的启动配置。

s.srv.ListenAndServe() 方法用来启动 HTTP 服务器。当该方法返回错误时,报错并退出。这里要注意,上述代码处理了一个特殊的错误情况:http.ErrServerClosed。当我们主动调用 http.ServerShutdown()Close() 方法时,ListenAndServe() 会返回这个特定的错误(http.ErrServerClosed),这表示服务器是被预期地、主动地关闭,而非意外崩溃。因此,这种情况不应被视作异常或错误,不需要额外处理或返回。

编译并运行

执行以下命令编译并运行:

$ ./build.sh
$ _output/fg-apiserver -c configs/fg-apiserver.yaml

打开一个新的 Linux 终端,执行以下命令测试功能是否正常:

$ curl http://127.0.0.1:6666/nonexist # 请求路径不存在时,返回预期错误
{"code":"PageNotFound","message":"Page not found."}
$ curl http://127.0.0.1:6666/healthz # 请求健康检查接口,返回服务器 ok 信息
{"status":"ok"}

附录: cURL 工具介绍

本节课采用 cURL 工具来测试 RESTful API,标准的 Linux 发行版都安装了 cURL 工具。cURL 可以很方便地完成对 REST API 的调用场景,比如:设置 Header,指定 HTTP 请求方法,指定 HTTP 消息体,指定权限认证信息等。

通过 -v 选项也能输出 REST 请求的所有返回信息。cURL 功能很强大,有很多参数,这里列出 REST 测试常用的参数:

-X/--request [GET|POST|PUT|DELETE|]  指定请求的 HTTP 方法
-H/--header                           指定请求的 HTTP Header
-d/--data                             指定请求的 HTTP 消息体(Body)
-v/--verbose                          输出详细的返回信息
-u/--user                             指定账号、密码
-b/--cookie                           读取 cookie

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

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

相关文章

PyTorch 深度学习实战(14):Deep Deterministic Policy Gradient (DDPG) 算法

在上一篇文章中&#xff0c;我们介绍了 Proximal Policy Optimization (PPO) 算法&#xff0c;并使用它解决了 CartPole 问题。本文将深入探讨 Deep Deterministic Policy Gradient (DDPG) 算法&#xff0c;这是一种用于连续动作空间的强化学习算法。我们将使用 PyTorch 实现 D…

Angular由一个bug说起之十四:SCSS @import 警告与解决⽅案

SCSS import 警告与解决⽅案 ⚠ 警告信息 在 SCSS 中&#xff0c;使⽤ import 可能会产⽣以下警告&#xff1a; Deprecation Warning: Sass import rules are deprecated and will be removed in Dart Sass 3.0.0. ? 为什么会有这个警告&#xff1f; Sass 官⽅已经废弃 imp…

PyTorch系列教程:基于LSTM构建情感分析模型

情感分析是一种强大的自然语言处理&#xff08;NLP&#xff09;技术&#xff0c;用于确定文本背后的情绪基调。它常用于理解客户对产品或服务的意见和反馈。本文将介绍如何使用PyTorch和长短期记忆网络&#xff08;LSTMs&#xff09;创建一个情感分析管道&#xff0c;LSTMs在处…

SEO新手基础优化三步法

内容概要 在网站优化的初始阶段&#xff0c;新手常因缺乏系统性认知而陷入技术细节的误区。本文以“三步法”为核心框架&#xff0c;系统梳理从关键词定位到内容布局、再到外链构建的完整优化链路。通过拆解搜索引擎工作原理&#xff0c;重点阐明基础操作中容易被忽视的底层逻…

Tcp网络通信的基本流程梳理

先来一张经典的流程图 接下介绍一下大概流程&#xff0c;各个函数的参数大家自己去了解加深一下印象 服务端流程 1.创建套接字&#xff1a;使用 socket 函数创建一个套接字&#xff0c;这个套接字后续会被用于监听客户端的连接请求。 需要注意的是&#xff0c;服务端一般有俩…

PHP函数缺陷详解

无问社区-官网&#xff1a;http://www.wwlib.cn 本期无人投稿&#xff0c;欢迎大家投稿&#xff0c;投稿可获得无问社区AI大模型的使用红包哦&#xff01; 无问社区&#xff1a;网安文章沉浸式免费看&#xff01; 无问AI大模型不懂的问题随意问&#xff01; 全网网安资源智…

深度学习GRU模型原理

一、介绍 门控循环单元&#xff08;Gated Recurrent Unit, GRU&#xff09; 是一种改进的循环神经网络&#xff08;RNN&#xff09;&#xff0c;专为解决传统RNN的长期依赖问题&#xff08;梯度消失/爆炸&#xff09;而设计。其核心是通过门控机制动态控制信息的流动。与LSTM相…

网络空间安全(31)安全巡检

一、定义与目的 定义&#xff1a; 安全巡检是指由专业人员或特定部门负责&#xff0c;对各类设施、设备、环境等进行全面或重点检查&#xff0c;及时发现潜在的安全隐患或问题。 目的&#xff1a; 预防事故发生&#xff1a;通过定期的安全巡检&#xff0c;及时发现并解决潜在的…

基于Python+SQLite实现(Web)验室设备管理系统

实验室设备管理系统 应用背景 为方便实验室进行设备管理&#xff0c;某大学拟开发实验室设备管理系统 来管理所有实验室里的各种设备。系统可实现管理员登录&#xff0c;查看现有的所有设备&#xff0c; 增加设备等功能。 开发环境 Mac OSPyCharm IDEPython3Flask&#xff…

面试系列|蚂蚁金服技术面【2】

今天继续分享一下蚂蚁金服的 Java 后端开发岗位真实社招面经&#xff0c;复盘面试过程中踩过的坑&#xff0c;整理面试过程中提到的知识点&#xff0c;希望能给正在准备面试的你一些参考和启发&#xff0c;希望对你有帮助&#xff0c;愿你能够获得心仪的 offer ! 第一轮面试完…

【JavaEE】网络原理之初识

1.❤️❤️前言~&#x1f973;&#x1f389;&#x1f389;&#x1f389; Hello, Hello~ 亲爱的朋友们&#x1f44b;&#x1f44b;&#xff0c;这里是E绵绵呀✍️✍️。 如果你喜欢这篇文章&#xff0c;请别吝啬你的点赞❤️❤️和收藏&#x1f4d6;&#x1f4d6;。如果你对我的…

Vscode工具开发Vue+ts项目时vue文件ts语法报错-红波浪线等

Vscode工具开发Vuets项目时vue文件ts语法报错-红波浪线等 解决方案 问题如题描述&#xff0c;主要原因是开发工具使用的代码检查与项目的中的ts不一致导导致&#xff0c;解决办法&#xff0c;修改 vscode 中&#xff0c; 快捷键&#xff1a;command shift p, 输入&#xff…

ROS实践(五)机器人自动导航(robot_navigation)

目录 一、知识点 1. 定位 2. 路径规划 (1)全局路径规划 (2)局部路径规划 3. 避障 二、常用工具和传感器 三、相关功能包 1. move_base(决策规划) 2. amcl(定位) 3. costmap_2d(代价地图) 4. global_planner(全局规划器) 5. local_planner(局部规划器…

【小沐学Web3D】three.js 加载三维模型(React)

文章目录 1、简介1.1 three.js1.2 react.js 2、three.js React结语 1、简介 1.1 three.js Three.js 是一款 webGL&#xff08;3D绘图标准&#xff09;引擎&#xff0c;可以运行于所有支持 webGL 的浏览器。Three.js 封装了 webGL 底层的 API &#xff0c;为我们提供了高级的…

软考教材重点内容 信息安全工程师 第19章 操作系统安全保护

19.1.1 操作系统安全概念 一般来说&#xff0c;操作系统的安全是指满足安全策略要求&#xff0c;具有相应的安全机制及安全功能&#xff0c;符合特定的安全标准&#xff0c;在一定约束条件下&#xff0c;能够抵御常见的网络安全威胁&#xff0c;保障自身的安全运行及资源安全。…

【机器学习】基于t-SNE的MNIST数据集可视化探索

一、前言 在机器学习和数据科学领域&#xff0c;高维数据的可视化是一个极具挑战但又至关重要的问题。高维数据难以直观地理解和分析&#xff0c;而有效的可视化方法能够帮助我们发现数据中的潜在结构、模式和关系。本文以经典的MNIST手写数字数据集为例&#xff0c;探讨如何利…

【Pycharm】Pycharm无法复制粘贴,提示系统剪贴板不可用

我也没有用vim的插件&#xff0c;检查了本地和ubutnu上都没有。区别是我是远程到ubutnu的pycharm&#xff0c;我本地直接控制windowes的pycharm是没问题的。现象是可以从外部复制到pycharm反之则不行。 ctl c ctlv 以及右键 都不行 参考&#xff1a;Pycharm无法复制粘贴&…

Flink-学习路线

最近想学习一下Flink&#xff0c;公司的实时需求还是不少的&#xff0c;因此结合ai整理了一份学习路线&#xff0c;记录一下。 当然&#xff0c;公司也有Scala版本Flink框架&#xff0c;也学习了一下。这里只说Java版本 1. Java基础 目标: 掌握Java编程语言的基础知识。 内容…

Atcoder ABC397-D 题解

https://atcoder.jp/contests/abc397/tasks/abc397_dhttps://atcoder.jp/contests/abc397/tasks/abc397_d 题目描述&#xff1a; 确定是否存在一对正整数,使得 思路&#xff1a; 首先对方程进行转化 设 即 接下来确定的范围 根据立方差公式 因此&#xff0c;我们可以从到来…

K8S学习之基础二十七:k8s中daemonset控制器

k8s中DaemonSet控制器 ​ DaemonSet控制器确保k8s集群中&#xff0c;所有节点都运行一个相同的pod&#xff0c;当node节点增加时&#xff0c;新节点也会自动创建一个pod&#xff0c;当node节点从集群移除&#xff0c;对应的pod也会自动删除。删除DaemonSet也会删除创建的pod。…