V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
爱意满满的作品展示区。
winterssy
V2EX  ›  分享创造

reqwest - 简单,易用的 Golang 网络请求库

  •  
  •   winterssy · 2019-09-29 12:56:14 +08:00 · 6589 次点击
    这是一个创建于 1642 天前的主题,其中的信息可能已经有所发展或是发生改变。

    Features:

    • GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, etc.
    • Easy set query params, headers and cookies.
    • Easy send form, JSON or files payload.
    • Easy set basic authentication or bearer token.
    • Easy customize root certificate authorities and client certificates.
    • Easy set proxy.
    • Automatic cookies management.
    • Customize HTTP client, transport, redirect policy, cookie jar and timeout.
    • Easy set context.
    • Easy decode responses, raw data, text representation and unmarshal the JSON-encoded data.
    • Concurrent safe.

    Github: https://github.com/winterssy/reqwest

    29 条回复    2019-09-30 07:54:47 +08:00
    AngryPanda
        1
    AngryPanda  
       2019-09-29 13:25:43 +08:00
    reqeast 啥时候出
    littlespider89
        2
    littlespider89  
       2019-09-29 13:36:01 +08:00
    yixiang
        3
    yixiang  
       2019-09-29 13:38:51 +08:00
    reqwest - An easy and powerful Rust HTTP Client ( https://github.com/seanmonstar/reqwest)

    一看还以为这个 rust 库被移植了。
    winterssy
        4
    winterssy  
    OP
       2019-09-29 13:41:05 +08:00 via Android
    @littlespider89 😅为什么要 PK,纯粹分享下自己的成果而已,无关其它。
    winterssy
        5
    winterssy  
    OP
       2019-09-29 13:43:48 +08:00 via Android
    @yixiang 本来是叫 grequests 的,只是昨晚偶然发现自己搞出来的玩意跟 rust reqwest 的 api 很像,就改成了现在这个名字,之前并不知道 reqwest...
    www6688w
        6
    www6688w  
       2019-09-29 15:21:47 +08:00
    支持一下,,( ̄ m  ̄)
    www6688w
        7
    www6688w  
       2019-09-29 15:24:20 +08:00
    楼主厉害,,我学了一段时间 Go,感觉还是没有基础吃透

    个人英语能力太弱,Go 目前的生态库大部分都是英文文档,学起来吃力
    seaguest
        8
    seaguest  
       2019-09-29 16:48:19 +08:00
    支持一下。
    目前觉得最好的还是这个:github.com/imroc/req
    winterssy
        9
    winterssy  
    OP
       2019-09-29 19:16:05 +08:00
    更新:为避免跟 Rust reqwest 引起不必要的误解,项目更名为 sreq。新仓库地址: https://github.com/winterssy/sreq
    snowwalf
        10
    snowwalf  
       2019-09-29 19:39:04 +08:00
    目前工作中在用这个 https://github.com/go-resty/resty/
    zealic
        11
    zealic  
       2019-09-29 19:54:28 +08:00
    543 次点击 ∙ 3 人收藏 ∙ 1 人感谢

    0 STAR
    winterssy
        12
    winterssy  
    OP
       2019-09-29 20:06:26 +08:00
    @zealic #11 原项目名叫 reqwest 有 7 star 1 fork,后面遇到反馈说碰瓷 Rust 同名项目,删库更名重建了。star 不是我看重的,未曾想跟其它项目对比 /竞争,纯粹分享自己的成果而已。
    Aether
        13
    Aether  
       2019-09-29 20:29:37 +08:00
    @winterssy 讨人厌。
    Aether
        14
    Aether  
       2019-09-29 20:30:02 +08:00
    我说怎么找不到了……还要翻进来重新点一次 star。黑心开发者!
    fengkx
        15
    fengkx  
       2019-09-29 20:46:31 +08:00
    支持一下~

    学习了代码。然后我想到一个问题。JSON()方法为什么不直接用 json.Decoder 呢。基于这个想法试了一下。


    JSON 方法用 Deocder 可以这样
    ```go
    func (r *Response) JSON(v interface{}) error {
    dec := json.NewDecoder(r.R.Body)
    return dec.Decode(v)
    }
    ```

    Bench 代码

    ```go
    func BenchmarkResponse_JSON(b *testing.B) {
    type testData struct {
    JSON struct {
    Msg string `json:"msg"`
    Num int `json:"num"`
    } `json:"json"`
    }
    ts := httptest.NewServer( http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    dec := json.NewDecoder(r.Body)
    var data testData
    dec.Decode(&data.JSON)
    enc := json.NewEncoder(w)
    enc.Encode(data)
    }))
    resp := reqwest.Post(ts.URL).
    JSON(
    reqwest.Data{
    "msg": "hello world",
    "num": 2019,
    }).
    Send().
    EnsureStatusOk()
    b.ResetTimer()
    for i:=0;i<b.N;i++ {
    var retData testData
    resp.JSON(&retData)
    }
    }
    ```

    结果如下
    ## Unmarshal
    ```plain
    $ go test -v -run="none" -bench="BenchmarkResponse_JSON" -benchtime="3s" -benchmem
    goos: linux
    goarch: amd64
    pkg: github.com/winterssy/reqwest
    BenchmarkResponse_JSON-4 5000000 959 ns/op 544 B/op 2 allocs/op
    PASS
    ok github.com/winterssy/reqwest 5.895s
    ```
    ## Decoder
    ```plain
    $ go test -v -run="none" -bench="BenchmarkResponse_JSON" -benchtime="3s" -benchmem
    goos: linux
    goarch: amd64
    pkg: github.com/winterssy/reqwest
    BenchmarkResponse_JSON-4 10000000 713 ns/op 896 B/op 3 allocs/op
    PASS
    ok github.com/winterssy/reqwest 7.758s
    ```

    跟我设想的不太一样。还以为 Decoder 会更快。为什么会这样呢?
    whoami9894
        16
    whoami9894  
       2019-09-29 21:10:50 +08:00
    winterssy
        17
    winterssy  
    OP
       2019-09-29 21:22:54 +08:00
    @fengkx #15 我在 windows 上测了一下,结果反而跟你预期的一样,decoder 性能更好。这可能跟测试环境有关,网上查了一下,选择 decoder 更为合适,后续会使用 decoder 替换 unmarshal,感谢反馈。

    stackoverflow 上类似的提问: https://stackoverflow.com/questions/21197239/decoding-json-using-json-unmarshal-vs-json-newdecoder-decode

    附上测试结果:
    ## unmarshal
    goos: windows
    goarch: amd64
    pkg: github.com/winterssy/sreq
    BenchmarkResponse_JSON-4 7664007 480 ns/op 544 B/op 2 allocs/op
    PASS
    ok github.com/winterssy/sreq 4.558s

    ## decoder
    goos: windows
    goarch: amd64
    pkg: github.com/winterssy/sreq
    BenchmarkResponse_JSON-4 8721448 409 ns/op 896 B/op 3 allocs/op
    PASS
    ok github.com/winterssy/sreq 4.386s
    winterssy
        18
    winterssy  
    OP
       2019-09-29 21:37:42 +08:00
    @fengkx #15 不对,你的测试结果也表示 decoder 更快吧。decoder 比 unmarshal 更快,但貌似需要更多的内存分配次数跟空间
    justin2018
        19
    justin2018  
       2019-09-29 22:39:39 +08:00
    nice 已 start
    deletelazy
        20
    deletelazy  
       2019-09-29 22:41:41 +08:00
    你这个库有点问题啊,不是 thread-safe 的。。
    deletelazy
        21
    deletelazy  
       2019-09-29 22:46:02 +08:00
    @deletelazy 额,似乎要手动加锁。。感觉不是太友好
    deletelazy
        22
    deletelazy  
       2019-09-29 22:52:44 +08:00
    似乎并发 GET,POST 等也有问题

    package main

    import (
    "fmt"
    )

    func main() {
    cs := make(chan *Client, 100)
    for i := 0; i < 10; i++ {
    go func(i int) {
    if i%2 == 1 {
    cs <- Get("123")
    } else {
    cs <- Post("456")
    }
    }(i)
    }
    for {
    select {
    case c := <-cs:
    fmt.Println(c.method, c.url)
    }
    }
    }
    winterssy
        23
    winterssy  
    OP
       2019-09-29 23:35:45 +08:00
    @deletelazy #22 因为是链式操作,自动加锁就要每一步都加锁,这样不是很好,所以改成了手动加锁。而且你的测试例子明显就有问题,Get("123"),Post("456") 无论你创建多少个都是共享同一个 client——std,如果你真要这么玩,先 New()一个。
    func main() {
    cs := make(chan *Client, 100)
    for i := 0; i < 10; i++ {
    go func(i int) {
    if i%2 == 1 {
    cs <- New().Get("123")
    } else {
    cs <- New().Post("456")
    }
    }(i)
    }

    for {
    select {
    case c := <-cs:
    fmt.Println(c.method, c.url)
    }
    }
    }
    deletelazy
        24
    deletelazy  
       2019-09-30 00:50:02 +08:00
    并发请求都要加锁的话效率是不是太低了
    如果并发请求使用每次 New 的方式,创建一堆对象浪费内存不说,每个请求新建一个 transport,连接也无法复用
    而且每次并发请求都要 New 的话,那这些全局方法还有什么意义呢
    winterssy
        25
    winterssy  
    OP
       2019-09-30 00:56:08 +08:00 via Android
    @deletelazy 并发不加锁如何解决数据竞争问题?而且并发你不需要 New,会复用 http client,这里提到 New 只是针对你贴出的代码而言
    winterssy
        26
    winterssy  
    OP
       2019-09-30 00:57:04 +08:00 via Android
    @deletelazy 提出问题前请先仔细阅读 api 文档
    deletelazy
        27
    deletelazy  
       2019-09-30 01:11:20 +08:00 via iPhone
    那你可以看下其他的 http 库或者看下 http.Get
    的实现有没加锁
    winterssy
        28
    winterssy  
    OP
       2019-09-30 01:40:25 +08:00 via Android
    @deletelazy 这根本没有可比性,net/http 在底层就实现了线程安全。而 sreq 是对 http api 的上层封装,它的设计决定了要加锁。且目前大多基于 net/http 封装的 request 库要么不支持并发安全,要么每次请求前克隆一份配置,要么加锁。结合具体场景分析,别章口就来😓
    fengkx
        29
    fengkx  
       2019-09-30 07:54:47 +08:00
    @fengkx
    @winterssy 单位看反了 /(ㄒoㄒ)/~~。看到了多一次内存分配下意识地看错了
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   954 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 31ms · UTC 21:35 · PVG 05:35 · LAX 14:35 · JFK 17:35
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.