.net core实践系列之短信服务-Sikiro.SMS.Job服务的实现

本文涉及的产品
云数据库 MongoDB,独享型 2核8GB
推荐场景:
构建全方位客户视图
短信服务,100条 3个月
短信服务,200条 3个月
简介: .net core实践系列之短信服务-Sikiro.SMS.Job服务的实现

前言


本篇会继续讲解Sikiro.SMS.Job服务的实现,在我写第一篇的时候,我就发现我当时设计的架构里Sikiro.SMS.Job这个可以选择不需要,而使用MQ代替。但是为了说明调度任务使用实现也坚持写了下。后面会一篇针对架构、实现优化的讲解。


源码地址:https://github.com/SkyChenSky/Sikiro.SMS


Quartz的简介


Quartz.NET是一款功能齐全的开源作业调度框架,小至的应用程序,大到企业系统都可以适用。Quartz是作者James House用JAVA语言编写的,而Quartz.NET是从Quartz移植过来的C#版本。


Quartz.Net的作用


  • Quartz.Net是多线程的,允许多个JOB同时执行。
  • Quartz.Net可以进行持久化,结合管理后台可以进行可视化的监控
  • Quartz.Net提供API进行远程操控,结合管理后台可以进行运维管理

在一般企业,可以利用Quartz.Net框架做各种的定时任务,例如,数据迁移、跑报表等等。


Cron表达式


字段名 是否必填 值范围 特殊字符
Seconds YES 0-59 , - * /
Minutes YES 0-59 , - * /
Hours YES 0-23 , - * /
Day of month YES 1-31 , - * ? / L W
Month YES 1-12 or JAN-DEC , - * /
Day of week YES 1-7 or SUN-SAT , - * ? / L #
Year NO empty, 1970-2099 , - * /


缺点


Quartz.Net的缺点很明显,没有自带的管理后台,而同款的Hangfir调度任务框架则会有更加良好的易用性。但是在Github上有不少人开源了Quartz.Net的管理后台,对此作为了弥补。


其他


其他Quartz.Net的信息可以看我之前记录的一篇文章《Quartz.NET的使用(附源码)

Quartz.Net DEMO:https://github.com/SkyChenSky/QuartzDotNetDemo.git


业务流程


image.png



从MongoDB持久化的数据,查询出状态为待处理并且定时时间小于当前时间的数据。通过Mongo驱动提供的FindOneAndUpdate对文档进行原子性操作(更新中间状态并查询出刚更新的文档)。如果有数据则发送到MQ,由Sikiro.SMS.Bus进行订阅发送,因为本次有数据,我认为可能还会有其他需要发送的数据,因此立刻调用JOB自身方法,进行下一条需要处理的数据进行发送。如果此次JOB的执行并没有数据,那么认为接下来一段时间没有需要处理的数据,这次调度结束。


TimeSendSms示例


public class TimeSendSms : BaseJob
    {
        private readonly SmsService _smsService;
        private readonly IBus _bus;
        public TimeSendSms(SmsService smsService, IBus bus)
        {
            _smsService = smsService;
            _bus = bus;
        }
        protected override void ExecuteBusiness()
        {
            _smsService.GetToBeSend();
            if (_smsService.Sms != null)
                _bus.Publish(_smsService.Sms.MapTo<SmsModel, SmsQueueModel>());
            _smsService.ContinueDo(ExecuteBusiness);
        }
        protected override void OnException()
        {
            _smsService.RollBack();
        }
    }


模板模式


Job的轮询处理流程基本相似,查询出需要执行数据-遍历业务处理-如果有异常则特殊处理,因此针对类似流程相同,但是实现有差异的程序,我们可以使用模板模式。


public abstract class BaseJob : IJob
    {
        private void OnException(Action action)
        {
            try
            {
                action();
            }
            catch (Exception e)
            {
                e.WriteToFile();
                OnException();
            }
        }
        public Task Execute(IJobExecutionContext context)
        {
            OnException(ExecuteBusiness);
            return null;
        }
        protected virtual void OnException()
        {
        }
        protected abstract void ExecuteBusiness();
    }


Mongo的原子性


原子性


原子是物理概念,指的是指化学反应不可再分的基本微粒。而计算机领域的原子性强调的对象是操作(指令、事务)。我们所说的指令组是原子操作,意思要么一起成功,要么一起失败。不允许2个指令里,一个成功一个失败的情况存在。


