用 shell 脚本做 restful api 接口监控

2022-08-19 10:36:14 浏览数 (1)

问题的提出

基于历史原因,公司有一个“三无”采集服务——无人员、无运维、无监控——有能力做的部门不想接、接了的部门没能力。于是就一直这样裸奔,直到前几天一个依赖于这个采集服务的大数据分析服务入口流量锐减,才发现居然是这个采集服务出问题了!而且问题不是简单的挂掉,而是这个采集服务给客户端下发的采集策略中,产品列表为空了!当时事出紧急,把所有产品开关挨个打开了一遍,算是临时解决了这个问题。事后复盘这个问题,从问题出现、到问题被感知到、再到问题被临时解决,这中间消耗的时间太长了,在新的采集服务上线之前,需要随时监控老的采集服务的接口状态,一旦有问题就可以立即处理。

问题的解决

对于后台开发或自动化测试来说,搞个监控是分分钟的事,对于我们这种客户端开发就不一样了,如果用 c/c 写代码倒是可以实现,但是一来慢、二来不灵活、三也不值当。于是重操旧业,用 shell 脚本搞起!话说我用的是 Windows 系统,为了在上面跑 shell 脚本,事先装了一个 msys2 系统 —— git bash,这段之前很多文章涉及过了,就不再赘述,就是对我的开发环境做个简要交待。

环境有了,现在整理一下我的思路,我希望做的是:访问后台 restful api 接口,从返回的结果中得到开启的产品数量,如果数量小于某个值,就向相关人员发送报警邮件,并记录日志。每隔一小时检查一次。

检查接口返回内容

访问 restful api 一般是通过 http 协议,这里我们选取 curl 做为拉取工具,写脚本如下:

代码语言:javascript复制
curl -s "http://***.******.***/v3/server_status?type=100&data_version=2.4"

出于安全考虑,这里域名被我用星号代替了,后面的两个 url 参数分别是请求的类型(100 表示获取产品列表)和当前协议版本号(2.4),如果一切正常的话,你会得到下面这一堆数据:

