02 Context的使用

news2024/9/21 14:28:34

对于 HTTP 服务而言,超时往往是造成服务不可用、甚至系统瘫痪的罪魁祸首。

context 标准库设计思路

为了防止雪崩,context 标准库的解决思路是:在整个树形逻辑链条中,用上下文控制器 Context,实现每个节点的信息传递和共享。

具体操作是:用 Context 定时器为整个链条设置超时时间,时间一到,结束事件被触发,链条中正在处理的服务逻辑会监听到,从而结束整个逻辑链条,让后续操作不再进行。
(也就是一个ctx到处传递)

在这里插入图片描述

从图中最后一层的代码 req.ctx = ctx 中看到,每个连接的 Context 最终是放在 request 结构体中的(这也是每个连接的request的ctx赋值的时机)。

所以,每个连接的Context 都是基于 baseContext 复制而来,对应到代码中就是,在为某个连接开启 Goroutine 的时候,为当前连接创建了一个 connContext,这个 connContext 是基于 server 中的 Context 而来,而 server 中 Context 的基础就是 baseContext。

因此,生成最终的 Context 的流程中,net/http 设计了两处可以注入修改的地方,都在 Server 结构里面,一处是 BaseContext,另一处是 ConnContext。

type Server struct {
  ...

    // BaseContext 用来为整个链条创建初始化 Context
    // 如果没有设置的话,默认使用 context.Background()
  BaseContext func(net.Listener) context.Context

    // ConnContext 用来为每个连接封装 Context
    // 参数中的 context.Context 是从 BaseContext 继承来的
  ConnContext func(ctx context.Context, c net.Conn) context.Context
    ...
}

最后,我们回看一下 req.ctx 是否能感知连接异常。
是可以的,因为链条中一个父节点为 CancelContext,其 cancelFunc 存储在代表连接的 conn 结构中,连接异常的时候,会触发这个函数句柄。

总结:标准包中ctx基于baseCtx逐级传递到每个连接中,经过多次的withCancel
、withValue,其中baseCtx和ConnCtx都是可以自己注入的。

封装一个自己的Context

在框架中使用Ctx,除了可以控制超时外,常用的有获取请求、返回结果、实现标准库ctx接口,也都要有。
先看一个不封装自定义ctx的控制器代码:

// 控制器, 接收参数为*http.Request, http.ResponseWriter
func Foo1(request *http.Request, response http.ResponseWriter) {
  obj := map[string]interface{}{
    "data":   nil,
  }
    // 设置控制器 response 的 header 部分
  response.Header().Set("Content-Type", "application/json")

    // 从请求体中获取参数
  foo := request.PostFormValue("foo")
  if foo == "" {
    foo = "10"
  }
  fooInt, err := strconv.Atoi(foo)
  if err != nil {
    response.WriteHeader(500)
    return
  }
    // 构建返回结构
  obj["data"] = fooInt 
  byt, err := json.Marshal(obj)
  if err != nil {
    response.WriteHeader(500)
    return
  }
    // 构建返回状态,输出返回结构
  response.WriteHeader(200)
  response.Write(byt)
  return
}

可以发现代码较为复杂,如果我们将内部代码封装起来,对外暴露高度语义化的接口函数,则框架的易用性会明显提升。比如:

// 控制器, 接收参数为ctx 
func Foo2(ctx *framework.Context) error {
  obj := map[string]interface{}{
    "data":   nil,
  }
    // 从请求体中获取参数
   fooInt := ctx.FormInt("foo", 10)
    // 构建返回结构  
  obj["data"] = fooInt
    // 输出返回结构
  return ctx.Json(http.StatusOK, obj)
}

这样封装性高,也更优雅易读。
这样就要求总的入口函数处handler(也就是core)的ServceHTTP方法内部将http.request和http.ResponseWriter进行封装。

func (c *Core) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	ctx := NewContext(r, w)
	handler := c.router["foo"] // r.URL.path
	if handler == nil {
		return
	}

	handler(ctx)
}

功能点:

  • 获取请求、返回结果
  • 实现标准库的Context接口
  • 为单个请求设置超时

一个简单的foo处理函数:

