无状态 WCF 服务中的 ConcurrencyMode.Multiple

发布于 2024-10-09 01:37:35 字数 1314 浏览 0 评论 0原文

我们当前有多个使用默认 ServiceBehavior 的 WCF 服务。由于可扩展性问题,我们正在考虑应用 ConcurrencyMode = ConcurrencyMode.Multiple 属性来提高吞吐量。我们所有的服务调用都是完全无状态的,例如:

PersonService.cs:

public class PersonService : IPersonService
{
  public GetPersonResponse GetPerson(GetPersonRequest request)
  {
    GetPersonResponse response = new GetPersonResponse();

    try
    {
      response.Person = Person.GetPerson(request.PersonID);

      return response;
    }
    catch (Exception ex)
    {
      return (GetPersonResponse) response.SetException(ex);
    }
  }
}

Person.cs:

public static class Person
{
  public static PersonDataContract GetPerson(int personID)
  {
    PersonDataContract pdc = null;

    // load contract from db...
    pdc = Database.Load<PersonDataContract>(personID);

    // Address is another static class in the same pattern as Person
    pdc.Addresses = Address.GetAddressesForPerson(personID);

    return pdc;
  }
}

Person 类中的所有方法都是静态的,以提高性能,无状态以保证线程安全。 Database 类也是静态的,但它的方法引用静态变量。

在这种情况下,为了使 ConcurrencyMode.Multiple 不会导致多线程问题,需要如何确保线程安全?我只考虑 Database 类,但是 Person 类(以及遵循相同模式的所有其他类)也需要锁定吗?

我知道所有类都应该是防弹的,以实现最大的安全性,但不幸的是时间限制不允许这样做......我们需要尽快交付代码。

We currently have multiple WCF services which are using the default ServiceBehavior. Due to issues with scalability, we are looking at applying the ConcurrencyMode = ConcurrencyMode.Multiple attribute to improve throughput. All of our service calls are completely stateless, for example:

PersonService.cs:

public class PersonService : IPersonService
{
  public GetPersonResponse GetPerson(GetPersonRequest request)
  {
    GetPersonResponse response = new GetPersonResponse();

    try
    {
      response.Person = Person.GetPerson(request.PersonID);

      return response;
    }
    catch (Exception ex)
    {
      return (GetPersonResponse) response.SetException(ex);
    }
  }
}

Person.cs:

public static class Person
{
  public static PersonDataContract GetPerson(int personID)
  {
    PersonDataContract pdc = null;

    // load contract from db...
    pdc = Database.Load<PersonDataContract>(personID);

    // Address is another static class in the same pattern as Person
    pdc.Addresses = Address.GetAddressesForPerson(personID);

    return pdc;
  }
}

All methods in the Person class are static to help performance, and stateless for thread safety. The Database class is also static, but its methods reference static variables.

In this context, what needs to be made thread-safe in order for ConcurrencyMode.Multiple to not cause multithreading issues? I'm thinking only the Database class, but does the Person class (and all other classes that follow the same pattern) need to be locked as well?

I know that all classes should be bulletproofed for maximum safety, but unfortunately time constraints don't allow this... we need to get code delivered ASAP.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

同尘 2024-10-16 01:37:35

如果您使用默认的“每次调用”激活机制(如果您的服务完全无状态,则该机制非常有效),那么添加 ConcurrencyMode.Multiple 绝对没有意义,因为每个传入请求都会获得自己的实例处理其请求的服务类。这是 InstanceContextMode 的首选和推荐设置。

在 MSDN 杂志上阅读有关 WCF 实例管理的更多信息:探索用于开发 WCF 的强大实例管理技术应用

唯一能从使用 ConcurrencyMode.Multiple 中受益的时候是当您拥有单例 WCF 服务时 - 但强烈建议不要这样做,因为它 a) 可扩展性的一大障碍,b) )正确编程非常棘手。

我的建议是:尝试更详细地缩小真正导致性能问题的原因。仅仅跳入 ConcurrencyMode.Multiple 似乎是错误的方法 - 它非常混乱,非常劳动密集型,大量代码,很多出错的机会......

If you use the default "per call" activation mechanism (which works great if your services are completely stateless), there's absolutely no point in adding the ConcurrencyMode.Multiple since each incoming request will get its own instance of the service class to handle its request. This is the preferred and recommended setting for InstanceContextMode.

Read more about instance management in WCF at MSDN Magazine: Discover Mighty Instance Management Techniques For Developing WCF Apps

The only time when you benefit from using ConcurrencyMode.Multiple is when you have a singleton WCF service - but this is highly discouraged, since it's a) a big hindrance for scalability, and b) extremely tricky to program properly.

My recommendation would be: try to narrow down more in detail what really causes the performance problems. Just jumping into ConcurrencyMode.Multiple seems like the wrong approach - it's very messy, very labor-intensive, lots of code, lots of chance of getting it wrong......

荆棘i 2024-10-16 01:37:35

如果您的服务是无状态且线程安全的,则最好将 InstanceContextMode.Single 与 ConcurrencyMode.Multiple 结合使用。