代码语言:javascript复制
/*
* 提示:该行代码过长,系统自动注释不进行高亮。一键复制会移除系统注释 
* {"message":"MjllcG T6g4UJWklDvBu3wq5D8ClRoHuUFFlepFL9xnoEdCHU1J8VLnN4GvaMv4FcareEbznrpp60fyXkr1MSFrSz6P3eUCPFUorS2w0NfhfHKfdXz1lzhV29LTFOc9rYNxOLO2RAswyrN6CmAYsVMBoNrPIq3uA50ymLbJFhsnWqOpIBofhdEnDzkS0T BBX112Nbw46prknIqY0UJW60aMnLuPVmjJKEAxWCgfCLrWDp0ts4uaTbvV8nvi7U bS9Oqxz1fNC40SfNcc2bLILZ8ZZ4givi2SwfHPs0mN6fF/ewACKzykjuIf6 xVfTnQBFZGiNLunKSz0fiNJXj226CMTVx2KNMG lHJgYqHpXxLiMcvbj/herzN0gINGEUExZ5GDNBYFph1MD2q6jUqtWwFGlSVRqi4Mz0FyCx6ADRB9SrZjopEYmFFtqkc6QvxQeC xgNmGu91vkW9o7rADkblQPetNjouJ2WfBH1WXB9Rm7mbIo5YKVrIABXo9YLkdELnZsaNlx/KOg0cazqwMyemlwI7 PexaB6gZQqYqL6Awx3 KptARPNib0mFeetMqPiJkl S1gNaiQ7I74YK7vzFDVDQjLCupTYoILlIewMHGqLmvnOnWNpI4ZKVVOPSUZpa73hzJfGuMN9jCuybOeTxR2JD1HX2 teA6Lr8mYGu9KW70guKtRvvP0VM2/DmGb9K57jWzJjaSngsffo4yY/gM0wqg6HE3QczHXdhdiOpT3jfghfV0DG6dkcxL8lADjBR1W9pGAHObKcY7iIBiIH5Jcm/oK9nFFCsG3 QAzrJv NyPtyCBJ/R9c5r3ihGK3 wDqudExy2JjmGC7FZdWqSSIcp2OXrQOBAC9 7BZW/qbJZpD6pcHOfFTRoYYxAVhkhxkHxca/OhSntxA158mN55RMl8rb1o0kJzli7Cty0HZiXbEqLKL5mH5vJDW2dGDPr7JtnolpP2MUDWJdBjH4WiIeqxB3bplEecvVWPtgDbq/8r0XamToOzRnn9X7gmdcWpjoP6oeLYFw1v7PgvcBr2SCxwif1zLBiXDEizDJ7V2kK1zs0DpiDVrota318624zkVosxHEvaidtJhthlGu qBTWVY2fwbYncatlZ/5WbHpowzHreE1C3bFn94G4xjYZlCpQqoFgQI/EoU/Ml2uXRISj53a7NVp1OMzZu6gkSJ8AQH44OxJAo3 tYlu6tdYOie2LNnIpSyfrHmxv7MliulK85kSRyA /IrKMSz91yC7zrIzPPSYwWXu pX9eiWRFuMxezFmYkZwqCVvea6yVvpNBOI5BQPtVQyzjdd9b0iCUtujNBJY4TH Pxw9O7chyc4lmoL3H0DH ofwkJ9xtK6YR/ygDH3CwURq4TS6SVIm8L/NOpF9lHbMU/TWdeMGYXrAMgZzgAQwMF2s4Cs51FNrmKsx3zUBQva9JpJue42G/Imj3tdReI0CGlmAxV72q1h t1KiW0nNSre5J0ZTkXEGO6Usx781k1X1p8MmzW1Qyj4hLJpevr7s7ahLpwlfeblCA4mhC1UGe9xKSpEhvINgOVXCdX8qfNv iwojVl8pwRfnLo4IIH0LuZRFw3Rn3UVJKGHLdyRHcf4zOQz8y4Rwe5EsC9aMWWImQD2Wx9XVKZP6kZs4WcQHlkodg9AXP0yAqmurPBmDa/nEH3/VkYfohqU2xOniUXhtcupA/eUMEcdxvB2dhNeuGZ6OWXTtW9NWF1DA30u1o/GzYSdMbAqXfeCX6E4T82xl6mc9let84abAT1YFvOcnFcC92bT4XFxnbZut6DYs4F6fdznrdR3j33ua V8Rbrk Fm6xJWLJEbsUAQA9EO5ebfD1UG5zHbbEMTgNl9auwgRG znBBSmdFH7XAdogJbDEtiRQG1uBM6d2DQDfW2dXoHfHTYMZB cxtwEEZxo3iqNGoRMknpr/EcWuqJN4slzYT0ckd2VYNJaVmwU1ukEGmgk0dY0hmUmR K2NG6ETMbZYQCSAoGvXgRjrjbXZkFz0SzA5aNKBnlmchWnpMZAKhn2DPkedloW oNN4AAs/S0lg4I1BbJ27jMc7As fGxbprstsHnCesLvSt yewg7eWdEb2FAEPiVuwGd91xeZAjqCgcfgruxQ8gBPcnA3wISUipd6IITHF/4pWDh4bCbfm/plJex4sLff4rlvDpu01e/hLsfiDKJ4CHXeZXU8 3iRdG BEIeSV9X9RmXW70M4lrIsdWCRYUOPuZ4swnVCsgLnnJVt14drWwF9gE/dAwUbsiCypLCqNjNf314Qkn3B9sXOF4s/HjifLLC2I6StX411g9uC0rIjMw2iJeDH6NmROm/dRVMkzkhzlVrEM4d9nSwBFZpMq4kTxMHgLJmUPQG8Qu40Rd/qQ46d7JLyBZsVH3 E5B/Dv9X5KyIWKP59XAc9uPtfiLK HWnG5clChWjG1uAGrYGGeP1J8iCtaw03GPieKFYEox9vKICjs x/FfWeLjrB66TwvMYlsEl/2t2OomJ7qnQXY2bQPlCYeuCgZf7Amvc3CKgKrSkQCMMO4uIBmHdyJeYctUV4VlkVE1BFN6MRyIIwXmQy7xdQb5yBFNl5FO1td1zn8Cm Kkklpjpwtg6G6QGGqX0jQvEmL0dxUMzHgkumxAgSIm7iB6ndD65Z13NKOIplb7Nn8kYX4b43t1lkkDOtcpGcx8LkZZU1sf C /bWQKuyL1LC5Wg4rIjy0UA78fq1gG6KAGCXP8AGEL5l6rHg9La NVI/LzdQwlEjAhQ7DBIClwrk943H1vapdEpMktGUHLZahoEL7CKSbe3vO5l62rVujsi5W9rmH3/DtS90dErXhrIidjyrGm u5JdcIpbaP qKwzgd/XEkU5YxqbJ2RgzuXRQpxtAVfptlU8PYj0FH0Fzs67dE0uestXcVTudThTxZ0NbxJ45u3KX9WqoAOf6UyO0XpwJAuPbiH3d gISYznEXlzsf6kZ4fc6z6eUX F2UPIQ/JaoK0VpTlV35VbjSOsSSQ7Dcb4VzO8WlMiAV4B0oVaJuAnie/R2InbMkaoxthhnooPue5TliJdPAcDP6aeTOlvhcvhY/0bPT32kECIZPJrO3S 9snwqR2Qw0LuYuJwibjw3HdMqG6t4Wt qiDbRj8sWMXXk8uPDthXUDqCwMETwBNQEQAf2gue72gFGho9qPyBm3PirjiUHjSEImQTWCzcMGQZE lIVP/2cQExe4 hdMcjPTnUu8Xx4tqKIjgliPJyLx/NouFB5JXniHCI281e0IDxRM4gGsuGGe4z1I0bGU3UAhd2xA4akw7PnayAZ jSm5xSy/bjtgoGuxegD4B8SoQzPYTsp6Bz3y6g7jPEWpvKqM12HJru7RsCVBPtO2SQ8nIjmdDuRo/Q6mzNHOiTnBUzfGNYK0AJPaJ4M5sHdaFcYjSRVgSFfzs9Fnz5pA1Nt7vLNCe3GSacXqdd6 VqrTb81Slfkac0OwNeCrNwiJUIo0UW2kTGU2tz9AXp7h2s31eBxrPNB0/jKE7TmnRRgOUBeB0GeglV9sJVZJqR8Zwgj8noJH7RFN3rzvtFfMElJGyAEKb7ip2egCCrs3745jrwdMt9wTQP XXqegmlXqJEi9bcJaSXpHmy76cOpabK9L/WZfOKncFo0TNq4Gxdx/nYP9j1SW7NHHvjuZ16/YWcJLDh0raK8Qfa0TPOnmXSyppDzE0GmzIjdJC1UntefS0zy7aYfhJ2EDukWq1QdEN/nEdZzGt8LrNQ6XhzpyuOdaKq1HN/JgddEjROJrviglXUdL6LO/x8ioiH7 oFv6hw39puL/9OUzMOe6jFR7baPLcaFHlnyy2S3xc1DTGZGsNczNkSRId69uOvKTMwPlYEiL38JyIeBG4csKHP3/fjmbaZes4skqbccB0q KBO/i0/7ZzhE5eZc/BLu0bfr1qAsAC0lYR2 rxZF86tAu6wcpGtMtsK/QHYgtB7e1DJEsqx7R1ZpIdbAVS/FI1BELn84n78NGzLEkulAWqUGTMqd1bqEk2YtqEpP2bLZmGBZQpcGFGBvu1TVaPzYbCNEA57YWK0BSOvUWy9H5 s8b4XcNfVFf6nBPpAycwJDy93MEK1BGc3gnQFb4m8ptnRr7qjgsCE22yOcKdYeri2ZzDlDLs35bVc/aAF2ZI5iZV9XLASgEsqerRJwAuzKdU0VMUm9tNO7MQwCPL0Gln1Mh4gqqzv4BTctWPVw7vnvioLLw5E2NmuFNl/CiwiQIWTIaHyvSCQyf/7wUxKlQi/YF4LhFZfkKZUqkXmqPQQFKZU5oW8Og6bkFx479G/XWPbZSoGE2LVza/RyfMTPYyHi1guQz/t8f 8r92GWTBEM1dlWpMRkqZ5PqKomfexnsoQaLx05DnYGoAj3b9y5bzJEglH5e2 31cjoM4t3aTMpMu3azdQTcq/zluSkLIyxZ8A8GEx6ns3zpbssISD3PPs4OVWRfCSums7miktgTw3iulexCvYxg10MermiwxGRjuU2ajcq4ph1KGPcWj/XAZFNHdnzwAtPxJnRyIJE3W0r4ZNoCXYbrlBdzsZRic9SBJouEOf3P 9TzlltBACAH6yVfRrwflZPGLqT3ISZG5rXg8s/F5aFN24xHU3VKC py1OqXYgkWfbsAFakHWw XHIIJwGjpnqL/OIxYF2ibXWDAXFKwQXqssJ7OGDXTLVnV Txhpj1LQKtCGjx w44y ovBhhD15/xuA/zZ/Xfp8K O0ziMqx yPkR762B7HjX8G1k9oJaVxkSffiBtqnHCGDeX6V0D/10A40d8gnfnBQbOIlAEWs05QtqrjmMXRoxUOGQbCtRwr84dEKfe6fw4P3CtDL tT6Q9pyANL/92 WxVXC2v3ng88BgOf XihZGq0Bjn/Fi78GsIZ4kCkrKDsmZo6pV8vWmytLjvw1jsGqej QgFLFKPcZjp859 j/iRnTVHjcSp29koDjX6tSpFPU rFHSeNb6WvKmjFtwjdPkcHMzE4dY86UcpdlqrgRVZbOzthnDAeXAB qOgvvjKYYasnVDjvdpoqC8Zf3ekX27T/AaH8s2fmnPCsXjr sOlr7Rn6zCtvzCfp4iIl1EN8XTTmlgIzQprcRFSEbp210k3GNkoDVSgVRHxFrdxnq5dAGwbJbtGuoFmenIf5LlCiByDkjEN982/V59cxpO8V5N2xiOZP7enKSbNrzEd6OvAsuMs1gaJbJB42r22udwFZl/4R0cC5bpYlPiNuDsMwzaxCvBXMRbGv6h 25FooEGfgHKmO Kb78zBR9aN5D9p1nJdMhqKkMf2EtqJvwBYOEKWbUBj/AE5054/lN9WqCxnszrVaOv50tF bcRfwI9 LbVvxZFNsOwe0o5eGXB2LWJ mX3qo3nGnsGnBKY4VZ kAA/ DTyWIZuSfA5utZ7sQUEODYagwBPbaXsJr3EVVhGYgk5GQod7Bqz3GYM8aBC9kM/WN/jtDu6rzjDMvws7hJ2ZQhmTXHksNCOakhjejUfioj3I8w1I/rZfvILDujxUSv3neSUE0mZzaduup0SWeRUP49etR7WwwOEl32Qa06StGrv4rPkA20u0fgwcLmdT 0JsI6RPypAjP1ubuzpiaMKGfXKosqJR5TC1Xua8CX su6wkl2eltKpO2VyJffNoJ8ZQsoiJ5Ab4NUa oiD7MFwh/lLldyo6LeKLbOJ02mmvNo1Z7z5w tAH7NJjDa1wLtw25oNl4gdlD2ZEnY6U35PLko1WQsCQ5ylitH6yNPuRG2RXmbsh/lnA8fB9aoXFbnuyMYsxdCTQoLKagGzIkW4kVUloiE0JFmcW0sC5OOT4n2m6vivuMUJCcvuJFdFl3Jt5Ku1 gRlzXuW5u0ZVFSodMaLkajDnrh8DXZbGs5ObzxwxEbl6DJ5LYwsYdt4 eq0yOSQ0dAzocECe5CLyB0tpRIjAYUJ/vV3wk5IEtEHfS sQemlQ2m91Ey2VmFdwPhlgRVhFLJ0OaYmdtPOzfYV512oKhjq1PWtRdru6N0kbWrmdbUkEHcBVI8caiWOl8sKG6MY5F re/wkTOxQPXyZin31PwF5ccqcir 4ESJay35TeKELT4LwZR5aUFpz2Jqj/HkHeGZmU1T2C9zL0DUa95lEMvMDrdal6JZXh7Vff75TuwOEhlr/j1qg3Ma7d3VWZSatsT0/sJbeMypWTRXoah1EkvviG6HzPG7MiWpLivm8mp2kTxvc9IV B26jVm Cir/AU69DDlrr0sJA0PnpsumWlJU8toZY5nNtlmOetQOvwAaqwewQN0xlNdsAKUOtNnmzdgshQY3862HjU1Yla2MnkACjxh2Gr4BAG2TJzbkRePe1/3cyWLAtm24WK85rWFVJKA o/XY9yC y/46u35NlXUpC0 q3Mg94txC cWCeOXCC1GrepQL64A7JUE/YlKOKrxGCPaAaG5yXB1o7rbY56MCHTcFwZQWMwTxQG6bhIlmRz5c/WfO/7jeUXkl6GHd/hIoz9MOsD7GLjpPBFsqGjtbgeirg9B3umUT8CxG6p5Qp/2z6HZVrD4przwA5tG77RgACgWOYtAxwnHA49kEbFQdwnrbi dawPZfawhJg2wW6QvjEmJe W1zsqiTC3BUyd9YM /XcygZlZgxjIQfKuhlzHc3SNv W4NRCAjAs1n zNGjTKTSc/6tB/eAYAxPnZLOnYTsvjC Zn3Aa62HZcCnwSCbYGNEpz7yB3v5v5iGhplaoU3BOzItg7KKI6uheXepUar61GWm9SJLGZ/ur2cPFrlIkSa8MYHpqAuOzKXcT 1R6QtDBayKVTHlVfL3K4t9SM9/4IVcg IypFhzCa7CK8ugEYsSDLktLtEo65 33V14GLLKZGcy3DvhdiOHGPaeNGy9huAtcfOdFybUbjsGFSbbphdd0/A5YhzuVRigT4jUxgTgdZtlLBbighDBclHMpzB/nBaqwTmGwMgfNNYdwPjc9hkTdjYs5y3ZV3EIKHIxGtDOgG096LvEQKknw48JEyFWySD/wHoe//ewl4PN zXXSqw78HiQvK2/AWUBE4jq8ACdC1qHIO51c3hKNZXvpDhuzwhTs kjAUPvMeyKvyGv8sJZivW2j3GvVkDYrQxM6DgupTK09Yn7E5/XUNa4Wt9nw0mX1xVOryNXUeU7xa/vh7LISW/62s96ds8T0lIyK2c6weLTnz/zDHC lHJlxRQmL024jAyrPQA7Lx4hhUckPbN6E5pOsUVVu3yDtP/ Wm/r2IYohAd1nYO3LKN4bmlg0gjeoZd72Xmnyn69EdDpjyjrFoDeFfLs7iq4xSs9xcdCXb1yD aLAzT8VPVG49s44FwxADJgCI1iK9q2LwaNakXY THcuL56FmcQobOvxhUeSyGpyu0elA1STXbPJc2flfBKhQkOZVlcXeeS2N4oLkhgq1Z2bVNltUkwT879sSPtUVsNW9/Qwgq 4QytnGTGByYHOnStA/PfAIItu hqusBh4amVocJfz0tYvKysUyqNwKY sqhkT6qEbgvT /qunsOSwy2RkzusOdVYBDrdP0KFDnfaTXhNpss79GdzMZr/aNK mSeO1FYTITv1dCJNqNN1MEQ//o2t6S3ONkoos6MwMldWF0EqqpEQjES2nT1mq2jYPsp4Mb4kQBeAixQ9Y5lCTnJ6TAyaYObhj/bliEWHjRGxTcEII/ytJIBVVV153ONkCOyGD67XpEIsZiLY2K9zrBtlOLLCCzrxX6CB/KQsdoT57/sVNdrG4gDHqQrDB9 0bm5KCz2KWy5/mb3EVML NjwBq6eZLM7PJEkWCeUjAH0b FjUUPWs9PVt4OrB2jgg4EZZliFzjhQw kmySDhXdj2/FHtWB/YFgPyuQQWSkxX2pPMB1nYIjqT/MsN3cZLyD/5yu/ BjfgbsQVrMRjy4dR1pY7ZjG qcX498dj32iGVyk8z5B2bsUFUo6Bu71e2SheWw0j3VwVsazVC7UMgzO1QWsQyGlwxR/Bh0Ct3NlQqUyw0JA7Ty0G9xFEXoQMy vtNR5LRAJALKMjcVuI8bNRLgggdna/SLL2YSnO2mIR58Y4cHYjjrpUlaiLI78bqn2NKvkawl2ok80sIHepCzZykPu6c6lgGam9yq4Xch1OSAT5 o8 nMQV3unjUBTP5Ew7Gy/U1Zuf/47/QfdeCC4lMFXjBlcULK4zoceLWTh1zjZYlByQPq2U7 MU3NnIzEuI6v1hwKcb66qqWYvFEQFW6L FgaST5aaawH8OVqMxA5pO75Vv96sq54f2KVN5Mh82hzATw4XHJPu49zjx3I fDBwLFV3zHQJD/qGCcQl/R9HgJR19BGaYQH6minuaK9fXb1apKAxnhZfm9vcIaT22fhh1rAVLHUJKqoKT2dx7rfUrNAYrw8pDgj3JidlXlDFuqmScJHhrYelPIdGWUmjpgqH/UFIBB63hwAKAZBzlYRFXCU661bJVja3QD/XFafwLqTX8BvTRqQNQcmExDEk4wWgBU61v2/dwPpYLoW/qW kGiG/cmTFbN7hXGWnTp6lOls3O3ggXIGj 1sx2T3Vdptq7SrEGDpstFcSPo7lxMVOuew73s9opZXOu2mtUe7DIcsicvJifsBUAlxDlHdAbO4fTG1kXF0v5ybqrkRaadDBU4F775h rNPLB ziL6achTKk3iVsSnWbrZfha2bgWWG2j9YM22vxy6VaiJbL9ZEHHeiv0LmViz7888iNI LXxSDI4XCmLHmdQNLk/NNpLAiYDzCl9lbxC5iFvABsaZigBuCqfc/EzqLNqSrHL4sugvUmY0rlqs7oh0OQnKiWJdIlDTQpo/cEmc4NA8SkBJ9qLl2poP8EvpOUyHCZUSl0jinzNe abAiyEzbD75ynac9VJuouk225cozgTsUlklmj/RpKoYxopmLMm xJfkC2NO4vlQpo42C/KJvMhQK4S32NVngSyhF4rhrkTC28hcBzdQsrsyMQKHygH13YoKRlXZCUoXzuFgMPBmqfWoc138oLYd4DNc9sqZhT8MTdw3OW9WOZNwsxPktVIXxqZdRkmkGXB/Pt/uoXZ2F OBhxFXyViwrhzqiIKw2KItKpPsdXW4iPuUrMx/a4JjNIKcIGbZx4zkdCJtqCQ6BAD18deDfS0NOnZJZ1mymAvWmDxdwU9zapBcpyVMCm6Ep0I0q pG PQo60wz5MC 8IlLY7dHP0uC4rSaXM/WATMGkSjQnnEb9GIdo5UbtdMCutoLF4K7tSIR1OUk4I4PJYn0xpPAbHBzm9gGJ408xN3oe0 DR7Do80H04MA0S9IodQWCAhKFY3MUulg7F3bDdHIUlzYJEDDKW y6eVjfYRe/6fnT2lneqw57alb9KnlM47OHJr4qnnoE oMSSSnr94KqRnudcA Ln9Pj/UTxbFrYQAsEywPaXnEKO9qZSEVc/W81oP gUgrG5zCWHYoSiZ5rwBlExGpHObXbvaLehuGBFXCe2dl7bk3wnkh2qK 1XHEHKf07midYc88bH4BerV1llc0HNWgvWQd2P/RkQPgePAVZ652p5PinZCk8ubOW1zuVa0t7eRn0w7layAHIf3Xody/g0 e0v7AaQarxZBfdcQmb/aFjGi1KCAOlnKNrWIb5zr3MN5q0L/h9WguM4MeBUZqzfND9BTH26wImlyp2uItCRJWYM7bQm4t4FcKMjkEKvj01wcJMd5bTScj 0BGoRHWUyQxRRlm2cVR2r1/bB3l7Dv DXkKECARa RRars9ftPEUkArdjbbWkBQJhCQSdeNR3Ym6mW82QAqBLXEN1FTcEtwMwcfy9Ep kP6yKYgZI/NK9pW2A838xhe71 3ul6/5xpZ3zMG4gYGJDLYgu54gSFM6bDe7gIs/tWer1o8uGD1q5OpDVZnpxMHsJl88XvPuUSkW3L1YcjxG4YFAYtfYAd1yNhjUDqCeE8xIQXb1QI6KkpHtWOJno5C5kwhdie3BIpE0Tlrq3zbBB9BUTG3mJoBe0Db6Ti QQpw5h4ospUfIH4R2HHHzi2tvZ3ew6V3xvNUBb/ZYfia1PRumG6EY759IC5Y/PjfB4pbPxgxfLPT2ywe6d/7UXplckeadRxS194/dtP3eUiLgAfS1rHI1H/qdYG xLHD nRMB8D7gLWlM8rrcIPBiFFNAcBwZEVRyr25LXrhjI6mxsQSxlHTqnf8iLZId2DUKaBI1G69mrzof3DD92LJKqD297EoYXqoSvt3nElOuzofSPiw7FsOeLGbZzFtAH8xMwZvTyN0NYLHyBMMdmocCwbeltpOhCApl0eNYDOngVAygoojQbS4hDFywmi2 WyfXRh6Vm9p0acjRDY/jOhpsizVff7TuSrDWcMalkXafclL90AWRFU7wmc5Z9R2QIQVgBHAKnr7tQhgwvjPwktzvdV7y6HHDwXFt4iAF2SlywyDKlGSHM2ppr71hxROyHweSJOVLAwKss6NQnZiKXqjDF NQxdIWfb2qjCVFhrBLtsXld6lolBS8k0dyHQ8zuuF BmDQHslYtzyZrPzF/zxFsOq0TGWIBXeamUevno59TsuopIcss1IBXFxWXFO2CFCuht5iOtGAGaYL8gFB/YBRCC5yPjmzn9J5m2ZEVXYHt/1Uol0eHsgle/rfede0VCZpmmN0AYLx2zo9676CDmkq6wAlGKD5fz62qbwR6tK1XUAbVrGYhr7SEvsXTEEvsqLERSDgiSj6ExAWdbeAFxrDZN85UXHVWhtcVcBy qcxPj53F3nXXkRb0QAiGe8HD9uB6JOa3pPsOduI7n8hRkhwxJrUAHFJKk4as8zMjP6Syadp59cnS uNMh0KdNAbD5LfzzSahOANUtDhYhnWDWkMuZDfxUNMiIWzOet/w92PYBO2GuaKfRnY7yRE9bWea3vh4hHZvnN8unYEzORBnznUGSsvnfneypLEvdD5TZWJdpNv8GJPhngHR p4euALzbjN3t8fJAYUBt8XIBN4rO0JAXMGyfv9FlD0Cdu6i3qoePgg9J06m74Q5rzul2XmZHoAv QGrozfqrSK ooOanVTR35hlO/fgrZ44Sl65Ux0tcveHETgZhzEGJj64bhQYexEdYviFFuNXRUv1et35RniFsToEUOINjRUYJy1aJGhx7T9GtUWMRmVcw1Ma5BKMYJMSIPZhYggn/bR2DbDVMQqwK9E8P ynhJFosWggfBgoyN9FGk8Z Q5lYd C5ME1rE1PyghAjQFDfkhyCA0j5wcpIFt0AiMn808fsKAMXP26ztiSgdlz2HVBrWzkrVKqNgnT4eJika RxV8pDrmY VEtccQYonh9RLmE9xi4Ce51  4SgIrF lhPaU9PNVni2wqGQELVsHQl1PR3vtEz M4SAlGFPx6qTfNualKef89JrPiOrIpBbYlSA31zuqyiie/0/5wununc475Fn43nVezHNe07HiLEqLwENqJgWSt EnLLP6XS1Qb7b/iGP98DxbX FhLy6OOVuiS6wOcBrE3rxn2cXqSgjtPa3fqbZTc9XeXhuK4HoT721qGJxoMwslHEgfNFhHbwsHhwnRIGSH/uMvLSPFAhxYE4qdReITMLN2cNwsC sP/sgPwPVH7UOqP2bPpFma2 CEMMGlVf8DaHEZEOUph6qkfUGHDXe9OkVmYWAxwcZVMdEfDR7EHIdvSGSy2462sp1Kzhzq4k65DsfvzMD1HxEWauj8BqD9qbbNLSkI48hznylKjdSIQBQvaV0Rg0wKIWvkIH73SYYJm4wR/ISUQ5GXMJLdKxQFGVKe7zPyQ6gb3CcJj0fnMlKJmoVamnk2X9fefpzw0qBfXCSwD3CV3TjbbgjqczmzLwE3v YXt234uX9hLtIXxn6nMESe3ICBG5FhGH9Dm8iaydk/FJQ/LRJFbnRBao2wqIvPrGh4mzCI6C0yJ17 yuLUjfWlDEmJnIaWmm6AJMf SPxX/w/CDj4U30kwU Xw OEy7X/ibiqJuivT/JgSEt2CKW1DaLUZwzuLwj4oLHrhtX0bW5ur8vk5mby8llz2rkJVng3FhojsD2AECCDbXBnD0sNZAXJSz fhx/MHLgRJNfX7h7wf1sZcp5BSMUXlN7kcEbrCxqsrshHHNZNsSIJhggbCU4MldbNaKZVRhKAdv/2UE2jEazHPoyIPClO8sNEhryd4ffmay5wI5qsSUcDWL0Sq0BwwdRthSUODmu6GF0XkaKGwSPiTcFmwMeoNB2DVW3NmHkeJFQ0weNCTnRwKYvIUbm4cARywVLUWvXg2adubypykGtyB8pmQUcpHWL5oo0MTg/lkP8svIz4qWOb37Lk j0e4usLlhICWq22jtSbZbtcC9DxNUSb3qy7Fj/dEiPB2TBXlYwAjpMqpWh9K0kkbBwLkikOiHAKLJYE3I83FQMkc5NtIpCKULeTZksnNM9H4G1rD0pk4o U5f B2NJ4A2QhOfJRJtrJkO/2HMcka942YhLHnU5Vz7aY8OjgEac/OzEXpaJOxWlt6VMNjwkpbrOsf4t1Idip0y1OZg4x4DnW3mDASrhXvbFB95bhZSRACYj2nGVBk607xqlYTAZWrkPYUjYdXpYxmrJsLUoAL3VYp1hhVHEqzfb8jdz6i5j 6lYNarBUGPsuSE49h234Toi0pzhnXFiOEiPgP2zr8ct0qAesFktDArffy3bQU2Zogl0m 0/iGhpikSFOXwgWHC0k81qoWpKA0Qej1NsWKJlkqx2JiQXouZa3hE4BqK/QhO2VxYZQDx03PlwJdSLN2KkrhoNBo3XzcwKlhakNvTwcOi5OIZV9rJEGL fMii6hyG8kxatocnR01H6XvdzcclDQTqNpXURPXJcVxwPh68IPK28qtmolUfwcVEdTzr7uljJSEXQ7UWqV tCFEOcryNYHpO3gQ79IZ/2WmtnXaLS2pPXHdgMI07bUfXN3TLitkVarvewULFYqoIGdspdP4F5hkG1ROzictHaK0EK1JlWiFMlFxXJa10lwy3TMd/VUzFTOaaPDvXCcCf7Sj1rl09ks0OA0WnLSAn2kDTy/TcaH6Ohp72HCCYIOYJNkP2MXbDGZ5sVkuAgDdomSQJNe 6dBxoVvWwzmlBfnF0GeVpcSieCLYmzMdZohOBhGGKRzaqdqemMNeQA03 kKQdXVTLEmhWH25AOVMLQd5kY6DqKYBlJIyFFAePRTbIk8V7QNKEv8/6YsUvTECSjacCcVeSTv3B4cLLR4ndu4kHVBlGaT/YkzlKxZVcVA3ZMmjAVWIAH2cKLLuP03LvqRNwfaMXQ6HMEtSmoBFwZNn99tQCOgBJJkMe96CHnEpWhnKsPy1LdQ7cwPdQUT7qojZrW038nvbVp0O/RdBkMhqJNr8MMaUYbAnf6dgT9FdVrux/MA2jxaMJH8jjqUkaY8KM58mRfiCr0MwwJNAY NWRVliAq q8mtuY8Io6COAZL/c wzglyKOSozbCK5VjsqWBJrXYia2JNbz5aixoVD4 BCVeowjDRgwkVyCEToXbfTq6xriwu/WMYjp7/RshjdCwqsrJeJNlmEcEBr5vF/BdsNIVOn6uH2mjTMsjNW29Z7/Yx9n203JKSbnocsIcCdOnyjHZwD7BncPFvSUOCJxwsHFSqardmn4yX/KzCV38wwSSYL0kU1kPSTHD1OT9nisxqcMqqXHFm84/VBD UyOi4fummpW4RX81bksVPoai36mAYqqDaca6EAO0T/fVyRnn/tNEM7T6Pp9zibVidEGT0N/ei3/xn4MnfEmMev3101tHdYAWAnfa4BICsjsvCTy OVcKd0nOvfXrEpKnDibH0N4fiOjaQfpQ5n8DCV/QJXZRHe73SqysVxl0p1eiNsmwpkJ0726LnRD6WRCXqgvDHIH/WxPtB8mXxxdUIP31K1pZ/sXdtrDejXMuSg3GPcWtFda dl32mluveOhDWEh xa13zOkm607CVMx/sIQDaH42N6RGTn9Q4goIUTAhGY5lQ2N459PnyU2v2DGQyvZ8GzlLGofN/Nk c/UvHXeKLLG6WBghCE0mBdPjKh9TYGvyEVSykmf42Nu/Eqp8vLEpZfveGYzh9cd7uevL8JweL1aS8BEWSUMcA943E2VxaqUG/P44F0jsnVlWy gw2p930VIl2W8PgwRsJZvKX3PUB7HVvETlnUzq1i0vGtE2o5SHhcgCi0OlBmAsARKxWolMIdw/HAnYwR17Io2Is30 UbO1pdV5FJTgyFucP48lplxyaJZSHGEMDoL2rALaneFeox/rqGJTwZ1AlupkgT6ELnvTLlfe1If dJIDSLYixos mrvcoNXmOqohachyDirqzMQkfBu9Q46Z V4V72mSeJICBkaylrrmYtjDF9LyevU9Bighqzehae/T2AhSgUk5CHROV/xx7ZGaC/Ff6E0MqFMjOm i2EI/PLj5cl0ivLp0QrlFgHRmmOi1sc pOOrc6A4VQ  q78giMGpGuspOZXLyLwwfceac7z5 zB838H5uM2H5chB022KACBTx9PyVOutXMAShdgQz479JFXJtKaP0KipaO1YSfkEP B2KIfA805CPTG4uHmt3saNq hAq0qu7MgnF0MwS5YAGWmrVZqCrbN70MbI20VwZ11KzIayVNIXnK/rV7hQMHRDeZs50R5pwf 6kIY/mgRdPB7SQLT3t954hqmMpgOeFcW2u0RYhR04Y5V2p4gpPAskTbylyXrT87C5/VOwrQgWfGCxwllyLbJyXVy2eu0nGMyTKHF1nnpWuFZrpVt1wg9Albl7ZGyOkVxrzGGG4cassB43piu8/mH1NvFYMONefpRarmLuCAVZvUi2aq1IYQgsfNuvsyCOFrw8b/837DYYAMyVbvEU6VoTHoTY4HuGeYAs72 c3iS6sKVr4g QtUzbF/KncUqOeDQoHmdZbQGajbu","md5":"7cc552ea3a1f12c13f63f96f53aec29b27ab7b59542cfaac0c2938375156fdfd","result":true}
*/

