框架分层

This commit is contained in:
橙子
2021-10-10 17:30:31 +08:00
parent 945437a2eb
commit cf062cadcd
59 changed files with 2908 additions and 14 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,8 @@
using System;
namespace Yi.Framework.Core
{
public class Class1
{
}
}

View File

@@ -0,0 +1,71 @@
using Consul;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CC.ElectronicCommerce.Core.ConsulExtend
{
public abstract class AbstractConsulDispatcher
{
protected ConsulClientOption _ConsulClientOption = null;
protected KeyValuePair<string, AgentService>[] _CurrentAgentServiceDictionary = null;
public AbstractConsulDispatcher(IOptionsMonitor<ConsulClientOption> consulClientOption)
{
this._ConsulClientOption = consulClientOption.CurrentValue;
}
/// <summary>
/// 负载均衡获取地址
/// </summary>
/// <param name="mappingUrl">Consul映射后的地址</param>
/// <returns></returns>
public string GetAddress(string mappingUrl)
{
Uri uri = new Uri(mappingUrl);
string serviceName = uri.Host;
string addressPort = this.ChooseAddress(serviceName);
return $"{uri.Scheme}://{addressPort}{uri.PathAndQuery}";
}
protected virtual string ChooseAddress(string serviceName)
{
ConsulClient client = new ConsulClient(c =>
{
c.Address = new Uri($"http://{this._ConsulClientOption.IP}:{this._ConsulClientOption.Port}/");
c.Datacenter = this._ConsulClientOption.Datacenter;
});
AgentService agentService = null;
//var response = client.Agent.Services().Result.Response;
////foreach (var item in response)
////{
//// Console.WriteLine("***************************************");
//// Console.WriteLine(item.Key);
//// var service = item.Value;
//// Console.WriteLine($"{service.Address}--{service.Port}--{service.Service}");
//// Console.WriteLine("***************************************");
////}
//this._CurrentAgentServiceDictionary = response.Where(s => s.Value.Service.Equals(serviceName, StringComparison.OrdinalIgnoreCase)).ToArray();
//升级consul实例获取
var entrys = client.Health.Service(serviceName).Result.Response;
List<KeyValuePair<string, AgentService>> serviceList = new List<KeyValuePair<string, AgentService>>();
for (int i = 0; i < entrys.Length; i++)
{
serviceList.Add(new KeyValuePair<string, AgentService>(i.ToString(), entrys[i].Service));
}
this._CurrentAgentServiceDictionary = serviceList.ToArray();
int index = this.GetIndex();
agentService = this._CurrentAgentServiceDictionary[index].Value;
return $"{agentService.Address}:{agentService.Port}";
}
protected abstract int GetIndex();
}
}

View File

@@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Consul;
using Microsoft.Extensions.Options;
namespace CC.ElectronicCommerce.Core.ConsulExtend
{
/// <summary>
/// 平均
/// </summary>
public class AverageDispatcher : AbstractConsulDispatcher
{
#region Identity
private static int _iTotalCount = 0;
private static int iTotalCount
{
get
{
return _iTotalCount;
}
set
{
_iTotalCount = value >= Int32.MaxValue ? 0 : value;
}
}
public AverageDispatcher(IOptionsMonitor<ConsulClientOption> consulClientOption) : base(consulClientOption)
{
}
#endregion
/// <summary>
/// 平均
/// </summary>
/// <returns></returns>
protected override int GetIndex()
{
return new Random(iTotalCount++).Next(0, base._CurrentAgentServiceDictionary.Length);
}
}
}

View File

@@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Consul;
using Microsoft.Extensions.Options;
namespace CC.ElectronicCommerce.Core.ConsulExtend
{
/// <summary>
/// 轮询
/// </summary>
public class PollingDispatcher : AbstractConsulDispatcher
{
#region Identity
private static int _iTotalCount = 0;
private static int iTotalCount
{
get
{
return _iTotalCount;
}
set
{
_iTotalCount = value >= Int32.MaxValue ? 0 : value;
}
}
public PollingDispatcher(IOptionsMonitor<ConsulClientOption> consulClientOption) : base(consulClientOption)
{
}
#endregion
/// <summary>
/// 轮询
/// </summary>
/// <param name="serviceCount"></param>
/// <returns></returns>
protected override int GetIndex()
{
return iTotalCount++ % base._CurrentAgentServiceDictionary.Length;
}
}
}

