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

2023-11-29 19:04

本文主要是介绍2021年2月24日 Go生态洞察:Contexts和Structs的深度解析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!


🌷🍁 博主猫头虎(🐅🐾)带您 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生态,共同成长。

这篇关于2021年2月24日 Go生态洞察:Contexts和Structs的深度解析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/433960

相关文章

Java 正则表达式URL 匹配与源码全解析

《Java正则表达式URL匹配与源码全解析》在Web应用开发中,我们经常需要对URL进行格式验证,今天我们结合Java的Pattern和Matcher类,深入理解正则表达式在实际应用中... 目录1.正则表达式分解:2. 添加域名匹配 (2)3. 添加路径和查询参数匹配 (3) 4. 最终优化版本5.设计思

使用Java将DOCX文档解析为Markdown文档的代码实现

《使用Java将DOCX文档解析为Markdown文档的代码实现》在现代文档处理中,Markdown(MD)因其简洁的语法和良好的可读性,逐渐成为开发者、技术写作者和内容创作者的首选格式,然而,许多文... 目录引言1. 工具和库介绍2. 安装依赖库3. 使用Apache POI解析DOCX文档4. 将解析

Java字符串处理全解析(String、StringBuilder与StringBuffer)

《Java字符串处理全解析(String、StringBuilder与StringBuffer)》:本文主要介绍Java字符串处理全解析(String、StringBuilder与StringBu... 目录Java字符串处理全解析:String、StringBuilder与StringBuffer一、St

Spring Boot循环依赖原理、解决方案与最佳实践(全解析)

《SpringBoot循环依赖原理、解决方案与最佳实践(全解析)》循环依赖指两个或多个Bean相互直接或间接引用,形成闭环依赖关系,:本文主要介绍SpringBoot循环依赖原理、解决方案与最... 目录一、循环依赖的本质与危害1.1 什么是循环依赖?1.2 核心危害二、Spring的三级缓存机制2.1 三

C#中async await异步关键字用法和异步的底层原理全解析

《C#中asyncawait异步关键字用法和异步的底层原理全解析》:本文主要介绍C#中asyncawait异步关键字用法和异步的底层原理全解析,本文给大家介绍的非常详细,对大家的学习或工作具有一... 目录C#异步编程一、异步编程基础二、异步方法的工作原理三、代码示例四、编译后的底层实现五、总结C#异步编程

go 指针接收者和值接收者的区别小结

《go指针接收者和值接收者的区别小结》在Go语言中,值接收者和指针接收者是方法定义中的两种接收者类型,本文主要介绍了go指针接收者和值接收者的区别小结,文中通过示例代码介绍的非常详细,需要的朋友们下... 目录go 指针接收者和值接收者的区别易错点辨析go 指针接收者和值接收者的区别指针接收者和值接收者的

MySQL中FIND_IN_SET函数与INSTR函数用法解析

《MySQL中FIND_IN_SET函数与INSTR函数用法解析》:本文主要介绍MySQL中FIND_IN_SET函数与INSTR函数用法解析,本文通过实例代码给大家介绍的非常详细,感兴趣的朋友一... 目录一、功能定义与语法1、FIND_IN_SET函数2、INSTR函数二、本质区别对比三、实际场景案例分

Go 语言中的select语句详解及工作原理

《Go语言中的select语句详解及工作原理》在Go语言中,select语句是用于处理多个通道(channel)操作的一种控制结构,它类似于switch语句,本文给大家介绍Go语言中的select语... 目录Go 语言中的 select 是做什么的基本功能语法工作原理示例示例 1:监听多个通道示例 2:带

Java图片压缩三种高效压缩方案详细解析

《Java图片压缩三种高效压缩方案详细解析》图片压缩通常涉及减少图片的尺寸缩放、调整图片的质量(针对JPEG、PNG等)、使用特定的算法来减少图片的数据量等,:本文主要介绍Java图片压缩三种高效... 目录一、基于OpenCV的智能尺寸压缩技术亮点:适用场景:二、JPEG质量参数压缩关键技术:压缩效果对比

关于WebSocket协议状态码解析

《关于WebSocket协议状态码解析》:本文主要介绍关于WebSocket协议状态码的使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录WebSocket协议状态码解析1. 引言2. WebSocket协议状态码概述3. WebSocket协议状态码详解3