本身是个 json,有用的域是 message 字段,它本身又是加密的 (为毛不直接走 https?)。好吧,我们需要一个解密工具,对于客户端开发来说手到擒来,直接把测试用例改改就弄出来一个:

代码语言:javascript复制
curl -s "http://***.******.***/v3/server_status?type=100&data_version=2.4" | ./jq -r ".message" | xargs ./test-decode

相比上面的语句,多了两个命令,其中 jq 是用来解析 json 的,负责将 message 字段提取出来,msys2 默认是不带这个命令的,可以访问以下网址获取:stedolan.github.com/jq/download/ ,安装后将命令所在目录添加到 PATH 环境变量、并重启系统后,就可以在 msys2 系统中使用 jq 了,不过我这里是直接把命令复制到了脚本所在目录,所以需要使用 ./jq 来指明;test-decode 就是我写的解密工具啦,它从命令行参数读取加密数据 (所以需要 xargs 进行转换,不然可以直接用管道线连接啦),将解密后的数据输出到标准输出。经过上面的处理,这一坨数据就能被人类所识别了:

代码语言:javascript复制
after decode:
/*
* 提示:该行代码过长,系统自动注释不进行高亮。一键复制会移除系统注释 
* {"products":[{"id":140,"name":"GrandDog","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":true},{"id":178,"name":"CubicostTRB","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":true},{"id":78,"name":"GTJ2017","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":true},{"id":137,"name":"GMD2017","aggre_status":true,"start":true,"enable_auto":false,"enable_filter":true},{"id":180,"name":"GDraw","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":true},{"id":276,"name":"GLC","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":true},{"id":164,"name":"GUX","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":true},{"id":67,"name":"GCCP5","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":true},{"id":261,"name":"GCCP6","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":true},{"id":17,"name":"TME","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":25,"name":"GWS","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":36,"name":"MOZIDIFFER","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":40,"name":"GMJ","aggre_status":true,"start":true,"enable_auto":false,"enable_filter":false},{"id":44,"name":"GCL2013","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":45,"name":"GGJ2013","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":56,"name":"MD_GMA","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":75,"name":"GDQ2015","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":76,"name":"GQI2017","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":77,"name":"GJG2015","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":80,"name":"GMP2016","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":83,"name":"Revit2GFC4GMP","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":100,"name":"GTJ2017CAD","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":112,"name":"GYZB2017","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":114,"name":"BIM5D_PC","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":115,"name":"GFYCM","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":125,"name":"GBCB","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":128,"name":"CubicostTAS","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":129,"name":"GMD","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":131,"name":"GAQ2017","aggre_status":true,"start":true,"enable_auto":false,"enable_filter":false},{"id":132,"name":"GBCB2017","aggre_status":true,"start":true,"enable_auto":false,"enable_filter":false},{"id":133,"name":"GBS2017","aggre_status":true,"start":true,"enable_auto":false,"enable_filter":false},{"id":134,"name":"GFYC2017","aggre_status":true,"start":true,"enable_auto":false,"enable_filter":false},{"id":135,"name":"GFYCM2017","aggre_status":true,"start":true,"enable_auto":false,"enable_filter":false},{"id":136,"name":"GMJ2017","aggre_status":true,"start":true,"enable_auto":false,"enable_filter":false},{"id":138,"name":"GSJ2017","aggre_status":true,"start":true,"enable_auto":false,"enable_filter":false},{"id":139,"name":"GJH2017","aggre_status":true,"start":true,"enable_auto":false,"enable_filter":false},{"id":142,"name":"TeamViewer","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":148,"name":"ZPert","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":160,"name":"GBS","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":162,"name":"GIR_C","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":163,"name":"TBQ2017","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":167,"name":"GYJC2017","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":177,"name":"GSXGZT2016","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":181,"name":"TBQD","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":182,"name":"TTED","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":183,"name":"TCFD","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":188,"name":"GSCApp","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":200,"name":"GFYC","aggre_status":true,"start":true,"enable_auto":false,"enable_filter":false},{"id":207,"name":"GDQ2017","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":217,"name":"GO","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":218,"name":"AppGbmp","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":222,"name":"GQI2018","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":226,"name":"GDS2017","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":228,"name":"GLDTCS","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":231,"name":"TenderGo","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":232,"name":"GDQ2018","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":233,"name":"SectionManual","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":234,"name":"BeamGo","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":235,"name":"GJG2018","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":236,"name":"RevitViewer","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":237,"name":"BIM5D_PC_TEST","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":238,"name":"BIM5D_PC_TRIAL","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":239,"name":"GEC5","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":240,"name":"GFYQ","aggre_status":true,"start":true,"enable_auto":false,"enable_filter":false},{"id":241,"name":"RoadDesigner","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":242,"name":"CECS100G","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":243,"name":"GBES","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":244,"name":"Ceshi","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":245,"name":"dpUpdate","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":246,"name":"GFY4","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":248,"name":"GGPT","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":249,"name":"GMA2020","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":250,"name":"JZYK","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":251,"name":"GVB5","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":252,"name":"GHW5","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":253,"name":"GUp","aggre_status":true,"start":true,"enable_auto":false,"enable_filter":false},{"id":254,"name":"BIM_COST","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":255,"name":"GICP5","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":256,"name":"bim5d_basic","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":257,"name":"GWH5","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":258,"name":"GFY4_2019","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":259,"name":"GDD2019","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":260,"name":"GCCP5_ShanDong_64","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":262,"name":"GSC6","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":263,"name":"GCCP6_WP","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":264,"name":"GEB6","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":265,"name":"GSH6","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":266,"name":"GTech2019","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":267,"name":"GPC5","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":268,"name":"GTJ2021","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":269,"name":"GDE2019","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":270,"name":"CubicostTIO","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":271,"name":"GCA5","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":272,"name":"GLC5","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":273,"name":"GMT5","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":274,"name":"GCN5","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":275,"name":"GHC5","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":277,"name":"GVB6","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":278,"name":"GJG2021","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":279,"name":"GJG","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":280,"name":"GAP","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":281,"name":"GSTP","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":283,"name":"TRS2021","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":284,"name":"TMEC","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":285,"name":"CubicostTMEC","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":286,"name":"GGF5","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":287,"name":"GRE5","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false},{"id":310,"name":"GA_CloudPlugin","aggre_status":false,"start":true,"enable_auto":false,"enable_filter":false}],"msg_type":100}
*/

