hello,大家好呀,我是既写 Java 又写 Go 的小楼,在写 Go 的过程中经常对比这两种语言的特性,踩了不少坑,也发现了不少有意思的地方,今天就来聊聊 Go 自带的 HttpClient 的超时机制。
在介绍 Go 的 HttpClient 超时机制之前,我们先看看 Java 是如何实现超时的。
写一个 Java 原生的 HttpClient,设置连接超时、读取超时时间分别对应到底层的方法分别是:
再追溯到 JVM 源码,发现是对系统调用的封装,其实不光是 Java,大部分的编程语言都借助了操作系统提供的超时能力。
然而 Go 的 HttpClient 却提供了另一种超时机制,挺有意思,我们来盘一盘。但在开始之前,我们先了解一下 Go 的 Context。
根据 Go 源码的注释:
// A Context carries a deadline, a cancellation signal, and other values across
// API boundaries.
// Context’s methods may be called by multiple goroutines simultaneously.
Context 简单来说是一个可以携带超时时间、取消信号和其他数据的接口,Context 的方法会被多个协程同时调用。
Context 有点类似 Java 的ThreadLocal,可以在线程中传递数据,但又不完全相同,它是显示传递,ThreadLocal 是隐式传递,除了传递数据之外,Context 还能携带超时时间、取消信号。
Context 只是定义了接口,具体的实现在 Go 中提供了几个:
针对 Context 的三个特性,可以通过 Go 提供的 Context 实现以及源码中的例子来进一步了解下。
这部分的例子来源于 Go 的源码,位于 src/context/example_test.go
使用 context.WithValue
来携带,使用 Value
来取值,源码中的例子如下:
// 来自 src/context/example_test.go
func ExampleWithValue() {
type favContextKey string
f := func(ctx context.Context, k favContextKey) {
if v := ctx.Value(k); v != nil {
fmt.Println("found value:", v)
return
}
fmt.Println("key not found:", k)
}
k := favContextKey("language")
ctx := context.WithValue(context.Background(), k, "Go")
f(ctx, k)
f(ctx, favContextKey("color"))
// Output:
// found value: Go
// key not found: color
}
先起一个协程执行一个死循环,不停地往 channel 中写数据,同时监听 ctx.Done()
的事件
// 来自 src/context/example_test.go
gen := func(ctx context.Context) <-chan int {
dst := make(chan int)
n := 1
go func() {
for {
select {
<