튜토리얼 칼럼에서 시간 관리가 필요한 친구들에게 도움이 되기를 바랍니다.
타임아웃 제어가 왜 필요한가요?
요청 시간이 너무 길면 사용자가 이 페이지를 떠났을 수 있으며 서버는 여전히 처리에 리소스를 소비하고 있으며 얻은 결과는 의미가 없습니다.
서버에서 너무 오랫동안 처리하면 너무 많은 리소스를 차지하게 됩니다. , 이로 인해 동시성이 감소합니다. 심지어 비가용 사고가 발생합니다- Go 시간 초과 제어의 필요성
Go는 일반적으로 백엔드 서비스를 작성하는 데 사용됩니다. 일반적으로 요청은 여러 개의 직렬 또는 병렬 하위 작업으로 완료됩니다. 또 다른 내부 요청일 수 있으므로 이 요청 시간이 초과되면 신속하게 반환하여 고루틴, 파일 설명자 등과 같이 점유된 리소스를 해제해야 합니다.
서버 측 공통 시간 제한 제어
프로세스 내 논리적 처리
HTTP 또는 RPC 요청과 같은 클라이언트 요청 읽기 및 쓰기- RPC 호출 또는 DB 액세스를 포함한 다른 서버 요청 호출, etc.
- 타임아웃 제어가 없으면 어떻게 될까요?
이 기사를 단순화하기 위해 hardWork
요청 기능을 예로 들어 보겠습니다. 이름에서 알 수 있듯이 처리 속도가 느려질 수 있습니다. func hardWork(job interface{}) error {
time.Sleep(time.Minute)
return nil}func requestWork(ctx context.Context, job interface{}) error {
return hardWork(job)}
이때 클라이언트는 항상 익숙한 화면을 보게 됩니다
hardWork
为例,用来做啥的不重要,顾名思义,可能处理起来比较慢。
func requestWork(ctx context.Context, job interface{}) error { ctx, cancel := context.WithTimeout(ctx, time.Second*2) defer cancel() done := make(chan error) go func() { done <p>这时客户端看到的就一直是大家熟悉的画面</p><p><img src="/static/imghwm/default1.png" data-src="https://img.php.cn/upload/article/000/000/020/ea4492ddee56842b8d2dd9e34d636408-1.jpg?x-oss-process=image/resize,p_40" class="lazy" alt="Go 시간 초과 제어 구현 방법에 대한 자세한 설명"></p><p>绝大部分用户都不会看一分钟菊花,早早弃你而去,空留了整个调用链路上一堆资源的占用,本文不究其它细节,只聚焦超时实现。</p><p>下面我们看看该怎么来实现超时,其中会有哪些坑。</p><h2> <span class="header-link octicon octicon-link"></span>第一版实现</h2><p>大家可以先不往下看,自己试着想想该怎么实现这个函数的超时,第一次尝试:</p><pre class="brush:php;toolbar:false">func main() { const total = 1000 var wg sync.WaitGroup wg.Add(total) now := time.Now() for i := 0; i <p>我们写个 main 函数测试一下</p><pre class="brush:php;toolbar:false">➜ go run timeout.go elapsed: 2.005725931s
跑一下试试效果
time.Sleep(time.Minute*2)fmt.Println("number of goroutines:", runtime.NumGoroutine())
超时已经生效。但这样就搞定了吗?
goroutine 泄露
让我们在main函数末尾加一行代码看看执行完有多少goroutine
➜ go run timeout.go elapsed: 2.005725931s number of goroutines: 1001
sleep 2分钟是为了等待所有任务结束,然后我们打印一下当前goroutine数量。让我们执行一下看看结果
done := make(chan error, 1)
goroutine泄露了,让我们看看为啥会这样呢?首先,requestWork
函数在2秒钟超时后就退出了,一旦 requestWork
函数退出,那么 done channel
就没有goroutine接收了,等到执行 done 这行代码的时候就会一直卡着写不进去,导致每个超时的请求都会一直占用掉一个goroutine,这是一个很大的bug,等到资源耗尽的时候整个服务就失去响应了。
那么怎么fix呢?其实也很简单,只要 make chan
的时候把 buffer size
设为1,如下:
➜ go run timeout.go elapsed: 2.005655146s number of goroutines: 1
这样就可以让 done 不管在是否超时都能写入而不卡住goroutine。此时可能有人会问如果这时写入一个已经没goroutine接收的channel会不会有问题,在Go里面channel不像我们常见的文件描述符一样,不是必须关闭的,只是个对象而已,<code>close(channel)
只是用来告诉接收者没有东西要写了,没有其它用途。
改完这一行代码我们再测试一遍:
panic("oops")
goroutine泄露问题解决了!
panic 无法捕获
让我们把 hardWork
函数实现改成
go func() { defer func() { if p := recover(); p != nil { fmt.Println("oops, panic") } }() defer wg.Done() requestWork(context.Background(), "any")}()
修改 main
函数加上捕获异常的代码如下:
func requestWork(ctx context.Context, job interface{}) error { ctx, cancel := context.WithTimeout(ctx, time.Second*2) defer cancel() done := make(chan error, 1) panicChan := make(chan interface{}, 1) go func() { defer func() { if p := recover(); p != nil { panicChan <p>此时执行一下就会发现panic是无法被捕获的,原因是因为在 <code>requestWork</code> 内部起的goroutine里产生的panic其它goroutine无法捕获。</p><p>解决方法是在 <code>requestWork</code> 里加上 <code>panicChan</code> 来处理,同样,需要 <code>panicChan</code> 的 <code>buffer size</code></p>대부분의 사용자는 국화를 잠시도 보지 않고 일찍 포기할 것이며 전체 통화 링크에 많은 리소스를 차지할 것입니다. 이 기사에서는 다른 세부 사항을 다루지 않을 것입니다. , 포커스 시간 초과 구현만 가능합니다. 🎜🎜타임아웃을 구현하는 방법과 어떤 함정이 있는지 살펴보겠습니다. 🎜🎜🎜🎜첫 번째 버전 구현🎜🎜아래 내용을 읽지 말고 이 함수의 시간 초과를 구현하는 방법에 대해 생각해 보세요. 먼저 시도해 보세요. 🎜<pre class="brush:php;toolbar:false">ctx, cancel := contextx.ShrinkDeadline(ctx, time.Second*2)🎜테스트할 주요 함수를 작성해 봅시다🎜
package mainimport ( "context" "fmt" "runtime" "sync" "time" "github.com/tal-tech/go-zero/core/contextx")func hardWork(job interface{}) error { time.Sleep(time.Second * 10) return nil}func requestWork(ctx context.Context, job interface{}) error { ctx, cancel := contextx.ShrinkDeadline(ctx, time.Second*2) defer cancel() done := make(chan error, 1) panicChan := make(chan interface{}, 1) go func() { defer func() { if p := recover(); p != nil { panicChan 🎜실행해 보세요. 🎜rrreee🎜타임아웃이 적용되었습니다. 그런데 이게 다 됐나요? 🎜🎜🎜🎜고루틴 유출🎜🎜주 함수 끝에 코드 한 줄을 추가하여 얼마나 많은 고루틴이 실행되었는지 확인하겠습니다.🎜rrreee🎜잠자기 2분은 모든 작업이 끝날 때까지 기다린 후 다음을 인쇄합니다. 현재 고루틴의 수. 실행해서 결과를 볼까요🎜rrreee🎜고루틴이 유출됐는데 왜 이런 일이 일어나는지 살펴볼까요? 우선, <code>requestWork</code> 함수는 2초의 시간 초과 후에 종료됩니다. <code>requestWork</code> 함수가 종료되면 <code>done 채널</code>에는 수신할 고루틴이 없습니다. <code>done 을 실행할 때 이 코드 줄이 멈춰서 작성할 수 없게 되어 각 시간 초과 요청이 고루틴을 차지하게 됩니다. 리소스가 소진되면 전체 서비스가 응답하지 않게 됩니다. 🎜🎜그럼 어떻게 고치나요? 사실, <code>make chan</code>을 할 때 다음과 같이 <code>버퍼 크기</code>를 1로 설정하면 됩니다. 🎜rrreee🎜이 방법으로 <code>done 시간 초과 여부에 관계없이 고루틴을 차단하지 않고 쓸 수 있습니다. 이때 누군가는 고루틴이 없는 채널에 글을 써서 수신하면 문제가 없느냐고 물을 수 있는데, Go에서는 채널이 일반적인 파일 디스크립터와는 달리 닫힐 필요가 없습니다. 단지 객체일 뿐입니다. <code>close(channel)</code>는 수신자에게 쓸 내용이 없으며 다른 목적이 없음을 알리는 데에만 사용됩니다. 🎜🎜이 코드 줄을 변경한 후 다시 테스트해 보겠습니다. 🎜rrreee🎜고루틴 누출 문제가 해결되었습니다! 🎜🎜🎜🎜패닉은 포착할 수 없습니다🎜🎜 <code>hardWork</code> 함수 구현을 🎜rrreee🎜로 변경하겠습니다. <code>main</code> 함수를 수정하고 다음과 같이 예외를 캡처하는 코드를 추가합니다. 🎜rrreee🎜이때 실행해보면 패닉을 캡쳐할 수 없다는 것을 알 수 있는데, 그 이유는 <code>requestWork</code> 내부 고루틴에서 발생한 패닉은 다른 고루틴에서 캡쳐할 수 없기 때문입니다. 🎜🎜해결책은 <code>requestWork</code>에 <code>panicChan</code>을 추가하여 처리하는 것입니다. 마찬가지로 <code>panicChan</code>의 <code>버퍼 크기</code>도 필요합니다. 다음과 같이 1이 됩니다: 🎜<pre class="brush:php;toolbar:false">func requestWork(ctx context.Context, job interface{}) error { ctx, cancel := context.WithTimeout(ctx, time.Second*2) defer cancel() done := make(chan error, 1) panicChan := make(chan interface{}, 1) go func() { defer func() { if p := recover(); p != nil { panicChan <p>改完就可以在 <code>requestWork</code> 的调用方处理 <code>panic</code> 了。</p><h2> <span class="header-link octicon octicon-link"></span>超时时长一定对吗?</h2><p>上面的 <code>requestWork</code> 实现忽略了传入的 <code>ctx</code> 参数,如果 <code>ctx</code> 已有超时设置,我们一定要关注此传入的超时是不是小于这里给的2秒,如果小于,就需要用传入的超时,<code>go-zero/core/contextx</code> 已经提供了方法帮我们一行代码搞定,只需修改如下:</p><pre class="brush:php;toolbar:false">ctx, cancel := contextx.ShrinkDeadline(ctx, time.Second*2)
Data race
这里 requestWork
只是返回了一个 error
参数,如果需要返回多个参数,那么我们就需要注意 data race
,此时可以通过锁来解决,具体实现参考 go-zero/zrpc/internal/serverinterceptors/timeoutinterceptor.go
,这里不做赘述。
完整示例
package mainimport ( "context" "fmt" "runtime" "sync" "time" "github.com/tal-tech/go-zero/core/contextx")func hardWork(job interface{}) error { time.Sleep(time.Second * 10) return nil}func requestWork(ctx context.Context, job interface{}) error { ctx, cancel := contextx.ShrinkDeadline(ctx, time.Second*2) defer cancel() done := make(chan error, 1) panicChan := make(chan interface{}, 1) go func() { defer func() { if p := recover(); p != nil { panicChan <h2> <span class="header-link octicon octicon-link"></span>更多细节</h2><p>请参考 <code>go-zero</code> 源码:</p>
go-zero/core/fx/timeout.go
go-zero/zrpc/internal/clientinterceptors/timeoutinterceptor.go
go-zero/zrpc/internal/serverinterceptors/timeoutinterceptor.go
项目地址
github.com/tal-tech/go-zero
欢迎使用 go-zero
并 star 支持我们!
위 내용은 Go 시간 초과 제어 구현 방법에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

go语言有缩进。在go语言中,缩进直接使用gofmt工具格式化即可(gofmt使用tab进行缩进);gofmt工具会以标准样式的缩进和垂直对齐方式对源代码进行格式化,甚至必要情况下注释也会重新格式化。

闭包(closure)是一个函数以及其捆绑的周边环境状态(lexical environment,词法环境)的引用的组合。 换而言之,闭包让开发者可以从内部函数访问外部函数的作用域。 闭包会随着函数的创建而被同时创建。

本篇文章带大家了解一下golang 的几种常用的基本数据类型,如整型,浮点型,字符,字符串,布尔型等,并介绍了一些常用的类型转换操作。

go语言叫go的原因:想表达这门语言的运行速度、开发速度、学习速度(develop)都像gopher一样快。gopher是一种生活在加拿大的小动物,go的吉祥物就是这个小动物,它的中文名叫做囊地鼠,它们最大的特点就是挖洞速度特别快,当然可能不止是挖洞啦。

是,TiDB采用go语言编写。TiDB是一个分布式NewSQL数据库;它支持水平弹性扩展、ACID事务、标准SQL、MySQL语法和MySQL协议,具有数据强一致的高可用特性。TiDB架构中的PD储存了集群的元信息,如key在哪个TiKV节点;PD还负责集群的负载均衡以及数据分片等。PD通过内嵌etcd来支持数据分布和容错;PD采用go语言编写。

在写 Go 的过程中经常对比这两种语言的特性,踩了不少坑,也发现了不少有意思的地方,下面本篇就来聊聊 Go 自带的 HttpClient 的超时机制,希望对大家有所帮助。


핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

AI Hentai Generator
AI Hentai를 무료로 생성하십시오.

인기 기사

뜨거운 도구

SublimeText3 Linux 새 버전
SublimeText3 Linux 최신 버전

MinGW - Windows용 미니멀리스트 GNU
이 프로젝트는 osdn.net/projects/mingw로 마이그레이션되는 중입니다. 계속해서 그곳에서 우리를 팔로우할 수 있습니다. MinGW: GCC(GNU Compiler Collection)의 기본 Windows 포트로, 기본 Windows 애플리케이션을 구축하기 위한 무료 배포 가능 가져오기 라이브러리 및 헤더 파일로 C99 기능을 지원하는 MSVC 런타임에 대한 확장이 포함되어 있습니다. 모든 MinGW 소프트웨어는 64비트 Windows 플랫폼에서 실행될 수 있습니다.

Eclipse용 SAP NetWeaver 서버 어댑터
Eclipse를 SAP NetWeaver 애플리케이션 서버와 통합합니다.

VSCode Windows 64비트 다운로드
Microsoft에서 출시한 강력한 무료 IDE 편집기

메모장++7.3.1
사용하기 쉬운 무료 코드 편집기
