最近在调优SliverLightweb程序,两个下手点:wcf调用方式优化;iis并发访问优化

来源:互联网 发布:mac图片编辑大小 编辑:程序博客网 时间:2024/06/07 05:18

      把同事总结的一份wcf调用方式记录下来,感谢同事的总结与分享

  XXX系统的silverlight项目中,一般会出现当两个页面同时想wcf发送请求时(比如查询),数据会按照请求顺序一个一个返回,不管第二个请求查询数据有多快,也会等到第一个请求数据返回后才返回,影响了项目的性能。

经调查,这与wcf的并发和实例有关系,得到如下结论:


调查结果:如下

1. 为什么会出现请求按顺序返回结果的现象?

因为我们的项目用的wcf并发模式是个单线程的模式,即某时刻只能有一个线程调用服务实例,其余的线程处于等待队列中。但是当某线程回调客户端操作时,该线程就不再锁定该服务实例,等待队列中的下个线程将锁定服务实例进行调用。当回调客户端的线程执行完客户端调用后再回到服务端时,它将进入线程等待队列中重新排队等待。

通俗的讲:当客户端三次请求(A,B,C)到达服务端后,A请求将锁定服务实例,并调用服务,B请求和C请求将在等待队列中排队。在A请求处理过程中进行回调时,A请求会暂时释放对服务实例的锁定,此时队列中的B请求会锁定服务,进行调用。当B请求处理回调时,B请求也会暂时释入服务实例锁定,此时C请求会锁定服务进行调用......。当A回调结束后会重入服务实例,并在等待队列的尾部排队等待后续处理

2. 如何解决这个现象?

经调查,发现WCF有两种控制并发的行为: InstanceContextMode 和 oncurrencyMode

具体为:

 InstanceContextMode服务行为用来控制实例而且可以设置成以下三种值:

     Single. 服务类的一个实例处理所有接收到的请求。这实现了一个单例。

     PerCall. 为每个接收到的请求创建一个服务类的实例。

     PerSession. 每个客户端会话创建一个服务类实例。当使用不支持会话的信道时,所有的服务调用与PerCall一样,即便是InstanceContextMode被设置成PerSession.

ConcurrencyMode服务行为用来控制一个服务实例内部的线程并发。默认设置,ConcurrencyMode.Single,指导WCF在某一时刻旨在服务类的实例中执行一个线程。这个行为可以被设置成三个值之一:

Single. 在一个时刻只有一个线程可以访问服务类。这是最安全的设置因为服务操作不必担心线程安全。

Reentrant. 在一个时刻仅有一个线程可以访问服务类,但是这个线程可以离开类并在稍后返回继续。

    Multiple. 多个线程可以同步访问服务类。这个设置要求类创建在线程安全基础上。

默认的情况下是,两者的行为的默认选择分别是:PerSessionSingle,这样就形成了请求-响应的调用模式,造成数据阻塞,所以我们修改这两个行为相互配合:

        InstanceContextMode

ConcurrencyMode

Single

PerCall

PerSession

Single默认

服务是单例:一个实例被创建来处理请求,当一个请求呗处理时,所有的顺序请求被加入队列并按照fifo(先进先出)处理。

属于最基本的wcf请求模式,不能解决问题

每次调用都创建一个实例。并发请求没有影响,因为每个实例都有它自己线程来执行。

优点可以解决问题,但每次请求都创建一个实例,相当于new一个服务对象,如果服务的构造函数有数据加载或复杂操作,会影响性能

每个客户端会话创建一个实例,而且仅有一个线程被创建来处理那个会话的请求,如果客户端再一次会话上使用多个异步调用,它们会被放入列并按照(fifo)顺序处理。

通俗理解就是多次请求,请求还是会阻塞,是项目使用的默认模式,不能解决问题。但这是针对会话来说,项目中使用的basicHttpBinding不支持会话,会降级为PerCall,和PerCall行为一样

Reentrant

服务是单例:一个实例被创建而且仅有一个现成被创建来处理请求,当一个请求被处理后,所有的请求按顺序被放入队列中并按照fifo顺序处理。

感觉这个跟ConcurrencyModesingle是一样的,不能解决问题

每次调用都创建一个实例。并发请求没有影响,因为每个实例都有它自己线程来执行。

优点可以解决问题,但每次请求都创建一个实例,相当于new一个服务对象,如果服务的构造函数有数据加载或复杂操作,会影响性能

同上。

每个客户端会话创建一个实例,而且仅有一个线程被创建来处理那个会话的请求,如果客户端再一次会话上使用多次异步调用,它们会被放入列并按照(fifo)顺序处理。单个线程可以离开这个方法,做其他工作,稍后返回,就像服务端异步编码。

通俗理解就是多次请求,请求还是会阻塞,不能解决问题,即使服务端有异步编码,但还是一个线程处理。并且这是针对会话来说,项目中使用的basicHttpBinding不支持会话,会降级为PerCall,和PerCall行为一样

Multiple

一个实例被创建但是多个线程可以再这个实例中并行执行。类的成员必须用同步代码保护,应为同样的成员可以被多个线程修改。

这个可以解决问题,并且是这几种配合中感觉比较好的但会遇到多线程的通病,需要做类成员保护,在项目中这点修改的点可能会多。

每次调用都创建一个实例。并发请求没有影响,因为每个实例都有它自己线程来执行。

优点可以解决问题,但每次请求都创建一个实例,相当于new一个服务对象,如果服务的构造函数有数据加载或复杂操作,会影响性能

同上。

每个客户端会话创建一个实例,有多个线程可以再实例中并发执行。如果客户端在一个会话总进行多次异步调用,它们是并行的,不会影响。类的成员必须用同步代码保护,因为同样的成员可以被多个线程修改。

这个是可以解决问题的,同Single差不多,但需要做类成员保护。并且这是针对会话来说,项目中使用的basicHttpBinding不支持会话,会降级为PerCall,和PerCall行为一样

从图中可以看出:当InstanceContextMode设成PerCall时,可以解决问题,但每次请求都实例化服务,影响性能。

 当InstanceContextMode设成PerSession时,按项目中basicHttpBinding的使用,应该会降级成PerCall,但没有PerCall的效果。

ConcurrencyMode设成Multiple时,服务器用多线程处理请求,可以解决问题,但要做好代码的线程保护。

推荐: ConcurrencyModeMultipleInstanceContextModeSingle

3. 使用方式

在服务的类前写如上的行为契约。

4. 总结

使用推荐的行为方式可以解决请求阻塞的问题,但经测试当客户端向服务发两个或多个并发请求时,响应时间或多或少都会受到影响,增加了查询时间,不可避免的发生了。

原创粉丝点击