2021年2月24日 Go生态洞察:Contexts和Structs的深度解析

news2024/11/26 23:32:55

🌷🍁 博主猫头虎(🐅🐾)带您 Go to New World✨🍁

🦄 博客首页——🐅🐾猫头虎的博客🎐
🐳 《面试题大全专栏》 🦕 文章图文并茂🦖生动形象🐅简单易学!欢迎大家来踩踩~🌺
🌊 《IDEA开发秘籍专栏》 🐾 学会IDEA常用操作,工作效率翻倍~💐
🌊 《100天精通Golang(基础入门篇)》 🐅 学会Golang语言,畅玩云原生,走遍大小厂~💐

🐅🐾猫头虎建议Go程序员必备技术栈一览表📖:

☁️🐳 Go语言开发者必备技术栈☸️:
🐹 GoLang | 🌿 Git | 🐳 Docker | ☸️ Kubernetes | 🔧 CI/CD | ✅ Testing | 💾 SQL/NoSQL | 📡 gRPC | ☁️ Cloud | 📊 Prometheus | 📚 ELK Stack


🪁🍁 希望本文能够给您带来一定的帮助🌸文章粗浅,敬请批评指正!🐅🐾🍁🐥


在这里插入图片描述

文章目录

    • 🐅🐾猫头虎建议Go程序员必备技术栈一览表📖:
  • 2021年2月24日 Go生态洞察:Contexts和Structs的深度解析 🌍
    • 摘要
    • 引言
    • 正文内容
      • 优先将contexts作为参数传递 📌
      • 在结构体中存储context会导致混淆 🚫
      • 规则的例外:保持向后兼容性
    • 总结
  • 原创声明

  • 原创作者: 猫头虎

  • 作者wx: Libin9iOak

  • 作者公众号: 猫头虎技术团队

在这里插入图片描述

2021年2月24日 Go生态洞察:Contexts和Structs的深度解析 🌍

摘要

🐆 猫头虎博主在此!今天我们要深入探讨Go语言中的一个重要话题:Contexts和Structs。这篇文章将深入探讨context.Context的正确使用方法,特别是在API设计中如何合理地运用。对于那些在互联网深处搜索“Go语言最佳实践”、“Contexts使用指南”或者“高效API设计”等词条的开发者们,这篇文章将是你的福音!

引言

在许多现代Go API中,函数和方法的第一个参数经常是context.Context。Context提供了一种在API边界和进程间传递截止日期、调用者取消以及其他请求范围值的手段。当库直接或间接地与远程服务器(如数据库、API等)交互时,通常会使用它。

根据Context的官方文档,建议不要在结构体类型中存储Context,而应将其传递给每个需要它的函数。本文将详细解释这一建议的原因,并提供例子说明为什么将Context作为参数传递比存储在其他类型中更为重要。

正文内容

优先将contexts作为参数传递 📌

要理解为什么不在结构体中存储context,让我们考虑首选的context-as-argument方法:

// Worker从远程作业编排服务器获取并添加作业。
type Worker struct { /* … */ }

type Work struct { /* … */ }

func New() *Worker {
  return &Worker{}
}

func (w *Worker) Fetch(ctx context.Context) (*Work, error) {
  _ = ctx // A per-call ctx is used for cancellation, deadlines, and metadata.
}

func (w *Worker) Process(ctx context.Context, work *Work) error {
  _ = ctx // A per-call ctx is used for cancellation, deadlines, and metadata.
}

这里,(*Worker).Fetch(*Worker).Process方法都直接接受一个context。通过这种传递参数的设计,用户可以设置每次调用的截止日期、取消和元数据。并且,传递给每个方法的context.Context的用途非常清晰:不期望一个方法中使用的context.Context会被其他方法使用。这是因为context的范围尽可能地缩小到所需的操作,这极大地提高了该包中context的实用性和清晰度。

在结构体中存储context会导致混淆 🚫

再次检查上面的Worker示例,但这次使用不推荐的context-in-struct方法。当你在结构体中存储context时,问题在于你将生命周期对调用者隐藏起来,或者更糟糕的是,以不可预测的方式将两个作用域混合在一起:

type Worker struct {
  ctx context.Context
}

func New(ctx context.Context) *Worker {
  return &Worker{ctx: ctx}
}

func (w *Worker) Fetch() (*Work, error) {
  _ = w.ctx // A shared w.ctx is used for cancellation, deadlines, and metadata.
}

func (w *Worker) Process(work *Work) error {
  _ = w.ctx // A shared w.ctx is used for cancellation, deadlines, and metadata.
}

(*Worker).Fetch(*Worker).Process方法都使用存储在Worker中的context。这阻止了Fetch和Process的调用者(可能本身具有不同的contexts)为每次调用指定截止日期、请求取消和附加元数据。例如:用户无法仅为(*Worker).Fetch设置截止日期,或仅取消`(*Worker

).Process调用。调用者的生命周期与共享的context交织在一起,而context的范围限定在创建Worker`的生命周期内。

与传递参数方法相比,这种API对用户来说也更加令人困惑。用户可能会问自己:

  • 既然New接受一个context.Context,那么构造函数是否正在执行需要取消或有截止日期的工作?
  • 传递给Newcontext.Context是否适用于(*Worker).Fetch(*Worker).Process中的工作?都不是?一个而不是另一个?

API需要大量文档明确告诉用户context.Context的确切用途。用户可能还需要阅读代码,而不是依赖于API结构所传达的内容。

最后,设计一个每个请求都没有context、因此无法充分尊重取消请求的生产级服务器可能相当危险。如果没有设置每次调用的截止日期,你的进程可能会积压并耗尽其资源(如内存)!

规则的例外:保持向后兼容性

当Go 1.7(引入了context.Context)发布时,大量API不得不以向后兼容的方式添加context支持。例如,net/httpClient方法,如GetDo,是context的理想候选者。使用这些方法发送的每个外部请求都将受益于随context.Context而来的截止日期、取消和元数据支持。

为了以向后兼容的方式支持context.Context,有两种方法:在结构体中包含context(如我们马上会看到的),以及复制函数,其中复制的函数接受context.Context并在其函数名称后缀中带有Context。应优先选择复制函数方法而不是context-in-struct方法,这在Keeping your modules compatible中有进一步讨论。然而,在某些情况下,这可能是不切实际的:例如,如果你的API暴露了大量函数,那么全部复制它们可能是不可行的。

net/http包选择了context-in-struct方法,这提供了一个有用的案例研究。让我们看看net/httpDo。在引入context.Context之前,Do的定义如下:

// Do发送一个HTTP请求并返回一个HTTP响应[...]
func (c *Client) Do(req *Request) (*Response, error)

在Go 1.7之后,如果不是为了保持向后兼容性,Do可能看起来如下所示:

// Do发送一个HTTP请求并返回一个HTTP响应[...]
func (c *Client) Do(ctx context.Context, req *Request) (*Response, error)

但是,保持标准库的向后兼容性并遵守Go 1兼容性承诺至关重要。因此,维护者选择在http.Request结构体中添加context.Context,以支持context.Context而不破坏向后兼容性:

// Request代表一个由服务器接收或客户端发送的HTTP请求。
// ...
type Request struct {
  ctx context.Context

  // ...
}

// NewRequestWithContext返回一个新的Request,给定方法、URL和可选的
// body。
// [...]
// 给定的ctx用于Request的生命周期。
func NewRequestWithContext(ctx context.Context, method, url string, body io.Reader) (*Request, error) {
  // 为了本文的简洁性进行了简化。
  return &Request{
    ctx: ctx,
    // ...
  }
}

// Do发送一个HTTP

总结

使用context时,我们应该将其作为方法的第一个参数传递,而不是存储在struct类型中。这样,用户

可以充分利用它的扩展性,通过调用栈构建一个强大的取消、截止和元数据信息树。并且,当它作为参数传入时,它的作用域是清晰可见的,这导致了整个栈的清晰理解和可调试性。

知识要点总结表格:

关键点描述
Context作为参数提高了可读性和灵活性
避免在Structs中存储Context防止生命周期和作用域混淆
向后兼容性在必要时,可以在struct中添加Context

本文被猫头虎的Go生态洞察专栏收录,详情点击这里。

下一篇预告:
下次我们将探讨2020年Go开发者调查的结果,深入了解Go社区的趋势和见解!🚀📊

在这里插入图片描述

原创声明

======= ·

  • 原创作者: 猫头虎

  • 作者wx: Libin9iOak
    在这里插入图片描述

  • 作者公众号: 猫头虎技术团队

在这里插入图片描述

学习复习Go生态

本文为原创文章,版权归作者所有。未经许可,禁止转载、复制或引用。

作者保证信息真实可靠,但不对准确性和完整性承担责任

未经许可,禁止商业用途。

如有疑问或建议,请联系作者。

感谢您的支持与尊重。

点击下方名片,加入IT技术核心学习团队。一起探索科技的未来,洞察Go生态,共同成长。

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

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

相关文章

数字图像处理(实践篇)十三 数据增强之给图像添加噪声!

目录 一 涉及的函数 二 实践 一 涉及的函数 skimage.util.random_noise( ) skimage.util.random_noise(image, modegaussian, seedNone, clipTrue, **kwargs) 函数的功能:为浮点型图片添加各种随机噪声。 输入: ①image:输入图像&…

数据结构第五课 -----二叉树的代码实现

作者前言 🎂 ✨✨✨✨✨✨🍧🍧🍧🍧🍧🍧🍧🎂 ​🎂 作者介绍: 🎂🎂 🎂 🎉🎉&#x1f389…

代码混淆的原理和方法详解

代码混淆的原理和方法详解摘要移动App的广泛使用带来了安全隐患,为了保护个人信息和数据安全,开发人员通常会采用代码混淆技术。本文将详细介绍代码混淆的原理和方法,并探讨其在移动应用开发中的重要性。 引言随着移动应用的普及,…

卷轴模式:金融领域的新趋势

卷轴模式在金融领域逐渐崭露头角,成为一种新型的投资策略。这种模式基于完成任务或达成特定目标来获取积分,利用这些积分进行投资或获取现实物品。它不同于传统的资金盘,而是以一种更稳健的方式运作,避免了资金盘的风险。 一、卷轴…

用BootLoader更新S32K144的固件

1、工具:MDK及S32K144的支持包 创芯科技的USB转CAN 及 驱动 Jlink烧写器及驱动 链接:https://pan.baidu.com/s/1jGRdGVEzrO86CpP5UQ2fYQ 提取码:nihd IAP固件升级上位机 2、BootLoader底层文件 a、用MDK打开BootLoader工程 b、更改配置…

《golang设计模式》第三部分·行为型模式-09-策略模式(Strategy)

文章目录 1. 概述1.1 作用1.1 角色1.2 类图 2. 代码示例2.1 设计2.2 代码2.3 类图 1. 概述 1.1 作用 策略(Strategy)是用于封装一组算法中单个算法的对象,这些策略可以相互替换,使得单个算法的变化不影响使用它的客户端。 1.1 …

C# API 文档自动生成器

文章目录 前言SandcastleDocFX 前言 最近要和别人交际,就要给API文档,但是感觉API文件手动写有点麻烦,想着怎么弄一个自动API文档生成 Sandcastle 折腾了两小时,好像不太好用 微软开源全新的文档生成工具DocFX DocFX 既然不好…

私域流量灵魂三问

私域流量灵魂三问: 1、是什么? 2、为什么做? 3、怎么做?

嵌入式LCD软件驱动流程与调试

前言 LCD屏在嵌入式中是应用比较广泛的,很多电子产品都是需要用到它来进行人机交互,那么工作中我们就少不了要对lcd进行软件驱动和调试。调试LCD需要掌握的学问有很多,没有一定的调试经验就很难对问题进行分析并解决。 博文推荐 LCD屏接口与…

网络协议系列:TCP三次握手,四次挥手的全过程,为什么需要三次握手,四次挥手

