创建WCF - Singleton对象池

时间:2015-04-04 12:38:35

标签: wcf singleton concurrent-collections objectpool

我正在尝试创建我的应用程序将使用的第三方许可证池(因为这些许可证是有限的)。我一直指this来编写我的代码。

基本上,我的Web应用程序连接到中间层WCF服务(每次调用实例)以执行所有业务功能。现在,要将此第三方工具与我的应用程序集成,我将创建第二个单独的WCF(自托管)服务作为许可证池来保存所有可用的许可证。因此,当web-app用户需要利用第三方功能进行任何活动时,我的中间层服务将首先从单身许可证池中获取许可证,执行业务任务,将许可证返回到池中,然后最终返回对用户的响应。

public class SessionPoolService : IDisposable
{
    //This holds the sessions currently in pool
    private BlockingCollection<AxSessionTicket> _pool;

    private int _blockingTimeout;
    public CancellationToken _cancel;
    private Guid Id;
    private static SessionPoolService _instance = null;

    //Factory
    public static SessionPoolService GetInstance()
    {
        int availableLicenses = int.Parse(System.Configuration.ConfigurationManager.AppSettings["AvailableLicenses"]);
        int blockingTimeOutInMS = int.Parse(System.Configuration.ConfigurationManager.AppSettings["BlockingTimeOutInMilliSecs"]);

        if (_instance == null)
            _instance = new SessionPoolService(new CancellationToken(), availableLicenses, blockingTimeOutInMS);

        return _instance;
    }

    private SessionPoolService(CancellationToken ct, int availableLicenses, int blockingTimeOutInMS)
    {
        _pool = new BlockingCollection<AxSessionTicket>(new ConcurrentQueue<AxSessionTicket>(), availableLicenses);

        _blockingTimeout = blockingTimeOutInMS;
        _cancel = ct;
        Id = Guid.NewGuid();
        Init(availableLicenses);
    }

    public string GetObject()
    {
        AxSessionTicket retObj = new AxSessionTicket(string.Empty);
        if (this._pool.IsCompleted)
        {
            ExceptionMgmtService.GetInstance().Error("Error occured at GetObject: Could not acquire License within specified time."); 
            throw new FaultException("Adding has been marked completed and there are no more items available in the collection.");
        }
        else
            try
            {
                if (!this._pool.TryTake(out retObj, _blockingTimeout, _cancel))
                {
                    ExceptionMgmtService.GetInstance().Error("Error occured at GetObject: Could not acquire License within specified time.");
                    throw new FaultException("Could not acquire License within specified time.");
                }
                else
                {
                    var sessionId = LicensePool.AuthenticationService.GetInstance().DoLogin(retObj.SessionId);
                    retObj = new AxSessionTicket(sessionId);

                    //HealthMonitorService.GetInstance().AddItem(retObj);
                }
            }
            catch (OperationCanceledException) // This will happen when operation is cancelled via the token.
            {
                //Logout all sessions
                ForceLogOutEntirePool();

                ExceptionMgmtService.GetInstance().Error("Error occured at GetObject: 'Get' operation has been cancelled.");
                throw new FaultException("'Get' operation has been cancelled.");
            }

        TracerService.GetInstance().TraceToConsole.TraceInformation("Instance #{1}: '{0}' issued. Pool strength: {2}", retObj.SessionId, this.Id, this._pool.Count);
        return retObj.SessionId;
    }

    public void ReturnObject(string lic, bool addToTransaction = true)
    {
        var sessionObj = new AxSessionTicket(lic);
        try
        {
            if (!_pool.TryAdd(sessionObj, _blockingTimeout, _cancel))
            {
                ExceptionMgmtService.GetInstance().Error("Error occured at ReturnObject: Could not add License within specified time.");
                throw new FaultException("Could not add License within specified time.");
            }
            else
            {
                TracerService.GetInstance().TraceToConsole.TraceInformation("Instance #{1}: Adding '{0}' to pool. Pool strength: {2}", lic, this.Id, this._pool.Count);

                //if (addToTransaction)
                //    HealthMonitorService.GetInstance().RemoveItem(new AxSessionTicket(lic));
            }
        }
        catch (OperationCanceledException)
        {
            //Logout the passed-in session.
            LicensePool.AuthenticationService.GetInstance().DoLogout(lic);

            //Logout all sessions
            ForceLogOutEntirePool();

            ExceptionMgmtService.GetInstance().Error("Error occured at ReturnObject: 'Return' operation has been cancelled.");
            throw new FaultException("'Return' operation has been cancelled.");
        }
    }

    public void ForceLogOutEntirePool()
    {
        //Stop any further additions.
        this._pool.CompleteAdding();

        foreach (var item in this._pool.GetConsumingEnumerable())
        {
            TracerService.GetInstance().TraceToConsole.TraceInformation("Logging out '{0}'.", item.SessionId);
            LicensePool.AuthenticationService.GetInstance().DoLogout(item.SessionId);
        }
    }

    private void Init(int availableLicenses)
    {
        TracerService.GetInstance().TraceToConsole.TraceInformation("Pool initializing..... ");
        // Fill till half the capacity.
        var authManager = LicensePool.AuthenticationService.GetInstance();
        for (int i = 0; i < availableLicenses; i++)
        {
            if (i < Math.Floor(availableLicenses / 2.0))
            {
                var sessionId = Guid.NewGuid().ToString();
                ReturnObject(sessionId, false);   //Fill-in dummy guids in first half and actual license-keys in second half.
            }
            else
            {
                var sessionId = authManager.DoLogin(string.Empty);
                ReturnObject(sessionId, false);
            }
        }

        //Start Monitor service
        //HealthMonitorService.GetInstance().RunMonitor();

        TracerService.GetInstance().TraceToConsole.TraceInformation("Pool ready for use...");
    }

    public void Dispose()
    {
        ForceLogOutEntirePool();
        HealthMonitorService.GetInstance().Dispose();
    }
}

[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single, ConcurrencyMode=ConcurrencyMode.Multiple)]
public class SessionPool : ISessionPool, IDisposable
{
    public SessionPoolService pool = SessionPoolService.GetInstance();
    public string GetSessionKey()
    {
        TracerService.GetInstance().TraceToConsole.TraceInformation("Caller #{0}: Request to acquire sessionTicket.", System.Threading.Thread.CurrentThread.ManagedThreadId);
        return pool.GetObject();
    }

    public void ReturnSessionKey(string sessionKey)
    {
        TracerService.GetInstance().TraceToConsole.TraceInformation("Caller #{0}: Request to release sessionTicket - '{1}'", System.Threading.Thread.CurrentThread.ManagedThreadId, sessionKey);
        pool.ReturnObject(sessionKey);
    }

    public void Dispose()
    {
        pool.Dispose();
    }
}

Q1 - 这是处理有限许可的正确方法吗?如果没有,有哪些替代方案?

Q2 - 只要我的单件服务顺序调用,池工作正常,但当我尝试发出并行调用时,我开始收到错误 - 比如,2个调用者被发出相同的许可证密钥,池发出空白密钥等我试图将并发 - 呼叫限制设置为我的池的大小,但它没有帮助。 你能帮忙指出上面的问题吗?

0 个答案:

没有答案