目标是让网站在后台每月进行一次用户统计,考虑过直接在数据库操作,但是最后还是选择直接在程序内实现。

虽然功能是实现了,但是看过文档和其他解释后还没能理解,所以这里只写How不写Why。

环境

  • dotnet core 2.1

参考

步骤

官方一共写了三种后台任务用例,分别为Timed background tasks(定时任务)、scoped service background tasks(有作用域服务) 和 Queued background tasks(队列任务)。

对于定时向数据库写入内容,我的实现方式是 timed bg task + scoped service 结合,即在定时任务内执行有作用域的服务(数据库操作)。一开始尝试只用定时方式,但是数据库的上下文依赖注入一直失败,之后在StackOverflow上找到了需要实现一样功能的人,发现他的写法结合了两种方式,最后添加了scoped service方式后成功写入数据库。

  1. 首先实现scoped service,在类中添加接口,以及实现接口的类,在类中写入数据库相关代码
    public class DbOperation {
        internal interface IScopedProcessingService {
            void DoWork();
        }
    
        internal class ScopedProcessingService : IScopedProcessingService {
            private readonly TestDbContext db;
    
            public ScopedProcessingService(TestDbContext _db) {
                db = _db;
            }
    
            ... ...
    
            public void DoWork() {
                var user = new User() {
                    name = GetRandomName(),
                    age = GetRandomAge()
                };
    
                db.Users.Add(user);
                db.SaveChanges();
            }
        }
    }

    DbOperation中定义了接口IScopedProcessingService和接口实现ScopedProcessingServiceScopedProcessingService中实现了接口中声明的,向数据库写入新条目的方法DoWork()

  2. 新建定时任务类,并在定时执行的方法中调用数据库操作服务
    internal class WriteToDb : IHostedService, IDisposable {
        private readonly ILogger logger;
        private Timer timer;
    
        public WriteToDb(ILogger<WriteToDb> _logger, IServiceProvider services) {
            Services = services;
            logger = _logger;
        }
    
        public IServiceProvider Services { get; }
    
        public Task StartAsync(CancellationToken cancellationToken) {
            logger.LogInformation("Starting");
    
            timer = new Timer(DoWork, null, TimeSpan.Zero, TimeSpan.FromHours(1)); //新建定时任务
    
            return Task.CompletedTask;
        }
    
        public void DoWork(object state) { //数据库服务
            using (var scope = Services.CreateScope()) {
                var scopedProcessingService =
                    scope.ServiceProvider.GetRequiredService<DbOperation.IScopedProcessingService>();
    
                scopedProcessingService.DoWork(); //调用写入新条目的方法
            }
            logger.LogInformation("Insert New User");
        }
    
        public Task StopAsync(CancellationToken cancellationToken) {
            logger.LogInformation("Stopping");
    
            timer?.Change(Timeout.Infinite, 0);
    
            return Task.CompletedTask;
        }
    
        public void Dispose() {
            timer?.Dispose();
        }
    }

    IHostedService中,StartAsync()StopAsync() 是必须实现的方法。应用程序准备好时会触发 StartAsync(),在应用程序“优雅地”关闭(原文:graceful shutdown)时,会触发 StopAsync()

    通过修改新建定时器的最后一个参数(代码中为TimeSpan.FromHours(1)) 来改变任务执行的周期,修改第三个参数(代码中为TimeSpan.Zero)来更改程序启动后执行任务的延时——指定为TimeSpan.Zero则在程序启动后立刻执行一次任务。

  3. 在Startup.cs中依赖注入
    public void ConfigureServices(IServiceCollection services) {
        ... ...
    
        services.AddHostedService<WriteToDb>();
        services.AddScoped<DbOperation.IScopedProcessingService, DbOperation.ScopedProcessingService>();
    
        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
    }

Continue reading “ASP.NET Core 通过 IHostedService 实现后台任务运行”

MTProxy使用MTProto协议,是专门为TG打造的代理工具。

环境

  • Ubuntu 16.04

参考&项目地址

步骤

整个过程很简单,ctrl+c、ctrl+v Github页面ReadMe的指令即可。

  1. 安装(编译)依赖
    apt install git curl build-essential libssl-dev zlib1g-dev
  2. clone MTProxy源
    git clone https://github.com/TelegramMessenger/MTProxy
    cd MTProxy
  3. 编译
    make && cd objs/bin

    如果编译失败,先执行

    make clean

    来清除编译失败的文件,然后再次编译

  4. 运行
    1. 生成密钥,用来连接TG的服务器
      curl -s https://core.telegram.org/getProxySecret -o proxy-secret
    2. 生成当前版本的TG设置,官方说明设置经常会变动,建议一天更新一次
      curl -s https://core.telegram.org/getProxyConfig -o proxy-multi.conf
    3. 生成代理密钥,用户输入此密钥来连接代理
      head -c 16 /dev/urandom | xxd -ps

      复制输出的内容

    4. 运行MTProxy
      ./mtproto-proxy -u nobody -p 8888 -H 443 -S <secret> --aes-pwd proxy-secret proxy-multi.conf -M 1

      使用上一步生成的密钥替换“<secret>”,最后的-M参数可自行设定,越大性能越好,对服务器的要求越高。

  5. 分享代理链接
    • 可以以 tg://proxy?server=SERVER_NAME&port=PORT&secret=SECRET 的形式自行编辑生成连接
    • 也可以通过关注TG官方代理Bot(@MTProxybot),根据提示输入代理信息来生成链接