func FooControllerHandler(ctx *framework.Context) error {
	finish := make(chan struct{}, 1)
	panicChan := make(chan interface{}, 1)

	durationCtx, cancel := context.WithTimeout(ctx.BaseContext(), 1*time.Second)
	defer cancel()

	go func() {
		defer func() {
			if p := recover(); p != nil {
				panicChan <- p
			}
		}()

		time.Sleep(10 * time.Second)
		ctx.Json(200, "ok")
		finish <- struct{}{}
	}()

	select {
	case <-finish:
		fmt.Println("finish")
	case p := <-panicChan:
		ctx.WriterMux().Lock()
		defer ctx.WriterMux().Unlock()
		log.Println(p)
		ctx.Json(500, "panic")
	case <-durationCtx.Done():
		ctx.WriterMux().Lock()
		defer ctx.WriterMux().Unlock()
		ctx.Json(500, "time out")
		ctx.SetHasTimeout()
	}

	return nil
}

这里注意有几点:

  1. Go中每开启一个goroutine,最好使用defer recover()语句来捕获panic异常
  2. 在写入responseWriter时注意加锁
  3. 如果超时后,不允许再写入responseWriter

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

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

相关文章

Package ‘oniguruma‘, required by ‘virtual:world‘, not found

一、操作系统环境 OS版本信息&#xff1a;Rocky Linux 9.1 PHP版本&#xff1a;8.0.26 安装的依赖&#xff1a; dnf -y install libXpm-devel libXext-devel gmp gmp-devel libicu* icu* net-snmp-devel libpng-devel libjpeg-devel freetype-devel libxslt-devel sqlite…

真正意义上的数字零售,最为重要的一点就是要回归零售本身

互联网浪潮的退却并未真正将人们的思维带离互联网的牢笼&#xff0c;相反&#xff0c;越来越多的人依然在用互联网式的眼光看待后互联网时代的事物。尽管这样一种做法可以在一定程度上取得一定的效果&#xff0c;但是&#xff0c;如果仅仅只是用互联网思维来揣度这一切&#xf…

基于虚拟机机的代码保护技术

虚拟机保护技术是基于x86汇编系统的可执行代码转换为字节码指令系统的代码&#xff0c;以达到保护原有指令不被轻易逆向和篡改的目的。 字节码&#xff08;Byte-code&#xff09;是一种包含执行程序&#xff0c;由一序列 op 代码/数据对组成的 &#xff0c;是一种中间码。字节是…

《第一行代码》 第五章:详解广播机制

如果你了解网络通信原理应该会知道&#xff0c;在一个 IP 网络范围中最大的IP 地址是被保留作为广播地址来使用的。比如某个网络的 IP 范围是 192.168.0XXX&#xff0c;子网掩码是255.255.255.0那么这个网络的广播地址就是 192.168.0255广播数据包会被发送到同-网络上的所有端口…

Spring Security OAuth2四种授权模式总结(七)

写在前面&#xff1a;各位看到此博客的小伙伴&#xff0c;如有不对的地方请及时通过私信我或者评论此博客的方式指出&#xff0c;以免误人子弟。多谢&#xff01;如果我的博客对你有帮助&#xff0c;欢迎进行评论✏️✏️、点赞&#x1f44d;&#x1f44d;、收藏⭐️⭐️&#…

【MySQL】MySQL表的增删改查(CRUD)

✨个人主页&#xff1a;bit me&#x1f447; ✨当前专栏&#xff1a;MySQL数据库&#x1f447; ✨算法专栏&#xff1a;算法基础&#x1f447; ✨每日一语&#xff1a;生命久如暗室&#xff0c;不碍朝歌暮诗 目 录&#x1f513;一. CRUD&#x1f512;二. 新增&#xff08;Creat…

将array中元素四舍五入取整的np.rint()方法

【小白从小学Python、C、Java】 【计算机等级考试500强双证书】 【Python-数据分析】 将array中元素四舍五入取整 np.rint()方法 选择题 关于以下python代码说法错误的一项是? import numpy as np a np.array([-1.7, 1.5, -0.2, 0.3]) print("【显示】a\n",a) pr…

BI是报表?BI是可视化?BI到底是什么?

很多企业认为只要买一个前端商业智能BI分析工具就可以解决企业级的商业智能BI所有问题&#xff0c;这个看法实际上也不可行的。可能在最开始分析场景相对简单&#xff0c;对接数据的复杂度不是很高的情况下这类商业智能BI分析工具没有问题。但是在企业的商业智能BI项目建设有一…

数字化系统使用率低的原因剖析

当“数字化变革”成为热门话题&#xff0c;当“数字化转型”作为主题频频出现在一个个大型会议中&#xff0c;我们知道数字化时代的确到来了。但是&#xff0c;根据Gartner的报告我们看到一个矛盾的现象——85%的企业数字化建设与应用并不理想、但对数字化系统的需求多年来持续…