在网页里显示会自动换行,实际上这段输出只有两行,而第二行才是我们需要的。提取第二行后交给 jq 解析出 products 域中的产品数据:

代码语言:javascript复制
curl -s "http://***.******.***/v3/server_status?type=100&data_version=2.4" | ./jq -r ".message" | xargs ./test-decode | tail -1 | ./jq ".products|.[]"

其中 jq ”.products|.[]“ 将把外层的元素去掉,对剩下的“纯纯“的内容进行 beautify 处理:

代码语言:javascript复制
{
  "id": 140,
  "name": "GrandDog",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": true
}
{
  "id": 178,
  "name": "CubicostTRB",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": true
}
{
  "id": 78,
  "name": "GTJ2017",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": true
}
{
  "id": 137,
  "name": "GMD2017",
  "aggre_status": true,
  "start": true,
  "enable_auto": false,
  "enable_filter": true
}
{
  "id": 180,
  "name": "GDraw",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": true
}
{
  "id": 276,
  "name": "GLC",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": true
}
{
  "id": 164,
  "name": "GUX",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": true
}
{
  "id": 67,
  "name": "GCCP5",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": true
}
{
  "id": 261,
  "name": "GCCP6",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": true
}
{
  "id": 17,
  "name": "TME",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 25,
  "name": "GWS",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 36,
  "name": "MOZIDIFFER",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 40,
  "name": "GMJ",
  "aggre_status": true,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 44,
  "name": "GCL2013",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 45,
  "name": "GGJ2013",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 56,
  "name": "MD_GMA",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 75,
  "name": "GDQ2015",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 76,
  "name": "GQI2017",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 77,
  "name": "GJG2015",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 80,
  "name": "GMP2016",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 83,
  "name": "Revit2GFC4GMP",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 100,
  "name": "GTJ2017CAD",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 112,
  "name": "GYZB2017",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 114,
  "name": "BIM5D_PC",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 115,
  "name": "GFYCM",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 125,
  "name": "GBCB",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 128,
  "name": "CubicostTAS",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 129,
  "name": "GMD",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 131,
  "name": "GAQ2017",
  "aggre_status": true,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 132,
  "name": "GBCB2017",
  "aggre_status": true,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 133,
  "name": "GBS2017",
  "aggre_status": true,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 134,
  "name": "GFYC2017",
  "aggre_status": true,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 135,
  "name": "GFYCM2017",
  "aggre_status": true,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 136,
  "name": "GMJ2017",
  "aggre_status": true,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 138,
  "name": "GSJ2017",
  "aggre_status": true,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 139,
  "name": "GJH2017",
  "aggre_status": true,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 142,
  "name": "TeamViewer",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 148,
  "name": "ZPert",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 160,
  "name": "GBS",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 162,
  "name": "GIR_C",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 163,
  "name": "TBQ2017",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 167,
  "name": "GYJC2017",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 177,
  "name": "GSXGZT2016",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 181,
  "name": "TBQD",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 182,
  "name": "TTED",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 183,
  "name": "TCFD",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 188,
  "name": "GSCApp",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 200,
  "name": "GFYC",
  "aggre_status": true,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 207,
  "name": "GDQ2017",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 217,
  "name": "GO",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 218,
  "name": "AppGbmp",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 222,
  "name": "GQI2018",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 226,
  "name": "GDS2017",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 228,
  "name": "GLDTCS",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 231,
  "name": "TenderGo",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 232,
  "name": "GDQ2018",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 233,
  "name": "SectionManual",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 234,
  "name": "BeamGo",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 235,
  "name": "GJG2018",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 236,
  "name": "RevitViewer",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 237,
  "name": "BIM5D_PC_TEST",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 238,
  "name": "BIM5D_PC_TRIAL",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 239,
  "name": "GEC5",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 240,
  "name": "GFYQ",
  "aggre_status": true,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 241,
  "name": "RoadDesigner",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 242,
  "name": "CECS100G",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 243,
  "name": "GBES",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 244,
  "name": "Ceshi",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 245,
  "name": "dpUpdate",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 246,
  "name": "GFY4",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 248,
  "name": "GGPT",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 249,
  "name": "GMA2020",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 250,
  "name": "JZYK",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 251,
  "name": "GVB5",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 252,
  "name": "GHW5",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 253,
  "name": "GUp",
  "aggre_status": true,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 254,
  "name": "BIM_COST",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 255,
  "name": "GICP5",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 256,
  "name": "bim5d_basic",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 257,
  "name": "GWH5",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 258,
  "name": "GFY4_2019",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 259,
  "name": "GDD2019",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 260,
  "name": "GCCP5_ShanDong_64",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 262,
  "name": "GSC6",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 263,
  "name": "GCCP6_WP",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 264,
  "name": "GEB6",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 265,
  "name": "GSH6",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 266,
  "name": "GTech2019",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 267,
  "name": "GPC5",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 268,
  "name": "GTJ2021",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 269,
  "name": "GDE2019",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 270,
  "name": "CubicostTIO",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 271,
  "name": "GCA5",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 272,
  "name": "GLC5",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 273,
  "name": "GMT5",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 274,
  "name": "GCN5",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 275,
  "name": "GHC5",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 277,
  "name": "GVB6",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 278,
  "name": "GJG2021",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 279,
  "name": "GJG",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 280,
  "name": "GAP",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 281,
  "name": "GSTP",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 283,
  "name": "TRS2021",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 284,
  "name": "TMEC",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 285,
  "name": "CubicostTMEC",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 286,
  "name": "GGF5",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 287,
  "name": "GRE5",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}
{
  "id": 310,
  "name": "GA_CloudPlugin",
  "aggre_status": false,
  "start": true,
  "enable_auto": false,
  "enable_filter": false
}

