using System; using System.Collections.Generic; using System.Diagnostics; using System.Net; using System.Threading; namespace CYQ.Data.Cache { /// /// The SocketPool encapsulates the list of PooledSockets against one specific host, and contains methods for /// acquiring or returning PooledSockets. /// internal class HostNode : IDisposable { //用于验证权限的委托事件。 internal delegate bool OnAfterSocketCreateDelegate(MSocket socket); internal event OnAfterSocketCreateDelegate OnAfterSocketCreateEvent; private static LogAdapter logger = LogAdapter.GetLogger(typeof(HostNode)); #region 可对外的属性 //Public variables and properties public readonly string Host; /// /// 扩展属性:如果链接后需要验证密码(如Redis可设置密码) /// public string Password; //Debug variables and properties public int NewSockets = 0; public int FailedNewSockets = 0; public int ReusedSockets = 0; public int DeadSocketsInPool = 0; public int DeadSocketsOnReturn = 0; public int Acquired = 0; /// /// 最后的错误信息。 /// public string Error; /// /// 当前 Socket 池的可用数量。 /// public int Poolsize { get { return socketQueue.Count; } } /// /// 主机节点是不是挂了。 /// public bool IsEndPointDead = false; public DateTime DeadEndPointRetryTime; #endregion /// /// 备份的Socket池,如果某主机挂了,在配置了备份的情况下,会由备份Socket池提供服务。 /// public HostNode HostNodeBak; /// /// Socket的挂科时间。 /// private DateTime socketDeadTime = DateTime.MinValue; private int maxQueue = 128; private int minQueue = 2; /// /// If the host stops responding, we mark it as dead for this amount of seconds, /// and we double this for each consecutive failed retry. If the host comes alive /// again, we reset this to 1 again. /// private int deadEndPointSecondsUntilRetry = 1; private const int maxDeadEndPointSecondsUntilRetry = 60 * 10; //10 minutes private HostServer hostServer; private Queue socketQueue = new Queue(16); internal HostNode(HostServer hostServer, string host) { this.hostServer = hostServer; string[] items = host.Split('-'); Host = items[0].Trim(); if (items.Length > 1) { Password = items[1].Trim(); } } /// /// Gets a socket from the pool. /// If there are no free sockets, a new one will be created. If something goes /// wrong while creating the new socket, this pool's endpoint will be marked as dead /// and all subsequent calls to this method will return null until the retry interval /// has passed. /// 这个方法扩展(备份链接池) /// internal MSocket Acquire() { //检测当前是否挂科,如果是(15分钟内),由备份服务器提供服务 if (socketDeadTime.AddMinutes(15) >= DateTime.Now && HostNodeBak != null) { return HostNodeBak.Acquire(); } //Do we have free sockets in the pool? //if so - return the first working one. //if not - create a new one. Interlocked.Increment(ref Acquired); lock (socketQueue) { while (socketQueue.Count > 0) { MSocket socket = socketQueue.Dequeue(); if (socket != null && socket.IsAlive) { Interlocked.Increment(ref ReusedSockets); return socket; } Interlocked.Increment(ref DeadSocketsInPool); } } //If we know the endpoint is dead, check if it is time for a retry, otherwise return null. if (IsEndPointDead) { if (DateTime.Now > DeadEndPointRetryTime) { //Retry IsEndPointDead = false; } else { //Still dead return null; } } //Try to create a new socket. On failure, mark endpoint as dead and return null. try { MSocket socket = new MSocket(this, Host); Interlocked.Increment(ref NewSockets); //Reset retry timer on success. //不抛异常,则正常链接。 if (OnAfterSocketCreateEvent != null) { OnAfterSocketCreateEvent(socket); } deadEndPointSecondsUntilRetry = 1; return socket; } catch (Exception e) { Interlocked.Increment(ref FailedNewSockets); logger.Error("Error connecting to: " + Host, e); //Mark endpoint as dead IsEndPointDead = true; //Retry in 2 minutes DeadEndPointRetryTime = DateTime.Now.AddSeconds(deadEndPointSecondsUntilRetry); if (deadEndPointSecondsUntilRetry < maxDeadEndPointSecondsUntilRetry) { deadEndPointSecondsUntilRetry = deadEndPointSecondsUntilRetry * 2; //Double retry interval until next time } socketDeadTime = DateTime.Now; //返回备份的池 if (HostNodeBak != null) { return HostNodeBak.Acquire(); } return null; } } /// /// Returns a socket to the pool. /// If the socket is dead, it will be destroyed. /// If there are more than MaxPoolSize sockets in the pool, it will be destroyed. /// If there are less than MinPoolSize sockets in the pool, it will always be put back. /// If there are something inbetween those values, the age of the socket is checked. /// If it is older than the SocketRecycleAge, it is destroyed, otherwise it will be /// put back in the pool. /// internal void Return(MSocket socket) { //If the socket is dead, destroy it. if (!socket.IsAlive || hasDisponse) { Interlocked.Increment(ref DeadSocketsOnReturn); socket.Close(); } else { //Clean up socket socket.Reset(); //Check pool size. if (socketQueue.Count >= maxQueue) { //If the pool is full, destroy the socket. socket.Close(); } else if (socketQueue.Count > minQueue && socket.CreateTime.AddMinutes(30) < DateTime.Now) { //socket 服务超过半小时的,也可以休息了,只保留最底个数。 //If we have more than the minimum amount of sockets, but less than the max, and the socket is older than the recycle age, we destroy it. socket.Close(); } else { //Put the socket back in the pool. lock (socketQueue) { socketQueue.Enqueue(socket); } } } } #region IDisposable 成员 bool hasDisponse = false; public void Dispose() { hasDisponse = true; while (socketQueue.Count > 0) { socketQueue.Dequeue().Close(); } } #endregion } }