软件测试项目实战(附全套实战项目教程+视频+源码)

开通博客以来&#xff0c;我更新了很多实战项目&#xff0c;但一部分小伙伴在搭建环境时遇到了问题。 于是&#xff0c;我收集了一波高频问题&#xff0c;汇成本篇&#xff0c;供大家参考&#xff0c;避免重复踩坑。 如果你还遇到过其他坑和未解决的问题&#xff0c;可在评论区…

webpack安装步骤(一)

系列文章目录 安装步骤系列文章目录前言一、Webpack是什么&#xff1f;Webpack官网解释解释内容如下图二、Webpack的安装步骤第一步&#xff1a;检查本机是否已经安装过Webpack&#xff08;全局&#xff09;1.操作如下2.结果如下图第二步&#xff1a;安装webpack&#xff08;非…

利用无线通讯技术构建工厂智能化立体仓储

立体仓库主要通过检测、信息识别、控制、通信、监控调度、大屏显示及计算机管理等装置组成。完成仓库各设备连接无线化&#xff0c;可大幅减少网线布防成本&#xff0c;缩短生产线调度时间&#xff0c;实现汽车装配生产线的柔性生产&#xff0c;提高汽车装配生产的自动化水平。…

短视频的素材在哪里找呢?推荐给你一个好办法

我刚刚在视频号做出了30万播放的小爆款&#xff0c;过去3年我做出了很多6位数播放的视频。在这里&#xff0c;我就大家分享20个我常用的素材渠道&#xff0c;其中一些渠道比较小众。除此之外&#xff0c;我也希望同时讲一下短视频的内容生产。为了方便大家浏览&#xff0c;我把…

使用web3连接Georli测试网络

文章目录1.使用geth方式在终端2.写成脚本2.1 通过metamask &#xff08;现成的太复杂&#xff0c;搞不太来&#xff09;2.2 通过自己的接口3.通过truffle方式连接 &#xff08;不成功&#xff09;目前的工作情况是&#xff0c;已在remix写好执行合约并部署在Georli测试网络中&a…

NJ 时钟自动调整功能(SNTP)

NJ 时钟自动调整功能(SNTP) 实验设备&#xff1a;NJ501-1300 实验目的&#xff1a;NJ使用ntp实现时钟自动调整 1. 实验概览 ​ 本次实验通过NJ的ntp功能&#xff0c;将PLC的时钟和阿里的ntp服务器时钟每隔1分钟同步一次。 阿里ntp服务器的域名为&#xff1a;ntp.aliyun.com…

Python fileinput模块:逐行读取多个文件

前面章节中&#xff0c;我们学会了使用 open() 和 read()&#xff08;或者 readline()、readlines() &#xff09;组合&#xff0c;来读取单个文件中的数据。但在某些场景中&#xff0c;可能需要读取多个文件的数据&#xff0c;这种情况下&#xff0c;再使用这个组合&#xff0…

力扣56.合并区间

文章目录力扣56.合并区间题目描述排序合并力扣56.合并区间 题目描述 以数组 intervals 表示若干个区间的集合&#xff0c;其中单个区间为 intervals[i] [starti, endi] 。请你合并所有重叠的区间&#xff0c;并返回 一个不重叠的区间数组&#xff0c;该数组需恰好覆盖输入中…

windows上配置IIS全过程

文章目录1️⃣ 配置IIS1.1 从开始打开服务器管理1.2 添加角色和功能1.3 添加角色和功能向导1.4 按照如下步骤选择2️⃣ 问题&#xff1a;缺少源文件解决方案优质资源分享作者&#xff1a;xcLeigh 文章地址&#xff1a;https://blog.csdn.net/weixin_43151418/article/details/1…

与感受野相关的几种网络结构

一、Inception 1. Inception v1 目的 通过设计一个稀疏网络结构&#xff0c;但是能够产生稠密的数据&#xff0c;既能增加神经网络表现&#xff0c;又能保证计算资源的使用效率。 结构 图1-1 Inception v1结构图 特点 共4个通道&#xff0c;其中3个卷积通道分别使用111111…

HashTable和HashMap的区别详解

一、HashMap简介 HashMap是基于哈希表实现的&#xff0c;每一个元素是一个key-value对&#xff0c;其内部通过单链表解决冲突问题&#xff0c;容量不足&#xff08;超过了阀值&#xff09;时&#xff0c;同样会自动增长。 HashMap是非线程安全的&#xff0c;只是用于单线程环境…