zoukankan      html  css  js  c++  java
  • 给产品同学解决一个小问题

    短、平、快。

    背景

    产品同学给了一个店铺 shopId 列表,想要知道这些店铺的最近一段时间的同城送订单的数量。

    shop.txt

    111,222,333
    

    有个现成的命令 curl_search 可以拿到单个店铺的同城送订单的数量:

    curl -s -H "Content-Type: application/json" -X POST -d '{"source":"xxx", "requestId":"111", "searchParam": {"shopId": 111, "endTime":1583424000,"startTime":1580486400, "deliveryType":"local_delivery"}}' http://127.0.0.1:7001/app/order/search
    

    curl_search 的结果如下。 total 即是同城送订单的数量。

    {"result":true,"code":0,"message":null,"data":{"success":true,"code":200,"message":"successful","data":{"orderNos":["202003051700001"],"total":22}}}
    

    想拿到的结果是:每行一个 shopId total

    111 22
    222 256
    333 1024
    

    方案

    使用Shell

    先编写一个 脚本 curl_orders.sh:

    #!/bin/sh
    
    shopId=$1
    
    curl -s -H "Content-Type: application/json" -X POST -d  '{"source":"xxx", "requestId":"'"${shopId}"'", "searchParam": {"shopId":"'"${shopId}"'", "endTime":1583424000,"startTime":1580486400,"deliveryType":"local_delivery"}}' http://127.0.0.1:7001/app/order/search | echo $shopId $(sed -r 's/^.*total.*:([0-9]+)}.*$/1/')
    

    然后使用 :

    cat /tmp/shop.txt | tr "," "
    " | xargs -I {} sh curl_orders.sh {}  > result.txt
    

    使用Python

    编写一个 Python 脚本 fetch_totals.py

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    
    import commands
    import json
    
    def fetchTotal(curl_cmd, success, fail):
    
        (status, result) = commands.getstatusoutput(curl_cmd) 
        if status == 0:
            return success(result)
        return fail(result)
    
    def success(result):
        obj = json.loads(result)
        if obj['result'] and obj['data']["success"]:
            return obj['data']['data']['total']
        return "Error"
    
    def fail(result):
        return "Error"
    
    if __name__ == '__main__':
      
       f = open('shop.txt')
       for line in f:
           shopIds = line.strip().split(',')
           for shopId in shopIds:
               cmd = """curl -s -H "Content-Type: application/json" -X POST -d  '{"source":"xxx", "requestId":"""+ str(shopId) + """, "searchParam": {"shopId":""" + str(shopId) + """, "endTime":"1583424000","startTime":"1580486400", "deliveryType":"local_delivery", "exportedFieldNames":[]}}' http://127.0.0.1:7001/app/order/search"""
               print shopId, " " , fetchTotal(cmd, success, fail)
    
    

    使用Python+Shell

    实际上,Shell 更适合构建任务处理的命令流,而 Python 更适合处理具体逻辑。两者结合食用更佳。先编写处理单个店铺的 Python 脚本 fetch_total.py :

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    
    import commands
    import json
    import sys
    
    def fetchTotal(curl_cmd, success, fail):
    
        (status, result) = commands.getstatusoutput(curl_cmd) 
        if status == 0:
            return success(result)
        return fail(result)
    
    def success(result):
        obj = json.loads(result)
        if obj['result'] and obj['data']["success"]:
            return obj['data']['data']['total']
        return "Error"
    
    def fail(result):
        return "Error"
    
    def buildCmd(args):
        shopId = args[1]
        return """curl -s -H "Content-Type: application/json" -X POST -d  '{"source":"xxx","requestId":"""+ str(shopId) + """, "searchParam": {"kdtId":""" + str(shopId) + """, "endTime":"1583424000","startTime":"1580486400", "deliveryType":"local_delivery"}}' http://127.0.0.1:7001/app/order/search"""
    
    if __name__ == '__main__':
        args = sys.argv
        shopId = args[1]
        print str(shopId), " " , fetchTotal(buildCmd(args), success, fail)
    

    然后使用:cat shop.txt | xargs -d "," -I {} python fetch_total.py {} 即可。

    求解

    方案选择

    最先想到用 Python 来解决。但是 python 解决要拼参数,写调用 curl 的脚本。有点麻烦。 接着想去数据平台用 hive 捞,但是没有同城送订单的标识。想到既然有 curl 命令可以拿到单个店铺的结果,只要解决批量的问题即可。

    这里有两个问题要解决:1. 从 curl_search 命令的结果中解析出 total ; 2. 批量调用 curl 命令,必须把 shopId 作为参数传进去。

    解析total

    解析 total , 使用 sed + 正则捕获能力。

    要领是:将 需要的东西 用 ( ) 括起来,然后用引用替换。比如,我只要 total 冒号后面的 数字, 首先将需要的部分 ([0-9]+) 括起来 ,然后使用 左边 "total".*: 和 右边 } 进行识别, 最后通过引用符号 $1 拿到。数字标识第几个(被捕获的分组)括号。

    正则表达式基础见: "正则表达式基础知识"

    参数化调用curl

    需要把 shopId 传到 curl 命令中。 但是 curl 命令的选项 -d 的参数 '{"shopId":"$shopId", "field2":"yyy"}',既有 双引号也有单引号,这些都会使得变量引用 $shopId 变成普通的字符串,而不是被替换成指定的值。 可以使用 "'${shopId}'" 来传入 shopId。

    xargs

    xargs 可以读取文件并将每一行数据作为参数传给指定的命令执行。 基本形式是: cat file.txt | xargs -I {} Command {} 。 {} 就是 file.txt 里的每一行数据。

    这里之所以将调用 curl 并解析出 total 写成一个 curl_orders.sh 脚本文件,也是为了 xargs 命令更加简洁。 xargs -d [sep] 是使用分隔符 sep 将输入分割成多条数据,然后将多条数据分别作为一个入参依次传递给指定命令处理。

    命令替换

    $(command) : 用命令 command 的计算值来替换 command 本身,从而能将命令的执行结果作为参数直接传给其它命令。

    相关示例:

    cat shop.txt | xargs -d "," -I {} echo $(echo {"source":"xxx","requestId":"98765432","searchParam":{"shopId":{},"endTime":1583424000,"startTime":1580486400,"deliveryType":"local_delivery"}})
    
    curl -s http://127.0.0.1:7001/app/order/search -H "Content-Type: application/json" -X POST -d $(echo {"source":"xxx","requestId":"98765432","searchParam":{"kdtId":37,"endTime":1583424000,"startTime":1580486400,"deliveryType":"local_delivery"}})
    

    管道

    Shell 最强大的一点是其”胶合“能力,可以将任意多个小程序组合起来完成一件事。 管道 | 将上一个程序的输出定向到下一个程序的输入,从而将两个相邻的程序连接起来。管道是实现胶合能力的强大机制。


    重定向

    > 是重定向符号,可以将命令执行结果重定向到文件里。当命令的结果非常大时适用。


    完整命令

    一般做法是,先将简单的命令调试好,再整合到一起。

    不过,依然太难了。需要把 “参数传入 curl 命令”,“命令替换”,“字符转义” 等全部整合。 尤其是 “参数传入 curl 命令” 这一步。

    完整的命令如下:

    sed 's/,/
    /g' shop.txt | xargs -I {} sh -c 'echo {}" "$(curl -s -H "Content-Type: application/json" -X POST -d "{"source":"xxx", "requestId":"123456879", "searchParam": {"shopId":{},"endTime":1583424000,"startTime":1580486400, "deliveryType":"local_delivery"}}" http://127.0.0.1:7001/app/order/search | sed -r "s/^.*total.*:([0-9]+)}.*$/1/")'
    
    sed 's/,/
    /g' shop.txt | xargs -I {} sh -c 'total=$(curl -s -H "Content-Type: application/json" -X POST -d "{"source":"xxx","requestId":"123456879", "searchParam": {"shopId":{},"endTime":1583424000,"startTime":1580486400, "deliveryTypeDesc":"local_delivery"}}" http://127.0.0.1:7001/app/order/search | sed -r "s/^.*total.*:([0-9]+)}.*$/1/");echo {} $total'
    

    你可能好奇为啥 -d 里面弄了那么多转义。主要是Java 服务端要整型,shell 默认给传字符串。搞这么多转义就是为了拼那个 json 参数串同时把店铺ID当做整数传过去。

    本来想省事写到一行,但写到一行太容易出错而且难以排查,而且有多个参数要传入时,可扩展性不佳。关键 curl 命令的选项参数既有单引号又有双引号,命令也很长,放在 shell 一行上,完成正确的字符串拼接都是很头疼的事情。 看来就是不适合整在一行的。不做这种不必要的折腾了。带有单双引号的较复杂的长命令行的拼接,写在脚本文件里更合适。

    引申

    使用循环如下。 有命名参数果然容易传参。

    for shopId in $(sed 's/,/
    /g' shop.txt); do echo $shopId $(curl -s -H "Content-Type: application/json" -X POST -d '{"source":"xxx", "requestId":"$shopId", "searchParam": {"shopId":"'$shopId'", "endTime":"1583424000","startTime":"1580486400", "deliveryType":"local_delivery"}}' http://127.0.0.1:7001/app/order/search | sed -r 's/^.*total.*:([0-9]+)}.*$/1/'); done
    
    sed 's/,/
    /g' shop.txt | while read line; do echo $line $(curl -s -H "Content-Type: application/json" -X POST -d '{"source":"xxx","requestId":"$line", "searchParam": {"shopId":"'$line'", "endTime":"1583424000","startTime":"1580486400", "deliveryType":"local_delivery"}}' http://127.0.0.1:7001/app/order/search | sed -r 's/^.*total.*:([0-9]+)}.*$/1/'); done
    

    小结

    此例展示 Shell 命令用来解决临时小任务所具备的”短、平、快“的特征。 不过,要构造复杂的命令,要对 Shell 非常熟悉才行,因为其中需要整合多种东西,尤其涉及到字符串转义和命令传参的时候;否则反而失去了“快”的优势。 此外,最好能将复杂命令拆解成单个命令或脚本,这样更容易写出简洁的整合命令。

    要构造较为复杂的任务脚本,还是采用自己较为熟悉的语言,比如 Python 。使用 Python 实现,拿到 curl 结果后,使用 json 模块解析即可得到 total ; 然后再写个 for 循环即可解决批量调用问题。

    结论是:用 python 写个脚本处理单条记录,然后用 xargs -I {} python xxx.py {} 就可以了。 这样,需要有一个正交互补的 python 工具包。 Shell 更适合构建任务处理的命令流,而 Python 更适合处理具体逻辑。两者结合食用更佳。


  • 相关阅读:
    .NET 面试题汇总(带答案)
    C#声明一个100大小的数组 随机生成1-100之间不重复的数
    添加和读取Resources嵌入资源文件(例如.dll和.ssk文件)
    C#DataTable转List<T>互转
    “不允许使用邮箱名称。服务器响应为:”的错误解决办法
    微信多开防撤回(带提示)2.8.0.133补丁
    逆向某网站的登录接口生成元素加密
    C#中new的三种用法
    SQL Server查询第31到40条数据
    关于EF框架EntityState的几种状态
  • 原文地址:https://www.cnblogs.com/lovesqcc/p/12431063.html
Copyright © 2011-2022 走看看