zoukankan      html  css  js  c++  java
  • 万物皆可 Serverless 之使用云函数 Timer 触发器实现每天自动定时打卡

    不晓得大家有没有遇到过定时打卡的需求,比如商品秒杀,火车票定时开售、每日健康打卡等。这时候我们往往可以通过一些技术手段,编写一些自动化操作的脚本,来实现定时自动打卡的操作。

    本文来自 Serverless 社区用户「乂乂又又」供稿

    当然本文并不探讨如何编写自动化的操作脚本,而是和大家介绍一下如何使用腾讯云函数的 Timer 触发器实现定时任务,来快速、稳定、低成本地实现一些 fancy 的操作(骚操作

    效果展示

    • 每日健康信息自动更新

    每日健康信息自动更新

    • 每日定时数据报告

    每日定时数据报告

    可以看到,定时任务搭配邮箱发送云函数运行结果,用起来还是蛮舒服的,还可以给自己做一个每日科技资讯推送、数据报告之类的小玩意,自娱自乐。其他的用途请大家大开脑洞,自行脑补吧~

    实战教程

    1. 新建云函数

    新建函数

    运行环境我们选择 python3,模板函数选择定时拨测,然后点击下一步

    定时拨测模板函数

    模板函数的描述里写着「本示例代码的功能是定时拨测 URL 列表中的地址,并通过邮件发送告警」

    而这正是我们想要的实现的功能,不过这个模板函数的邮件发送有点问题,我们稍后会详细说明

    2. 模板函数分析

    下面我们来分析一下这段示例代码

    # -*- coding: utf8 -*-
    import sys
    import os
    
    sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)) + "/..")
    
    import logging
    import json
    import requests
    from email.mime.text import MIMEText
    from email.header import Header
    import smtplib
    
    logger = logging.getLogger()
    logger.setLevel(logging.DEBUG)
    
    # Third-party SMTP service for sending alert emails. 第三方 SMTP 服务,用于发送告警邮件
    mail_host = "smtp.qq.com"       # SMTP server, such as QQ mailbox, need to open SMTP service in the account. SMTP服务器,如QQ邮箱,需要在账户里开启SMTP服务
    mail_user = "XXXXXXXXX@qq.com"  # Username 用户名
    mail_pass = "****************"  # Password, SMTP service password. 口令,SMTP服务密码
    mail_port = 465  # SMTP service port. SMTP服务端口
    
    # The URL address need to dial test. 需要拨测的URL地址
    test_url_list = [
        "http://www.baidu.com",
        "http://www.qq.com",
        "http://wrong.tencent.com",
        "http://unkownurl.com"
    ]
    
    # The notification list of alert emails. 告警邮件通知列表
    email_notify_list = {
        "XXXXXXXXX@qq.com",
        "XXXXXXXXX@qq.com"
    }
    
    
    def sendEmail(fromAddr, toAddr, subject, content):
        sender = fromAddr
        receivers = [toAddr]
        message = MIMEText(content, 'plain', 'utf-8')
        message['From'] = Header(fromAddr, 'utf-8')
        message['To'] = Header(toAddr, 'utf-8')
        message['Subject'] = Header(subject, 'utf-8')
        try:
            smtpObj = smtplib.SMTP_SSL(mail_host, mail_port)
            smtpObj.login(mail_user, mail_pass)
            smtpObj.sendmail(sender, receivers, message.as_string())
            print("send email success")
            return True
        except smtplib.SMTPException as e:
            print(e)
            print("Error: send email fail")
            return False
    
    
    def test_url(url_list):
        errorinfo = [serverless]
        for url in url_list:
            resp = None
            try:
                resp = requests.get(url, timeout=3)
                print (resp)
            except (
            requests.exceptions.Timeout, requests.exceptions.ConnectionError, requests.exceptions.ConnectTimeout) as e:
                logger.warn("request exceptions:" + str(e))
                errorinfo.append("Access " + url + " timeout")
            else:
                if resp.status_code >= 400:
                    logger.warn("response status code fail:" + str(resp.status_code))
                    errorinfo.append("Access " + url + " fail, status code:" + str(resp.status_code))
        if len(errorinfo) != 0:
            body = "
    ".join(errorinfo)
            subject = "Please note: PlayCheck Error"
            for toAddr in email_notify_list:
                print ("send message [%s] to [%s]" % (body, toAddr))
                sendEmail(mail_user, toAddr, subject, body)
    
    
    def main_handler(event, context):
        test_url(test_url_list)
    
    
    if __name__ == '__main__':
        main_handler("", "")
    

    这里要讲一下云函数的执行入口,

    这个模板函数的默认入口是 main\_handler(event, context) 这个函数,

    这个入口函数是可以自行配置的,具体配置方法可以翻看官方的文档

    def main_handler(event, context):
        test_url(test_url_list)
    

    另外这里的 py 文件的主函数入口,实际上是可以缺省的。这里加上应该是为了方便本地调试和运行函数。

    if __name__ == '__main__':
        main_handler("", "")
    

    然后看一下依赖库的导入部分

    import requests
    from email.mime.text import MIMEText
    from email.header import Header
    import smtplib
    

    注意到有 import requests,但本地文件并没有 requests 库,说明腾讯云函数的运行环境中已经安装了 requests 库,并不需要我们再手动上传添加 requests 依赖。

    def test_url(url_list):
        errorinfo = [serverless]
        for url in url_list:
            resp = None
            try:
                resp = requests.get(url, timeout=3)
                print (resp)
            except (
            requests.exceptions.Timeout, requests.exceptions.ConnectionError, requests.exceptions.ConnectTimeout) as e:
                logger.warn("request exceptions:" + str(e))
                errorinfo.append("Access " + url + " timeout")
            else:
                if resp.status_code >= 400:
                    logger.warn("response status code fail:" + str(resp.status_code))
                    errorinfo.append("Access " + url + " fail, status code:" + str(resp.status_code))
        if len(errorinfo) != 0:
            body = "
    ".join(errorinfo)
            subject = "Please note: PlayCheck Error"
            for toAddr in email_notify_list:
                print ("send message [%s] to [%s]" % (body, toAddr))
                sendEmail(mail_user, toAddr, subject, body)
    

    这里的 test\_url 函数的思路非常清晰,首先请求 url\_list 内的目标网页,如果请求超时或者出现错误码就会记录下 errorinfo。

    当 errorinfo 列表非空时,也就是有链接的访问出现问题时就会调用 sendEmail 函数

    def sendEmail(fromAddr, toAddr, subject, content):
        sender = fromAddr
        receivers = [toAddr]
        message = MIMEText(content, 'plain', 'utf-8')
        message['From'] = Header(fromAddr, 'utf-8')
        message['To'] = Header(toAddr, 'utf-8')
        message['Subject'] = Header(subject, 'utf-8')
        try:
            smtpObj = smtplib.SMTP_SSL(mail_host, mail_port)
            smtpObj.login(mail_user, mail_pass)
            smtpObj.sendmail(sender, receivers, message.as_string())
            print("send email success")
            return True
        except smtplib.SMTPException as e:
            print(e)
            print("Error: send email fail")
            return False
    

    sendEmail 函数负责登录邮箱并发送 errorinfo 邮件提醒

    smtpObj = smtplib.SMTP_SSL(mail_host, mail_port)
    smtpObj.login(mail_user, mail_pass)
    smtpObj.sendmail(sender, receivers, message.as_string())
    

    下面我们再看一下云函数的配置文件

    定时器配置

    注意图中画红圈的部分

    "CronExpression": "* */1 * * * * *",
    

    这是 Cron 表达式,用来描述定时任务开始执行时间用的,这里的 * */1 * * * * * 表示每分钟执行一次云函数,以达到网站监控拨测的功能。有关 Cron 表达式的具体用法可翻阅腾讯云官方文档。

    以上就是整个拨测示例云函数的工作流程。下面就让我们来照葫芦画瓢编写自己的云函数吧。

    3. 请求数据分析

    喜闻乐见的抓包环节,看看打卡的时候时手机应用都和服务器交流了些啥

    应用数据首页

    点进去看一下

    登陆应用

    OK,这里我们已经看到了应用的登录过程,这里提交了 usernamepasswordtype 三个参数,分别对应我们的用户名,登陆密码和用户类型,后面我们只需要把这些数据重新发送给服务器就可以模拟登陆 App 了

    提交信息

    这里就是向服务器发送我们填写的健康信息,一会我们再把这些信息一股脑再重新抛给服务器就好了

    4. 编写云函数

    根据上面的分析,直接上代码

    def myHealth(user, pwd, email):
        errorinfo = [serverless]
        s = requests.Session()  # 新建一个request对象
        data = {  # 登陆信息
            'username': user,
            'password': pwd,
            'type': 'student',
        }
        r = s.post(server+'/authentication/login', json=data)  # 登录
        if r.json()['ok']:
            errorinfo.append('登陆成功')
        else:
            s.close()
            errorinfo.append('登陆失败')
            return
        data = {  # 健康信息
            "home": "在家",
            "address": "",
            "keepInHome": "否",
            "keepInHomeDate": 'null',
            "contact": "否",
            "health": "否",
            "familyNCP": "否",
            "fever": "否",
            "feverValue": "",
            "cough": "否",
            "diarrhea": "否",
            "homeInHubei": "否",
            "arriveHubei": "无",
            "travel": "无",
            "remark": "无",
            "submitCount": '0'
        }
        r = s.post(server+'/student/healthInfo/save', json=data)  # 提交健康信息
        if r.json()['ok']:
            errorinfo.append('提交健康信息成功')
        else:
            errorinfo.append('提交健康信息失败:'+r.json()['message'])
        s.close()  # 关闭连接
        emailTask(errorinfo, email)  # 发送邮件
    

    嗯,替换一下模板函数里面的 test\_url 函数就 ok 了

    不过前面我有提到邮件发送有问题,下面我们来看下 sendemai 函数里邮件内容编码部分

        message['From'] = Header(fromAddr, 'utf-8')
        message['To'] = Header(toAddr, 'utf-8')
        message['Subject'] = Header(subject, 'utf-8')
    

    这里的收件人,发件人和主题信息都经过了 Header(string, 'utf-8') 来编码。不过在我用 163 邮箱发信时,这种方法只能自己给自己的邮箱发邮件,给别人发会被邮件系统当成垃圾邮件发送失败。所以如果你需要给其他邮箱发邮件的话,这里需要去掉编码,改成

        message['From'] = fromAddr
        message['To'] = toAddr
        message['Subject'] = subject    
    

    这样就可以正常发送邮件了

    5. 设置触发器

    OK,我们把修改好的云函数保存一下

    配置函数

    然后把内存改到 64mb,超时时间给个 3s 即可

    添加触发器

    最后添加定时触发器,这里我们选择自定义触发周期。

    Cron 表达式 0 0 6 * * * * 代表每天早上 6 点触发一次,注意千万不要写成 * * 6 * * * * ,不然将会在每天的 6-7 点内每秒触发一次。这样的话就,画面太美不敢想象,哈哈哈 ~

    写在最后

    以上,想必现在你已经 get 了如何使用 Timer 触发器来触发云函数了,何不赶快自己动手尝试一下呢?

    发挥你的想象力,试着做些有趣又有用的小东西吧!本文仅供学习交流之用途,不要学来干坏事哦~

    Serverless Framework 30 天试用计划

    我们诚邀您来体验最便捷的 Serverless 开发和部署方式。在试用期内,相关联的产品及服务均提供免费资源和专业的技术支持,帮助您的业务快速、便捷地实现 Serverless!

    详情可查阅:Serverless Framework 试用计划

    One More Thing

    3 秒你能做什么?喝一口水,看一封邮件,还是 —— 部署一个完整的 Serverless 应用?

    复制链接至 PC 浏览器访问:https://serverless.cloud.tencent.com/deploy/express

    3 秒极速部署,立即体验史上最快的 Serverless HTTP 实战开发!

    传送门:

    欢迎访问:Serverless 中文网,您可以在 最佳实践 里体验更多关于 Serverless 应用的开发!


    推荐阅读:《Serverless 架构:从原理、设计到项目实战》

  • 相关阅读:
    49. 字母异位词分组
    73. 矩阵置零
    Razor语法问题(foreach里面嵌套if)
    多线程问题
    Get json formatted string from web by sending HttpWebRequest and then deserialize it to get needed data
    How to execute tons of tasks parallelly with TPL method?
    How to sort the dictionary by the value field
    How to customize the console applicaton
    What is the difference for delete/truncate/drop
    How to call C/C++ sytle function from C# solution?
  • 原文地址:https://www.cnblogs.com/serverlesscloud/p/13038797.html
Copyright © 2011-2022 走看看