MongoDB 原子操作


MongoDB的原子操作就是要么这个文档完整的保存到Mongodb,要么没有保存到Mongodb,不会出现查询到的文档没有保存完整的情况。


MongoDB的文档的保存,修改,删除等操作都是原子性,除此之外还提供了FindOneAndDelete、FindOneAndUpdate、FindOneAndReplace等原子操作。


以FindOneAndUpdate为例,对某文档FindOneAndUpdate,可以文档B进行Update操作完成后返回出文档B的结果,根据参数返回结果是更新前还是更新后(一般我们需要更新后)。


而这FindOneAndUpdate的操作对于我们更新到中间状态的非常实用:


  • 避免进行Update后无法良好的查询到刚Update的文档
  • 避免应用集群部署时批量更新后,无法良好分配任务
  • 批量更新多个文档需要isolated标识隔离,全局锁在大并发情况下性能并不乐观


虽然以上可以通过更新时标识版本号进行解决,这无疑增加实现难度。


MongoDB锁机制


Mongodb并发操作又读写锁来进行控制。


简单来说


当进行读操作的时候会加读锁,这个时候其他读操作可以也获得读锁,但是不能加写锁,也就是说不能进行写操作。


当进行写操作的时候会加写锁,这个时候其他操作无法加任何锁,也就是说不能进行其他的读操作和写操作。


多个JOB的并发性


综上所述,落实到我们应用场景,在部署多个调度任务服务,或者JOB多个线程去跑时,我们可以使用FindOneAndUpdate,每个调度任务每次只处理一个文档,Update操作的时候会进行写锁阻塞其他进程(进程)的写操作。那么就可以保证每个调度任务都可以只处理唯一一个有效的文档,避免重复处理。


下面是我的Sikiro.Nosql.Mongo的FindOneAndUpdate封装示例,因为Update字段的不友好,所以我封装了一下Lambda表达式,ReturnDocument = ReturnDocument.After标识响应数据是更新前还是更新后的文档。