View File

@@ -0,0 +1,66 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Consul;
using Microsoft.Extensions.Options;
namespace CC.ElectronicCommerce.Core.ConsulExtend
{
/// <summary>
/// 权重
/// </summary>
public class WeightDispatcher : AbstractConsulDispatcher
{
#region Identity
private static int _iTotalCount = 0;
private static int iTotalCount
{
get
{
return _iTotalCount;
}
set
{
_iTotalCount = value >= Int32.MaxValue ? 0 : value;
}
}
public WeightDispatcher(IOptionsMonitor<ConsulClientOption> consulClientOption) : base(consulClientOption)
{
}
#endregion
protected override string ChooseAddress(string serviceName)
{
ConsulClient client = new ConsulClient(c =>
{
c.Address = new Uri($"http://{base._ConsulClientOption.IP}:{base._ConsulClientOption.Port}/");
c.Datacenter = base._ConsulClientOption.Datacenter;
});
AgentService agentService = null;
var response = client.Agent.Services().Result.Response;
this._CurrentAgentServiceDictionary = response.Where(s => s.Value.Service.Equals(serviceName, StringComparison.OrdinalIgnoreCase)).ToArray();
var serviceDictionaryNew = new List<AgentService>();
foreach (var service in base._CurrentAgentServiceDictionary)
{
serviceDictionaryNew.AddRange(Enumerable.Repeat(service.Value, int.TryParse(service.Value.Tags?[0], out int iWeight) ? 1 : iWeight));
}
int index = new Random(DateTime.Now.Millisecond).Next(0, int.MaxValue) % serviceDictionaryNew.Count;
agentService = serviceDictionaryNew[index];
return $"{agentService.Address}:{agentService.Port}";
}
/// <summary>
/// 不需要了
/// </summary>
/// <returns></returns>
protected override int GetIndex()
{
throw new NotImplementedException();
}
}
}

View File

@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace CC.ElectronicCommerce.Core.ConsulExtend
{
/// <summary>
/// 使用Consul时需要配置
/// </summary>
public class ConsulClientOption
{
public string IP { get; set; }
public int Port { get; set; }
public string Datacenter { get; set; }
}
}

View File

@@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CC.ElectronicCommerce.Core.ConsulExtend
{
public class ConsulRegisterOption
{
/// <summary>
/// 服务自身IP
/// </summary>
public string IP { get; set; }
/// <summary>
/// 服务自身Port
/// </summary>
public int Port { get; set; }
/// <summary>
/// 组名称
/// </summary>
public string GroupName { get; set; }
/// <summary>
/// 心跳检查地址
/// </summary>
public string HealthCheckUrl { get; set; }
/// <summary>
/// 心跳频率
/// </summary>
public int Interval { get; set; }
/// <summary>
/// 心跳超时
/// </summary>
public int Timeout { get; set; }
/// <summary>
/// 移除延迟时间
/// </summary>
public int DeregisterCriticalServiceAfter { get; set; }
/// <summary>
/// 标签,额外信息,用于权重
/// </summary>
public string Tag { get; set; }
}
}

View File

