我一直在通过AJAX调用使用服务堆栈一段时间没有问题,但最近创建了一个使用服务堆栈客户端(特别是JsonServiceClient)的快速winforms应用程序。
但是 - 我遇到了一个问题,即我一直在通话中超时,这在前两次尝试中成功运行。它看起来像服务堆栈客户端持有某些资源,或者我以错误的方式使用客户端。它仅在针对远程服务运行时发生(每次在本地计算机上运行)。这是我的代码,例外:
var url = "http://www.TestServer.com/api";
var taskId = Guid.Parse("30fed418-214b-e411-80c1-22000a5b9fe5");
var email = "admin@example.com";
using (var client = new JsonServiceClient(url))
{
var result = client.Send(new Authenticate {UserName = "username", Password = "Password01", RememberMe = true});
client.Put(new AssignTask { AdminTaskId = taskId, Assignee = email });//Call #1 - works fine
client.Put(new AssignTask { AdminTaskId = taskId, Assignee = email });//Call #2 - works fine
try
{
client.Put(new AssignTask { AdminTaskId = taskId, Assignee = email });//Call #3 - works fine
}
catch (WebException ex)
{
//Times out every time
//at System.Net.HttpWebRequest.GetRequestStream(TransportContext& context)
//at System.Net.HttpWebRequest.GetRequestStream()
//at ServiceStack.Net40PclExport.GetRequestStream(WebRequest webRequest)
//at ServiceStack.ServiceClientBase.<>c__DisplayClassa.<SendRequest>b__9(HttpWebRequest client)
//at ServiceStack.ServiceClientBase.PrepareWebRequest(String httpMethod, String requestUri, Object request, Action`1 sendRequestAction)
//at ServiceStack.ServiceClientBase.SendRequest(String httpMethod, String requestUri, Object request)
//at ServiceStack.ServiceClientBase.Send[TResponse](String httpMethod, String relativeOrAbsoluteUrl, Object request)
//at ServiceStack.ServiceClientBase.Put[TResponse](String relativeOrAbsoluteUrl, Object requestDto)
//at ServiceStack.ServiceClientBase.Put(Object requestDto)
//at SSClientIssue.Program.Main(String[] args) in c:\Users\David\Documents\Visual Studio 2013\Projects\SSClientIssue\SSClientIssue\Program.cs:line 27
throw;
}
}
超时后,我可以关闭并重新加载应用程序(服务器保持运行状态),然后再次获得相同的行为(两次成功通话)。 IIS日志显示第3次调用未进入服务器,因此看起来像是客户端问题。
我一直在看这个8个小时,我觉得我的眼睛开始流血......如果有人可以帮我,我会给你买啤酒!
答案 0 :(得分:7)
问题是由于您的ServiceClient
请求未指定已知的响应类型。
可以使用IReturn<T>
标记(推荐)在Request DTO上标记响应类型:
public class GetAllAdminUsernamesRequest : IReturn<List<string>> { ... }
通过在Request DTO上添加此功能,ServiceClient能够自动推断并转换响应,例如:
List<string> response = client.Get(new GetCurrentAdminUserAdminTasks());
否则,在请求DTO上指定响应的替代方法是在呼叫站点上指定它,例如:
List<string> response = client.Get<List<string>>(new GetCurrentAdminUserAdminTasks());
如果您不这样做,则响应未知,因此ServiceClient将返回基础HttpWebResponse
,以便您自己检查响应。
HttpWebResponse tasks = client.Get(new GetCurrentAdminUserAdminTasks());
为了能够检查和读取HttpWebResponse
,ServiceClient无法处理响应,因此由呼叫站点提出正确处理它的请求,即:
using (HttpWebResponse tasks = client.Get(new GetCurrentAdminUserAdminTasks())) {}
using (HttpWebResponse adminUsers = client.Get(new GetAllAdminUsernames())) {}
try
{
using (client.Put(new AssignTask { AdminTaskId = taskId, Assignee = user })) {}
using (client.Put(new AssignTask { AdminTaskId = taskId, Assignee = user })) {}
using (client.Put(new AssignTask { AdminTaskId = taskId, Assignee = user })) {}
using (client.Put(new AssignTask { AdminTaskId = taskId, Assignee = user })) {}
}
...
处理您的WebResponses回复将解决您的问题。
如果不这样做,基础WebRequest
将限制打开的连接,并且在任何时候只允许有限数量的同时连接通过,可能作为防止DDOS攻击的安全防护。这就是保持底层连接打开并阻止WebRequest
阻止等待它们被释放的原因。