【问题标题】:Issues sending http put request every 60 seconds to RoR app每 60 秒向 RoR 应用发送 http put 请求的问题
【发布时间】:2013-08-13 20:28:23
【问题描述】:

客户端代码有一个奇怪的问题,应该每 60 秒向我的 RoR 网站/应用发送 HTTP Put 请求。

问题是客户端应用程序将一些请求推送到网站(从 2 到 9 请求)。然后客户端代码在几次初始成功推送后停止发送 http put 请求。

信息:客户端应用程序是一个 C# windows 应用程序。网站正在运行 RoR 3.2。

发送http put的c#代码

    static void HttpPutRequest(string Json)
    {
        Logger("Sending JSON: " + Json);
        try
        {
            var httpWebRequest = (HttpWebRequest)WebRequest.Create("https://miningmonitor.herokuapp.com/workers/update");
            httpWebRequest.ContentType = "application/json";
            httpWebRequest.Method = "PUT";
            using (var streamWriter = new StreamWriter(httpWebRequest.GetRequestStream()))
            {
                Logger("To URL: " + httpWebRequest.Address.ToString());
                streamWriter.WriteLine(Json);
                streamWriter.Flush();
                streamWriter.Close();
                try
                {
                    var httpResponse = (HttpWebResponse)httpWebRequest.GetResponse();
                    var wRespStatusCode = httpResponse.StatusCode;
                    Logger("Website return code: " + wRespStatusCode.ToString());
                }
                catch (WebException we)
                {
                    var wRespStatusCode = ((HttpWebResponse)we.Response).StatusCode;
                    Logger(" Exception and Website return code: " + wRespStatusCode.ToString());
                }
            }
        }
        catch (WebException we2)
        {
            var GetRequestStreamExp = ((HttpWebResponse)we2.Response).StatusCode;
            Logger(" Exception trying to setup httpWebRequest.GetRequestStream: " + GetRequestStreamExp.ToString());
        }
    }

客户端 C# 日志记录语句

Sending JSON: {"worker_user_name":"test:worker1","hashrate":"1.91","accepted":"5953","rejected":"152","hw_errors":"0","num_gpu":"3","gpus":["72.00","0.64","73.00","0.64","74.00","0.63"]}
To URL: https://miningmonitor.herokuapp.com/workers/update
Website return code: OK
Sending JSON: {"worker_user_name":"test:worker1","hashrate":"1.91","accepted":"5956","rejected":"152","hw_errors":"0","num_gpu":"3","gpus":["72.00","0.64","73.00","0.64","74.00","0.62"]}
To URL: https://miningmonitor.herokuapp.com/workers/update
Website return code: OK
Sending JSON: {"worker_user_name":"test:worker1","hashrate":"1.91","accepted":"5964","rejected":"152","hw_errors":"0","num_gpu":"3","gpus":["72.00","0.64","73.00","0.64","74.00","0.63"]}
To URL: https://miningmonitor.herokuapp.com/workers/update
Website return code: OK
Sending JSON: {"worker_user_name":"test:worker1","hashrate":"1.91","accepted":"5970","rejected":"152","hw_errors":"0","num_gpu":"3","gpus":["72.00","0.64","73.00","0.64","74.00","0.63"]}
To URL: https://miningmonitor.herokuapp.com/workers/update
Website return code: OK
Sending JSON: {"worker_user_name":"test:worker1","hashrate":"1.91","accepted":"5978","rejected":"152","hw_errors":"0","num_gpu":"3","gpus":["72.00","0.64","73.00","0.64","74.00","0.63"]}
To URL: https://miningmonitor.herokuapp.com/workers/update
Website return code: OK
Sending JSON: {"worker_user_name":"test:worker1","hashrate":"1.91","accepted":"5989","rejected":"153","hw_errors":"0","num_gpu":"3","gpus":["72.00","0.64","73.00","0.64","74.00","0.63"]}
To URL: https://miningmonitor.herokuapp.com/workers/update
Website return code: OK
Sending JSON: {"worker_user_name":"test:worker1","hashrate":"1.91","accepted":"5995","rejected":"153","hw_errors":"0","num_gpu":"3","gpus":["72.00","0.64","73.00","0.64","74.00","0.63"]}
To URL: https://miningmonitor.herokuapp.com/workers/update
Website return code: OK
Sending JSON: {"worker_user_name":"test:worker1","hashrate":"1.91","accepted":"5999","rejected":"153","hw_errors":"0","num_gpu":"3","gpus":["72.00","0.64","73.00","0.63","74.00","0.63"]}
Sending JSON: {"worker_user_name":"test:worker1","hashrate":"1.91","accepted":"6009","rejected":"153","hw_errors":"0","num_gpu":"3","gpus":["72.00","0.64","73.00","0.64","74.00","0.63"]}
Sending JSON: {"worker_user_name":"test:worker1","hashrate":"1.91","accepted":"6015","rejected":"153","hw_errors":"0","num_gpu":"3","gpus":["72.00","0.63","73.00","0.64","74.00","0.63"]}
Sending JSON: {"worker_user_name":"test:worker1","hashrate":"1.91","accepted":"6021","rejected":"153","hw_errors":"0","num_gpu":"3","gpus":["72.00","0.64","73.00","0.64","74.00","0.63"]}
Sending JSON: {"worker_user_name":"test:worker1","hashrate":"1.91","accepted":"6029","rejected":"153","hw_errors":"0","num_gpu":"3","gpus":["72.00","0.64","73.00","0.64","74.00","0.64"]}