@@ -0,0 +1,69 @@
using Consul;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CC.ElectronicCommerce.Core.ConsulExtend
{
/// <summary>
/// HTTP模式
/// </summary>
public static class ConsulRegiterExtend
{
/// <summary>
/// 自动读取配置文件完成注册
/// </summary>
/// <param name="app"></param>
/// <param name="configuration"></param>
/// <returns></returns>
public static async Task UseConsulConfiguration(this IApplicationBuilder app, IConfiguration configuration)
{
ConsulRegisterOption consulRegisterOption = new ConsulRegisterOption();
configuration.Bind("ConsulRegisterOption", consulRegisterOption);
ConsulClientOption consulClientOption = new ConsulClientOption();
configuration.Bind("ConsulClientOption", consulClientOption);
await UseConsul(app, consulClientOption, consulRegisterOption);
}
/// <summary>
/// 基于提供信息完成注册
/// </summary>
/// <param name="app"></param>
/// <param name="healthService"></param>
/// <returns></returns>
public static async Task UseConsul(this IApplicationBuilder app, ConsulClientOption consulClientOption, ConsulRegisterOption consulRegisterOption)
{
using (ConsulClient client = new ConsulClient(c =>
{
c.Address = new Uri($"http://{consulClientOption.IP}:{consulClientOption.Port}/");
c.Datacenter = consulClientOption.Datacenter;
}))
{
await client.Agent.ServiceRegister(new AgentServiceRegistration()
{
ID = $"{consulRegisterOption.IP}-{consulRegisterOption.Port}-{Guid.NewGuid()}",//唯一Id
Name = consulRegisterOption.GroupName,//组名称-Group
Address = consulRegisterOption.IP,
Port = consulRegisterOption.Port,
Tags = new string[] { consulRegisterOption.Tag },
Check = new AgentServiceCheck()
{
Interval = TimeSpan.FromSeconds(consulRegisterOption.Interval),
HTTP = $"http://{consulRegisterOption.IP}:{consulRegisterOption.Port}{consulRegisterOption.HealthCheckUrl}",
Timeout = TimeSpan.FromSeconds(consulRegisterOption.Timeout),
DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(consulRegisterOption.DeregisterCriticalServiceAfter)
}
});
Console.WriteLine($"{JsonConvert.SerializeObject(consulRegisterOption)} 完成注册");
}
}
}
}

View File

@@ -0,0 +1,31 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
namespace CC.ElectronicCommerce.Core.ConsulExtend
{
public static class HealthCheckMiddleware
{
/// <summary>
/// 设置心跳响应
/// </summary>
/// <param name="app"></param>
/// <param name="checkPath">默认是/Health</param>
/// <returns></returns>
public static void UseHealthCheckMiddleware(this IApplicationBuilder app, string checkPath = "/Health")
{
app.Map(checkPath, applicationBuilder => applicationBuilder.Run(async context =>
{
Console.WriteLine($"This is Health Check");
context.Response.StatusCode = (int)HttpStatusCode.OK;
await context.Response.WriteAsync("OK");
}));
}
}
}

View File

@@ -0,0 +1,47 @@
using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;
namespace CC.ElectronicCommerce.Core
{
/// <summary>
/// 加密用的
/// </summary>
public class MD5Helper
{
/// <summary>
/// MD5 加密字符串
/// </summary>
/// <param name="content">源字符串</param>
/// <returns>加密后字符串</returns>
public static string MD5EncodingOnly(string content)
{
// 创建MD5类的默认实例MD5CryptoServiceProvider
MD5 md5 = MD5.Create();
byte[] bs = Encoding.UTF8.GetBytes(content);
byte[] hs = md5.ComputeHash(bs);
StringBuilder stb = new StringBuilder();
foreach (byte b in hs)
{
// 以十六进制格式格式化
stb.Append(b.ToString("x2"));
}
return stb.ToString();
}
/// <summary>
/// MD5盐值加密
/// </summary>
/// <param name="content">源字符串</param>
/// <param name="salt">盐值</param>
/// <returns>加密后字符串</returns>
public static string MD5EncodingWithSalt(string content, string salt)
{
if (salt == null) return content;
return MD5EncodingOnly(content + "{" + salt.ToString() + "}");
}
}
}

View File