然后就可以得到下发产品列表中的产品数量了:

代码语言:javascript复制
1 lines=$(curl -s "http://***.******.***/v3/server_status?type=100&data_version=2.4" | ./jq -r ".message" | xargs ./test-decode | tail -1 | ./jq ".products|.[]" | wc -l)
2 # each item takes 8 line
3 prods=$(($lines/8))
4 echo "product count : $prods" >> log.txt

没有找到 jq 怎么输出 json 数组元素个数,这里直接用 wc 计算行数除以 8 (每个产品占用 8 行)得到。同理得到密钥的数量(每个产品一个单独的密钥):

代码语言:javascript复制
1 lines=$(curl -s "http://***.******.***/v3/server_status?type=101&data_version=2.4" | ./jq -r ".message" | xargs ./test-decode | tail -1 | ./jq ".keys|.[]" | wc -l)
2 # each item takes 4 line
3 keys=$(($lines/4))
4 echo "keys count : $keys" >> log.txt

这两个数量必需一致,且大于某个域值,这里设定为 100.

代码语言:javascript复制
 1 limit=100
 2 if [ $prods -ne $keys ]; then 
 3     echo "product count != key count, fatal error" >> log.txt
 4     send_email "$prods" "$keys" "$limit"
 5     exit
 6 fi
 7 
 8 if [ $prods -lt $limit ]; then 
 9     echo "products list too less, warning" >> log.txt