从那时起,它只创建该 json 而不发送它。

显示放置请求的服务器日志

 Aug 13 10:31:52 miningmonitor heroku/router: at=info method=PUT path=/workers/update host=miningmonitor.herokuapp.com fwd="198.244.99.204" dyno=web.1 connect=8ms service=101ms status=200 bytes=2005 

我每 60 秒运行一次的方法是在 c# 中使用计时器。但我只是困惑为什么代码停止输入 using 语句并因此停止发送 JSON 字符串。您可以通过 C# 代码中的日志记录语句来判断这一点。我不确定 using 语句是如何工作的,我只是从网上找到的一个示例中修改了该语句以发送 http 请求。因此,如果有人也可以解释 using 语句,那就太好了。

添加定时器代码。

    public void update(string user_worker)
    {

      System.Timers.Timer timer = new System.Timers.Timer(60000);
      timer.Elapsed += (sender, e) =>
      {
          //query the miner for summary and gpucount information
          String SummaryQuery = QueryMiner("summary");
          String gpuNum = FindKey(QueryMiner("gpucount"), "Count");
          //String PoolQuery = QueryMiner("pools");
          int numgpus = Convert.ToInt32(gpuNum);
          //Array of strings to hold each gpu query
          String[] gpuQueries = new String[numgpus];
          //add the GPU queries into the array
          for (int i = 0; i < numgpus; i++)
              gpuQueries[i] = QueryMiner("gpu|" + i);

          //now add information specific to each gpu to a list
          List<string> gpuList = new List<string>();
          for (int i = 0; i + 1 <= gpuQueries.Length; i++)
          {
              gpuList.Add(FindKey(gpuQueries[i], "Temperature"));
              gpuList.Add(FindKey(gpuQueries[i], "MHS 5s"));
          }
          //set all the values that we have gotten from the queries
          this.worker_user_name = user_worker;
          this.hashrate = FindKey(SummaryQuery, "MHS av");
          this.accepted = FindKey(SummaryQuery, "Accepted");
          this.rejected = FindKey(SummaryQuery, "Rejected");
          this.hw_errors = FindKey(SummaryQuery, "Hardware Errors");
          this.num_gpu = gpuNum;
          this.gpus = gpuList.ToArray();
          //create JSON from the workerUpdate object
          string JSON = JsonConvert.SerializeObject(this);
          //send to website
          HttpPutRequest(JSON);
      };
     timer.Start();
    }

我现在在日志中发现了异常。例外情况如下。但不确定是什么意思。

Exception: System.NullReferenceException: Object reference not set to an instance of an object.
 at MiningMonitorClientW.WorkerUpdate.HttpPutRequest(String Json)
 at MiningMonitorClientW.WorkerUpdate.<>c__DisplayClass1.<update>b__0(Object sender, ElapsedEventArgs e)

更新: 所以我做了更多的调试,我注意到当我收到错误时,我的代码会一直运行到这一行

using (var streamWriter = new StreamWriter(httpWebRequest.GetRequestStream()))

也许它试图创建一个流写入,当我请求流时我得到一个空响应?不确定。但例外是说某事是空的。

【问题讨论】:

  • 您错过了最重要的一段代码。.Net 中提供的众多计时器中,您使用的是哪个计时器以及如何使用?
  • 现在把定时器代码放进去!
  • 异常似乎是说您使用空参数调用了方法HttpPutRequest。在方法开头打勾:if (Json == null) throw new ArgumentNullException();
  • 我添加了 if 语句,我仍然得到与上面相同的异常。同样奇怪的是,一旦出现此错误,错误就会不停地重复,直到我重新启动程序,但奇怪的是我仍在打印出可读的 JSON 字符串(通过日志)。
  • 我觉得这条线有问题using (var streamWriter = new StreamWriter(httpWebRequest.GetRequestStream()))

标签: c# http networking


【解决方案1】:

您应该在整个 HttpPutRequest 方法体周围放置一个 try/catch 块。 httpWebRequest.GetRequestStream() 可能失败或超时。

【讨论】:

  • 我要试试。我正在使用 heroku 托管网站,我想知道他们是否在某个时间点后阻止了请求?
  • 不确定我是否正确创建了 try/catch 块,但更新了主帖中的代码。运行客户端应用程序后,我看到了相同的行为,但没有新的日志信息。
  • 实际上,他应该在整个 Elapsed 事件处理程序周围放置一个 try/catch,因为System.Timers.Timer 将抑制任何逃脱处理程序的异常。如果您想知道是否发生异常,您必须在处理程序中捕获它们。
  • 我实际上不认为这会起作用,因为计时器永远运行。
  • 你需要做catch(Exception we2)。特别是它可能不是WebException
【解决方案2】:

这不会解决您的问题,但会帮助您找出问题所在。

必须在您的事件处理程序中添加一个 try/catch。否则,如果发生异常,您将永远不会知道它,因为System.Timers.Timer 将抑制任何逃脱处理程序的异常。在下面的代码中,我只是在您现有的代码中添加了一个 try/catch:

public void update(string user_worker)
{

  System.Timers.Timer timer = new System.Timers.Timer(60000);
  timer.Elapsed += (sender, e) =>
  {
    try
    {
      //query the miner for summary and gpucount information
      String SummaryQuery = QueryMiner("summary");
      String gpuNum = FindKey(QueryMiner("gpucount"), "Count");
      //String PoolQuery = QueryMiner("pools");
      int numgpus = Convert.ToInt32(gpuNum);
      //Array of strings to hold each gpu query
      String[] gpuQueries = new String[numgpus];
      //add the GPU queries into the array
      for (int i = 0; i < numgpus; i++)
          gpuQueries[i] = QueryMiner("gpu|" + i);

      //now add information specific to each gpu to a list
      List<string> gpuList = new List<string>();
      for (int i = 0; i + 1 <= gpuQueries.Length; i++)
      {
          gpuList.Add(FindKey(gpuQueries[i], "Temperature"));
          gpuList.Add(FindKey(gpuQueries[i], "MHS 5s"));
      }
      //set all the values that we have gotten from the queries
      this.worker_user_name = user_worker;
      this.hashrate = FindKey(SummaryQuery, "MHS av");
      this.accepted = FindKey(SummaryQuery, "Accepted");
      this.rejected = FindKey(SummaryQuery, "Rejected");
      this.hw_errors = FindKey(SummaryQuery, "Hardware Errors");
      this.num_gpu = gpuNum;
      this.gpus = gpuList.ToArray();
      //create JSON from the workerUpdate object
      string JSON = JsonConvert.SerializeObject(this);
      //send to website
      HttpPutRequest(JSON);
    }
    catch (Exception ex)
    {
        // handle exception here
    }
  };
 timer.Start();
}

catch (Exception) 通常是不好的做法,但是System.Timers.Timer 的设计强制它。正如文档所说:

在 .NET Framework 2.0 及更早版本中,Timer 组件捕获并抑制由事件处理程序为 Elapsed 事件引发的所有异常。此行为可能会在 .NET 的未来版本中发生变化框架。

这种行为在 .NET 4.5 中没有改变。

如果你没有捕捉到异常,那么计时器会抑制它,这意味着你永远不会知道异常发生了。

更新

在大多数情况下,您应该只捕获那些您知道如何处理的特定异常。例如,如果你想捕捉IOExceptionWebException,你可以这样写:

catch (WebException wex)
{
    // handle WebException
}
catch (IOException ioex)
{
    // handle IOException
}

正如我上面所说,捕获Exception 被认为是一种不好的做法,因为您可能不知道如何处理它,而且最好让您的程序因未处理的异常而崩溃,而不是盲目地继续前进的未知错误。但是因为System.Timers.Timer 会抑制异常,所以你必须在Elapsed 处理程序中捕获所有异常,如果没有其他原因,只能记录它们,这样你就知道到底发生了什么。或者你可能有一些方法让你的处理程序通知主线程事情不正常并且需要关闭。所以在你的Elapsed 处理程序中,你必须捏住鼻子,做你通常不应该做的事情。在所有特定异常之后,添加:

catch (Exception ex)
{
    // handle all other exceptions
}

【讨论】:

  • 感谢您向我展示一些示例代码。如果我想捕获多个异常,我可以有多个 catch 块,还是我可以只说 Exception EX,它会捕获任何抛出的异常?
  • 终于遇到异常了!谢谢你帮助我。为原始帖子添加例外
猜你喜欢
  • 2018-07-12
  • 1970-01-01
  • 2022-12-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-13
  • 2021-01-17
相关资源
最近更新 更多