@@ -0,0 +1,253 @@
using CC.ElectronicCommerce.Common.IOCOptions;
using Microsoft.Extensions.Options;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CC.ElectronicCommerce.Core
{
/// <summary>
/// 一个Exchange----多个Queue-----弄个缓存映射关系,初始化+支持全新绑定
/// 全局单例使用
///
/// 关系应该是直接配置到RabbitMQ了---程序只是向某个位置写入即可
///
///
/// 全量更新--耗时---阻塞实时更新---换不同的exchange
/// </summary>
public class RabbitMQInvoker
{
#region Identity
private readonly RabbitMQOptions _rabbitMQOptions;
private readonly string _HostName = null;
private readonly string _UserName = null;
private readonly string _Password = null;
public RabbitMQInvoker(IOptionsMonitor<RabbitMQOptions> optionsMonitor) : this(optionsMonitor.CurrentValue.HostName, optionsMonitor.CurrentValue.UserName, optionsMonitor.CurrentValue.Password)
{
this._rabbitMQOptions = optionsMonitor.CurrentValue;
}
public RabbitMQInvoker(string hostName, string userName = "cc", string password = "cc")
{
this._HostName = hostName;
this._UserName = userName;
this._Password = password;
}
#endregion
#region Init
private static object RabbitMQInvoker_BindQueueLock = new object();
private static Dictionary<string, bool> RabbitMQInvoker_ExchangeQueue = new Dictionary<string, bool>();
private void InitBindQueue(RabbitMQConsumerModel rabbitMQConsumerModel)
{
if (!RabbitMQInvoker_ExchangeQueue.ContainsKey($"InitBindQueue_{rabbitMQConsumerModel.ExchangeName}_{rabbitMQConsumerModel.QueueName}"))
{
lock (RabbitMQInvoker_BindQueueLock)
{
if (!RabbitMQInvoker_ExchangeQueue.ContainsKey($"InitBindQueue_{rabbitMQConsumerModel.ExchangeName}_{rabbitMQConsumerModel.QueueName}"))
{
this.InitConnection();
using (IModel channel = _CurrentConnection.CreateModel())
{
channel.ExchangeDeclare(exchange: rabbitMQConsumerModel.ExchangeName, type: ExchangeType.Fanout, durable: true, autoDelete: false, arguments: null);
channel.QueueDeclare(queue: rabbitMQConsumerModel.QueueName, durable: true, exclusive: false, autoDelete: false, arguments: null);
channel.QueueBind(queue: rabbitMQConsumerModel.QueueName, exchange: rabbitMQConsumerModel.ExchangeName, routingKey: string.Empty, arguments: null);
}
RabbitMQInvoker_ExchangeQueue[$"InitBindQueue_{rabbitMQConsumerModel.ExchangeName}_{rabbitMQConsumerModel.QueueName}"] = true;
}
}
}
}
/// <summary>
/// 必须先声明exchange--检查+初始化
/// </summary>
/// <param name="rabbitMQConsumerModel"></param>
private void InitExchange(string exchangeName)
{
if (!RabbitMQInvoker_ExchangeQueue.ContainsKey($"InitExchange_{exchangeName}"))//没用api确认
{
lock (RabbitMQInvoker_BindQueueLock)
{
if (!RabbitMQInvoker_ExchangeQueue.ContainsKey($"InitExchange_{exchangeName}"))
{
this.InitConnection();
using (IModel channel = _CurrentConnection.CreateModel())
{
channel.ExchangeDeclare(exchange: exchangeName, type: ExchangeType.Fanout, durable: true, autoDelete: false, arguments: null);
}
RabbitMQInvoker_ExchangeQueue[$"InitExchange_{exchangeName}"] = true;
}
}
}
}
//public void UnBindQueue(string exchangeName, string queueName)
//{
//}
private static object RabbitMQInvoker_InitLock = new object();
private static IConnection _CurrentConnection = null;//链接做成单例重用--channel是新的
private void InitConnection()
{
//https://blog.csdn.net/weixin_30646315/article/details/99101279
if (_CurrentConnection == null || !_CurrentConnection.IsOpen)
{
lock (RabbitMQInvoker_InitLock)
{
if (_CurrentConnection == null || !_CurrentConnection.IsOpen)
{
var factory = new ConnectionFactory()
{
HostName = this._HostName,
Password = this._Password,
UserName = this._UserName
};
_CurrentConnection = factory.CreateConnection();
}
}
}
}
#endregion
/// <summary>
/// 只管exchange---
/// 4种路由类型
///
/// Send前完成交换机初始化
/// </summary>
/// <param name="exchangeName"></param>
/// <param name="message">建议Json格式</param>
public void Send(RabbitMQConsumerModel rabbitMQConsumerModel, string message)
{
this.InitExchange(rabbitMQConsumerModel.ExchangeName);
this.InitBindQueue(rabbitMQConsumerModel);
if (_CurrentConnection == null || !_CurrentConnection.IsOpen)
{
this.InitConnection();
}
using (var channel = _CurrentConnection.CreateModel())//开辟新的信道通信
{
try
{
channel.TxSelect();//开启Tx事务---RabbitMQ协议级的事务-----强事务
var body = Encoding.UTF8.GetBytes(message);
channel.BasicPublish(exchange: rabbitMQConsumerModel.ExchangeName,
routingKey: string.Empty,
basicProperties: null,
body: body);
channel.TxCommit();//提交
Console.WriteLine($" [x] Sent {body.Length}");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
Console.WriteLine($"【{message}】发送到Broker失败{ex.Message}");
channel.TxRollback(); //事务回滚--前面的所有操作就全部作废了。。。。
}
}
}
/// <summary>
/// 固定无消费队列名字---转移到目标队列---定好时间
/// </summary>
/// <param name="targetExchangeName"></param>
/// <param name="message"></param>
/// <param name="delaySecond"></param>
public void SendDelay(string targetExchangeName, string message, int delaySecond)
{
this.InitExchange(targetExchangeName);
if (_CurrentConnection == null || !_CurrentConnection.IsOpen)
{
this.InitConnection();
}
using (var channel = _CurrentConnection.CreateModel())//开辟新的信道通信
{
try
{
string delayExchangeName = "ZhaoxiMSA_DelayExchange";
//普通交换器
channel.ExchangeDeclare(delayExchangeName, "fanout", true, false, null);
//参数设置
Dictionary<string, object> args = new Dictionary<string, object>();
args.Add("x-message-ttl", delaySecond * 1000);//TTL 毫秒
args.Add("x-dead-letter-exchange", targetExchangeName);//DLX
args.Add("x-dead-letter-routing-key", "routingkey");//routingKey
channel.QueueDeclare("ZhaoxiMSA_DelayQueue", true, false, false, args);
channel.QueueBind(queue: "ZhaoxiMSA_DelayQueue",
exchange: delayExchangeName,
routingKey: string.Empty,
arguments: null);
////DLX--- //死信队列绑定
//channel.ExchangeDeclare("ZhaoxiMSA_exchange_dlx", "fanout", true, false, null);
//channel.QueueDeclare("ZhaoxiMSA_queue_dlx", true, false, false, null);
//channel.QueueBind("ZhaoxiMSA_queue_dlx", "ZhaoxiMSA_exchange_dlx", "routingkey", null);
channel.TxSelect();//开启Tx事务---RabbitMQ协议级的事务-----强事务
var properties = channel.CreateBasicProperties();
var body = Encoding.UTF8.GetBytes(message);
channel.BasicPublish(exchange: delayExchangeName,
routingKey: string.Empty,
basicProperties: properties,
body: body);
channel.TxCommit();//提交
Console.WriteLine($" [x] Sent {body.Length}");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
Console.WriteLine($"【{message}】发送到Broker失败{ex.Message}");
channel.TxRollback(); //事务回滚--前面的所有操作就全部作废了。。。。
}
}
}
#region Receive
/// <summary>
/// 注册处理动作
/// </summary>
/// <param name="rabbitMQConsumerMode"></param>
/// <param name="func"></param>
public void RegistReciveAction(RabbitMQConsumerModel rabbitMQConsumerMode, Func<string, bool> func)
{
this.InitBindQueue(rabbitMQConsumerMode);
Task.Run(() =>
{
using (var channel = _CurrentConnection.CreateModel())
{
var consumer = new EventingBasicConsumer(channel);
channel.BasicQos(0, 0, true);
consumer.Received += (sender, ea) =>
{
string str = Encoding.UTF8.GetString(ea.Body.ToArray());
if (func(str))
{
channel.BasicAck(deliveryTag: ea.DeliveryTag, multiple: false);//确认已消费
}
else
{
//channel.BasicReject(deliveryTag: ea.DeliveryTag, requeue: true);//放回队列--重新包装信息,放入其他队列
}
};
channel.BasicConsume(queue: rabbitMQConsumerMode.QueueName,
autoAck: false,//不ACK
consumer: consumer);
Console.WriteLine($" Register Consumer To {rabbitMQConsumerMode.ExchangeName}-{rabbitMQConsumerMode.QueueName}");
Console.ReadLine();
Console.WriteLine($" After Register Consumer To {rabbitMQConsumerMode.ExchangeName}-{rabbitMQConsumerMode.QueueName}");
}
});
}
#endregion
}
}