10     send_email "$prods" "$keys" "$limit"
11     exit
12 fi
13 
14 echo "gux server ok" >> log.txt

如果有任何异常发生,通过 send_mail 函数来向相关人员发送告警邮件。

异常情况下发送报警邮件

铛铛铛~ 进入本文核心,现在如果接口中的产品数据为空或减少到特定值(产品一般只增加不减少),就需要通过邮件通知相关责任人了。这里使用的是 sendmail 命令,在 msys2 环境中,没有自带这个命令,需要事先安装 Windows 版本的压缩包、并设置 PATH 环境变量包含该命令所在的目录,重启机器后就可以在 msys2 环境中直接访问了。这里给一个下载链接:www.glob.com.au/sendmail/sendmail.zip。注意这个命令只是模拟 sendmail -t 选项,并不是原生的 sendmail 命令,但是用法和原生命令没什么区别。在开始看 send_email 函数之前,让我们先了解一些基本原理。

邮箱工作原理

你通过 qq 的账号给 126 发了一封邮件,它经历了哪些流程呢?请看下图

抛开复杂的协议不谈,单说整个流程,就是你先通过邮件客户端把邮件发到 qq 的邮件服务器,后者通过地址路邮给 126 的邮件服务器,最后你的邮件客户端再去后者的邮件服务器里拉取收到的邮件。对于 sendmail 命令而言,最主要就是进行发送账号的各种配置。

