说起单例模式,想必大家都不陌生,在创建型模式中应该是最常用的设计模式之一了。此处就不再过多叙述,可以查看《单例模式》的文章。
应用需求
某软件公司承接了一个服务器负载均衡(LB,Load Balance
)软件的开发工作,该软件运行在一台负载均衡服务器上,可以将并发访问和数据流量分发到服务器集群中的多台设备上进行并发处理,提高了系统的整体处理能力,缩短了响应时间。由于集群中的服务器需要动态删减,且客户端请求需要统一分发,因此需要确保负载均衡器的唯一性,只能有一个负载均衡器来负责服务器的管理和请求的分发,否则将会带来服务器状态的不一致以及请求分配冲突等问题。
分析:对于上面的需求场景,问题的关键是如何确保
LB
的 唯一性 ?
单例模式设计服务器负载均衡器
接下来我们尝试使用【单例模式】设计服务器负载均衡器。
1、创建控制台应用程序
使用 vs 创建【控制台应用程序】,命名为:ConsoleApp1
,或者使用 cli
命令:
dotnet new console -n ConsoleApp1
2、模拟 LB 创建类对象
using System.Collections;
namespace ConsoleApp1;
/// <summary>
/// 模拟负载均衡器
/// </summary>
internal class LoadBalancer
{
//服务器集合
private static readonly ArrayList? serverList = null;
//静态构造函数
static LoadBalancer()
{
serverList = new ArrayList();
}
//增加服务器
public void AddServer(string server)
{
serverList?.Add(server);
}
//删除服务器
public void RemoveServer(string server)
{
serverList?.Remove(server);
}
//使用Random类随机获取服务器
public string GetServer()
{
int i = Random.Shared.Next(serverList.Count); //随机选一台服务器
return serverList[i].ToString();
}
}
3、构建单例对象构造器
namespace ConsoleApp1;
/// <summary>
/// 单例对象构造器
/// </summary>
/// <typeparam name="T"></typeparam>
public class SingletonConstructor<T> where T : class, new()
{
private static T? _Instance;
private readonly static object _lockObj = new();
/// <summary>
/// 获取单例对象的实例
/// </summary>
/// <returns></returns>
public static T GetInstance()
{
if (_Instance != null) return _Instance;
lock (_lockObj)
{
if (_Instance == null)
{
var item = Activator.CreateInstance<T>();
System.Threading.Interlocked.Exchange(ref _Instance, item);
}
}
return _Instance;
}
}
4、通过单例对象构造器创建 LB 实例对象
在 Main
方法中,添加如下代码:
static void Main(string[] args)
{
// 1. 使用单例对象构造器(SingletonConstructor)创建 LB 实例对象
var balancer1 = SingletonConstructor<LoadBalancer>.GetInstance();
var balancer2 = SingletonConstructor<LoadBalancer>.GetInstance();
var balancer3 = SingletonConstructor<LoadBalancer>.GetInstance();
if (balancer1.Equals(balancer2) && balancer2.Equals(balancer3))
{
Console.WriteLine("服务器负载均衡器(LB)实例对象具有唯一性!");
}
for (int i = 0; i < 5; i++)
{
balancer1.AddServer($"Server {i + 1}");
}
for (int i = 0; i < 20; i++)
{
string server = balancer1.GetServer();
Console.WriteLine($"底[{i + 0}]次分发请求至服务器:{server}");
}
Console.ReadKey();
}
5、启动 ConsoleApp1 项目运行
输入信息:
服务器负载均衡器(LB)实例对象具有唯一性!
第[1]次分发请求至服务器:Server 2
第[2]次分发请求至服务器:Server 1
第[3]次分发请求至服务器:Server 3
第[4]次分发请求至服务器:Server 2
第[5]次分发请求至服务器:Server 2
第[6]次分发请求至服务器:Server 1
第[7]次分发请求至服务器:Server 2
第[8]次分发请求至服务器:Server 3
第[9]次分发请求至服务器:Server 2
第[10]次分发请求至服务器:Server 3
6、单例对象构造器另一种使用
除了上面的单例对象构造器的使用方式,还可以这样使用,改造如下:
- 修改
LoadBalancer
继承SingletonConstructor
单例对象构造器。
/// <summary>
/// 模拟负载均衡器
/// </summary>
internal class LoadBalancer : SingletonConstructor<LoadBalancer>
{
// 其他代码不变
}
- 修改客户端调用方式。
var balancer1 = LoadBalancer.GetInstance();
var balancer2 = LoadBalancer.GetInstance();
var balancer3 = LoadBalancer.GetInstance();
这样就改造好了,启动运行效果相同。
以上就是 SingletonConstructor(单例对象构造器)
的完整使用,更佳优雅的构建单例对象。使用的时候,注意具体的类对象,必须是无参构造函数,遵循单例模式的规则即可。
总结
通过模拟 LB
操作,体验使用【单例模式】设计并确保 LB
的 唯一性
至关重要,在实际的项目应用中,类似的场景很多,而对于单例模式的创建,我们可以封装 SingletonConstructor(单例对象构造器)
来快速创建具体类的单例对象,SingletonConstructor
使用的两种方式,依据实际情况自行选择吧。