干货---实现思路:RPC Thrift 服务端 注册服务端信息到zookeeper上。客户端从zookeeper获取服务端信息,并实现负载。
经验---注意事项。
Thrift服务端
使用ZooKeeperNet nugget包。
使用zookeeper的坑,
1、)没有zookeeper.open的方法。
2、)zookeeper 实例化【new ZooKeeper(connectstring, sessionTimeout, watcher);】后,连接CONNECTED并不是同步的。
zookeeper监视,使用一次后就失效。坑
1)、zookeeper 监视某个节点,无论增加删除等操作,一律都只有EventType.NodeChildrenChanged。
2)想检测某个末端节点,使用【_zookeeper.Exists方法即可】。
功能:
1)实现简单负载,轮询负载。随机负载。
2)实现从监视节点取出服务器节点信息。
3)长连接线程池未实现。可以去掉。
4)实现自动释放socket连接资源避免,线上出问题。
使用 public static Client GetSingleInstance(string rpcServicenamespace) 实列化,负载才能正常使用。
public class Client : IDisposable
{
/// <summary>
/// 客户端池-根据不同业务自动选择对应业务客户端
/// </summary>
static Dictionary<string, Client> _clientPool = new Dictionary<string, Client>();
/// <summary>
///客户端-TBufferedTransport
/// </summary>
internal Dictionary<string, TBufferedTransport> _tBufferedTransportPool = new Dictionary<string, TBufferedTransport>();
/// <summary>
/// zookeeper注册唯一标识
/// </summary>
private string _rpcServicenamespace = null;//"XXXX.XXXX.RegionCityRPC"
/// <summary>
/// 使用一个zookeeper客户端
/// </summary>
private static ZooKeeper _zookeeper = null;
/// <summary>
/// 多线程锁
/// </summary>
internal static object _look = new object();
/// <summary>
/// zookeeper-所有监视节点
/// </summary>
public IEnumerable<string> _nodeChildren = null;
/// <summary>
/// 轮询负载计数器
/// </summary>
private int CountLoadbalance = 0;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="rpcServicenamespace"></param>
public Client(string rpcServicenamespace)
{
this._rpcServicenamespace = rpcServicenamespace;
}
/// <summary>
/// 获取一个唯一实例
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public static T GetSingleInstance<T>(Dictionary<string, T> dictionary, string key, T t) where T : class
{
if (!dictionary.Keys.Contains(key))
{
lock (_look)
{
if (!dictionary.Keys.Contains(key))
{
TBufferedTransport tbufferedTransport = t as TBufferedTransport;
//if (t != null)
//{
// try
// {
// tbufferedTransport.Open();
// }
// catch (Exception e)
// {
// Console.WriteLine("初始化TBufferedTransport连接异常!");
// }
//}
dictionary.Add(key, t);
}
}
}
return dictionary[key];
}
/// <summary>
/// 获取一个唯一实例
/// </summary>
/// <param name="rpcServicenamespace"></param>
/// <returns></returns>
public static Client GetSingleInstance(string rpcServicenamespace)
{
/*
if (!_clientPool.Keys.Contains(rpcServicenamespace))
{
lock (_look)
{
if (!_clientPool.Keys.Contains(rpcServicenamespace))
{
_clientPool.Add(rpcServicenamespace, new Client(rpcServicenamespace));
}
}
}
return _clientPool[rpcServicenamespace];
*/
return GetSingleInstance<Client>(_clientPool, rpcServicenamespace, new Client(rpcServicenamespace));
}
//public void FilterTBufferedTransportPool(Client client)
//{
// foreach (var item in client._tBufferedTransportPool)
// {
// item.Value.IsOpen
// }
//}
/// <summary>
/// 获取baoxian/appSettings配置
/// </summary>
/// <returns></returns>
private static NameValueCollection GetConfig()
{
return (NameValueCollection)ConfigurationManager.GetSection("baoxian/appSettings");
}
/// <summary>
/// 初始化
/// </summary>
public void Init()
{
CreateZookeeperClient();
}
/// <summary>
/// 创建一个zookeeper客户端
/// </summary>
/// <returns></returns>
public ZooKeeper CreateZookeeperClient()
{
string connectstring = GetConfig()["ZookeeperConnectString"];// "192.168.1.3:2331
TimeSpan sessionTimeout = new TimeSpan(0, 0, 60);
var watcher = new ClientWatcher(_rpcServicenamespace);
if (_zookeeper == null)//单线程
{
lock (_look)
{
if (_zookeeper == null)//多线程
{
_zookeeper = new ZooKeeper(connectstring, sessionTimeout, watcher);
string path = string.Format(GetConfig()[_rpcServicenamespace], _rpcServicenamespace);//string.Format("/test/servicecenter-dev/services/{0}/providers", "YinXin.BaoXian.UserOrderCount");
int count = 0;
while (true)
{
Console.WriteLine("_zookeeper 重连次数:" + count);
count++;
System.Threading.Thread.Sleep(1000);//zookeeper的这个第三方有个坑。1、)没有zookeeper.open的方法。2、)zookeeper 实例化【new ZooKeeper(connectstring, sessionTimeout, watcher);】后,连接CONNECTED并不是同步的。
if (_zookeeper.State == ZooKeeper.States.CONNECTED)
{
GetChildrenWatcher();//zookeeper监视,使用一次后就失效。坑1)、zookeeper 监视某个节点,无论增加删除等操作,一律都只有EventType.NodeChildrenChanged。2)想检测某个末端节点,使用【_zookeeper.Exists方法即可】。
break;
}
if (count >= 60)//这里大于1分钟连接不上
{
Logs.WriteLogInfo("_zookeeper网络连接可能有问题!");
Console.WriteLine("_zookeeper网络连接可能有问题!");
break;
}
}
/*
watcher.WaitUntilConnected();
while (true)
{
System.Threading.Thread.Sleep(5000);
var tt = _zookeeper.GetChildren(path, watcher);
var aa = tt + "333";
}*/
}
}
}
return _zookeeper;
}
/// <summary>
/// 客户端监视-想监视的节点
/// </summary>
public void GetChildrenWatcher()
{
string path = string.Format(GetConfig()[_rpcServicenamespace], _rpcServicenamespace);//string.Format("/test/servicecenter-dev/services/{0}/providers", "XXX.XXX.UserOrderCount");
//var watcher = new ClientWatcher();
IEnumerable<string> nodeChildren = null;
bool isError = false;
try
{
nodeChildren = _zookeeper.GetChildren(path, true);
}
catch (Exception e)
{
Console.WriteLine(e.StackTrace);
isError = true;
}
if (!isError)
{
lock (_look)
{
_nodeChildren = nodeChildren;
}
}
if (nodeChildren == null)
{
return;
}
foreach (var item in nodeChildren)
{
_zookeeper.Exists(path + "/" + item, true);
}
}
/// <summary>
/// 随机
/// </summary>
/// <returns></returns>
public string Random()
{
IEnumerable<string> nodeChildren = null;
if (_nodeChildren != null)
{
lock (_look)
{
nodeChildren = _nodeChildren;
}
}
Random random = new Random();
int count = nodeChildren.Count();
if (count == 0)
{
return "false";
}
int index = random.Next(0, nodeChildren.Count() - 1);
return nodeChildren.ToList()[index];
}
/// <summary>
/// 根据负载服务器生成TBufferedTransport
/// </summary>
/// <returns></returns>
public TBufferedTransport GetTSocket(string servers)
{
//string servers = thriftClient.Random();//随机负载
if (string.IsNullOrWhiteSpace(servers))
{
return null;
}
var list = servers.Split(':');
if (list.Count() == 2)
{
//var tbufferedTransport = GetSingleInstance<TBufferedTransport>(_tBufferedTransportPool, servers, new TBufferedTransport(new TSocket(list[0], int.Parse(list[1]))));
return GetSingleInstance<TBufferedTransport>(_tBufferedTransportPool, servers, new TBufferedTransport(new TSocket(list[0], int.Parse(list[1]))));
}
else
{
return null;
}
}
/// <summary>
/// 轮询
/// </summary>
/// <returns></returns>
public string Loadbalance()
{
int index = 0;
IEnumerable<string> nodeChildren = null;
if (_nodeChildren != null)
{
lock (_look)
{
nodeChildren = _nodeChildren;
CountLoadbalance++;
CountLoadbalance = CountLoadbalance > _nodeChildren.Count() ? 1 : CountLoadbalance;
index = CountLoadbalance;
}
}
if (index - 1 >= 0 && nodeChildren != null && nodeChildren.Count() != 0)
{
return nodeChildren.ToList()[index - 1];
}
else
{
Console.WriteLine("轮询--无节点");
Logs.WriteLogInfo("轮询--无节点");
return "轮询无节点";
}
}
/// <summary>
/// Dispose
/// </summary>
public void Dispose()
{
//关闭并销毁长连接
_zookeeper.Dispose();
foreach (var item in this._tBufferedTransportPool)
{
item.Value.Close();
item.Value.Dispose();
}
//throw new NotImplementedException();
}
}
/// <summary>
/// zookeeper 监测事件实现
/// </summary>
internal class ClientWatcher : IWatcher
{
private Client client;
//private readonly ManualResetEventSlim _connected = new ManualResetEventSlim(false);
//private WatchedEvent _event;
public ClientWatcher(string _rpcServicenamespace)
{
client = Client.GetSingleInstance(_rpcServicenamespace);
}
//public void WaitUntilConnected()
//{
// _connected.Wait();
// if (_event == null) throw new ApplicationException("bad state");
// if (_event.State != KeeperState.SyncConnected)
// throw new ApplicationException("cannot connect");
//}
/// <summary>
/// 实现Process监控事件
/// </summary>
/// <param name="event"></param>
public void Process(WatchedEvent @event)
{
//_event = @event;
switch (@event.State)
{
case KeeperState.Disconnected:
break;
case KeeperState.Expired:
break;
case KeeperState.NoSyncConnected:
break;
case KeeperState.SyncConnected:
//_connected.Set();
break;
case KeeperState.Unknown:
break;
default:
break;
}
switch (@event.Type)
{
case EventType.NodeChildrenChanged:
client.GetChildrenWatcher();//监测节点
Console.Write(" EventType.NodeChildrenChanged");
break;
case EventType.NodeCreated:
Console.Write("EventType.NodeCreated:");
break;
case EventType.NodeDataChanged:
Console.Write("EventType.NodeDataChanged:");
break;
case EventType.NodeDeleted:
if (!string.IsNullOrWhiteSpace(@event.Path))
{
#region socket 长连接时使用,慎用
string path = @event.Path.Substring(0, @event.Path.LastIndexOf('/'));
string name = @event.Path.Substring(@event.Path.LastIndexOf('/') + 1);
lock (Client._look)
{
client._tBufferedTransportPool[name].Close();
client._tBufferedTransportPool[name].Dispose();
client._tBufferedTransportPool.Remove(name);
}
#endregion
}
Console.Write("EventType.NodeDeleted:");
break;
case EventType.None:
Console.Write("EventType.None:");
break;
default:
break;
}
}
}