配置发送账号

在配置发送账号前,我们先选择一个可靠的账号,这里我选取 QQ(不要问为什么,问就是请参考附录)。首先登录 mail.qq.com

在邮箱的设置中找到账户相关设置,开通 IMAP/SMTP 服务:

开启后可以获取授权码,一个账户可以获取多个授权码,用在不同应用里:

出于安全考虑,这里把授权码隐掉了。

配置 sendmail 命令

有了账号和授权码就可以配置 sendmail 命令啦,下面打开命令目录内的 sendmail.ini 文件,加入以下几行:

代码语言:javascript复制
 1 ; configuration for fake sendmail
 2 
 3 ; if this file doesn't exist, sendmail.exe will look for the settings in
 4 ; the registry, under HKLMSoftwareSendmail
 5 
 6 [sendmail]
 7 
 8 ; you must change mail.mydomain.com to your smtp server,
 9 ; or to IIS's "pickup" directory.  (generally C:InetpubmailrootPickup)
10 ; emails delivered via IIS's pickup directory cause sendmail to
11 ; run quicker, but you won't get error messages back to the calling
12 ; application.
13 
14 smtp_server=smtp.qq.com
15 
16 ; smtp port (normally 25)
17 
18 smtp_port=25
19 
20 ; SMTPS (SSL) support
21 ;   auto = use SSL for port 465, otherwise try to use TLS
22 ;   ssl  = alway use SSL
23 ;   tls  = always use TLS
24 ;   none = never try to use SSL
25 
26 smtp_ssl=auto
27 
28 ; the default domain for this server will be read from the registry
29 ; this will be appended to email addresses when one isn't provided
30 ; if you want to override the value in the registry, uncomment and modify
31 
32 ;default_domain=mydomain.com
33 
34 ; log smtp errors to error.log (defaults to same directory as sendmail.exe)
35 ; uncomment to enable logging
36 
37 error_logfile=error.log
38 
39 ; create debug log as debug.log (defaults to same directory as sendmail.exe)
40 ; uncomment to enable debugging
41 
42 ;debug_logfile=debug.log
43 
44 ; if your smtp server requires authentication, modify the following two lines
45 
46 auth_username=2057975342@qq.com
47 auth_password=*****************
48 
49 ; if your smtp server uses pop3 before smtp authentication, modify the 
50 ; following three lines.  do not enable unless it is required.
51 
52 pop3_server=
53 pop3_username=
54 pop3_password=
55 
56 ; force the sender to always be the following email address
57 ; this will only affect the "MAIL FROM" command, it won't modify 
58 ; the "From: " header of the message content
59 
60 force_sender=
61 
62 ; force the sender to always be the following email address
63 ; this will only affect the "RCTP TO" command, it won't modify 
64 ; the "To: " header of the message content
65 
66 force_recipient=
67 
68 ; sendmail will use your hostname and your default_domain in the ehlo/helo
69 ; smtp greeting.  you can manually set the ehlo/helo name if required
70 
71 hostname=