public T GetAndUpdate<T>(string database, string collection, Expression<Func<T, bool>> predicate, Expression<Func<T, T>> updateExpression)
        {
            var db = _mongoClient.GetDatabase(database);
            var col = db.GetCollection<T>(collection);
            var updateDefinitionList = MongoExpression<T>.GetUpdateDefinition(updateExpression);
            var updateDefinitionBuilder = new UpdateDefinitionBuilder<T>().Combine(updateDefinitionList);
            return col.FindOneAndUpdate(predicate, updateDefinitionBuilder, new FindOneAndUpdateOptions<T, T>
            {
                ReturnDocument = ReturnDocument.After
            });


SQL Server的UpdateSelect


SQL Server的操作也具有上述FindOneAndUpdate的功能,我们公司成他为UpdateSelect,下面是示例代码:


UPDATE TOP ( 100 )
        SYS_USER WITH ( UPDLOCK, READPAST )
SET     USER_STATUS = 1
OUTPUT  INSERTED.[USER_NAME] ,
        INSERTED.SYS_USERID ,
        INSERTED.EMAIL
FROM    SYS_USER
WHERE   CREATE_DATETIME < '2018-09-13'
        AND USER_STATUS = 2;


目录
相关文章
|
27天前
|
人工智能 开发框架 .NET
如何掌握.NET技术,引领开发前沿:.NET技术的核心能力、在现代开发中的应用实践、以及如何通过.NET技术实现持续创新。
.NET技术已成为软件开发不可或缺的部分,本文分三部分探讨:核心能力如多语言支持、统一运行时环境、丰富的类库及跨平台能力;现代开发实践包括企业级应用、Web与移动开发、云服务及游戏开发;并通过性能优化、容器化、AI集成等方面实现持续创新,使开发者站在技术前沿。
42 3
|
5天前
|
开发框架 NoSQL .NET
利用分布式锁在ASP.NET Core中实现防抖
【9月更文挑战第5天】在 ASP.NET Core 中,可通过分布式锁实现防抖功能,仅处理连续相同请求中的首个请求,其余请求返回 204 No Content,直至锁释放。具体步骤包括:安装分布式锁库如 `StackExchange.Redis`;创建分布式锁服务接口及其实现;构建防抖中间件;并在 `Startup.cs` 中注册相关服务和中间件。这一机制有效避免了短时间内重复操作的问题。
|
14天前
|
jenkins 测试技术 持续交付
解锁.NET项目高效秘籍:从理论迷雾到实践巅峰,持续集成与自动化测试如何悄然改变游戏规则?
【8月更文挑战第28天】在软件开发领域,持续集成(CI)与自动化测试已成为提升效率和质量的关键工具。尤其在.NET项目中,二者的结合能显著提高开发速度并保证软件稳定性。本文将从理论到实践,详细介绍CI与自动化测试的重要性,并以ASP.NET Core Web API项目为例,演示如何使用Jenkins和NUnit实现自动化构建与测试。每次代码提交后,Jenkins自动触发构建流程,通过编译和运行NUnit测试确保代码质量。这种方式不仅节省了时间,还能快速发现并解决问题,推动.NET项目开发迈向更高水平。
32 8
|
15天前
|
Kubernetes 监控 Devops
【独家揭秘】.NET项目中的DevOps实践:从代码提交到生产部署,你不知道的那些事!
【8月更文挑战第28天】.NET 项目中的 DevOps 实践贯穿代码提交到生产部署全流程,涵盖健壮的源代码管理、GitFlow 工作流、持续集成与部署、容器化及监控日志记录。通过 Git、CI/CD 工具、Kubernetes 及日志框架的最佳实践应用,显著提升软件开发效率与质量。本文通过具体示例,助力开发者构建高效可靠的 DevOps 流程,确保项目成功交付。
41 0
|
27天前
|
人工智能 前端开发 开发工具
NET在企业级应用、Web开发、移动应用、云服务及游戏领域的创新实践
.NET技术自2000年推出以来,在软件开发领域扮演着关键角色。本文从核心优势出发,探讨其统一多语言平台、强大工具集、跨平台能力及丰富生态系统的价值;随后介绍.NET在企业级应用、Web开发、移动应用、云服务及游戏领域的创新实践;最后分析性能优化、容器化、AI集成等方面的挑战与机遇,展望.NET技术的未来发展与潜力。
42 2
|
11天前
|
Java Spring UED
Spring框架的异常处理秘籍:打造不败之身的应用!
【8月更文挑战第31天】在软件开发中,异常处理对应用的稳定性和健壮性至关重要。Spring框架提供了一套完善的异常处理机制,包括使用`@ExceptionHandler`注解和配置`@ControllerAdvice`。本文将详细介绍这两种方式,并通过示例代码展示其具体应用。`@ExceptionHandler`可用于控制器类中的方法,处理特定异常;而`@ControllerAdvice`则允许定义全局异常处理器,捕获多个控制器中的异常。
29 0
|
15天前
|
开发框架 监控 .NET
开发者的革新利器:ASP.NET Core实战指南,构建未来Web应用的高效之道
【8月更文挑战第28天】本文探讨了如何利用ASP.NET Core构建高效、可扩展的Web应用。ASP.NET Core是一个开源、跨平台的框架,具有依赖注入、配置管理等特性。文章详细介绍了项目结构规划、依赖注入配置、中间件使用及性能优化方法,并讨论了安全性、可扩展性以及容器化的重要性。通过这些技术要点,开发者能够快速构建出符合现代Web应用需求的应用程序。
28 0
|
15天前
|
缓存 数据库连接 API
Entity Framework Core——.NET 领域的 ORM 利器,深度剖析其最佳实践之路
【8月更文挑战第28天】在软件开发领域,高效的数据访问与管理至关重要。Entity Framework Core(EF Core)作为一款强大的对象关系映射(ORM)工具,在 .NET 开发中扮演着重要角色。本文通过在线书店应用案例,展示了 EF Core 的核心特性和优势。我们定义了 `Book` 实体类及其属性,并通过 `BookStoreContext` 数据库上下文配置了数据库连接。EF Core 提供了简洁的 API,支持数据的查询、插入、更新和删除操作。
34 0
|
18天前
|
开发框架 监控 .NET
【Azure 应用程序见解】在Docker中运行的ASP.NET Core应用如何开启Application Insights的Profiler Trace呢?
【Azure 应用程序见解】在Docker中运行的ASP.NET Core应用如何开启Application Insights的Profiler Trace呢?
|
18天前
|
Linux C# C++
【Azure App Service For Container】创建ASP.NET Core Blazor项目并打包为Linux镜像发布到Azure应用服务
【Azure App Service For Container】创建ASP.NET Core Blazor项目并打包为Linux镜像发布到Azure应用服务