TCP三次握手,四次挥手的全过程,为什么需要三次握手,四次挥手 一. TCP三次握手,四次挥手的全过程,为什么需要三次握手,四次挥手前言TCP协议的介绍三次握手三次握手流程:1. A 的 TCP 向 B 发送 连…

LVS-NAT实验

实验前准备: LVS负载调度器:ens33:192.168.20.11 ens34:192.168.188.3 Web1节点服务器1:192.168.20.12 Web2节点服务器2:192.168.20.13 NFS服务器:192.168.20.14 客户端(win11…

速速报名!请查收 2023 龙蜥操作系统大会超全指南

亲爱的小伙伴们,大家好!我是大家的老朋友小龙!自 2023 龙蜥操作系统大会宣布启动以来,小龙收到了来自四面八方的诸多期待和小心心。首届龙蜥大会正如火如荼地进行中,为表示对关注社区的每一位小伙伴由衷的感谢&#xf…

排序算法基本原理及实现1

📑打牌 : da pai ge的个人主页 🌤️个人专栏 : da pai ge的博客专栏 ☁️宝剑锋从磨砺出,梅花香自苦寒来 📑插入排序 &#x1f4…

人工智能即将彻底改变你使用计算机的方式

文章目录 每个人的私人助理“Clippy 是一个机器人,而不是特工。”卫生保健“一半需要心理健康护理的美国退伍军人没有得到治疗。”教育生产率娱乐和购物科技行业的冲击波技术挑战隐私和其他重大问题 今天我仍然像保罗艾伦和我创办微软时一样热爱软件。但是&#xff…

Linux系统之一次性计划任务at命令的基本使用

Linux系统之一次性计划任务at命令的基本使用 一、at命令介绍二、at命令的使用帮助2.1 at命令的help帮助信息2.2 at命令的语法解释 三、at命令的日常使用3.1 立即执行一次性任务3.2 指定时间执行一次性任务3.3 查询计划任务3.4 其他指定时间用法3.5 删除已经设置的计划任务3.6 显…

C语言——计算Fibonacci数列

方式一 for循环 (20位) #define _CRT_SECURE_NO_WARNINGS 1#include<stdio.h> int main() {int n;int a[20]{1,1};for ( n 1; n <20; n){a[n]a[n-2]a[n-1];}for ( n 0; n < 20; n){if(n%50)printf("\n");printf("%12d ",a[n]);}return 0; …

【每日一题】无限集中的最小数字

文章目录 Tag题目来源题目解读解题思路方法一&#xff1a;有限集合方法二&#xff1a;有序集合 写在最后 Tag 【有序集合】【2023-11-29】 题目来源 2336. 无限集中的最小数字 题目解读 设计一个类实现移除无限集中的最小整数以及向该无限集中增加一个原集合中不存在的整数。…

【JavaEE初阶】 HTTP响应报文

文章目录 &#x1f332;序言&#x1f38d;200 OK&#x1f340;404 Not Found&#x1f384;403 Forbidden&#x1f334;405 Method Not Allowed&#x1f38b;500 Internal Server Error&#x1f333;504 Gateway Timeout&#x1f332;302 Move temporarily&#x1f38d;301 Move…

vs2012切换版本后WebUI项目加载失败,右键重新加载提示已经在解决方案中打开了具有该名称的项目

问题描述 vs2012切换版本后&#xff0c;回到主版本web项目加载失败&#xff0c;右键重新加载提示已经在解决方案中打开了具有该名称的项目 解决办法 打开根目录.csproj文件&#xff0c;找到第一个ProectTypeGuides节点&#xff0c;删除掉。然后关闭vs,重新打开此项目即可。这…

rust-flexi_logger

flexi_logger 是字节开源的rust日志库。目前有log4rs、env_log 等库&#xff0c;综合比较下来&#xff0c;还是flexi_logger简单容易上手&#xff0c;而且自定义很方便&#xff0c;以及在效率方面感觉也会高&#xff0c;下篇文章我们来测试下。 下面来看下怎么使用 关注 vx gol…