文中标黄的就是我们要加入的配置,这里授权码同样被我隐掉了(哎呀,邮箱暴露了~)。命令配置好了以后,就可以在 shell 脚本里调用 sendmail 命令了

使用 sendmail 命令发送邮件

终于可以回到我们之前提到的 send_mail 函数了,它有三个参数,分别是产品数量、密钥数量和最小限制值:

代码语言:javascript复制
 1 function send_email() 
 2 {
 3     pc=$1 # product count
 4     kc=$2 # key count
 5     lm=$3 # count limit
 6     from="2057975342@qq.com"
 7     # for multiple receiver, not work
 8     #to="yunh@******.com;anlj@******.com"
 9     # using an array of receivers
10     to=("yunh@******.com" "anlj@******.com" "yuyf-a@******.com" "duanxd@******.com" "zhangcj-c@******.com" "linc@******.com" "sunyd@******.com" "zhangb-l@******.com")
11     subject="gux server exception"
12     content="gux restful api exception: nproducts: $pc nkeys: $kc nnproducts count != keys count nor products count < $lm !!nnn"
13     mail="From: 'gux monitor' <$from>nSubject: $subjectnn$content"
14 
15     for var in ${to[@]};
16     do
17         mail=$(echo -e "To: <$var>n$mail")
18     done
19     echo -e "$mail" >> mail.txt
20     echo -e "$mail" | sendmail -t
21 }

获取各个参数 (line 3~5) 用于后续拼接邮件正文 (line 12),对于 sendmail 命令来说 (line 20) 使用 -t 参数后允许将所有参数通过一个格式化的文本来设置,这个格式类似这样:

代码语言:javascript复制
Subject: title
From: sender@mail.com
To: receiver@mail.com
Cc: copy@mail.com

body...

由两部分组成,邮件标题由 Subject(抬头)、From(发件人)、To(收件人)、Cc(抄送)……组成,两个空行之后是邮件正文。上面大段代码都是在设置这些内容 (line 6 ~ 13),注意为了减少对我 qq 账号的关注,这里对 From 域设置了别名 ‘gux monitor’,这样收到邮件就比较直观啦。下面是一次真实的报警邮件:

另外对于群发,不能简单的在 To 域设置多个接收人,而是要分别对每个接收人进行一次单独的 To 域设置(允许多个 To 域),这里使用 shell 数组对收件人进行了遍历处理 (line 10,15 ~ 18),最后一次性发送 (line 20),下面是打印到 mail.txt 里的邮件原文:

代码语言:javascript复制
To: <zhangb-l@******.com>
To: <sunyd@******.com>
To: <linc@******.com>
To: <zhangcj-c@******.com>
To: <duanxd@******.com>
To: <yuyf-a@******.com>
To: <anlj@******.com>
To: <yunh@******.com>
From: 'gux monitor' <2057975342@qq.com>
Subject: gux server exception

gux restful api exception: 
products: 0 
keys: 0 

products count != keys count 
or products count < 100 !!

注意由于遍历时是向邮件头添加 To 域,整个收件人列表是呈倒序排列的。然后效果就是上面截图的啦~

设置定时任务

最后就是把这个脚本设置成定时执行的任务了,这块可以参考之前写过的一篇文章 《查看博客园积分与排名趋势图的工具 》,第 3 节。下面是经过一段时间后,脚本输出的日志:

代码语言:javascript复制
2020年07月 8日 16:00:01
product count : 103
keys count : 103
gux server ok
2020年07月 8日 16:55:15
product count : 103
keys count : 103
gux server ok
2020年07月 8日 17:00:01
product count : 103
keys count : 103
gux server ok
2020年07月 8日 18:00:01
product count : 103
keys count : 103
gux server ok
2020年07月 8日 19:00:01
product count : 103
keys count : 103
gux server ok

……

2020年07月13日 10:00:02
product count : 104
keys count : 104
gux server ok
2020年07月13日 11:00:01
product count : 104
keys count : 104
gux server ok
2020年07月13日 12:00:02
product count : 104
keys count : 104
gux server ok
2020年07月13日 13:00:02
product count : 104
keys count : 104
gux server ok
2020年07月13日 14:00:02
product count : 0
keys count : 0
products list too less, warning

输出太长,中间有省略。可以观察到 7.13 日 14:00 有一次报警 (不过后来证明是一次乌龙,汗~)

结语

后来查出问题的根因,居然是采集服务在和产品中心同步产品时(管理员登录的情况下),如果后者恰巧挂了,会导致前者同步不到数据,就会把所有产品开关重置掉!一个隐藏了 3 years 的 bug 啊,教训深刻。不过话说回来,不管代码怎么 low,接口监控是不可少的。除了用来作接口监控,我还用 shell 脚本给其它服务做简单测试,例如验证升级服务能否正常下发版本、验证用户中心能否正常登录等等,凡是通过 restful api 提供服务的,基本可以通过 curl jq 搞定,甚至通过 tcp 长连接实现的消息推送服务也可以用 shell 脚本来验证。不过这一系列的内容,因为涉及接口安全,我没法提供 git 下载地址(压根没有相应的 git 库),望大家理解,有需要的同学可以照猫画虎,把里面的接口换成自己能访问的,来动手验证一下。

参考

1. 在windows下配置sendmail服务器

2. Using Sendmail on Windows

3. Linux简单配置SendMail发送邮件

4. linux shell 发送email 邮件

5. 不可或缺的 sendEmail

6. 配置mail命令的IMAP和SMTP,接收邮件和发送邮件

7. shell 发邮件命令之 sendmail

8. jq : Linux下json的命令行工具

9. 使用jq工具在Shell命令行处理JSON数据

10. 命令行 JSON 处理工具 jq 的使用介绍

11. shell脚本处理JSON数据工具jq

12. HowTo: Install jq

13. https://github.com/stedolan/jq

0 人点赞