View File

@@ -0,0 +1,99 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CC.ElectronicCommerce.Core
{
public static class SnowflakeHelper
{
public static long Next()
{
SnowflakeTool snowflakeTool = new SnowflakeTool(1);
return snowflakeTool.NextId();
}
private class SnowflakeTool
{
//机器ID
private static long nodeId;
private static long twepoch = 687888001020L; //唯一时间,这是一个避免重复的随机量,自行设定不要大于当前时间戳
private static long sequence = 0L;
private static int workerIdBits = 4; //机器码字节数。4个字节用来保存机器码(定义为Long类型会出现最大偏移64位所以左移64位没有意义)
public static long maxWorkerId = -1L ^ -1L << workerIdBits; //最大机器ID
private static int sequenceBits = 10; //计数器字节数10个字节用来保存计数码
private static int workerIdShift = sequenceBits; //机器码数据左移位数,就是后面计数器占用的位数
private static int timestampLeftShift = sequenceBits + workerIdBits; //时间戳左移动位数就是机器码和计数器总字节数
public static long sequenceMask = -1L ^ -1L << sequenceBits; //一微秒内可以产生计数,如果达到该值则等到下一微妙在进行生成
private long lastTimestamp = -1L;
/// <summary>
/// 机器码
/// </summary>
/// <param name="workerId"></param>
public SnowflakeTool(long workerId)
{
if (workerId > maxWorkerId || workerId < 0)
throw new Exception(string.Format("节点id 不能大于 {0} 或者 小于 0 ", workerId));
SnowflakeTool.nodeId = workerId;
}
public long NextId()
{
lock (this)
{
long timestamp = TimeGen();
if (this.lastTimestamp == timestamp)
{ //同一微妙中生成ID
SnowflakeTool.sequence = (SnowflakeTool.sequence + 1) & SnowflakeTool.sequenceMask; //用&运算计算该微秒内产生的计数是否已经到达上限
if (SnowflakeTool.sequence == 0)
{
//一微妙内产生的ID计数已达上限等待下一微妙
timestamp = TillNextMillis(this.lastTimestamp);
}
}
else
{ //不同微秒生成ID
SnowflakeTool.sequence = 0; //计数清0
}
if (timestamp < lastTimestamp)
{ //如果当前时间戳比上一次生成ID时时间戳还小抛出异常因为不能保证现在生成的ID之前没有生成过
throw new Exception(string.Format("Clock moved backwards. Refusing to generate id for {0} milliseconds",
this.lastTimestamp - timestamp));
}
this.lastTimestamp = timestamp; //把当前时间戳保存为最后生成ID的时间戳
long nextId = (timestamp - twepoch << timestampLeftShift) | SnowflakeTool.nodeId << SnowflakeTool.workerIdShift | SnowflakeTool.sequence;
return nextId;
}
}
/// <summary>
/// 获取下一微秒时间戳
/// </summary>
/// <param name="lastTimestamp"></param>
/// <returns></returns>
private long TillNextMillis(long lastTimestamp)
{
long timestamp = TimeGen();
while (timestamp <= lastTimestamp)
{
timestamp = TimeGen();
}
return timestamp;
}
/// <summary>
/// 生成当前时间戳
/// </summary>
/// <returns></returns>
private long TimeGen()
{
return (long)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds;
}
}
}
}

View File

@@ -0,0 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
</Project>