• 什么?Shell也能并行化


    作为一名后台开发,写shell脚本可能是工作中避免不了的,比如日志分析过滤、批量请求和批量插入数据等操作,这些如果单纯靠人工手动去处理既费时又费力,有了shell脚本就可以轻松搞定,当然有人会说可以用python或者其他编程语言,这并不是不可以,但没有哪个有shell这么简单方便快捷的。需要依赖库不说,还要懂对应语言的语法才行。

    不知道大家在工作中有没有经常会遇到测试或者产品跑过来说要在数据库批量插入大量的样本数据,在下就经常遇到,比如昨天测试同学就找上门需要对一批用户调用http接口进行一系列操作,很自然想到对用户文件进行while循环读取然后调用curl命令发送http请求,于是写出如下脚本代码:

    #! /bin/sh
    #$1 为用户列表文件,以参数形式传入
    cat $1 | while read line
    do curl
    "http://gztest.gf.com.cn:8000/otcblueup/client_id=010100047752&prod_code=${line}&occur_amount=100000&env_no=146" done

    这样写有没有问题?功能性的问题自然是没有,但是存在性能问题,实际的运行效果是这样的。

    什么?Shell也能并行化

    效果图一

    命令是串行执行的,批量处理的场景往往数据量比较大,再加上中间的网络延时,往往需要等待很长时间才能彻底执行完成【如果愿意等也无所谓】。作为程序员对技术的追求是极致的,学过那么多语言的并发技能(进程、线程、协程、异步多路复用等),shell脚本是否也可以进行并发呢?对上面的串行代码进行修改如下:

    #! /bin/sh#
    $1 为用户列表文件,以参数形式传入
    cat $1 | while read line
    do    
        {        
            echo $line        
            curl "http://gztest.gf.com.cn:8000/otcblueup/client_id=010100047752&prod_code=${line}&occur_amount=100000&env_no=146"    
        }& 
    #{}中的命令将以新的子进程中执行而不阻塞父进程
    done        

    &语法意思是将{}中的代码将作为一个整体切换到后端运行而不阻塞下一次循环,再次执行的效果如下图。

    什么?Shell也能并行化

    效果图二

    可以看到命令瞬间执行完成。并行化虽然是实现了,但是可以发现shell主进程已经执行完了,才看到子进程的打印,这是因为父进程没有等待子进程完成就退出了。

    进一步思考,如果存在任务串行依赖应该怎么做呢?比如有第二步操作需要等待上面的http请求执行完才能执行的需求,应该怎么去改进脚本呢?想到unix下的wait函数,同样shell也有对应的wait命令可用,再次对脚本修改如下。

    #!/bin/sh
    cat $1 | while read line
    do    
        {        
            curl "http://gztest.gf.com.cn:8000/otcblueup/client_id=010100047752&prod_code=${line}&occur_amount=100000&env_no=146"    
        }& 
    #{}中的命令将以新的子进程中执行而不阻塞父进程
    done
    echo "wait all task finish,then exit"
    wait
    echo "success"    
    什么?Shell也能并行化

    效果图三

    执行后wait似乎没有生效,"wait all task finish,then exit"以及"success"在后台进程完成前就输出了,这是因为while循环的输入来自于cat输出到管道中的数据,wait实际等待的是cat命令的结束,用重定向的方式将cat文件的内容传给while循环可解决此问题。

    #!/bin/sh
    while read line
    do   
         {       
             curl "http://gztest.gf.com.cn:8000/otcblueup/client_id=010100047752&prod_code=${line}&occur_amount=100000&env_no=146"    
        }& 
    #{}中的命令将以新的子进程中执行而不阻塞父进程
    done < $1 
    #重定向
    echo "wait all task finish,then exit!!!"
    wait
    echo "success"    

    下面截图展示符合预期的结果,wait等待了所有后台进程完成最后输出success标识。

    什么?Shell也能并行化

    效果图四

    更进一步,上面的并发是一次性启动了所有任务,这对于机器资源以及性能会有很大影响,有没有什么方式可以控制shell进程并发度呢?这里介绍一种利用管道进行并发控制方式。用mkfifo创建first in first out管道,控制并发逻辑如下。

    #!/bin/bash
    #并发数
    threadTask=2
    #创建fifo管道
    fifoFile="test_fifo"
    rm -f ${fifoFile}
    mkfifo ${fifoFile}
    # 建立文件描述符关联
    exec 9<> ${fifoFile}
    rm -f ${fifoFile}
    # 预先向管道写入数据
    for ((i=0;i<${threadTask};i++))
    do    
        echo "" >&9
    done
    echo "wait all task finish,then exit!!!"
    while read line
        do    
            read -u9    
            {        
                curl "http://gztest.gf.com.cn:8000/otcblueup/client_id=010100047752&prod_code=${line}&occur_amount=100000&env_no=146"       
                echo "" >&9    
             }&
     #{}中的命令将以新的子进程中执行而不阻塞父进程
    done < $1
    wait
    # 关闭管道
    exec 9>&-
    echo
    echo "success"    

    并发控制效果如下,并发度设置为2【这样并发控制效果明显】,可以看到一次性启动2个进程任务,每完成一个会重新启动一个新的进程直到所有进程任务完成。

    什么?Shell也能并行化

    效果图五

    总结:

    shell脚本简单实用,如果能多多掌握一些shell技巧,将大大提高后端人员日常工作效率。从而减少无效加班,降低代码工作者996.ICU风险。

    转自https://www.toutiao.com/a6773190691846619661/?timestamp=1577336958&app=news_article&group_id=6773190691846619661&req_id=201912261309170100140400961B1091A0

  • 相关阅读:
    设计模式之命令模式
    设计模式之享元模式
    设计模式之建造者模式
    设计模式之策略模式
    设计模式之责任链模式
    maven打包三种方式
    java native:Java本地方法调用(jni方式)
    数据库五大约束使用方法
    数据库连接池的配置方式(阿里,日本)
    tomcat数据库连接池的几种配置方法
  • 原文地址:https://www.cnblogs.com/cangqinglang/p/12101493.html
Copyright © 2020-2023  润新知