这将避免创建服务实例,减少内存占用,减少GC活动。

几乎我开发的每项服务都是以这种方式完成的......

除非需要为每个调用或会话保留单独的实例,否则我会按照他的方式进行。

其他答案提到“单例 WCF 服务 - 但这是非常不鼓励的,因为它 a) 可扩展性的一大障碍,b) 正确编程极其棘手。”

我们并不强烈劝阻它,对可扩展性没有影响(事实上可能是有益的),而且没有什么棘手的。

更新:对于 InstanceContextMode.Single 的作用似乎存在混淆。
此设置将强制 WCF 引擎仅创建该服务的一个实例。

有些人错误指出它不适用于 basicHttpBinding。

所以我发布一个例子来一劳永逸地解决这个问题。

这是我的服务类:

using System.ServiceModel;

namespace WcfService1
{
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
    public class Service1 : IService1
    {
        private int _singleCounter = 0;//this field will be shared across all requests
        public Service1() 
        {
            //this executes only ONCE
        }
        public string GetData()
        {
            //this will increment with each request 
            //because it is a SINGLE instance the count
            //will be preserved
            _singleCounter++;
            return string.Format("Requests on this instance: {0}", _singleCounter);
        }
    }
}

这是我的服务合同:

using System.ServiceModel;

namespace WcfService1
{    
    [ServiceContract]
    public interface IService1
    {
        [OperationContract]
        string GetData();
    }
}

这是我的配置文件:

<?xml version="1.0"?>
<configuration>
  <appSettings>
    <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
  </appSettings>
  <system.web>
    <compilation debug="true" targetFramework="4.5" />
    <httpRuntime targetFramework="4.5"/>
  </system.web>
  <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <!-- To avoid disclosing metadata information, set the values below to false before deployment -->
          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
          <!-- To receive exception details in faults for debugging purposes, set the value below to true.  Set to false before deployment to avoid disclosing exception information -->
          <serviceDebug includeExceptionDetailInFaults="false"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <protocolMapping>
        <add binding="basicHttpsBinding" scheme="https" />
    </protocolMapping>    
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
  </system.serviceModel>
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true"/>
    <directoryBrowse enabled="true"/>
  </system.webServer>
</configuration>

现在是结果的一些屏幕截图:
这是第一个请求,您可以看到计数为 1:
输入图片这里的描述

现在我再次点击,你可以看到计数是2:
输入图片此处描述

点击几下后:

如您所见,它是一个SINGLE实例,在多个请求中保存一个变量

If your service is stateless and thread safe it is better to use InstanceContextMode.Single combined with ConcurrencyMode.Multiple.

This will avoid the creation of instances of the service, reduce the memory footprint, reduce GC activity.

Almost every service I develop is done in this way....

Unless there was the need to keep separate instances per call or session I would got his way.

Other answers mention "singleton WCF service - but this is highly discouraged, since it's a) a big hindrance for scalability, and b) extremely tricky to program properly."

It is NOT highly discouraged, has no impact on scalability ( as a matter of fact can be benefitial ), and there is NOTHIN tricky about it.

UPDATE: There seems to be confusion about what InstanceContextMode.Single does.
This setting will force the WCF engine to create only ONE instance of the service.

Some people have WRONGLY stated it does not work with basicHttpBinding.

So I am posting an example to clear this once and for all.

Here is my Service class:

using System.ServiceModel;

namespace WcfService1
{
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
    public class Service1 : IService1
    {
        private int _singleCounter = 0;//this field will be shared across all requests
        public Service1() 
        {
            //this executes only ONCE
        }
        public string GetData()
        {
            //this will increment with each request 
            //because it is a SINGLE instance the count
            //will be preserved
            _singleCounter++;
            return string.Format("Requests on this instance: {0}", _singleCounter);
        }
    }
}

Here is my service contract:

using System.ServiceModel;

namespace WcfService1
{    
    [ServiceContract]
    public interface IService1
    {
        [OperationContract]
        string GetData();
    }
}

This is my config file:

<?xml version="1.0"?>
<configuration>
  <appSettings>
    <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
  </appSettings>
  <system.web>
    <compilation debug="true" targetFramework="4.5" />
    <httpRuntime targetFramework="4.5"/>
  </system.web>
  <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <!-- To avoid disclosing metadata information, set the values below to false before deployment -->
          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
          <!-- To receive exception details in faults for debugging purposes, set the value below to true.  Set to false before deployment to avoid disclosing exception information -->
          <serviceDebug includeExceptionDetailInFaults="false"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <protocolMapping>
        <add binding="basicHttpsBinding" scheme="https" />
    </protocolMapping>    
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
  </system.serviceModel>
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true"/>
    <directoryBrowse enabled="true"/>
  </system.webServer>
</configuration>

And now some screen shots of the results:
This is the first request as you can see the count is 1:
enter image description here

Now I click again and you can see the count is 2:
enter image description here

A few clicks later:
enter image description here

As you can see it is a SINGLE instance keeping a variable across many requests.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文