现在开始介绍如何将 Hangfire 结合我的 CrawlService
使用
因为 CrawlService
需要操作数据库,所以是用到了依赖注入的,所以我们需要让 Hangfire 也支持依赖注入 。
官方文档有一小节是关于 IOC 容器的(https://docs.hangfire.io/en/latest/background-methods/using-ioc-containers.html),不过并没有介绍 AspNetCore 的容器,直接自己动手 丰衣足食咯~
添加 AspNetCore 的依赖注入容器一开始搜了好久没找到 , 最终是在Github上找到一个例子代码,里面的 AspNetCore 版本好老,居然是1.1版本,我都没用过… 不过并不影响我节俭他的写法~
这一步需要 JobActivator
的子类
来写一个 , 我把它放在 Infrastructure
目录下
using Hangfire;public class HangfireActivator : JobActivator {private readonly IServiceProvider _serviceProvider;public HangfireActivator(IServiceProvider serviceProvider) {_serviceProvider = serviceProvider;}public override object? ActivateJob(Type jobType) {return _serviceProvider.GetService(jobType);}}
这里是在 HangfireActivator
的构造函数中把 AspNetCore 的 IOC 容器对象传入,并且重写 ActivateJob
方法,让 Hangfire 才激活任务的时候从 IOC 容器中获取对象,比较好理解 。
修改服务注册代码其实服务注册部分是一样的,无须修改
不过按照习惯,为了使 Program.cs
或者 Startup.cs
代码比较简洁,我还是写了扩展方法来实现这部分 。
【Asp-Net-Core开发笔记:集成Hangfire实现异步任务队列和定时任务】在 Extensions
目录中添加 ConfigureHangfire.cs
public static class ConfigureHangfire {public static void AddHangfirePkg(this IServiceCollection services, IConfiguration configuration) {services.AddHangfire(conf => conf.SetDataCompatibilityLevel(CompatibilityLevel.Version_170).UseSimpleAssemblyNameTypeSerializer().UseRecommendedSerializerSettings().UseRedisStorage());services.AddHangfireServer();}public static void UseHangfire(this WebApplication app) {GlobalConfiguration.Configuration.UseActivator(new HangfireActivator(app.Services));app.UseHangfireDashboard();}}
可以看到有修改的地方就是在添加中间件之前,配置了 Activator
这行代码:
GlobalConfiguration.Configuration.UseActivator(new HangfireActivator(app.Services));
直接把 IOC 容器传入
搞定~
接着在 Program.cs
(我用的 .Net6)中使用这个扩展方法就完事了~
builder.Services.AddHangfirePkg(builder.Configuration);// 中间件app.UseHangfire();
创建任务有了依赖注入之后,创建异步任务是这样的 。也就是多了个泛型参数 。
BackgroundJob.Enqueue<CrawlService>(a => a.CrawlAllProc());
定时任务,每小时执行一次
RecurringJob.AddOrUpdate<CrawlService>(a => a.CrawlAllProc(), Cron.Hourly);
改造一下数据采集代码OK , 最后回到一开始的数据采集代码,做如下修改:
public class CrawlService {// 依赖注入一些服务private readonly IBaseRepository<Proc> _repo;public async Task CrawlAllProc() {for(var i=1; i<2000; i++) {// await CrawlProcList(i);BackgroundJob.Enqueue(() => CrawlProcList(i, 100));}}public async Task CrawlProcList(int page, int pageSize = 100) {// 具体代码省略了var procList = ; //...foreach (var proc in procList) {// await CrawlProc(proc);BackgroundJob.Enqueue(() => CrawlProc(proc));}}public async Task CrawlProc(Proc proc) { }}
把原来 await
的地方注释掉,换成用 Hangfire 创建异步任务,运行起来 , 打开dashboard,可以看到任务噌的一下就上到几千,速度极快~
需要注意的就是小结OK,这样就初步搞定了数据采集 & 定时采集的功能,这部分刚好是我国庆第一天加班完成的,后续的就交给时间吧~ 国庆剩下几天的假期让它跑个够 , 等假期结束再回去看看效果如何 , 到时有新的进展我也会及时更新博客 。CrawlProcList
方法的第二个参数pageSize
我们给了默认值100 , 在正常使用是没问题的,可以不传入这个参数,默认就是100 。
但BackgroundJob.Enqueue
方法里不能省略这个参数,不然会报错说编译器无法解析啥的,这个应该是C#的语言限制,具体我暂时还没去深入研究 。
对了,我还打算封装个异步任务和定时任务的接口(似乎 AspNetCore 没有这部分功能?),因为我不想代码和 Hangfire 有太高的耦合 , 封装成抽象的接口,以后如果换别的组件也没有压力 。推荐阅读
- 【疫情动态条形图】用Python开发全球疫情排名动态条形图bar_chart_race
- 初等数论学习笔记 III:数论函数与筛法
- 前端程序员学习 Golang gin 框架实战笔记之一开始玩 gin
- 驱动开发:通过Async反向与内核通信
- gRPC+Protocol Buffer Go微服务实战 - 用户服务开发
- WPF开发经验-实现自带触控键盘的TextBox
- 前端三件套 HTML+CSS+JS基础知识内容笔记
- 驱动开发:通过ReadFile与内核层通信
- 三十六 Java开发学习----SpringBoot三种配置文件解析
- 《Vision Permutator: A Permutable MLP-Like ArchItecture For Visual Recognition》论文笔记