IIS配置设置和异步Silverlight调用 [英] IIS Configuration settings and async Silverlight calls

查看:61
本文介绍了IIS配置设置和异步Silverlight调用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用Silverlight的应用程序。问题基于更新过程。



更新使用代理(通过InvokeAsync)调用Web服务。



它将调用BeginInvoke X次,然后调用EndInvoke时我们会发现它们是否失败。



这些中的每一个请求是一个单独的HTTP请求(实际上是拒绝服务攻击)。



假设我们发送了2000个更新,实际上只有大约500-600个处理,其余的不会。



我运行了wireshark捕获,似乎所有条目都被发送到服务器并得到服务器的确认。



我最初的想法是尝试让这个应用程序同步运行,但是如果没有某种框架(至少那就是我得出的结论),这是不可能的,并且有很多在我工作的地方背后的政治。一位同事表示,如果可能只是某些需要更改的IIS配置,可能没有必要完成所有工作。



我试过更改队列服务器和应用程序池的长度,我增加了每个处理器的线程数,检查CPU使用率是否有限(NoLimit vs Kill)。我已经看了几个星期,我完全迷失了。任何帮助都会有很大的吸引力。如果我错过了什么,请告诉我。谢谢!



更新:我应该补充一点,服务器没有看到任何错误。由于缺乏错误信息,DBA和我一样沮丧。调试模式下的应用程序在随机数量的更新后返回CommunicationException。



I am working on an application that uses Silverlight. The issue is based around the update process.

The update uses delegates (via InvokeAsync) which place calls to a web service.

It will call BeginInvoke X number of times and THEN when EndInvoke is called we find out if they failed or not.

Each one of these requests is a seperate HTTP request (practically a denial of service attack).

Say we send in 2000 updates, only about 500-600 will actually process and the rest wont.

I ran a wireshark capture and it appears that all the entries are being sent to the server and are acknowledged by the server.

My initial thought was to attempt to make this application run synchronously but that isn't possible for Silverlight without some kind of framework (at least that's what I've come to conclude) and there is a lot of politics behind that where I'm working. A coworker said it may not be necessary to do all that work if it may just be some IIS configurations that need to be changed.

I've tried changing the queue length for the server and the application pool, I've increased the number of threads per processor, checked to see if the CPU usage was limited (NoLimit vs Kill). I've been looking at this for a couple weeks and I'm completely lost. Any help would be greatly appretiated. Let me know if I've missed anything. Thank you!

Update: I should add that the server does not see any errors. The DBA's are as frustrated as I am because of the lack of error information. The application in debug mode returns CommunicationException after a random number of updates.

