V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
xiangyuecn
V2EX  ›  程序员

C#中的废物 WebRequest

  •  
  •   xiangyuecn ·
    xiangyuecn · 2018-09-10 17:13:29 +08:00 · 10794 次点击
    这是一个创建于 2271 天前的主题,其中的信息可能已经有所发展或是发生改变。

    吐槽+骂街贴:吃屎的.net ,一个基本的 http 请求也搞的这么难用,因为一个功能放弃一门(暂且叫语言吧)。

    一直用的 WebRequest,直到今天...

    1. 完全没法根据需要设定超时

    有 Timeout、ReadWriteTimeout ?没用的!看 MSDN 的解释:

    https://msdn.microsoft.com/zh-cn/library/system.net.httpwebrequest.timeout(v=vs.110).aspx 域名系统 (DNS) 查询可能需要最多 15 秒钟才能返回或超时。 如果您的请求包含需要解析的主机名,并设置 Timeout 为一个值小于 15 秒,可能需要 15 秒或更长之前 WebException 引发来指示您的请求超时

    至少包含一个隐藏的不可控因素,也就是说你没法简单的完全控制请求超时,Timeout 完全是摆设,TMD 的.net 侮辱了 Timeout 这个单词。

    ReadWriteTimeout 算是摆设吧,是数据流 read、write 调用超时,意思相近于发送一个直接后等待下一个字节的时间不能超过这个,并非 100%读取完数据和发送完数据的超时控制。然后他默认的 5 分钟完全是废物设定,没错,废物。( java 的 HttpURLConnection 有个设定好像也是如此吧记得)。

    没有 ConnectTimeout 选项。没有 ConnectTimeout 选项。没有 ConnectTimeout 选项。(根源所在)

    2. 曲线救国 BeginGetRequestStream

    使用异步的发送数据流可以变相实现 ConnectTimeout,拿到请求数据流代表已经连上了服务器。

    但 GET、HEAD 明确不支持获取 RequestStream。意思就是说 GET 请求你往请求体里面塞东西,.net 就把你写的渣渣代码给干掉,从源头上拒绝你的操作。看到这个逼玩意怎么一个火字了得。

    劳资发个请求你还要管我的 method 是什么!

    GET 没法拿到请求数据流,GET 没法拿到请求数据流,GET 没法拿到请求数据流。(已经过了底线)

    WebRequest 没法实现

    比如:严格控制一个 GET 请求耗时在 3 秒以内,超过时间就超时,用来检测 url 是否可以访问。遇到类似 www.google.com 就,,,呵呵了。POST 用 BeginGetRequestStream 没这个问题

    要自己实现整体的请求超时功能,他不支持,开个线程也好,开个计时器也好。

    GET 不让发请求体,可能很科学,但遇上真不爽

    我只是想拿一下发送请求的数据流而已,并非真要往 stream 里面塞数据。

    放弃.net

    用第三方库解决此类问题?关键是框架给的功能已经能满足 98%的功能了,卡在了这 2%必须实现的功能上而已。用第三方的是不可能的,这辈子都不可能为了这么小小的功能 load 一个 100M 的第三方库的。

    so..用 java 吧,或者 php 也行,至少写一个简单的网络请求没这糟心。遇到忍无可忍的地方就换个语言,不是针对.net ,我是说所有的语言!

    (文中 C#语言==.net 框架 并非你异议中的 C#语言!=.net 框架,不服去车上拿刀,拿紧)

    84 条回复    2018-09-13 23:06:45 +08:00
    ruatyy
        1
    ruatyy  
       2018-09-10 17:17:49 +08:00   ❤️ 1
    兄弟早点转行吧,.net 不行了,微软块倒闭了。
    对了,世界上最好的语言是 PHP,滑稽.jpg
    cjw1115
        2
    cjw1115  
       2018-09-10 17:23:29 +08:00   ❤️ 7
    WebRequst 是什么??难道不是用 HttpClient 吗?
    daigouspy
        3
    daigouspy  
       2018-09-10 17:24:47 +08:00 via Android
    @cjw1115 同意
    xiangyuecn
        4
    xiangyuecn  
    OP
       2018-09-10 17:29:52 +08:00
    @cjw1115 @daigouspy 用什么都无所谓,关键是需要的功能有没有,有 System.Net.Http.HttpClient 这个框架自带的,配置设置基本没有,因为够简单,满足 98%的需求吧,2%就呵呵,仅此而已
    ZhLTE
        5
    ZhLTE  
       2018-09-10 17:30:25 +08:00
    php 是世界上最好的语言! 什么语言 get 可以发数据流啊
    fgodt
        6
    fgodt  
       2018-09-10 17:33:48 +08:00
    阻碍一个人的永远不是语言
    xiangyuecn
        7
    xiangyuecn  
    OP
       2018-09-10 17:34:27 +08:00
    @ZhLTE 比如 get 一下 www.dropbox.com ,能达到预期( 2 秒超时就是 2 秒超时)的语言就是 ok,.net 就是渣渣
    catcn
        8
    catcn  
       2018-09-10 17:35:55 +08:00
    var a = document.getElementsByName('description')[0].content;
    console.info(a.substring(27, 29));
    yulitian888
        9
    yulitian888  
       2018-09-10 17:36:10 +08:00   ❤️ 5
    WebClient、HttpClient、HttpWebRequest 的区别不打算了解一下?
    ZhLTE
        10
    ZhLTE  
       2018-09-10 17:37:19 +08:00
    你是想要一个 无论数据是否传输 connect 时间 2s 就算超时的方法吗
    xiangyuecn
        11
    xiangyuecn  
    OP
       2018-09-10 17:37:40 +08:00
    @catcn 刀呢
    enenaaa
        12
    enenaaa  
       2018-09-10 17:38:09 +08:00
    能写这么长的文来喷也是真爱
    xiangyuecn
        13
    xiangyuecn  
    OP
       2018-09-10 17:42:14 +08:00
    @yulitian888 字面意思 HttpClient extend WebClient,WebClient using HttpWebRequest,仅字面意思

    @ZhLTE 差不多这意思,从请求创建开始,不管是哪里卡了,只要到时间还么有最终结果就是超时
    xupefei
        14
    xupefei  
       2018-09-10 17:47:32 +08:00 via Android
    把这个连接扔到 Task 里也不行?
    daigouspy
        15
    daigouspy  
       2018-09-10 17:49:56 +08:00 via Android   ❤️ 5
    @xiangyuecn 包在一个线程里面,时间到就 abort,绝对好使。你的需求是应用层面了,不应该要求类库自带,类库只是提供基本需求,你的需求需要自己包装。
    ZhLTE
        16
    ZhLTE  
       2018-09-10 17:54:59 +08:00   ❤️ 1
    可以试试 task 的 CancellationTokenSource
    luozic
        17
    luozic  
       2018-09-10 17:55:26 +08:00   ❤️ 2
    丰富了 block 名单
    crab
        18
    crab  
       2018-09-10 17:56:46 +08:00
    winhttp
    xiangyuecn
        19
    xiangyuecn  
    OP
       2018-09-10 17:57:34 +08:00
    @xupefei 隔壁 java 笑了,哈哈哈,一些简单功能的缺失,用大的代价来弥补一下的做法,不到万不得已,不开相对重量级的 task,毕竟大部分请求 50ms 以内就完成了,一个 task 下来可能额外需要等待 0-50ms 才会做实际的任务

    @daigouspy 一样

    网络请求这种耗费时间的,按理应该提供比较精细的超时控制,jdk 就没有这个问题。
    想到正则表达式都有超时控制,一个网络请求基本的东西,还要开线程控制超时,就是底层太简陋了。
    .net 明显的缺陷看 dns 查询部分,太不靠谱
    zkd8907
        20
    zkd8907  
       2018-09-10 17:58:03 +08:00
    blockList.push(this);
    cdwyd
        21
    cdwyd  
       2018-09-10 17:58:52 +08:00 via Android   ❤️ 2
    c#真是垃圾语言,竟然不能自动赚钱
    xiangyuecn
        22
    xiangyuecn  
    OP
       2018-09-10 18:01:38 +08:00
    @luozic
    @zkd8907
    @cdwyd

    我也是一个杠精,何况在我自己写的帖子里面,百毒不侵,(doge+滑稽+辣稽
    AngelCriss
        23
    AngelCriss  
       2018-09-10 18:02:28 +08:00 via Android
    自己造个轮子不就好了
    xupefei
        24
    xupefei  
       2018-09-10 18:03:04 +08:00 via Android   ❤️ 1
    @xiangyuecn 注意你的语气。
    Task 是在线程池上的,和 Thread 不一样。Thread 是最重的,但是要说花掉 50ms 我觉得还是不可能的。
    fwee
        25
    fwee  
       2018-09-10 18:04:36 +08:00
    LZ 得换个 GET 能发请求体的语言
    xiangyuecn
        26
    xiangyuecn  
    OP
       2018-09-10 18:06:21 +08:00
    @xupefei 不要在意其他人的回复,我回复 19 楼是很认真的,task 你没有遇到 task 的线程池占满的情况,不排除永远等待的可能

    其他楼层杠的我脾气很暴躁,包涵包涵~
    qdwang
        27
    qdwang  
       2018-09-10 18:15:07 +08:00   ❤️ 1
    .net 库在易用性上确实做的不好,我觉得可能是因为发展的时候,没有得到很多类似楼主这样的反馈的缘故。
    xiangyuecn
        28
    xiangyuecn  
    OP
       2018-09-10 18:15:11 +08:00
    @fwee 研究 get request body 的时候发现的一篇文章: https://blog.csdn.net/q_an1314/article/details/51298073,我不是要用 GET 来发请求体,而是遇上了需要拿到请求 Stream 才能处理 ConnectTimeout,但 GET 不让拿 RequestStream,导致 GET 部分无解,真遇上了“ GET 不让发请求体”,有点为难
    oyjc
        29
    oyjc  
       2018-09-10 18:35:30 +08:00
    开个 socket 自己连接不行么 --!
    verrickt
        30
    verrickt  
       2018-09-10 19:05:54 +08:00   ❤️ 1
    @xiangyuecn
    >完全没法根据需要设定超时
    查了下,HttpClient 的 timeout 也包含了[DNS 查询]( https://docs.microsoft.com/en-us/dotnet/api/system.net.http.httpclient.timeout?view=netframework-4.7.2#System_Net_Http_HttpClient_Timeout)
    不过 LZ 可以试试这样
    ``` csharp

    var client = new HttpClient();
    var cts = new CancallationTokenSource();
    var task = client.GetAsync("http://example.com",cts.Token);
    cts.CancelAfter(TimeSpan.FromSeconds(2));
    try
    {
    var response = await task;
    response.EnsureSuccess();
    }
    catch(TaskCancelldException)
    {
    // time out
    }
    ```

    跟 LZ 一起吐槽,HTTPClient 完全无法得知上传进度。
    神奇的是,官方不推荐的的`System.Web.WebClient`和`Windows.Web.Http`是可以得到上传进度的,亲儿子`System.Web.Http`却不行
    workaround 有
    - 继承`StreamContent `用`SerializeToStreamAsync`去做手脚。
    - 写个 Wrapper 流。

    两者都非常丑,并且都只能得到流被序列化的进度,无法得到网络传输的进度。


    Bonus
    [论打开自动更新的重要性]( https://verrickt.github.io/2018/05/21/hell-mode-debugging/)
    ShareDuck
        31
    ShareDuck  
       2018-09-10 19:08:41 +08:00 via Android
    先收藏这个主题,回去试试。
    xiangyuecn
        32
    xiangyuecn  
    OP
       2018-09-10 19:32:02 +08:00
    @verrickt 嗯嗯,HttpClient 和 WebRequest 一个尿性:it may take 15 seconds or more before a WebException is thrown to indicate a timeout on your request,timeout 是不管 dns 解析部分超时

    用 CancallationTokenSource 和 @ZhLTE #16 的建议一个意思,HttpClient 有相应方法支持,WebRequest 有点不好办
    hellohello123
        33
    hellohello123  
       2018-09-10 20:13:06 +08:00
    歪个楼,这个 C# 这门语言有什么关系,明明是类库实现的问题,最多吐槽 .NET
    liuguang
        34
    liuguang  
       2018-09-10 21:35:24 +08:00
    哈哈,自己学的不深,不会用也不要把黑锅往语言上扔啊。
    https://docs.microsoft.com/zh-cn/dotnet/api/system.net.webrequest.timeout?view=netframework-4.7.2#System_Net_WebRequest_Timeout
    timeout 可以设置的。。。
    liuguang
        35
    liuguang  
       2018-09-10 21:37:07 +08:00
    下载的话,想要进度条,用 WebClient 啊,异步下载,有各种事件
    lolizeppelin
        37
    lolizeppelin  
       2018-09-10 22:10:46 +08:00 via Android
    get 发流是可以的 用 post 然后覆盖请求方法
    标准的 http 服务都会认的
    loqixh
        38
    loqixh  
       2018-09-10 22:22:49 +08:00
    get 什么时候不可以发请求体? 用 SendAsync 就可以了
    xiangyuecn
        39
    xiangyuecn  
    OP
       2018-09-10 22:26:48 +08:00
    @liuguang public abstract class WebRequest

    abstract 需要查实现 public class HttpWebRequest : System.Net.WebRequest 然后会发现帖子正文中的引用内容
    xiangyuecn
        40
    xiangyuecn  
    OP
       2018-09-10 22:32:33 +08:00
    @verrickt 嗯,学习了。IAsyncResult 估计是历史包袱。
    forerunner
        41
    forerunner  
       2018-09-10 23:08:51 +08:00
    然后再次确认一下,你用的是 .net framework 还是 .net core ?版本是什么?不知道我为何这么问的话,出门左转 Java 组欢迎你……
    xiangyuecn
        42
    xiangyuecn  
    OP
       2018-09-10 23:51:34 +08:00
    @forerunner .net framework 4.5

    ----
    .net core 的实现瞄了一下文档发现没什么区别好像,dns 查询部分都是没纳入 timeout 中
    nannanziyu
        43
    nannanziyu  
       2018-09-11 00:23:46 +08:00   ❤️ 5
    @xiangyuecn
    用 HttpClient 的话,一行解决
    await new HttpClient().GetAsync("http://www.baidu.com",new CancellationTokenSource(2000).Token);

    要用 WebRequest 的话,也是一行解决
    var task = await Task.WhenAny(WebRequest.CreateHttp("http://www.baidu.com").GetResponseAsync(), Task.Delay(2000));

    // then
    if (task is Task<WebResponse>)
    {
    var r = await (task as Task<WebResponse>);
    }
    else
    {
    // timeout
    }

    楼主有吐槽的时间,不如多学学基础知识……
    longaiwp
        44
    longaiwp  
       2018-09-11 00:36:35 +08:00
    快转行吧,.NET 不行了,微软要倒闭了。你去学 PHP 吧,世界上最好的语言
    nannanziyu
        45
    nannanziyu  
       2018-09-11 00:36:58 +08:00
    另外
    “ get 里发 body ”
    “拿到请求 Stream 才能处理 ConnectTimeout ”
    “ task 下来可能额外需要等待 0-50ms ”
    我想调侃两句都无从下嘴,这对 http 和 thread 一点概念都没有。。。
    20150517
        46
    20150517  
       2018-09-11 00:43:26 +08:00 via Android
    我记得我 15 年前开发.net 就知道这毛病了,我已经不用.net 好多年了,怎么到现在还没修好?
    xiaojunjor
        47
    xiaojunjor  
       2018-09-11 08:47:09 +08:00
    keymao
        48
    keymao  
       2018-09-11 08:58:38 +08:00   ❤️ 1
    先说下结论,.Net 的 WebRequest... 说实在的 之前做过贴吧的一些辅助工具的开发,后来全是用 HttpClient 自己封装工具,不要指望微软给你做好了。 微软就是这样的风格,习惯了就好了。 能用,够用,谈不上好用,自己封装就完了。也别怪微软了,毕竟 wp 这种本来是战略高度的玩意儿都完全玩崩了。
    nannanziyu
        49
    nannanziyu  
       2018-09-11 09:20:58 +08:00
    @keymao
    一行就能搞定的东西也能扯到封装
    菜就老老实实承认一句有那么难吗?
    guolaopi
        50
    guolaopi  
       2018-09-11 09:35:17 +08:00
    @nannanziyu 别说了。。我要转 java。。。(滑稽)
    passerbytiny
        51
    passerbytiny  
       2018-09-11 09:35:24 +08:00   ❤️ 3
    “严格控制一个 GET 请求耗时在 3 秒以内,超过时间就超时,用来检测 url 是否可以访问”

    对于这种不符合惯例的专用需求,任何语言,包括它们的扩展底层的第三方类库,都不会实现。楼主至少需要先学习下 HTTP 协议的连接过程,深入一点的话还要学习 TCP 协议和 OSI 模型。

    你第一段转摘的 MSDN 的文档,已经涉及了 HTTP 协议的一部分: 域名系统 (DNS) 查询可能需要最多 15 秒钟才能返回或超时。ReadWriteTimeout 的超时也是完全没问题的,传递大量数据(比如下载文件),你觉得以下这两种情况哪种会提前检测到超时:预估 60 分钟下载完,60 分钟还没下载完,算超时; 5 分钟没有读写数据,算超时。

    楼主以为 Java 能实现,我想你只是看了 javadoc,而没有测试。测试完估计你又要骂 Java 废物了。

    通常,服务器需要高并发所以必须能精确控制超时,客户端无需高并发所以不需要精确控制超时。楼主现在需要客户端精确控制超时,非惯例需求,就要自己实现。请在开骂前先想想自己是多数还是少数,别一不小心就当了代表。
    xuanbg
        52
    xuanbg  
       2018-09-11 09:44:40 +08:00
    哎。。。我给 HTTPWebRequest 封装了两层,第一层是通用封装,只是抽象了调用方法,简化了参数。第二层是我项目的专用封装,进一步简化了参数。楼主你想要方便、好用还通用,建议你也做两层封装。
    weizhen199
        53
    weizhen199  
       2018-09-11 10:06:32 +08:00
    HttpClient 啦,那个早不用了
    dhssingle
        54
    dhssingle  
       2018-09-11 10:07:09 +08:00
    坐等楼主来喷 Java 和 PHP
    arsom
        55
    arsom  
       2018-09-11 10:09:08 +08:00 via iPhone
    别人喷个 net,非要扯 php。我觉得有些 v 站的人好自卑哈哈
    wizardforcel
        56
    wizardforcel  
       2018-09-11 10:13:19 +08:00
    根据我多年开发辅助的经验,我的实际测试结果是那两个 Timeout 有用。不信你访问一个比较慢的站点,明显能感觉到设了会立即报错。
    CRVV
        57
    CRVV  
       2018-09-11 10:34:31 +08:00
    @passerbytiny

    curl https://icanhazip.com --max-time 0.2 -v

    Python
    requests.get('https://icanhazip.com', timeout=0.2)

    这俩都可以设不同的时间不同的域名,看到不同的错

    别随便用“任何语言”这么强硬的词语
    xiangyuecn
        58
    xiangyuecn  
    OP
       2018-09-11 10:36:20 +08:00
    @nannanziyu 嗯嗯,还在学,新收到 Task.WhenAny、Task.Delay 新知识。

    查询了一下 Task.Delay,https://blog.csdn.net/wushang923/article/details/41015063 本质上是开了一个计时器。

    试了一下 WebRequest 的 GetRequestStreamAsync、GetResponseAsync,昨天测试没发现这两个方法,和 BeginGetRequestStream 和 BeginGetResponse 一样是异步方法,功能差不多,前面的两个简洁多了。

    现在已经换掉了昨天写的 BeginGetRequestStream、BeginGetResponse,直接用的 BeginGetRequestStream().Wait(timeout),这部分代码少了一半。

    隐约发现 HttpWebRequest 好像已经过时了。。

    -----------

    #45 这几点如果抛开上下文来看,确实很滑稽,我现在看一遍我自己也发笑。

    --

    ConnectTimeout 是 WebRequest 没有提供的,看文档这个应该是和 GetRequestStream 超时差不多,所以 ConnectTimeout≈Timeout(GetRequestStream 之前设置值),但 Timeout 有点小缺陷( dns 查询部分)。

    所以就有了“拿到请求 Stream 才能处理 ConnectTimeout ”

    --

    GetRequestStream 方法 GET、HEAD method 是禁止调用的,就有了“ get 里发 body ”,并非真要往 stream 里面塞数据。

    --

    一个 task 对象背后的具体实现由于不明确,认定有 0-50ms 延时不为过,参考 Task.Delay,测试过程中发会多出 30ms 左右。

    done.


    ------------------


    其实写这个帖子根源还是我想要一个 ConnectTimeout 设定,因为请求一个 url,如果连接不上服务器应该立即返回错误,而 ConnectTimeout 应该远远小于 GetResponse 的超时。比如:ConnectTimeout=2 秒能满足 99.9%的需求,但 GetResponseTimeout 就需要根据不同业务来定,如访问 V2EX,会 GetResponseTimeout=10 秒,虽然很多情况下 1 秒就能拿到响应,但很多时候 V2 会卡很久才会开始返回响应头(经常刷 V2 就是这个感觉)。

    应用场景:用户提交任意 URL,尽快速度测试 URL 是否有效,URL 中 50%可能是被墙地址,比如 google.com ,先决条件:需要让 V2EX 等可能会响应慢的地址尽可能通过测试。

    简单有效的解决方案就是使用 ConnectTimeout=2 秒,如果连接不上服务器 2 秒就超时了,不管是被墙还是服务器宕机。GetResponseTimeout=10 秒,让响应慢的地址尽可能通过测试。

    回到.net ,POST 可以用 GetRequestStream 来支持 ConnectTimeout。GET 目前学习发现暂时无解,只能等到 GetResponseTimeout 超时。


    又洋洋洒洒的写了一篇作文,耗时半小时,划水,划水



    ----

    @passerbytiny 好吧,发现帖子正文和上面楼层自己回复的和想要的功能有点出入,这个里在详细重新描述了一下。缺乏一个功能肯定会带来某些功能的丧失,我这个请求功能是从 java 里写的一个类中移植过来的,java 里面 HttpURLConnection 可控制的参数多些,有 setConnectTimeout。
    passerbytiny
        59
    passerbytiny  
       2018-09-11 11:00:08 +08:00
    @CRVV
    centos 7 执行结果
    # curl https://icanhazip.com --max-time 0.2 -v
    curl: option --max-time: expected a proper numerical parameter
    curl: try 'curl --help' or 'curl --manual' for more information

    Python 的,请上实际测试结果,你怎么就能确定 timeout 指的是命令超时时间,而不是 GET 请求的超时时间。
    keymao
        60
    keymao  
       2018-09-11 11:05:58 +08:00
    @nannanziyu 嗯? 连我需求都不知道就说一句话能解决? 你说的那是楼主的需求 不是我的 谢谢

    我很菜 .Net 很好 这就是你想听的 你听到了 别回复我了 谢谢 再见。
    LokiSharp
        61
    LokiSharp  
       2018-09-11 11:12:00 +08:00
    C# 挺好,但是 .net 是我见过最垃圾的环境
    xiangyuecn
        62
    xiangyuecn  
    OP
       2018-09-11 11:13:04 +08:00
    @wizardforcel 98%情况下是有用的,我需要那 2%,嘿嘿
    stnaw
        63
    stnaw  
       2018-09-11 11:26:52 +08:00
    C#,你已经是一门成熟的语言了。
    该学会自己码代码了
    passerbytiny
        64
    passerbytiny  
       2018-09-11 11:27:47 +08:00
    @CRVV
    curl 我测试了,自己看结果吧。复现很简单,把 DNS 服务器改成无效服务器,然后执行 curl 命令。

    2018 年 09 月 11 日 星期二 11:23:18 CST
    [root@lxl:~]# ping www.sogou.com
    ping: www.sogou.com: 未知的名称或服务
    [root@lxl:~]# date
    2018 年 09 月 11 日 星期二 11:23:41 CST

    [root@lxl:~]# date
    2018 年 09 月 11 日 星期二 11:24:06 CST
    [root@lxl:~]# curl --connect-timeout 1 http://www.sogou.com
    curl: (28) Resolving timed out after 1510 milliseconds
    [root@lxl:~]# date
    2018 年 09 月 11 日 星期二 11:24:29 CST

    [root@lxl:~]# date
    2018 年 09 月 11 日 星期二 11:25:07 CST
    [root@lxl:~]# curl --max-time 1 http://www.sogou.com
    curl: (28) Resolving timed out after 1510 milliseconds
    [root@lxl:~]# date
    2018 年 09 月 11 日 星期二 11:25:30 CST
    CRVV
        65
    CRVV  
       2018-09-11 11:36:58 +08:00
    @passerbytiny

    man curl
    -m, --max-time <time>
    Maximum time in seconds that you allow the whole operation to take. This is useful for preventing your batch jobs
    from hanging for hours due to slow networks or links going down. Since 7.32.0, this option accepts decimal val-
    ues, but the actual timeout will decrease in accuracy as the specified timeout increases in decimal precision.

    请用一个稍微新一点的 curl


    看了一下 requests 的文档,这个确实不是整个操作的超时
    hibobby
        66
    hibobby  
       2018-09-11 11:42:16 +08:00
    这喷的毫无道理了吧,你感觉.net 提供的类库不好用自己拿 C#实现一个啊。
    http 协议的底层也是 tcp/IP 自己拿 socket 实现一个 http 协议的 client 不就行了 来喷干啥
    xiangyuecn
        67
    xiangyuecn  
    OP
       2018-09-11 12:00:04 +08:00
    #66 还有 #29 我站着不动,你们去拿刀。

    要用 socket 实现 httpclient,不否认可以实现,里面的工作量有多大,写出来比框架自带的是好还是坏,我就不知道了。老是诱导我们这群小白去磨一些很基础但很重要,深入发现里面异常庞大的东西。
    xiangyuecn
        68
    xiangyuecn  
    OP
       2018-09-11 12:03:21 +08:00
    @oyjc @hibobby 想要的功能实现不了,新手吐槽一下而已,别这么认真
    CRVV
        69
    CRVV  
       2018-09-11 12:07:41 +08:00
    @passerbytiny

    这个我也可以复现,看起来像一个 curl 的 bug,他的网站还专门写了 “ When the set time has elapsed, curl will exit no matter what is going on at that moment ”
    V2EX 说“请不要在每一个回复中都包括外链,这看起来像是在 spamming ”,所以没有加上链接


    可以给你另找一个

    ```
    package main

    import (
    "context"
    "log"
    "net/http"
    "time"
    )

    func main() {
    req, err := http.NewRequest("GET", "url", nil)
    if err != nil {
    log.Fatal(err)
    }
    ctx, _ := context.WithTimeout(context.Background(), time.Second)
    req = req.WithContext(ctx)
    _, err = http.DefaultClient.Do(req)
    if err != nil {
    log.Fatal(err)
    }
    }
    ```
    把 DNS 设成不能用的服务器之后,这段 Go 的输出是
    $ time ./timeout
    2018/09/11 03:56:53 Get url: context deadline exceeded
    ./timeout 0.00s user 0.02s system 2% cpu 1.020 total
    karllynn
        70
    karllynn  
       2018-09-11 14:16:26 +08:00
    有发帖的时间都解决问题了。。
    rwecho
        71
    rwecho  
       2018-09-11 16:57:00 +08:00
    Task 是 C#灵魂了, 代码比 Begin 更易懂
    mritd
        72
    mritd  
       2018-09-11 19:26:50 +08:00 via iPhone
    哈哈,有点意思,没用过 C#,不过如果真如楼主所说这个 API 倒是真的反人类,写了好几年 java,最近在写 go,楼主可以看看 go 的标准库 http 请求发送,保准你开森
    mmdsun
        73
    mmdsun  
       2018-09-11 19:37:04 +08:00 via Android
    检测 url 可否访问方式很多。。。你开个定时器就好 2 秒没反应直接跳出。或者用 ping 命令。java 一般用第三方的库
    snw
        74
    snw  
       2018-09-11 20:42:03 +08:00 via Android
    被 Windows (尤其 Win10 )花式卡住甚至挂死的那么多年了,就知道不该对微软家产品的这方面有任何期待(滑稽)

    WebClient 确实最简单易用,其次是 HttpClient,这两个完不成的话那就只能用 HttpWebRequest 写冗长又丑陋的代码了,怨念是必然的。
    reus
        75
    reus  
       2018-09-11 21:38:00 +08:00
    @passerbytiny “任何语言”?呵呵。看看 go 的 https://godoc.org/net/http#Transport,里面的 ResponseHeaderTimeout 字段就是控制你写完请求之后,到收到第一个响应头的超时时间,完全切合需求。为什么 go 可以实现?难道 go 实现的 HTTP 协议有什么不同?
    reus
        76
    reus  
       2018-09-11 21:41:44 +08:00
    @passerbytiny 更简单的,http.Client 本身就有一个 Timeout 字段: https://godoc.org/net/http#Client ,而且 GET 请求也能带 body。
    paranoiagu
        77
    paranoiagu  
       2018-09-11 23:15:48 +08:00 via Android
    .net 的网络请求容易卡死。
    wizardforcel
        78
    wizardforcel  
       2018-09-12 00:08:33 +08:00 via Android
    @xiangyuecn 如果这 2%不是随机出现的,那么就可以自己定制一套东西。比如拿 Socket 或者 TCPClient 封装出来。
    xeaglex
        79
    xeaglex  
       2018-09-12 11:15:09 +08:00
    我就想问一个问题,httpClient 多线程下内存泄漏的毛病什么时候修好?
    xiangyuecn
        80
    xiangyuecn  
    OP
       2018-09-12 20:50:30 +08:00
    @rwecho @paranoiagu @xeaglex 从发起到完成一个请求,.net 背后套入太多。


    顺带发现一个好玩的:

    昨天开 50 个线程(Task 实现的)去请求数据,请求数据中用到了 GetResponseAsync,会发现开始时速度极慢,而且会产生超时请求,2 分钟后请求速度达到最高速率并且稳定,最后发现是线程池的锅。

    .net 4.5 中线程池中最小线程数饱和后,会新开新线程。但不是需要多少立即开多少,基本上 1 秒开一个新线程,启动异常缓慢。导致突然增加的 50 个线程只有少量在工作,其他排队。进而导致 GetResponseAsync 没有线程来处理,导致超时。

    https://bbs.csdn.net/topics/390454205?page=1。

    感觉用.net 来做定时采集任务是要废了,请求不好用,线程池还不能进行突发操作,略略略。。。
    xeaglex
        81
    xeaglex  
       2018-09-13 06:42:28 +08:00 via Android
    @xiangyuecn 不是不能突发操作,是默认预构造的线程较少,当需求不能满足,会有新的线程入池,这个过程慢于普通线程的构造。新入池的线程不会销毁,所以后面就会快了
    xeaglex
        82
    xeaglex  
       2018-09-13 06:46:58 +08:00 via Android
    @xiangyuecn 如果你一定要启动快点,设置下最小线程数就行了
    xiangyuecn
        83
    xiangyuecn  
    OP
       2018-09-13 10:01:15 +08:00
    @xeaglex 测试了一下调大了最小线程数,新开的线程立马就飚了起来。

    刚刚,细细思考一下 ThreadPool 这种延迟开新线程的行为存在的意义,发现一个事实:纯粹是微软某个程序员意淫出来的结果。

    [1]

    一般常见的池类配置存在两个控制参数:MaxActive 最大活跃数、MaxIdle 最大空闲数;你需要连接立即给你,只要不超过最大连接数,并且池内保留已开启的。

    .net ThreadPool:只有 MaxThread 最大活跃数;你需要线程?看你设置了 MinThreads 没有,已开线程数量超过 MinThreads ?不好意思,新血液小水管慢慢流,等着业务超时吧。


    [2]

    ThreadPool 这个延迟功能初衷本来是好的,可以防止程序因为突然不好的原因( bug 或者其他非预期因素)打开大量线程产生的问题 [非业务需求] 。但如果程序确实需要大量线程来处理,不管你怎么延迟,最终还是会开到满足需要的线程数量 [业务实际需求] ;此时 ThreadPool.SetMinThreads 入场。

    但是因为程序功能需要,往往需要调用 ThreadPool.SetMinThreads 调大可快速开启的线程数量,由于 ThreadPool.SetMinThreads 算是一个竞争资源(下面 [3] 解释)。最终的设定会趋向于 ThreadPool.SetMinThreads==ThreadPool.SetMaxThreads,并且趋向于全局只初始化设定一次(如程序启动时)。

    最终结果就是 ThreadPool.SetMinThreads 失去存在的意义,ThreadPool 延迟开新线程没有任何价值。


    [3]

    ThreadPool.SetMinThreads 存在竞争因素,不敢随意调用,最终结果趋向于在程序初始化时设定 ThreadPool.SetMinThreads==ThreadPool.SetMaxThreads。

    注意:MinThreads 数量并非会保留的空闲( Idle )线程数量,是要新开线程时如果已开启线程数量没有超过 MinThreads 就会立即开启,否则等着吧。新开的线程超过时间没用会自动关闭,具体保留多少个空闲( Idle )线程,没有设置方法。

    默认:SetMinThreads 10
    事物 A:事物开始时 SetMinThreads 200 (实际需要 100-300 个线程),事物结束还原成 10
    事物 B:事物开始时 SetMinThreads 50 (实际需要 50-80 个线程),事物结束还原成 10

    竞争情况:AB 都在运行(无需管谁先开始的),A 需要 30 分钟,B 需要 5 分钟,不管谁先结束,MinThreads 都会变成比另外事物开始时设定值小,都 [有可能] 对另外一个事物产生影响。

    解决办法:那就不在事物开始时设定,改成每次从线程池中拿线程时设定 MinThreads,问题解决!

    然后演变变成了:

    MinThreads=谁大谁说了算;

    整个程序 MinThreads≈会最大设定那个值。

    整个程序 MinThreads≈MaxThreads。


    [结论]

    当有相对多点的线程需求时,ThreadPool 这种延迟开新线程的行为没有意义。ThreadPool.SetMinThreads 废物,不如换成 SetMaxIdleThreads。

    new Thread(run).Start()没这种问题,但线程启动耗时太长(到无法接受的程度)。ThreadPool.QueueUserWorkItem(run)和 Task.Run(run)共享 ThreadPool。

    实现线程池比实现 HttpClient 简单 99 倍,是时候自己实现一个.net 的线程池了(大哭

    -----

    洋洋洒洒又写作文,测试+码字耗时 1 小时,学习就是这样的 @karllynn #70
    xeaglex
        84
    xeaglex  
       2018-09-13 23:06:45 +08:00
    @xiangyuecn 额,也许你对线程池上下限有点误解。根据业务拥堵程度动态调整线程池容量不是合适的做法,无上限的资源池+无延迟的入池速度更是危险的东西,试想如果你的业务遭遇 DOS,你这样做是什么后果?设置下限的意义在于节约资源,设置上限的意义在于防止环境崩溃。你应该知道你的服务需要承受多大的压力,在压力到来前尽可能节约系统资源,并在压力到来后减小对外部环境的影响。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1410 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 30ms · UTC 17:33 · PVG 01:33 · LAX 09:33 · JFK 12:33
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.