System.ServiceModel.CommunicationException was unhandled by user code
  Message=The remote server returned an error: NotFound.
  StackTrace:
       at System.ServiceModel.AsyncResult.End[TAsyncResult](IAsyncResult result)
       at System.ServiceModel.Channels.ServiceChannel.EndCall(String action, Object[] outs, IAsyncResult result)
       at System.ServiceModel.ClientBase`1.ChannelBase`1.EndInvoke(String methodName, Object[] args, IAsyncResult result)
       at WaterFrontApplication.WorkOrderServiceWrapper.WorkOrderServiceClient.WorkOrderServiceClientChannel.EndAssignCrew(String& fault, IAsyncResult result)
       at WaterFrontApplication.WorkOrderServiceWrapper.WorkOrderServiceClient.WaterFrontApplication.WorkOrderServiceWrapper.WorkOrderService.EndAssignCrew(String& fault, IAsyncResult result)
       at WaterFrontApplication.WorkOrderServiceWrapper.WorkOrderServiceClient.OnEndAssignCrew(IAsyncResult result)
       at System.ServiceModel.ClientBase`1.OnAsyncCallCompleted(IAsyncResult result)
  InnerException: System.Net.WebException
       Message=The remote server returned an error: NotFound.
       StackTrace:
            at System.Net.Browser.AsyncHelper.BeginOnUI(SendOrPostCallback beginMethod, Object state)
            at System.Net.Browser.BrowserHttpWebRequest.EndGetResponse(IAsyncResult asyncResult)
            at System.ServiceModel.Channels.HttpChannelFactory.HttpRequestChannel.HttpChannelAsyncRequest.CompleteGetResponse(IAsyncResult result)
       InnerException: System.Net.WebException
            Message=The remote server returned an error: NotFound.
            StackTrace:
                 at System.Net.Browser.BrowserHttpWebRequest.InternalEndGetResponse(IAsyncResult asyncResult)
                 at System.Net.Browser.BrowserHttpWebRequest.<>c__DisplayClass5.<EndGetResponse>b__4(Object sendState)
                 at System.Net.Browser.AsyncHelper.<>c__DisplayClass4.<BeginOnUI>b__1(Object sendState)
            InnerException:

推荐答案

所以看起来这个问题可能出现在进入服务器的异步调用的洪流中,但实际上它正在客户端崩溃。这让我很好奇,我可能会编写一个测试程序,只是为了深入挖掘这个问题并写一篇关于它的文章。那里有的东西,但我不确定具体的问题。



我认为有两种方法可以解决这个问题但是无可否认,他们都是变通办法。我认为这是一个框架问题,因此我认为直接解决它是不可能的。



第一个可能的解决方案是限制呼叫限制你发出的同时请求的数量。这可能变得相当复杂。其实现可能是设置队列,当需要处理任务时,将每个任务放在foreach循环中的队列中。然后,启动后台工作线程,该线程检查队列是否存在任务。如果存在任务,则线程将从队列中弹出x个任务,然后执行服务方法AWAITing(框架中的新异步方法)结果(或者在工作线程本身关闭方法并加入线程)。结果是您可以执行100个请求并等待所有100个请求完成。然后,检查队列计数并重新开始。您实际上可以使用应用程序启动后台工作程序并让它一直运行。它正在睡眠或处理排队的请求。



这种方法的缺点是构建后台工作程序以限制调用Web服务然后编组的复杂性回复到UI。您可能需要某种静态类来表示您的任务状态或具有线程安全属性访问器的结果。并非不可能,但相当复杂,容易遗漏会导致跨线程异常的事情......每个多线程开发人员最喜欢的例外。 :-P



或者,如果您可以更改可能是最佳方式的Web服务。您可以编写Web服务的替代版本(救援的多态性!),它接受一系列任务。然后,客户端代码将成为传递任务列表的单个调用。迭代任务的繁重工作发生在服务器上,这样整个事情就减少到一个调用和响应。



这将是一个更具可扩展性的解决方案我觉得。如果您有很多很多很多任务需要立即更新,则可能会遇到POST消息的大小限制。但是在客户端,您可以设置最大数量的数组元素,在源列表中检测它,并在需要时进行多次调用。这样的方案可能会扩展到500个同时调用时间5000个阵列元素,然后你必须看看另一个方法。



不幸的是,这些都不是简单的解决方案但我认为你的应用程序现在已经扩展到框架限制,所以你将不得不退回迷宫的这一部分,并寻找另一条通向目标的路径。
So it looks like the issue may be somewhere in the flood of async calls going to the server but it is actually breaking on the client side. This makes me curious and I may write up a test program just to dig into this deeper and maybe write an article about it. There is something going on there but I'm not exactly sure the specific issue.

I think there are two approaches to solving this but they are both, admittedly, workarounds. I think this is a framework issue and so solving it directly is out of the question in my opinion.

The first possible solution would be to throttle the calls limiting the number of simultaneous requests you send out. This could get pretty complicated. An implementation of this might be to setup a queue and when the tasks need processed, put each task on the queue in the foreach loop. Then, kick off a background worker thread which checks the queue for the presence of tasks. If tasks are present, the thread would pop x number of tasks off the queue and then execute the service method AWAITing (new async methods in the framework) the results (or kicking the methods off on worker threads themselves and joining the threads). The result is that you may execute 100 requests and wait for all 100 to complete. Then, check the queue count and start over. You could actually start the background worker with the app and let it run all the time. It is either sleeping or processing queued requests.

The drawback to this method is the complexity of building a background worker to throttle the calls out to the web service then marshaling the responses back to the UI. You would probably need some sort of static class to represent your task status or results that had thread-safe property accessors. Not impossible but fairly complicated and easy to miss something that would result in a cross-thread exception... every multithreading developer's favorite exception. :-P

Alternately, if you can change the web service that might be the best way to go. You could write an alternate version of the web service (polymorphism to the rescue!) which accepts an array of tasks. Then the client code becomes a single call passing in your list of tasks. The heavy lifting for iterating the tasks takes place on the server such that the whole thing is reduced to a single call and response.

This would be a more scalable solution as well I think. You could potentially run into the size limit of a POST message if you had lots and lots and lots of tasks to update at once. But on the client side you could set a maximum number of array elements, detect that in your source list, and make multiple-calls if you needed to. A scheme like that could potentially expand to 500 simultaneous calls times 5000+ array elements before you would have to look at another approach.

Unfortunately, neither of these are easy solutions but I think your app, as it stands now, has scaled into a framework limitation so you're going to have to backtrack out of this part of the maze and look for another route to the goal.


我把事件处理程序拿出去了,我确信我之前已经尝试过了。它现在仍然无法正常工作。这次我在Fiddler有700多个条目但是,由于成功的随机性,如果我在当天晚些时候运行它,这可能会更少。我已经发布了原始代码和我已经完成的工作。



承包商代码:

I took the event handler out and I'm sure I've tried that before. It's still not working right now. I got 700+ entries in Fiddler this time but, due to randomness of success, this could be less if I run this later on in the day. I've posted the original code and what I've done to it.

Contractor's code:
if (tasksToSend.Count > 0)
{
    this.stoneTasks = tasksToSend.ToList();

    // Update Task
    foreach (AssignTaskCriteria task in tasksToSend)
    {
        this.MinimumNumberOfRequests = 0;
        this.MaximumNumberOfRequests = 0;
        this.NumberOfRequestsReceived = 0;
        this.IsIndeterminate = false;
        this.BusyContent = "Updating the task \n" + "Please wait...";
        this.IsBusy = true;
        var proxy = new WorkOrderServiceClient();
        proxy.AssignCrewCompleted += this.ProxyUpdateTaskCompleted;
        proxy.AssignCrewAsync(task, this.currentRequestUserStateID);
        this.MaximumNumberOfRequests++;
    }
}





我的修改:



My modifications:

if (tasksToSend.Count > 0)
{
    this.stoneTasks = tasksToSend.ToList();
    this.MinimumNumberOfRequests = 0;
    this.MaximumNumberOfRequests = 0;
    this.NumberOfRequestsReceived = 0;
    WorkOrderServiceClient proxy;
    proxy = new WorkOrderServiceClient();
    proxy.AssignCrewCompleted += this.ProxyUpdateTaskCompleted;
    // Update Task
    foreach (AssignTaskCriteria task in tasksToSend)
    {
        this.IsIndeterminate = false;
        this.BusyContent = "Updating the task \n" + "Please wait...";
        this.IsBusy = true;
        proxy.AssignCrewAsync(task, this.currentRequestUserStateID);
        this.MaximumNumberOfRequests++;
    }
}







承包商的完整方法..它只用于检查第一次更新是否成功,因为在循环开始时max = 0。我重写了整个方法。




Contractor's complete method... it only used to check the success of the first update because of that max = 0 at the beginning of the loop. I rewrote this whole method.

public void ProxyUpdateTaskCompleted(object sender, AssignCrewCompletedEventArgs e)
        {
            if (((Guid)e.UserState).CompareTo(this.currentRequestUserStateID) == 0)
            {
                this.NumberOfRequestsReceived++;

                if (this.NumberOfRequestsReceived == this.MaximumNumberOfRequests)
                {
                    if (e.Error == null)
                    {
                        if (e.fault == null)
                        {
                            if (e.Result != null && e.Result.resultField.CompareTo("SUCCESS") == 0)
                            {
                                this.IsBusy = false;
                                this.DisplayAssignTaskMessage();
                                this.UpdateViewTaskDataGrid("AssignTask");
                                this.UpdateTaskOnMap();
                                this.CloseConfigAssignTask();
                            }
                            else
                            {
                                this.IsBusy = false;
                                this.ShowErrorMessage("Error updating the Task \n");
                            }
                        }
                        else
                        {
                            this.IsBusy = false;
                            this.ShowErrorMessage(string.Format("Error updating the Task: {0}", e.fault));
                        }
                    }
                    else
                    {
                        this.IsBusy = false;
                        this.ShowErrorMessage(string.Format("Error updating the Task: {0}", e.Error.Message));
                    }
                }
            }
        }


我不知道这是否真的合适但你对事件的解释帮助我想出了这个绑匪。它似乎工作!我刚刚成功更新了2317条记录。我看着他们都通过Fiddler,然后查询数据库确认。如果您有任何改进的想法,请告诉我,但基本想法已经确定。我计划在一件事情上进行更多的错误处理。



而不是循环遍历foreach循环中的任务,在执行的方法中调用一次分配任务/继续按钮。



当满足我们在最后一个发送的条件时,在完成的方法中再次调用它。



0-99

100-199

200-299 ...



I don't know if this is really "proper" but your explanation of events helped me come up with this "bandaid." It seems to work! I just successfully updated 2317 records. I watched them all go through Fiddler and then queried the database to confirm. If you have any ideas for improving this let me know but the basic idea is laid out. I plan on putting in some more error handling for one thing.

Instead of looping through the tasks in a foreach loop, this is called once in the method that executes on the assign tasks/continue button.

It is then called again in the completed method when the condition is met that we are on the last one sent.

0-99
100-199
200-299 ...

/// <summary>
/// Send only MAX 100 items from the list at one time. Will be recalled until update is complete.
/// </summary>
public void sendFragmentOfList()
{
    AssignTaskCriteria taskToSend;
    int current = this.newStart;

    WorkOrderServiceClient proxy;
    proxy = new WorkOrderServiceClient();
    proxy.AssignCrewCompleted += new EventHandler<AssignCrewCompletedEventArgs>(ProxyUpdateTaskCompleted);
    this.newStart += 100;

    while (current < this.newStart)

    {

        if (current < MaximumNumberOfRequests)

        {

            taskToSend = this.stoneTasks.ElementAt(current);

            proxy.AssignCrewAsync(taskToSend, this.currentRequestUserStateID);

        }

        current++;

    }



}





已完成方法的片段。如果我们处于LAST任务中,那么在通知用户结果之前执行不同的if语句。





Snippet from the completed method. If we are on the LAST task then a different if statement is executed before that notifies the user of results.

if (this.NumberOfRequestsReceived == this.newStart - 1)
    sendFragmentOfList();


这篇关于IIS配置设置和异步Silverlight调用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