关于脚本:linux下依赖库的拷贝脚本

#!/bin/sh# 可执行程序名app_name="qtdemo"# 指标文件夹dst="./app"# 利用 ldd 提取依赖库的具体门路liblist=$(ldd $app_name | awk '{ if (match($3,"/")){ printf("%s "), $3 } }')# 指标文件夹的检测if [ ! -d $dst ];then mkdir $dstfi# 拷贝库文件和可执行程序到指标文件夹cp $liblist $dstcp $app_name $dst

April 4, 2023 · 1 min · jiezi

关于脚本:基于dos与shell-实现数据库批量跑库脚本dmmysql

bat mysql跑库@echo offchcp 65001%~d0cd %~dp0set basepath=%~dp0set | findstr mysqlif "%ERRORLEVEL%" == "0" ( goto paraminit) ELSE ECHO THERE IS NOT MYSQL ENV !goto end:paraminitset /p mysqlhost="ip 地址,默认值 ==> (127.0.0.1) : "set /p mysqlport="请输出mysql服务端口,默认值 ==> (3306) :"set /p rootpwd="请输出mysql超管明码,默认值 ==> (root) :"set /p kupwd="请输出oneaccess各个库拥有者对应的明码,默认值 ==> (Bamboocloud@1234) : "set /p suffix="请输出oneaccess对应的库及用户的后缀,默认为空串: "if "%mysqlhost%"=="" set mysqlhost=127.0.0.1if "%mysqlport%"=="" set mysqlport=3306if "%rootpwd%"=="" set rootpwd=rootif "%kupwd%"=="" set kupwd=Bamboocloud@1234if "%suffix%"=="" set suffix=set sql_dir=sql_cmd_executermdir /s/q %basepath%\%sql_dir%mkdir -p %basepath%\%sql_dir% xcopy /y/e/i %basepath%\bam %basepath%\%sql_dir% xcopy /y/e/i %basepath%\bim %basepath%\%sql_dir% xcopy /y/e/i %basepath%\epass %basepath%\%sql_dir% xcopy /y/e/i %basepath%\apphub %basepath%\%sql_dir% goto replacepwdfohcs:replacepwdfohcs@setlocal enabledelayedexpansionfor /f "delims=" %%i in (%basepath%\%sql_dir%\bam_mysql_db_create.sql) do (set content=%%iset content=!content:bam_password=abcdefghijklmnopqrst!set content=!content:bam=bam%suffix%!set content=!content:abcdefghijklmnopqrst=%kupwd%!echo !content!>>$1)move $1 %basepath%\%sql_dir%\bam_mysql_db_create.sql || EXIT \Bfor /f "delims=" %%i in (%basepath%\%sql_dir%\bim_mysql_db_create.sql) do (set content=%%iset content=!content:bim_password=abcdefghijklmnopqrst!set content=!content:bim=bim%suffix%!set content=!content:abcdefghijklmnopqrst=%kupwd%!echo !content!>>$2)move $2 %basepath%\%sql_dir%\bim_mysql_db_create.sql || EXIT \Bfor /f "delims=" %%i in (%basepath%\%sql_dir%\apphub_mysql_db_create.sql) do (set content=%%iset content=!content:apphub_password=abcdefghijklmnopqrst!set content=!content:apphub=apphub%suffix%!set content=!content:abcdefghijklmnopqrst=%kupwd%!echo !content!>>$3)move $3 %basepath%\%sql_dir%\apphub_mysql_db_create.sql || EXIT \Bfor /f "delims=" %%i in (%basepath%\%sql_dir%\epass_mysql_db_create.sql) do (set content=%%iset content=!content:epass_password=abcdefghijklmnopqrst!set content=!content:epass=epass%suffix%!set content=!content:abcdefghijklmnopqrst=%kupwd%!echo !content!>>$4)move $4 %basepath%\%sql_dir%\epass_mysql_db_create.sql || EXIT \Bgoto mysqlsqlexec:mysqlsqlexecset _timeTemp=0call:GetTimeSeconds %time%set time1=%_timeTemp%rem choice 期待5秒 再计算另外一个工夫点 mysql -h %mysqlhost% -uroot -p%rootpwd% -P %mysqlport% -D "mysql" < %basepath%\%sql_dir%\bam_mysql_db_create.sql -vvv mysql -h %mysqlhost% -uroot -p%rootpwd% -P %mysqlport% -D "mysql" < %basepath%\%sql_dir%\bim_mysql_db_create.sql -vvv mysql -h %mysqlhost% -uroot -p%rootpwd% -P %mysqlport% -D "mysql" < %basepath%\%sql_dir%\apphub_mysql_db_create.sql -vvv mysql -h %mysqlhost% -uroot -p%rootpwd% -P %mysqlport% -D "mysql" < %basepath%\%sql_dir%\epass_mysql_db_create.sql -vvv mysql -h %mysqlhost% -uroot -p%rootpwd% -P %mysqlport% -D "bam%suffix%" < %basepath%\%sql_dir%\bam\bam_mysql_table_and_data.sql -vvv mysql -h %mysqlhost% -uroot -p%rootpwd% -P %mysqlport% -D "bim%suffix%" < %basepath%\%sql_dir%\bim_mysql_table_and_data.sql -vvv mysql -h %mysqlhost% -uroot -p%rootpwd% -P %mysqlport% -D "apphub%suffix%" < %basepath%\%sql_dir%\apphub_mysql_table_and_data.sql -vvv mysql -h %mysqlhost% -uroot -p%rootpwd% -P %mysqlport% -D "epass%suffix%" < %basepath%\%sql_dir%\epass_mysql_table_and_data.sql -vvv@echo off@REM "goto printexetimes:printexetimescall:GetTimeSeconds %time%set time2=%_timeTemp%set /a interTime=%time2% - %time1%echo echo 已全量跑库实现 hcs_mysql_bat 耗时: %interTime% 秒 !!goto end:GetTimeSecondsset tt=%1rem windows下%time%工夫的格局个别为hour:minute:second.number 上面两行只是去掉冒号和顿号变成hour minute second number ,此处只是取hour minute secondset tt=%tt:.= %set tt=%tt::= %set index=1for %%a in (%tt%) do ( if !index! EQU 1 ( set hh=%%a )^ else if !index! EQU 2 ( set mm=%%a )^ else if !index! EQU 3 ( set ss=%%a )set /a index=index+1)set /a _timeTemp=(%hh%*60+%mm%)*60+%ss%goto:end:endmysql删库@echo offchcp 65001set | findstr mysqlif "%ERRORLEVEL%" == "0" (goto paraminit)echo there is no mysql envgoto end:paraminitset /p mysqlhost="请输出ip:"set /p mysqlport=请输出端口set /p rootpwd=请输出明码set /p suffix=请输出后缀if "%mysqlhost%"=="" set mysqlhost=127.0.0.1if "%mysqlport%"=="" set mysqlport=3306if "%rootpwd%"=="" set rootpwd=rootif "%suffix%"=="" set suffix=136goto mysqldelexec:mysqldelexecmysql -h %mysqlhost% -uroot -p%rootpwd% -P %mysqlport% -e "use mysql;DROP USER IF EXISTS 'bam%suffix%'@'%%';DROP USER IF EXISTS 'bam%suffix%'@'localhost';DROP DATABASE IF EXISTS `bam%suffix%`;DROP USER IF EXISTS 'bim%suffix%'@'%%';DROP USER IF EXISTS 'bim%suffix%'@'localhost';DROP DATABASE IF EXISTS `bim%suffix%`;DROP USER IF EXISTS 'apphub%suffix%'@'%%';DROP USER IF EXISTS 'apphub%suffix%'@'localhost';DROP DATABASE IF EXISTS `apphub%suffix%`;DROP USER IF EXISTS 'epass%suffix%'@'%%';DROP USER IF EXISTS 'epass%suffix%'@'localhost';DROP DATABASE IF EXISTS `epass%suffix%`;" -vvvgoto end:endpausebash ...

January 16, 2023 · 5 min · jiezi

关于脚本:PythonSeaTable-从数字文本列生成条形码图片并写入表格

不论是录入信息时须要用扫码器扫码录入、核查信息时用于扫码辨认,还是有别的生成条形码的须要,那咱们如何把表格中的数字、文本列疾速生成条形码图片呢?这在 SeaTable 表格中用 Python 脚本就能够轻松实现,即把表格中的某一个字段,如身份证号、电话号转换成相应的条形码,并存储到图片列中。例如,你有如下一张表格,外面有电话号码和条码图片两列,须要把每一行的电话号码转换成条码,并传到条码图片(BarcodeImage)列中去。怎么实现呢? 在 SeaTable 中用 Python 生成条形码新建 Python 脚本首先须要在表格中,点击“脚本”性能,新建一个 Python 脚本。 写入代码而后在脚本中写入如下代码(如果须要线下运行,须要装置Python-barcode 依赖包) import osimport timeimport barcodefrom barcode.writer import ImageWriterfrom seatable_api import Base, contextapi_token = context.api_token or "859ad340d9a2b11b067c11f43078992e14853af5"server_url = context.server_url or "https://cloud.seatable.cn"TEXT_COL = "PhoneNum" # 须要转换成条码的列BARCODE_IMAGE_COL = "BarcodeImage" # 图片列, 存储条码TABLE_NAME = 'Table1'BARCODE_TYPE = 'code128'CUSTOM_OPTIONS = { "module_width": 0.2, # 单个条纹的最小宽度, mm "module_height": 15.0, # 条纹带的高度, mm "quiet_zone": 6.5, # 图片两边与首尾两条纹之间的间隔, mm "font_size": 10, # 条纹底部文本的大小,pt "text_distance": 5.0, # 条纹底部与条纹之间的间隔, mm}CODE = barcode.get_barcode_class(BARCODE_TYPE)base = Base(api_token, server_url)base.auth()def get_time_stamp(): return str(int(time.time()*100000))for row in base.list_rows(TABLE_NAME): # 如果图片列中已有内容, 则跳过 if row.get(BARCODE_IMAGE_COL): continue # 如果电话号码列为空,则跳过 if not row.get(TEXT_COL): continue try: row_id = row.get('_id') msg = str(row.get(TEXT_COL)) # 生成条码对象 code_img = CODE(msg, writer=ImageWriter()) save_name = "%s_%s" % (row_id, get_time_stamp()) # 保留为图片并暂存 file_name = code_img.save("/tmp/%s" % save_name, options=CUSTOM_OPTIONS) # 将图片上传至 Base 表格 info_dict = base.upload_local_file(file_name, name=None, file_type='image', replace=True) img_url = info_dict.get('url') row[BARCODE_IMAGE_COL] = [img_url] base.update_row('Table1', row_id, row) # 移除暂存文件 os.remove(file_name) except Exception as error: print("error occured during barcode generate", error) continue运行脚本一键点击运行按钮,脚本运行实现后,表格中的电话号就会主动生成相应的条形码,并保留到条形码图片列。方便快捷。 ...

July 29, 2022 · 1 min · jiezi

关于脚本:测试-split-计时-shell-脚本参考

测试 split 计时 shell 脚本参考#!/bin/bash# set -euxo#如果是一个新的环境,则须要设置变量TEST_ENV="new"TEST_ENV=#如果在dble本机导入split后的mysqldump文件,则须要设置变量LOADIN="locally",#如果须要将split后的mysqldump文件cp到后端mysql所在主机去执行导入,则须要设置变量LOADIN="remote"LOADIN="locally"test_dir='/tmp/splittest/'dble_conf_dir='/opt/dble/conf/'ex_dump_file_name='benchmarksql_with_data'dump_file_name='benchmarksql_with_data'shardingNum=10ex_shardingNum=10MYSQL='/usr/bin/mysql -uxxxx -pxxxx -P3306'MYSQL_HOST[0]='xx.xx.xx.101'MYSQL_HOST[1]='xx.xx.xx.102'MYSQL_HOST[2]='xx.xx.xx.105'MYSQL_HOST[3]='xx.xx.xx.108'DBLE_ROOT='/usr/bin/mysql -uroot -p111111 -P9066 -h127.0.0.1'function create_backend_mysqls_from_dble_cmd () { #make file drop_and_create_shardingnodes_10dn for further test drop_ex_shardingnodes="${test_dir}drop_ex_shardingnodes_dnx.sql" create_shardingnodes="${test_dir}create_shardingnodes_dnx.sql" rm -f ${drop_ex_shardingnodes} cat>"${drop_ex_shardingnodes}"<<EOFDROPdrop database @@shardingnode="an\$1-${ex_shardingNum}";EOFDROP rm -f ${create_shardingnodes} cat>"${create_shardingnodes}"<<EOFCREATEreload @@config_all;create database @@shardingnode="an\$1-${shardingNum}";EOFCREATE ${DBLE_ROOT} < ${drop_ex_shardingnodes} if [[ $? != 0 ]]; then echo "fail to drop backend schema via dble admin cmd line" exit 1 fi #change sharding.xml conf of new_sharding.xml mv ${dble_conf_dir}sharding.xml ${dble_conf_dir}sharding_${ex_shardingNum}.xml mv ${dble_conf_dir}sharding_${shardingNum}.xml ${dble_conf_dir}sharding.xml ${DBLE_ROOT} < ${create_shardingnodes} if [[ $? != 0 ]]; then echo "fail to create backend schema via dble admin cmd line" exit 1 fi # check backend mysqldbs has been created ${MYSQL} -h${MYSQL_HOST[1]} -e "show databases;" | grep "dh_dn_" if [[ $? != 0 ]]; then echo "fail to create backend schema via dble admin cmd line" fi }function generate_rm_old_split_dump_files () { filename="${test_dir}rm_old_split_dump_files.sh" rm -f ${filename}cat>"${filename}"<<EOF#!/usr/bin/expect -fset timeout -1set host [lindex \$argv 0]set file_name [lindex \$argv 1]spawn ssh test@\$host "rm -f \$file_name.sql-an*;ls -l /tmp/splittest/"expect "*continue connecting*"send "yes\r"expect "*password:"send "mypasswordfortestuser\r"expect eofEOF}function rm_old_dump_files_on_each_shardingnodes () { rm -f ${test_dir}${ex_dump_file_name}.sql-an* # 新环境中首次跑测试的时候须要生成这样一个文件 ${test_dir}rm_old_split_dump_files.sh if [[ "${TEST_ENV}" == "new" ]]; then generate_rm_old_split_dump_files fi for host in "${MYSQL_HOST[@]}";do echo "${host}" expect ${test_dir}rm_old_split_dump_files.sh "${host}" "${test_dir}${ex_dump_file_name}" done}# split_cmd.sql 文件中有 dump_file_name,此处还能够参数化 -l等参数的数值function get_split_exec_cost_time () { filename="${test_dir}split_cmd.sql" rm -f ${filename} cat>"${filename}"<<EOFsplit /opt/splitTest/${dump_file_name}.sql /tmp/splittest -sbenchmarksql;EOF { echo "$(date +["%Y-%m-%d %H:%M:%S"]) <==========================> start exec split cmd on dble manager 9066" echo "$(date +["%Y-%m-%d %H:%M:%S"]) <=======> startTime" # echo "${DBLE_ROOT} < ${filename}" echo "split /opt/splitTest/${dump_file_name}.sql /tmp/splittest -sbenchmarksql; " }>> time_record.log timer_start=$(date +%s) ${DBLE_ROOT} < ${filename} if [[ $? != 0 ]]; then echo "$(date +["%Y-%m-%d %H:%M:%S"]) !!!!!!!!!!! fail to exec !!!!!!!!!!" >> time_record.log fi timer_end=$(date +%s) duration=$(echo "$timer_end" "$timer_start" | awk '{print $1-$2}')s { echo "dble治理端执行split的耗时: ${duration} " echo "total time is ${duration} " echo "$(date +["%Y-%m-%d %H:%M:%S"]) <=======> endTime" echo "$(date +["%Y-%m-%d %H:%M:%S"]) <==========================> finish exec split cmd on dble manager 9066" echo " " }>> time_record.log }# dble本机近程连贯后端mysql,别离并发导入function loadAll_shardingDumpFiles_on_dble_server () { { echo " " echo "$(date +["%Y-%m-%d %H:%M:%S"]) <==========================> loadAll_shardingDumpFiles_on_dble_server startTime" echo "$(date +["%Y-%m-%d %H:%M:%S"]) <=======> startTime" }>> time_record.log timer_s=$(date +%s) for ((i=1;i<="${shardingNum}";i++)) do { if [ ! -f "${test_dir}${dump_file_name}.sql-an${i}.sql" ]; then mv ${test_dir}${dump_file_name}.sql-an"${i}"-*dump ${test_dir}${dump_file_name}.sql-an"${i}".sql fi node=$(( i % 4 )) case ${node} in 1) host=${MYSQL_HOST[0]};; 2) host=${MYSQL_HOST[1]};; 3) host=${MYSQL_HOST[2]};; 0) host=${MYSQL_HOST[3]};; esac echo "${MYSQL} -h${host} dh_dn_${i} < ${test_dir}${dump_file_name}.sql-an${i}.sql" >> time_record.log ${MYSQL} -h${host} dh_dn_"${i}" < ${test_dir}${dump_file_name}.sql-an"${i}".sql }& done wait timer_e=$(date +%s) tduration=$(echo "$timer_e" "$timer_s" | awk '{print $1-$2}')s { echo "split后的dumpfile文件从dble本机并发导入,总时长(即最长耗时)为: ${tduration} " echo "$(date +["%Y-%m-%d %H:%M:%S"]) <=======> endTime" echo "$(date +["%Y-%m-%d %H:%M:%S"]) <==========================> loadAll_shardingDumpFiles_on_dble_server finishTime" echo " " }>> time_record.log}# local导入耗时不须要此处function generate_send_split_dump_files () { filename="${test_dir}send_split_dump_files.sh" rm -f ${filename}cat>"${filename}"<<EOF#!/usr/bin/expect -fset timeout -1set file_num [lindex \$argv 0]set host [lindex \$argv 1]set file_name [lindex \$argv 2]spawn scp \$file_name.sql-an\$file_num.sql test@\$host:/tmp/splittest/.expect "*continue connecting*"send "yes\r"expect "*password:"send "mypasswordfortestuser\r"expect eofEOF}# local导入耗时不须要此处function change_split_dumpfile_name_and_cp_them_to_their_local () { # 新环境中首次跑测试的时候须要生成这样一个文件 ${test_dir}t_dump_files.sh if [[ ("${TEST_ENV}" == "new") && ("${LOADIN}" == "remote") ]]; then generate_send_split_dump_files fi { echo " " echo "$(date +["%Y-%m-%d %H:%M:%S"]) <==========================> start change split filename and cp them to their local mysql server hosts" echo "$(date +["%Y-%m-%d %H:%M:%S"]) <=======> startTime" }>> time_record.log timer_st=$(date +%s) for ((i=1;i<="${shardingNum}";i++)) do { if [ ! -f "${test_dir}${dump_file_name}.sql-an${i}.sql" ]; then mv ${test_dir}${dump_file_name}.sql-an"${i}"-*dump ${test_dir}${dump_file_name}.sql-an"${i}".sql fi node=$(( i % 4 )) case ${node} in 1) host=${MYSQL_HOST[0]};; 2) host=${MYSQL_HOST[1]};; 3) host=${MYSQL_HOST[2]};; 0) host=${MYSQL_HOST[3]};; esac # scp ${test_dir}${dump_file_name}.sql-an"${i}".sql test@${host}:/tmp/splittest/. expect "${test_dir}"send_split_dump_files.sh "${i}" "${host}" "${test_dir}${dump_file_name}" } done timer_ed=$(date +%s) cpduration=$(echo "$timer_ed" "$timer_st" | awk '{print $1-$2}')s { echo "split后的dumpfile文件cp到对应后端mysql本机,总时长(即最长耗时)为: ${cpduration} " echo "$(date +["%Y-%m-%d %H:%M:%S"]) <=======> endTime" echo "$(date +["%Y-%m-%d %H:%M:%S"]) <==========================> finish change split filename and cp them to their local mysql server hosts" echo " " }>> time_record.log}function exec_romote_test_time_on_each_hosts () { { echo " " echo "$(date +["%Y-%m-%d %H:%M:%S"]) <==========================> exec_remote_loadin_on_each_host startTime" echo "$(date +["%Y-%m-%d %H:%M:%S"]) <=======> startTime" }>> time_record.log timer_s=$(date +%s) for host in "${MYSQL_HOST[@]}" do { echo "${host}" expect "${test_dir}"exec_shell_on_remote_server.sh "${host}" }& done wait timer_e=$(date +%s) tduration=$(echo "$timer_e" "$timer_s" | awk '{print $1-$2}')s { echo "split后的dumpfile文件从后端mysql本机并发导入,总时长(即最长耗时)为: ${tduration} " echo "$(date +["%Y-%m-%d %H:%M:%S"]) <=======> endTime" echo "$(date +["%Y-%m-%d %H:%M:%S"]) <==========================> exec_remote_loadin_on_each_host finishTime" echo " " }>> time_record.log}function clean_backend_schemas_and_old_dump_files () { rm_old_dump_files_on_each_shardingnodes create_backend_mysqls_from_dble_cmd}function test_of_split_dumpfile_on_mysql_respectively () { echo "NOW we are testing scp split_dumpfiles to mysql and loadin respectively,and shardingnode num is ${shardingNum}--------------------------------------------------------------">> time_record.log clean_backend_schemas_and_old_dump_files get_split_exec_cost_time change_split_dumpfile_name_and_cp_them_to_their_local exec_romote_test_time_on_each_hosts echo "NOW we have finished the testing of scp split_dumpfiles to mysql and loadin respectively,and shardingnode num is ${shardingNum}---------------------------------------------">> time_record.log}function test_of_split_dumpfile_on_dbleServer () { echo "NOW we are testing loadin on dble server locally,and shardingnode num is ${shardingNum}--------------------------------------------------------------">> time_record.log clean_backend_schemas_and_old_dump_files get_split_exec_cost_time loadAll_shardingDumpFiles_on_dble_server echo "NOW we have finished the testing of loadin on dble server locally,and shardingnode num is ${shardingNum}---------------------------------------------">> time_record.log}if [ ${LOADIN} == "locally" ]; then test_of_split_dumpfile_on_dbleServerelif [ ${LOADIN} == "remote" ]; then test_of_split_dumpfile_on_mysql_respectivelyelse echo "please check the file line 7: The value of variable LOADIN should be one of 'locally' or 'remote' ;The variable maybe on line 7" exit 1 fi

July 13, 2022 · 4 min · jiezi

关于脚本:脚本之美│VBS-入门交互实战

一、什么是 VBSVBS 是一种 Windows 脚本语言,全称是 Microsoft Visual Basic Script Editon,微软公司可视化 BASIC 脚本版vbs 是零碎内置的,代码可在 windows 零碎中间接执行,不须要编译环境,十分的不便vbs 脚本简略,高效,大部分性能都能够实现,利用好 vbs 脚本,能够极大的提高效率,能够用它来做一些重复繁琐的机器操作二、第一个 VBS 脚本在桌面上新建一个文本文档关上文本文档,在其中输出 msgbox "Hello World!"将文本文档的格局后缀 .txt 改为 .vbs而后双击运行文件即可 三、msgbox 语法语法:msgbox "对话框内容","对话框类型(参数:0,1,2,3,4,5)","对话框题目"对话框内容:是指弹窗的主体内容对话框类型:能够抉择 0-5 共6种类型的对话框模式,0时对话框只有确定按钮;1时对话框有确定按钮、勾销按钮;2时对话框有停止按钮、重试按钮、疏忽按钮;3时对话框有是按钮、否按钮、勾销按钮;4时对话框有是按钮、否按钮并且对话框禁止点击右上角的敞开按钮;5时对话框有重试按钮、勾销按钮。如果不填默认为0对话框题目:是指弹窗的题目msgbox "飞兔小哥送你一份奖品待支付", 3, "舒适揭示" 四、中文乱码上图能够看到中文乱码了这次要是因为编码谬误导致的,失常的文本文档它的编码格局是 UTF-8 的,然而 VBS 脚本须要编码格局是 ANSI 才能够失常运行中文这时候咱们须要 关上文本文档 ► 点击左上角文件 ► 另存为 ► 抉择编码为ANSI ► 确定 即可解决 五、弹窗交互性能下面咱们只是实现了很简略的展现性能,那么咱们须要实现交互性能,就要用到变量了在 vbs 中能够通过 dim 定义变量,语法为:dim 变量名1,变量名2,变量名3...变量名n其中 inputbox 能够用来接管用户手动输出的参数dim namename = inputbox("请通知我您是谁", "这是交互的题目")msgbox name,,"欢迎您" 六、表白恶搞之前很火的敞开不了的表白弹窗,就是 vbs 做进去的应用 dim a(5) 能够定义数组,5示意这个数组外面有多少元素应用 Select Case 示意这是一个选项其中抉择批准的返回值为6,不批准的返回值为7如果点击了不批准,那就循环从数组中取值展现如果用户点击了批准,那就诡计未遂,退出选项MsgBox "佛前哭求"MsgBox "奈何桥期待"MsgBox "五百次回眸"MsgBox "千年的回首"MsgBox "百世的轮回"MsgBox "换你今朝一世情缘"MsgBox "可否"dim a(5)a(0)="天大,地大,女友最大"a(1)="工资上交"a(2)="房写你名"a(3)="帮清购物车"a(4)="保大"a(5)="你就许可我把^o^"Dim jDoSelect Case msgbox ("姑娘,做我女朋友吧", 4)Case 6 MsgBox "you are my girlfriend," + Chr(13) + "from this day until to my last days."exit doCase 7msgbox a(i)i=i+1if i >= 6 theni = 0end ifend SelectLoop ...

June 23, 2022 · 1 min · jiezi

关于脚本:脚本执行常见错误buildsh-caller-not-found

1 bash与dash从Ubuntu 6.10开始,默认应用dash(theDebian Almquist Shell)而不是bash(the GNUBourne-Again Shell)但Login Shell还是bash. 起因是dash更快、更高效,而且它合乎POSIX标准。Ubuntu在启动的时候会运行很多shell脚本,应用dash能够放慢启动速度。 2 执行脚本经常会有一些不出名的谬误比方:build.sh: caller: not found 我还遇到过还有一些其余莫名其妙的谬误 先看下本人的脚本是用bash解析还是dash解析的,脚本咱们个别默认都是用bash 先用命令ls -l /bin/sh看看本人零碎的脚本解析 3 切换sh为bash/dashsudo dpkg-reconfigure dash呈现一个界面抉择否切换为bash解析即可解决:build.sh: caller: not found 切换回去从新执行一遍命令抉择是即可

February 28, 2022 · 1 min · jiezi

关于脚本:使用脚本一键打包并上传docker镜像

笔者搞了一年多微前端我的项目,一个团队治理十个微利用,换成docker镜像部署后,公布操作一下从原来的脚本直连服务器的1分钟变成了几十分钟,尤其上传每个利用到各自的阿里云仓库。这里就再写个脚本一键打包docker镜像并上传阿里云。 本文只讲怎么制作一个脚本帮忙去加重开发人员累赘,对于docker-compose的配置见:应用各种姿态难受的部署微前端我的项目(上:打包与上传) 效果图 间接上代码/** * @name docker镜像打包上传脚本 * @author weilan * @time 2021.02.22 */const fs = require('fs');const path = require('path');const util = require('util');const { log } = require('../utils/log');const exec = util.promisify(require('child_process').exec);const sub_app_ath = path.resolve();let sub_apps = fs.readdirSync(sub_app_ath).filter(i => /^subapp|master/.test(i));const inquirer = require('inquirer'); // 用于命令行交互/** * @name 命令行交互配置项 */const question = [ { type: 'confirm', name: 'dist', message: '是否须要打包前端动态资源?', }, { type: 'confirm', name: 'env', message: '请抉择是否须要打包成不联网的内网部署', when: function (answers) { // 当answer为true的时候才会发问以后问题 return answers.dist } }, { type: 'checkbox', name: 'apps', message: '请抉择要公布的模块', choices: sub_apps, validate: function (val) { if (val.length) { // 校验 return true; } return "抉择不能为空"; } },]/** * @name 依据命令交互配置后果做逻辑解决 */inquirer.prompt(question).then(async (answer) => { let subApps = answer.apps; let buildScript = answer.env ? 'yarn build --Intranet' : 'yarn build'; let needDist = answer.dist; let now = +new Date(); // 登录阿里云 const { error: loginError } = await exec('docker login --username=哈哈哈 --password=嘿嘿 registry.cn-zhangjiakou.aliyuncs.com'); if (loginError) { log.red(loginError, '登录镜像核心失败') return; } console.log(`开始顺次解决 ${JSON.stringify(subApps)} ......`); subApps.reduce((chain, item) => { return chain.then(() => publishIamge(item, now, needDist, buildScript)) }, Promise.resolve())});/** * @name 打包镜像并推送阿里云 * @param {String} moduleName 模块名 * @param {String} now 以后版本工夫戳 * @param {Boolean} needDist 是否须要打包前端动态资源 * @param {String} buildScript 前端动态资源打包命令 */async function publishIamge(moduleName, now, needDist, buildScript) { // 打包前端动态资源 if (needDist) { console.log('开始打包前端动态资源' + moduleName); const { error } = await exec(buildScript, { cwd: path.resolve(moduleName) }); if (error) { log.red(moduleName, '前端代码打包谬误:', error) return; } log.green(moduleName + '前端代码打包胜利') } // 打包镜像 console.log(`开始打包镜像 ${moduleName} ......`); const { stdout: buildStdout, error: buildError } = await exec('docker-compose build ' + moduleName); if (buildError) { log.red(buildError, '镜像打包谬误') return; } log.cyan(buildStdout) log.green('镜像打包实现,开始制作镜像标签') // 更新镜像标签 const imageName = 'ibp2fe_' + moduleName; const { error: tagError } = await exec(`docker tag ${imageName} registry.cn-zhangjiakou.aliyuncs.com/futureweb/${imageName}:${now}`); if (tagError) { log.red(tagError, '镜像标签异样') return; } log.green('镜像版本标签更新结束,开始更新last标签') // 更新镜像标签last版本 const { error: tagLastError } = await exec(`docker tag ${imageName} registry.cn-zhangjiakou.aliyuncs.com/futureweb/${imageName}`); if (tagLastError) { log.red(tagError, '镜像last标签异样') return; } log.green('镜像last标签更新结束,开始上传') const { stdout: pushStdout, error: pushError } = await exec('docker push registry.cn-zhangjiakou.aliyuncs.com/futureweb/' + imageName); if (pushError) { log.red(pushError, '镜像上传失败') return; } log.cyan(pushStdout) log.green('镜像上传胜利')}process.on('unhandledRejection', (reason, p) => { console.log('Unhandled Rejection at: Promise', p, 'reason:', reason); // application specific logging, throwing an error, or other logic here});实现思路和注意事项首先思考命令行交互的形式,将须要做的配置以抉择的形式让开发人员确认,最初抉择要公布的模块提前登录阿里云账号这里能够抉择并发执行所选全副模块,然而这样日志输入会无序,且电脑压力极大会临时死机;因而这里我用顺次解决的形式打包所选模块的前端动态资源执行docker-compose build xxx 打包所选模块镜像这里有个须要留神的点,因为是工具函数解决,每个模块的门路由node读取,因而你的模块名和docker-compose.yml里的服务名、容器名最好都统一;另外留神你的镜像打包后是你docker-compose.yml外的根目录下划线连贯你的docker-compose服务名,因而你的阿里云镜像仓库命名最好和这个组合后镜像名统一;以上都是为了不便工具函数可能通用解决各个模块镜像。制作镜像标签。这里我会制作一个本次公布工夫戳的版本和一个lastet版本,后面不便回退,前面不便运维部署时无需关怀标签版本。也能够拉取git tag来做镜像的tag。制作镜像后上传至阿里云镜像核心

February 24, 2021 · 2 min · jiezi

Dubbo-在-K8s-下的思考

序言Dubbo在2011开源之后,一直是国内最受欢迎的RPC框架,之后spring boot和Spring Cloud的面世,助推了微服务的火热程度。计算机的世界变化很快,自从容器和K8s登上舞台之后,给原有的RPC领域带来了很大的挑战。这个文章主要讲述RPC领域遇到的问题,以及RPC怎么去拥抱K8s怀抱的一些思考。 K8S介绍kubernetes是一个开源的,用于管理云平台中多个主机上的容器化的应用,Kubernetes的目标是让部署容器化的应用简单并且高效,Kubernetes提供了应用部署,规划,更新,维护的一种机制。kubernetes简称K8s。 在Kubernetes中,最小的管理元素不是一个个独立的容器,而是Pod。Pod的生命周期需要注意以下几点: 容器和应用可能随时被杀死Pod Ip和主机名可能变化 (除非使用StatefulSet进行定制)写到本地的磁盘的文件可能消失,如果想不失效,需要用存储卷应用,容器,Pod的关系 应用部署在容器中,一般情况下一个应用只部署在一个容器中一个Pod下可以包含一个或多个容器,一般情况下一个Pod只建议部署一个容器。下列场景除外: side car一个容器的运行以来与本地另外一个容器。如一个容器下应用负责下载数据,另外一个容器下应用向外提供服务Service 如果一些Pods 提供了一些功能供其它的Pod使用,在kubernete集群中是如何实现让这些前台能够持续的追踪到这些后台的?答案是:Service。 Kubernete Service 是一个定义了一组Pod的策略的抽象,这些被服务标记的Pod一般都是通过label Selector决定的。Service抽象了对Pod的访问。 默认的Service,通过一个集群Ip获取A Record。但是有时需要返回所有满足条件的Pod Ip列表,这时候可以直接使用 Headless Services。 参考:Https://kubernetes.io/ 推荐书籍:kubernetes in action RPC介绍和分析随着微服务的普及,应用之间的通信有了足够多的成熟方案。Dubbo在2011年开源之后,被大量的中小型公司采用;在Spring Boot推出之后,Spring逐渐焕发出第二春,随即Spring Cloud面世,逐渐占领市场,在中国市场中,和Dubbo分庭抗争;gRPC是google推出的基于Http2的端到端的通信工具,逐渐地在K8s市场上占据统治地位,如etcd,istio等都采用gRPC作为通信工具;Service Mesh从开始概念上就火热,现在逐渐走向成熟,Istio + Envoy(其他sidecar)逐渐开始走上舞台。 应用开发者视角 从功能层面来说,对开发者有感知的功能有:服务实现,服务暴露(注解或配置),服务调用(注解或配置),服务治理等。 从选型角度会关注以下几点:易用性(开发易用性和开箱即用),性能,功能,扩展性等。 框架开发者视角 关键流程:服务暴露,服务注册,服务发现,服务调用,服务治理。 关键知识点:序列化,网络通信,服务路由,负载均衡,服务限流,熔断,降级等服务治理。 主流技术实现DUBBO/HSF Dubbo提供了面向接口的远程方法调用。应用开发者定义接口,编写服务并暴露;Client端通过接口进行调用。 Dubbo注册服务的维度是接口维度,每个接口会在注册中心写入一条数据。 Dubbo支持条件路由,脚本路由,Tag路由等。这些路由规则都是强依赖于IP地址。 备注:Dubbo和HSF的大部分机制都是相似的,所以下面都以Dubbo作为方案进行讨论。 SpringCloudSpring Cloud通过Rest形式进行网络调用。应用开发者可以自己编写暴露Rest服务,如springmvc。 Spring Cloud里的服务注册是应用维度(Eureka),Client端和Server端通过约定的方式进行通信。 Spring Cloud提供了一套标准API,而其中Netflix是其中的佼佼者,对这套API进行了实现,对大部分开发者来说,可以回直接依赖和使用Netflix,所以可以说是Netflix提供成了Spring Cloud的核心。但是作为商业公司对开源投入往往会多变,如Eureka已经体制维护。 gRPCgRPC 是一个 基于 HTTP/2 协议设计的 RPC 框架,它采用了 Protobuf 作为 IDL。gRPC作为端到端的通信方案,可以解决现在的多语言问题。 gRPC本身不提供服务注册,服务治理的功能。但现在可以看到gRPC有趋势往这方面扩展的野心。 K8sK8s体系里暂时没有公允的通信框架,一般推荐gRPC作为RPC框架。 K8s体系下,默认情况下,Pod的Ip是变化的,所以Pod和Pod之间需要通信的话,有几种方式: Service+DNS:新建一个Service,可以通过标签选择到一组Pod列表,这个service对应一个不变的集群Ip;Client端通过DNS方式或者直接访问集群Ip。这个集群Ip,约等于实现了负载均衡 (Iptable方式)。headless service:headless service和上面的service的区别是,它不提供集群Ip,通过主机名的形式获取一组Ip列表,Client端自己决定访问哪个Pod。Istio + Envoy ...

November 5, 2019 · 2 min · jiezi

Solo支付宝开源的Android专项测试工具

1.前言近年来,随着移动互联网的蓬勃发展,移动测试技术也取得了长足的进步,从早期基于测试脚本的单机自动化,到录制回放、图像识别、云测平台等测试技术贴合实际业务需求深度应用和创新,测试效率从而一次又一次被提升。 本文主要介绍支付宝在移动端上实现的一套无线化、非侵入、免 Root 的 Android 专项测试方案 Solo。直接操控手机,即可实现自动化的功能、性能、兼容性、以及稳定性测试等工作。 1.1 移动测试 1.0 时代 移动测试 1.0 时代,也可以称之为探索期。由于厌倦了日复一日的手工操作,如何提升测试效率成为了移动测试领域最重要的课题,在此期间,除了 Monkey、UiAutomator、Instruments 等官方提供的工具,业界还涌现了一批优秀的开源自动化测试工具/框架,在自动化驱动能力的基础之上,不仅可以实现基本功能的验证,还可以结合性能采集方案、遍历算法等实现各类专项测试的自动化。在这个阶段,自动化测试的常见形态是在单机或本地少数几台 PC 上部署测试环境,再利用 Jenkins 等工具实现持续集成。 1.2 移动测试 2.0 时代 伴随着测试技术的持续发展、又得益于 STF 的开源,业界开始出现了云测平台的概念,将真机设备、任务管理、自动化框架以及专项测试方案打包在平台中作为服务提供出去,给用户带来了一站式的测试体验。另一方面,远程调试、设备调度等技术的引入极大的提升了设备的利用率,测试人员不再需要为缺少测试设备或测试任务排队耗时而担心。对于云测平台用户而言,在此阶段常见的测试形态是:在本地 PC 上开发测试脚本,再上传至云测平台执行,最后可在平台中查看测试报告,测试流程简单且清晰。 1.3 移动测试 2.0+ 在保留了上述“云测”的玩法之外,移动测试 2.0+ 时代下的测试技术提供的往往不再是某一个独立的小工具,更多的是带来一套完整的解决方案,例如为用户提供一套定制化的 IDE 环境,结合录制回放、图像识别等技术,用户可能只需要做一些简单的框选、拖拽就能完成测试脚本的开发。另一方面,由于办公环境、硬件条件等因素的限制,越来越多的测试人员希望可以在移动端上直接发起测试,做到移动测试“移动测”。当然,无论是云端、IDE 端、还是移动端,都应该做到能力互通,即“多端多通”,这样才能让测试方案更加灵活、适用于更多场景。 2.无线驱动的Android专项测试方案:Solo“多端多通”的概念比较广,仅凭一篇文章可能无法阐述清楚,所以下面将会重点介绍为了迎接“移动 2.0+”时代,我们在移动端上实现的一套无线化、非侵入、免 Root 的 Android 专项测试方案 Solo。直接操控手机,即可实现自动化的功能、性能、兼容性、以及稳定性测试等工作。 2.1 整体架构 这套方案中,底层依赖主要是“无线 ADB、系统辅助功能、Chrome 调试以及图像识别技术”,后文将会介绍它们具体的应用场景。同时,在底层依赖的基础上,我们封装了一套核心能力,由“控件定位、事件驱动、性能采集以及依赖注入”组成,并在服务层实现了录制、回放、数据处理等公共服务能力。在架构的最顶端,结合界面交互逻辑包装出了各个功能的入口。 2.2 无线 ADB 大家都知道,对于 Android 自动化,ADB shell 的执行能力是一切的基础。 在 PC 上,通过 Android SDK 提供的ADB client 与同样运行于 PC 中的 ADB server 通信,再由 ADB server 通过 USB 与位于设备中的 Adbd 通信。要实现一套无线化的方案,必须要摆脱对 USB 线的依赖。好在 Android 系统还提供了一种基于 Socket 的 ADB 连接模式,既然是这样,那么只需要按照 ADB 通信协议在端上与本机的 5555 端口进行通信即可获得 ADB shell 的执行能力。 ...

July 12, 2019 · 2 min · jiezi

性能压测工具选型对比

本文是《Performance Test Together》(简称PTT)系列专题分享的第二期,该专题将从性能压测的设计、实现、执行、监控、问题定位和分析、应用场景等多个纬度对性能压测的全过程进行拆解,以帮助大家构建完整的性能压测的理论体系,并提供有例可依的实战。 该系列专题分享由阿里巴巴 PTS 团队出品。 第一期:《压测环境的设计和搭建》,点击这里。 本文致力于给出性能压测的概念与背景介绍,同时针对市场上的一些性能压测工具,给出相应的对比,从而帮助大家更好地针对自身需求实现性能压测。 为什么要做性能压测在介绍性能压测概念与背景之前,首先解释下为什么要做性能压测。从09年的淘宝双十一大促导致多家合作银行后台系统接连宕机,到春运期间12306购票难,再到前不久聚美优品促销活动刚开始就遭秒杀。根据Amazon统计,每慢100毫秒,交易额下降1%。这些事件和统计数据为大家敲响了警钟,也客观说明了性能压测对于企业应用的重要性。 从具体的作用上讲,性能压测可以用于新系统上线支持、技术升级验证、业务峰值稳定性保障、站点容量规划以及性能瓶颈探测。 1. 新系统上线支持在新系统上线前,通过执行性能压测能够对系统的负载能力有较为清晰的认知,从而结合预估的潜在用户数量保障系统上线后的用户体验。 2. 技术升级验证在系统重构过程中,通过性能压测验证对比,可以有效验证新技术的高效性,指导系统重构。 3. 业务峰值稳定性保障在业务峰值到来前,通过充分的性能压测,确保大促活动等峰值业务稳定性,保障峰值业务不受损。 4. 站点容量规划通过性能压测实现对站点精细化的容量规划,指导分布式系统机器资源分配。 5. 性能瓶颈探测通过性能压测探测系统中的性能瓶颈点,进行针对性优化,从而提升系统性能。 综上所述,性能压测伴随着系统开发、重构、上线到优化的生命周期,因此有效的性能压测对系统的稳定性具有重要的指导意义,是系统生命周期中不可或缺的一部分。 性能压测概念性能压测是通过自动化的测试工具模拟多种正常、峰值以及异常负载条件来对系统的各项性能指标进行测试。从测试目的上性能压测又可以划分为负载测试、压力测试、并发测试、配置测试以及可靠性测试。 负载测试是测试当负载逐渐增加时,系统各项性能指标的变化情况。压力测试是通过确定一个系统的瓶颈或者不能接受的性能点,来获得系统能提供的最大服务级别的测试。并发测试通过模拟用户并发访问,测试多用户并发访问同一个软件、同一个模块或者数据记录时是否存在死锁等性能问题。配置测试是通过对被测系统的软/硬件环境的调整,了解各种不同方法对软件系统的性能影响的程度,从而找到系统各项资源的最优分配原则。可靠性测试是在给系统加载一定业务压力的情况下,使系统运行一段时间,以此检测系统是否稳定。总的来说,性能压测是在对系统性能有一定程度了解的前提下,在确定的环境下针对压测需求进行的一种测试。 如何选取性能压测工具在选取合适的性能压测工具之前,我们需要先先了解执行一次完整的性能压测所需要的步骤: 1. 确定性能压测目标:性能压测目标可能源于项目计划、业务方需求等 2. 确定性能压测环境:为了尽可能发挥性能压测作用,性能压测环境应当尽可能同线上环境一致 3. 确定性能压测通过标准:针对性能压测目标以及选取的性能压测环境,制定性能压测通过标准,对于不同于线上环境的性能压测环境,通过标准也应当适度放宽 4. 设计性能压测:编排压测链路,构造性能压测数据,尽可能模拟真实的请求链路以及请求负载 5. 执行性能压测:借助性能压测工具,按照设计执行性能压测 6. 分析性能压测结果报告:分析解读性能压测结果报告,判定性能压测是否达到预期目标,若不满足,要基于性能压测结果报告分析原因 由上述步骤可知,一次成功的性能压测涉及到多个环节,从场景设计到施压再到分析,缺一不可。工欲善其事,必先利其器,而一款合适的性能工具意味着我们能够在尽可能短的时间内完成一次合理的性能压测,达到事半功倍的效果。 工具选型对比在论述了性能压测必要性之后,如何选取性能压测工具成为一个重要的议题?本文选取了市场上主流性能压测工具:(ab)Apache Bench、LoadRunner、JMeter、阿里云PTS,并从多个方面出发分析了各个工具的优缺点,汇总后的优缺点如下表所示: 压测工具Apache Bench(ab)LoadRunnerJMeterPTS学习成本低高高低安装部署成本低高高低是否免费是否是否是否支持多协议否是是是压测结果是否能够图形化展示否是是是是否支持TPS模式否否否是是否有链路、场景编排管理支持否是是是是否支持场景录制否是是是生态环境强弱弱弱弱强监控指标是否完备否否否是是否原生支持流量地域定制否否否是Apache Bench(ab) ab是一款用来针对HTTP协议做性能压测的命令行工具,支持在本地环境发起测试请求,验证服务器的处理性能。它主要具有以下特点: 首先,作为一款开源工具,ab具有较好的扩展性,测试开发人员可以基于自身需求对其进行二次开发,同时它对HTTP协议支持度较好,比如支持设定HTTP请求头、支持Cookie以及HTTP的多种方法。此外,使用ab时还可以通过指定性能压测产生的总请求数、并发数与压测时长控制性能压测,结合其能够输出性能压测过程中的TPS(每秒事务数)、RT(响应时延)等信息的特点,ab具有简单易上手的特点。但ab也存在一些缺点,如无图形化界面支持,支持协议较为单一,只支持HTTP协议,缺少对HTTPS协议、WebSocket等协议的支持,对于较为复杂的性能压测场景,ab缺少链路编排、场景管理等支持,只能够对单一地址发起性能压测,此外,它的性能压测统计指标纬度较少,缺少性能压测过程中的数据统计,只能够在压测结束后获取相关的统计数据,无法实时获取系统负载等指标,难以应用于生产环境下的性能压测。 总的来说,ab作为一款命令行测试工具,适用于本地对支持HTTP协议的单一地址进行性能压测,但缺少相应的链路编排、场景管理、数据可视化等大规模性能压测基础功能,无法应用于生产环境。 LoadRunner LoadRunner,是一款发布于1993年11月的预测系统行为和性能的负载测试工具。通过以模拟上千万用户实施并发负载及实时性能监测的方式来确认和查找问题,LoadRunner作为一款历史悠久的商业性能压测工具,能够对整个企业架构进行测试。企业使用LoadRunner能最大限度地缩短测试时间,优化性能和加速应用系统的发布周期。 LoadRunner可适用于各种体系架构的自动负载测试,能预测系统行为并评估系统性能。 LoadRunner从组件上可划分为四部分: 负载生成器:模拟用户对服务器发起请求虚拟用户生成器:捕捉用户业务流,用于录制和生成脚本控制器:用于提供场景设计与场景监控,能够实时监控脚本的运行情况分析器:汇集来自各种负载生成器的日志并格式化报告,以便可视化运行结果数据和监控数据从组件划分上可以看出 LoadRunner 对于性能压测拥有较为系统的支持,结合多个组件的功能特性,用户可以较为方便地设计复杂背景下的性能压测场景,例如结合场景设计设置虚拟用户数量、设置执行时间等,结合虚拟用户生成器实现复杂链路、场景的高效设计与编排。此外,LoadRunner支持设置思考时间、集合点,还可以结合分析器实现压测报告统计数据、指标的可视化,助力测试人员理解性能压测结果。但 LoadRunner 作为一款商业软件,价格较高,需要本地安装,安装过程较复杂,在实际设计执行压测时需要编写相应的脚本,对使用人员来说学习成本比较高,此外缺少监控告警等支持,性能压测过程中难以实时发现问题。 总的来说,LoadRunner 作为一款性能压测商业软件,功能较为齐全,使用者能够借助 LoadRunner 达到简单的性能压测场景编排、施压目标;但它也存在学习成本居高不下、扩展性差等缺点,此外支持的协议有限,不适合复杂的性能压测环境。 JMeter Apache JMeter是Apache组织开发的基于Java的压力测试工具。它可以用于测试静态和动态资源,例如静态文件、Java 小服务程序、CGI 脚本、Java 对象、数据库、FTP 服务器等等。另外,JMeter能够对应用程序做功能/回归测试,通过创建带有断言的脚本来验证你的程序返回了你期望的结果。为了最大限度的灵活性,JMeter允许使用正则表达式创建断言。同时JMeter支持对性能压测结果做图形分析。 JMeter 作为一款开源软件,扩展性强,具有强大的开源社区支持,社区内开发者活跃程度高,也正是在开源社区的积极发展下,JMeter 具有性能压测的诸多特性,如支持场景编排、断言设置,支持对多种资源施压,有图形化界面支持,支持脚本录制,使用人员能够较为简单的设计并发起性能压测,此外 JMeter 提供资源监控、性能压测报告生成等功能。但在需要高负载施压的场景下,JMeter 需要部署分布式环境,部署成本比较高,在使用时,需要编写相应的脚本,而每个脚本文件只能保存一个测试用例,学习门槛居高不下的同时也不利于脚本的维护,此外它缺少监控告警等支持,在性能压测过程中使用人员难以借助 JMeter 实时发现问题。 ...

July 2, 2019 · 1 min · jiezi

让开发部署提速-8-倍我参与贡献这款-IDE-插件的全过程

如何像参与开源那样,去参与一款 IDE 插件的设计? 作为一款 IDE 插件的使用者,我是否能决定下一个版本的功能? 自从产品经理银时小伙和他的开发小哥们在去年12月发布 Cloud Toolkit(一款 IDE 插件)以来,已帮助数以万计的开发者们提高了业务的部署效率。期间,开发者们不仅是 Cloud Toolkit 的使用者,同时也作为设计者参与了插件的更新迭代。 本文来自开发者徐靖峰,分享了他和 Cloud Toolkit 的故事 遇见 Cloud Toolkit在与中间件小姐姐的一次聊天中,偶然间了解到这款插件,小姐姐跟我提到自己正在运营一款 IDE 开发者工具,能够使开发部署效率提高 8 倍,出于好奇心,我就上手体验了一下,看看究竟是一个什么样的产品。使用了一段时间之后,便迫不及待地向小姐姐分享了我作为开发者对插件的一些看法。 我对这款产品最直观的感受:这是一款发布工具,帮助用户在 IDE 中直接打包应用并部署到各种终端。一开始看到这款产品位于阿里云的页面中,原本以为是一款和阿里云服务强绑定的产品,但试用过后才发现,即使对于普通的云主机,也非常适用,还可以解决很多开发运维的痛点,非阿里云用户可以放心使用。 在 Cloud Toolkit 出现之前作为一个 Java 程序员,我们大多数会在 Intellij IDEA 中基于 SpringBoot 来开发 WEB 应用,所以本文中的测评将会基于以下几个架构来构建: 开发环境:IDEA项目组织方式:Maven开发框架:SpringBoot在接触 Cloud Toolkit 之前,用什么方法来部署一个 SpringBoot 应用呢?作为一个偏正经的测评人员,我不会为了凸显出 Cloud Toolkit 的强大而去翻出一些上古的部署工具来做对比,而是直接使用 Intellij IDEA 的内置功能与之对比。 第一步:配置服务器信息 在 Tools -> Deployment 中找到 IDEA 对项目部署支持的内置插件,我们可以在其中进行服务器信息的配置,包括服务器地址和权限认证,并且在 Mapping 选项卡中完成本地工程与服务器路径的映射。 第二步:配置 Maven 打包插件<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins></build>由于是 SpringBoot 应用,配置专用的打包插件后,可以将整个工程打成一个 fatjar,示例工程非常简单: ...

June 28, 2019 · 3 min · jiezi

威胁快报挖矿团伙8220进化rootkit挖矿趋势兴起

近日,阿里云安全团队发现8220挖矿团伙为了更持久的驻留主机以获得最大收益,开始使用rootkit技术来进行自我隐藏。这类隐藏技术的使用在watchdogs等挖矿蠕虫使用后开始出现逐渐扩散和进化的趋势,此后预计主机侧的隐藏和对抗将成为主流。 背景阿里云安全团队蠕虫监控平台发现8220挖矿团伙的下载脚本出现更新,除了下载必要的驻留脚本、挖矿程序之外,新增加了一个 so文件的下载地址:http://107.174.47.156/1.so。 8220挖矿团伙是一个长期活跃的利用多个漏洞进行攻击和部署挖矿程序的国内团伙[1-2],该团伙组合利用WebLogic XMLDecoder 反序列化漏洞(CVE-2017-10271)、Drupal RCE(CVE-2018-7600)、JBoss 反序列化命令执行漏洞(CVE-2017-12149)等多个漏洞进行攻击并部署挖矿程序进行牟利。 通过对相关脚本和该so的简单分析,我们确认8220团伙已经在其攻击工具包使用ProcessHider[3]对自身进行隐藏。ProcessHider是被众多恶意软件广泛利用的rootkit。挖矿蠕虫利用该工具使管理员难以通过常规手段检测到挖矿进程,从而提高挖矿进程的存活时间以最大化挖矿收益。随着时间的推移,可能会有越来越多的挖矿蠕虫加入rootkit功能。 蠕虫检测如果在云控制台看到明显的CPU 上升,机器整体性能卡顿,但是登录机器使用 PS/TOP 等命令却无法定位到具体占用CPU的进程,此时该机器就很可能已经感染此类使用 rootkit 进行隐藏的挖矿蠕虫。 1.主机检测该蠕虫利用LD_PRELOAD机制,使得自身的so文件先于系统加载,并通过劫持 readdir函数,使依赖该 API 返回的PS/TOP等系统命令无法正确返回结果 。 因此用户使用这些受影响的系统命令将无法看到真实的进程。 如上图所示,该蠕虫隐藏的进程关键字是kworkerds。 在主机侧可以使用以下命令对蠕虫进行检测 查看动态链接库#ldd /bin/top|grep usr /usr/local/lib/libkk.so (0x00007f0f94026000)发现存在异常的so文件查看 PRE_LOAD系统变量#cat /etc/ld.so.preload /usr/local/lib/libkk.so发现preload文件被修改查看 crontab#crontab -l */30 * * * * (curl -s http://107.174.47.156/mr.sh||wget -q -O - http://107.174.47.156/mr.sh)|bash -sh发现crontab出现奇怪的定时任务使用普通top 查看到的进程: LD_PRELOAD依赖于动态链接过程,因此使用静态编译的busybox执行top命令,将可以看到真实的全部进程。 2.网络检测随着对抗的不断升级,挖矿蠕虫的自我隐藏功能必将不断升级,而面对全副武装的蠕虫病毒,普通用户在主机侧与其进行强对抗成本极高并且收益较低。针对该类蠕虫通过网络侧的NTA(Network Traffic Analysis)功能进行检测更加有效。因为无论攻击者在主机侧采用了何种隐藏手法,但其远程下载、C&C通信、矿池通信等行为均会在网络流量中留下痕迹。 下图是在网络侧云防火墙通过检测到主机感染后下载后门文件的行为发现该蠕虫的记录。 修复方案1.由于本地命令可能都已被劫持,因此首先下载静态编译的busybox来执行指令,保证执行的系统命令不受劫持影响。 下载二进制#wget https://www.busybox.net/downloads/binaries/1.27.1-i686/busybox赋予执行权限#chmod +x busybox2.清理动态劫持 ./busybox rm -f /usr/local/lib/libkk.so 2>/dev/null./busybox chattr -i /etc/ld.so.preload 2>/dev/null./busybox chattr -i /usr/local/lib/libkk.so 2>/dev/null./busybox rm -f /etc/ld.so.preload./busybox touch /etc/ld.so.preload./busybox chattr +i /etc/ld.so.preloadldconfig3.杀恶意进程和相关文件 ...

June 11, 2019 · 1 min · jiezi

后端相关技能七脚本

批处理基本语法# 关闭单行回显@# 关闭命令回显(从下一行开始)echo off# 注释:: # 注释rem# 暂停pause# 重定向输出>>>

April 27, 2019 · 1 min · jiezi

阿里新一代分布式任务调度平台Schedulerx20破土而出

1. 产品简介Schedulerx2.0是阿里中间件自研的基于Akka架构的新一代分布式任务调度平台,提供定时、任务编排、分布式跑批等功能。使用Schedulerx2.0,您可以在控制台配置管理您的定时任务,查询历史执行记录,查看运行日志。借助Schedulerx2.0,您还可以通过工作流进行任务编排和数据传递。Schedulerx2.0还提供了简单易用的分布式编程模型,简单几行代码就可以将海量数据分布式到多台机器上执行。 Schedulerx2.0提供了任务调度与执行的一整套解决方案,在阿里巴巴集团内部广泛使用并久经考验,具有高可靠、海量任务、秒级别调度等能力。 上线时间:2019-04-30 2. 背景Schedulerx2.0是Schedulerx1.0(DTS)的下一代产品,采用全新的架构,是全新自研的下一代分布式任务调度平台,不但解决了老产品的性能瓶颈,还提供了更多更快更强的能力。 更多:支持多种时间表达式,任务编排,支持更多的业务场景。单集群支持上千万任务,一天上十亿次调度,支持更多的任务数。更快:支持秒级别调度,处理准实时业务。更强:支持日志查询、原地重跑、重刷数据等多种操作,提供更强的运维能力和排错手段,解决为什么没跑,为什么失败,为什么跑得慢等问题。3. 功能3.1 强大的定时调度器3.1.1 Crontab 支持unix crontab表达式,不支持秒级别。 3.1.2 Fixed rate 众所周知,crontab必须被60整除,比如想每隔40分钟跑一次,cron不支持。Fixed rate专门用来做定期轮询,表达式简单,不支持秒级别。 3.1.3 Fixed delay 适合对实时性要求比较高的业务,比如每次执行完成隔10秒再跑,那么second delay非常适合你。并且second delay能支持到秒级别。 3.1.4 日历 支持多种日历,还可以自定义导入日历。比如金融业务需要在每个交易日执行。 3.1.5 时区 跨国的业务,需要在每个国家的时区定时执行某个任务。 3.2 任务编排支持工作流(DAG)进行任务编排,操作简单,前端直接单手操作拖拖拽拽即可。详细的任务状态图能一目了然看到下游任务为什么没跑。 3.3 任务类型支持多种任务类型,可以无限扩展。 java:可以跑在用户进程中,也可以上传jar包动态加载。shell:前端直接写shell脚本。python:前端直接写python脚本,需要机器有python环境。go:前端直接写go脚本,需要机器有go环境。自定义:用户甚至可以自定义任务类型,然后实现一个plugin就行了。3.4 执行方式&分布式编程模型3.4.1 执行方式 单机:随机挑选一台机器执行广播:所有机器同时执行且等待全部结束并行计算:map/mapreduce模型,1~300个子任务,有子任务列表。内存网格:map/mapreduce模型,10W以下子任务,无子任务列表,基于内存计算,比网格计算快。网格计算:map/mapreduce模型,100W以下子任务,无子任务列表,基于文件H2计算。3.4.2 分布式编程模型 Map模型:类似于hadoop mapreduce里的map。只要实现一个map方法,简单几行代码就可以将海量数据分布式到客户自己的多台机器上执行,进行跑批。MapReduce模型:MapReduce模型是Map模型的扩展,新增reduce接口,所有子任务完成后会执行reduce方法,可以在reduce方法中返回该任务实例的执行结果,或者回调业务。3.5 强大的运维能力数据大盘:控制台提供了执行记录大盘和执行列表,可以看到每个任务的执行历史,并提供操作。查看日志:每条执行记录,都可以详情中的日志页面实时看到日志。如果任务运行失败了,前端直接就能看到错误日志,非常方便。原地重跑:任务失败,修改完代码发布后,可以点击原地重跑。标记成功:任务失败,如果后台把数据处理正确了,重跑又需要好几个小时,直接标记成功就好了。Kill:实现JobProcessor的kill()接口,你就可以在前端kill正在运行的任务,甚至子任务。3.6 数据时间Schedulerx2.0可以处理有数据状态的任务。创建任务的时候可以填数据偏移。比如一个任务是每天00:30运行,但是实际上要处理上一天的数据,就可以向前偏移一个小时。运行时间不变,执行的时候通过context.getDataTime()获得的就是前一天23:30。 3.7 重刷数据既然任务具有了数据时间,一定少不了重刷数据。比如一个任务/工作流最终产生一个报表,但是业务发生变更(新增一个字段),或者发现上一个月的数据都有错误,那么就需要重刷过去一个月的数据。 通过重刷数据功能,可以重刷某些任务/工作流的数据(只支持天级别),每个实例都是不同的数据时间。 3.8 失败自动重试实例失败自动重试:在任务管理的高级配置中,可以配置实例失败重试次数和重试间隔,比如重试3次,每次间隔30秒。如果重试3次仍旧失败,该实例状态才会变为失败,并发送报警。子任务失败自动重试:如果是分布式任务(并行计算/内网网格/网格计算),子任务也支持失败自动重试和重试间隔,同样可以通过任务管理的高级配置进行配置。3.9 支持原生Spring之前的老产品Schedulerx1.0(DTS)和spring的结合非常暴力,对bean的命名有强要求,经常遇到注入失败的问题。Schedulerx2.0支持原生spring语法,接入更加的方便。 3.10 报警监控失败报警超时报警报警方式:短信 本文作者:黄晓萌阅读原文 本文为云栖社区原创内容,未经允许不得转载。

April 24, 2019 · 1 min · jiezi

用Node EJS写一个爬虫脚本每天定时给心爱的她发一封暖心邮件

本文首发于个人博客:Vince’Blog项目源码:NodeMail,欢迎star,说不定哪天脱单了就能用到了写在前面自从用邮箱注册了很多账号后,便会收到诸如以下类似的邮件,刚开始还以为是一张图片,后来仔细一看不是图片呀,好像还是HTML呀,于是好奇宝宝我Google一下,查阅多篇资料后总结出怎么用前端知识和Node做一个这样的“邮件网页”。确认主题知道怎么实现功能后,思考着我该写什么主题呢,用一个HTML模板随便给小伙伴们发个邮件炫个技?不行,作为一个很cool的程序员怎么能这么low呢,最近天气变化幅度大,温度捉摸不定,女朋友总是抱怨穿少了又冷穿多了又热,嗨呀,要不我就写个每天定时给宝宝发送天气预报的邮件,另外想起宝宝喜欢看ONE·一个这个APP上的每日更新,要不发天气预报的同时,再附赠一个“ONE的每日订阅”?机智又浪漫,开始搬砖~剧透本来是想最后放效果图的,怕你们看到一半就没兴趣了,就在前面剧透一下我最后做出来的效果图吧~待解决的问题1. 如何获取天气预报和ONE上的data?答:获取data有两种方法,第一种方法是获取天气预报和ONE的API,第二种是用node爬虫获取天气预报和ONE网页的信息。后来找了下,发现ONE并没有API接口,为了让两者统一,于是决定使用node上的一个插件叫cheerio,配合superagent能够很方便地爬取网页上的信息。2. 如何做出HTML的这种邮件?答:之前学过一段时间的express这个框架,接触到模版引擎这个概念,传入data便可获得html文件,再结合node的fs模块,获取到这个html文件,便可以结合node的邮件插件发送HTML邮件啦!3. 如何用node发送邮件?感谢无私的开源开发者,开发了一款发送邮件的Node插件nodemailer,兼容主流的Email厂商,只需要配置好邮箱账号和smtp授权码,便可以用你的邮箱账号在node脚本上发文件,很cool有没有~4. 如何做到每日定时发送?其实可以通过各种hack的方式写这么一个定时任务,但是既然node社区有这个定时的轮子,那我们直接用就好了,node-schedule是一个有着各种配置的定时任务发生器,可以定时每个月、每个礼拜、每天具体什么时候执行什么任务,这正符合每天早晨定时给宝宝发送邮件的需求。一切准备就绪,开始做一次浪漫的程序员编写代码网页爬虫这里我们使用到superagent和cheerio组合来实现爬虫:分析网页DOM结构,如下图所示:用superagent来获取指定网页的所有DOM:superagent.get(URL).end(function(err,res){ //}用cheerio来筛选superagent获取到的DOM,取出需要的DOMimgUrl:$(todayOne).find(’.fp-one-imagen’).attr(‘src’),type:$(todayOne).find(’.fp-one-imagen-footer’).text().replace(/(^\s*)|(\s*$)/g, “”),text:$(todayOne).find(’.fp-one-cita’).text().replace(/(^\s*)|(\s*$)/g, “")以下就是爬取ONE的代码,天气预报网页也是一个道理:const superagent = require(‘superagent’); //发送网络请求获取DOMconst cheerio = require(‘cheerio’); //能够像Jquery一样方便获取DOM节点const OneUrl = “http://wufazhuce.com/"; //ONE的web版网站superagent.get(OneUrl).end(function(err,res){ if(err){ console.log(err); } let $ = cheerio.load(res.text); let selectItem=$(’#carousel-one .carousel-inner .item’); let todayOne=selectItem[0]; //获取轮播图第一个页面,也就是当天更新的内容 let todayOneData={ //保存到一个json中 imgUrl:$(todayOne).find(’.fp-one-imagen’).attr(‘src’), type:$(todayOne).find(’.fp-one-imagen-footer’).text().replace(/(^\s*)|(\s*$)/g, “”), text:$(todayOne).find(’.fp-one-cita’).text().replace(/(^\s*)|(\s*$)/g, “”) }; console.log(todayOneData);})EJS模版引擎生成HTML通过爬虫获取到了数据,那么我们就能够通过将date输入到EJS渲染出HTML,我们在目录下创建js脚本和ejs模版文件:app.jsconst ejs = require(’ejs’); //ejs模版引擎const fs = require(‘fs’); //文件读写const path = require(‘path’); //路径配置//传给EJS的数据let data={ title:’nice to meet you~’}//将目录下的mail.ejs获取到,得到一个模版const template = ejs.compile(fs.readFileSync(path.resolve(__dirname, ‘mail.ejs’), ‘utf8’));//将数据传入模版中,生成HTMLconst html = template(data);console.log(html)mail.ejs<!DOCTYPE html><html lang=“en”><head> <meta charset=“UTF-8”> <meta name=“viewport” content=“width=device-width, initial-scale=1.0”> <meta http-equiv=“X-UA-Compatible” content=“ie=edge”> <title>Document</title></head><body> <h1> <%= title %> </h1></body></html>用Node发送邮件这里我们可以发送纯text也可以发送html,注意的是邮箱密码不是你登录邮箱的密码,而是smtp授权码,什么是smtp授权码呢?就是你的邮箱账号可以使用这个smtp授权码在别的地方发邮件,一般smtp授权码在邮箱官网的设置中可以看的到,设置如下注释。const nodemailer = require(’nodemailer’); //发送邮件的node插件let transporter = nodemailer.createTransport({ service: ‘126’, // 发送者的邮箱厂商,支持列表:https://nodemailer.com/smtp/well-known/ port: 465, // SMTP 端口 secureConnection: true, // SSL安全链接 auth: { //发送者的账户密码 user: ‘账户@126.com’, //账户 pass: ‘smtp授权码’, //smtp授权码,到邮箱设置下获取 } });let mailOptions = { from: ‘“发送者昵称” <地址@126.com>’, // 发送者昵称和地址 to: ’like@vince.studio’, // 接收者的邮箱地址 subject: ‘一封暖暖的小邮件’, // 邮件主题 text: ’test mail’, //邮件的text // html: html //也可以用html发送 }; //发送邮件transporter.sendMail(mailOptions, (error, info) => { if (error) { return console.log(error); } console.log(‘邮件发送成功 ID:’, info.messageId);}); Node定时执行任务这里我们用到了node-schedule来定时执行任务,示例如下:var schedule = require(“node-schedule”); //1. 确定的时间执行var date = new Date(2017,12,10,15,50,0); schedule.scheduleJob(date, function(){ console.log(“执行任务”);});//2. 秒为单位执行 //比如:每5秒执行一次var rule1 = new schedule.RecurrenceRule(); var times1 = [1,6,11,16,21,26,31,36,41,46,51,56]; rule1.second = times1; schedule.scheduleJob(rule1, function(){ console.log(“执行任务”); });//3.以分为单位执行//比如:每5分种执行一次var rule2 = new schedule.RecurrenceRule(); var times2 = [1,6,11,16,21,26,31,36,41,46,51,56]; rule2.minute = times2; schedule.scheduleJob(rule2, function(){ console.log(“执行任务”); }); //4.以天单位执行//比如:每天6点30分执行var rule = new schedule.RecurrenceRule();rule.dayOfWeek = [0, new schedule.Range(1, 6)];rule.hour = 6;rule.minute =30;var j = schedule.scheduleJob(rule, function(){ console.log(“执行任务”); getData();});思路与步骤当所有的问题都解决后,便是开始结合代码成一段完整的程序,思路很简单,我们来逐步分析:由于获取数据是异步的,并且不能判断出哪个先获取到数据,这个是可以将获取数据的函数封装成一个Promise对象,最后在一起用Promise.all来判断所有数据获取完毕,再发送邮件// 其中一个数据获取函数,其他的也是类似function getOneData(){ let p = new Promise(function(resolve,reject){ superagent.get(OneUrl).end(function(err, res) { if (err) { reject(err); } let $ = cheerio.load(res.text); let selectItem = $("#carousel-one .carousel-inner .item”); let todayOne = selectItem[0]; let todayOneData = { imgUrl: $(todayOne) .find(".fp-one-imagen”) .attr(“src”), type: $(todayOne) .find(".fp-one-imagen-footer") .text() .replace(/(^\s*)|(\s*$)/g, “”), text: $(todayOne) .find(".fp-one-cita") .text() .replace(/(^\s*)|(\s*$)/g, “”) }; resolve(todayOneData) }); }) return p}将爬取数据统一处理,作为EJS的参数,发送邮件模板。function getAllDataAndSendMail(){ let HtmlData = {}; // how long with let today = new Date(); let initDay = new Date(startDay); let lastDay = Math.floor((today - initDay) / 1000 / 60 / 60 / 24); let todaystr = today.getFullYear() + " / " + (today.getMonth() + 1) + " / " + today.getDate(); HtmlData[“lastDay”] = lastDay; HtmlData[“todaystr”] = todaystr; Promise.all([getOneData(),getWeatherTips(),getWeatherData()]).then( function(data){ HtmlData[“todayOneData”] = data[0]; HtmlData[“weatherTip”] = data[1]; HtmlData[“threeDaysData”] = data[2]; sendMail(HtmlData) } ).catch(function(err){ getAllDataAndSendMail() //再次获取 console.log(‘获取数据失败: ‘,err); })}发送邮件具体代码function sendMail(HtmlData) { const template = ejs.compile( fs.readFileSync(path.resolve(__dirname, “email.ejs”), “utf8”) ); const html = template(HtmlData); let transporter = nodemailer.createTransport({ service: EmianService, port: 465, secureConnection: true, auth: EamilAuth }); let mailOptions = { from: EmailFrom, to: EmailTo, subject: EmailSubject, html: html }; transporter.sendMail(mailOptions, (error, info={}) => { if (error) { console.log(error); sendMail(HtmlData); //再次发送 } console.log(“Message sent: %s”, info.messageId); }); }安装与使用如果你觉得这封邮件的内容适合你发送的对象,可以按照以下步骤,改少量参数即可运行程序;git clone https://github.com/Vincedream…打开main.js,修改配置项//纪念日let startDay = “2016/6/24”;//当地拼音,需要在下面的墨迹天气url确认const local = “xiangtan”;//发送者邮箱厂家let EmianService = “163”;//发送者邮箱账户SMTP授权码let EamilAuth = { user: “xxxxxx@163.com”, pass: “xxxxxx”};//发送者昵称与邮箱地址let EmailFrom = ‘“name” <xxxxxx@163.com>’;//接收者邮箱地let EmailTo = “like@vince.studio”;//邮件主题let EmailSubject = “一封暖暖的小邮件”;//每日发送时间let EmailHour = 6;let EmialMinminute= 30;终端输入npm install安装依赖,再输入node main.js,运行脚本,当然你的电脑不可能不休眠,建议你部署到你的云服务器上运行。最后冬天到了,是不是也该用程序员的专业知识给身边的人带来一些温暖呢,源代码与demo已经放到github上,要不试一试?GitHub:https://github.com/Vincedream/NodeMail ...

February 27, 2019 · 3 min · jiezi

Centos7下一键安装LNMP环境脚本

#!/bin/bashread -p ‘The installation process takes about 5 to 10 minutes, depending on the performance of your server. To confirm the installation of the lnmp environment, press the Enter key.‘function red(){ echo -e “\t\033[31m$1\033[0m”}function green(){ echo -e “\t\033[32m$1\033[0m”}yum clean all &> /dev/nullyum -y install epel-release &> /dev/nullwhile :do cat /etc/ld.so.conf | grep lib64 &> /dev/null if [ $? -ne 0 ];then echo ‘’’/usr/local/lib/usr/local/lib64/usr/lib/usr/lib64’’’ >> /etc/ld.so.conf ldconfig -v &> /dev/null fi libzip_stat=rpm -qa |grep libzip &amp;&gt; /dev/null &amp;&amp; echo 'yes' || echo 'no' mariadb_stat=rpm -qa |grep mariadb &amp;&gt; /dev/null &amp;&amp; echo 'yes' || echo 'no' yum_stat=yum repolist |tail -1 |awk '{print $2}' if [ $libzip_stat = yes ];then rpm -e –nodeps rpm -qa | grep libzip continue elif [ $mariadb_stat = yes ];then rpm -e –nodeps rpm -qa | grep mariadb continue elif [ $yum_stat = 0 ];then red ‘Yum error,Check your yum source!!!(maybe network,yum configure…)’ exit else echo -e “\033[05m\033[35mStart testing the environment…\033[0m” echo -e “\t\033[32mStart testing the environment… [ OK ]\033[0m” break fi donefunction need_rpm(){ yum -y install make yum -y install gmake yum -y install gcc yum -y install gcc-c++ yum -y install wget yum -y install openssl yum -y install openssl-devel yum -y install curl yum -y install curl-devel yum -y install libjpeg yum -y install libjpeg-devel yum -y install libpng yum -y install libpng-devel yum -y install freetype yum -y install freetype-devel yum -y install pcre yum -y install pcre-devel yum -y install libxslt yum -y install libxslt-devel yum -y install bzip2 yum -y install bzip2-devel yum -y install libxml2 yum -y install libxml2-devel}function down_app(){ wget -P /lnmp/ https://nginx.org/download/nginx-1.14.2.tar.gz &> /dev/null && echo -e “\t\033[32mNginx Download completed!\033[0m” || echo -e “\t\033[31mNginx Download Faild!\033[0m” wget -P /lnmp/ http://cn2.php.net/get/php-7.3.0.tar.gz/from/this/mirror &> /dev/null && echo -e “\t\033[32mPHP Download completed!\033[0m” || echo -e “\t\033[31mPHP Download Faild!\033[0m” wget -P /lnmp/ https://nih.at/libzip/libzip-1.2.0.tar.gz &> /dev/null && echo -e “\t\033[32mLibzip Download completed!\033[0m” || echo -e “\t\033[31mLibzip Download Faild!\033[0m” wget -P /lnmp/ https://dev.mysql.com/get/Downloads/MySQL-5.7/mysql-5.7.24-linux-glibc2.12-x86_64.tar.gz &> /dev/null && green ‘Mysql Download completed!’ || red ‘Mysql Download Faild!’}function nginx_install_configure(){ id nginx &> /dev/null && userdel nginx useradd -M -s /sbin/nologin nginx &>/dev/null cd /lnmp / && tar -xf nginx-1.14.2.tar.gz && cd nginx-1.14.2/ ./configure –user=nginx –group=nginx –prefix=/usr/local/nginx –with-http_ssl_module –with-stream &>> /tmp/.nginx.configure.log if [ $? -ne 0 ];then red ‘Nginx configure error!!! See /tmp/.nginx.configure.log’ exit else echo ’’’ Configuration summary: nginx path prefix: “/usr/local/nginx” nginx binary file: “/usr/local/nginx/sbin/nginx” nginx modules path: “/usr/local/nginx/modules” nginx configuration prefix: “/usr/local/nginx/conf” nginx configuration file: “/usr/local/nginx/conf/nginx.conf” nginx pid file: “/usr/local/nginx/logs/nginx.pid” nginx error log file: “/usr/local/nginx/logs/error.log” nginx http access log file: “/usr/local/nginx/logs/access.log” nginx http client request body temporary files: “client_body_temp” nginx http proxy temporary files: “proxy_temp” nginx http fastcgi temporary files: “fastcgi_temp” nginx http uwsgi temporary files: “uwsgi_temp” nginx http scgi temporary files: “scgi_temp” ’’’ fi make &>> /tmp/.nginx.make.log if [ $? -ne 0 ];then red ‘Nginx make error!!! See /tmp/.nginx.make.log’ fi make install &>> /tmp/.nginx.makeinstall.log if [ $? -ne 0 ];then red ‘Nginx make install error!!! See /tmp/.nginx.makeinstall.log’ fi cd /usr/local/nginx/ cp -p conf/nginx.conf conf/nginx.conf.bak sed -i ‘65,68s/#//g’ conf/nginx.conf sed -i ‘70,71s/#//g’ conf/nginx.conf sed -i ’s/fastcgi_params/fastcgi.conf/g’ conf/nginx.conf green ‘Nginx installed and configure successful.’ /usr/local/nginx/sbin/nginx -t &> /dev/null && /usr/local/nginx/sbin/nginx netstat -nutlp |grep 80 &> /dev/null if [ $? -ne 0 ];then red ‘Nginx start error!!!’ else green ‘Nginx started.’ fi}function mysql_install_configure(){ id mysql &> /dev/null && userdel mysql useradd -M -s /sbin/nologin mysql &> /dev/null cd /lnmp/ tar -xf mysql-5.7.24-linux-glibc2.12-x86_64.tar.gz mv mysql-5.7.24-linux-glibc2.12-x86_64 /usr/local/mysql cd /usr/local/mysql/ mkdir data && chown -R mysql:mysql /usr/local/mysql/ ./bin/mysqld –user=mysql –basedir=/usr/local/mysql –datadir=/usr/local/mysql/data –initialize &>> /tmp/.mysql.initialize.log if [ $? -ne 0 ];then red ‘Mysql initialize error!!! See /tmp/.mysql.initialize.log’ else pass=tail -1 /tmp/.mysql.initialize.log | awk '{print $NF}' green ‘Mysql initialize successful.)’ green “Initial password: $pass” echo ’’’ Configuration summary: Mysql path prefix: “/usr/local/mysql” Mysql data path: “/usr/local/mysql/data” ’’’ fi ./support-files/mysql.server start &>> /tmp/.mysql.start.log netstat -nutlp | grep mysql &> /dev/null if [ $? -ne 0 ];then red ‘Mysql start error!!! See /tmp/.mysql.start.log’ else green ‘Mysql started.’ fi}function libzip_install_configure(){ cd /lnmp/ tar -xf libzip-1.2.0.tar.gz cd libzip-1.2.0 ./configure &> /dev/null make &> /dev/null make install &> /dev/null cp /usr/local/lib/libzip/include/zipconf.h /usr/local/include/zipconf.h green ‘Libzip installed.’}function php_install_configure(){ cd /lnmp/ tar -xf mirror cd php-7.3.0/ while : do ./configure –prefix=/usr/local/php –with-curl –with-freetype-dir –with-gd –with-gettext –with-iconv-dir –with-kerberos –with-libdir=lib64 –with-libxml-dir –with-mysqli –with-openssl –with-pcre-regex –with-pdo-mysql –with-pdo-sqlite –with-pear –with-png-dir –with-jpeg-dir –with-xmlrpc –with-xsl –with-zlib –with-bz2 –with-mhash –enable-fpm –enable-bcmath –enable-libxml –enable-inline-optimization –enable-gd-native-ttf –enable-mbregex –enable-mbstring –enable-opcache –enable-pcntl –enable-shmop –enable-soap –enable-sockets –enable-sysvsem –enable-sysvshm –enable-xml –enable-zip &>> /tmp/.php.configure.log if [ $? -ne 0 ];then red ‘PHP configure error!!! See /tmp/.php.configure.log’ exit else green ‘PHP configure is ok’ while : do make &>> /tmp/.php.make.log if [ $? -ne 0 ];then red ‘PHP make error!!! See /tmp/.php.make.log’ exit else green ‘PHP make is ok’ while : do cp /usr/local/lib/libzip/include/zipconf.h /usr/local/include/zipconf.h make install &>> /tmp/.php.makeinstall.log if [ $? -ne 0 ];then red ‘PHP makeinstall error!!! See /tmp/.php.makeinstall.log’ exit else green ‘PHP Installed.’ break fi done break fi done break fi done cp php.ini-development /usr/local/php/lib/php.ini cp /usr/local/php/etc/php-fpm.conf.default /usr/local/php/etc/php-fpm.conf cp sapi/fpm/php-fpm /usr/local/bin sed -i ’s/; cgi.fix_pathinfo=1/ cgi.fix_pathinfo=0/g’ /usr/local/php/lib/php.ini groupadd www-data &> /dev/null useradd -g www-data www-data &> /dev/null cp /usr/local/php/etc/php-fpm.d/www.conf.default /usr/local/php/etc/php-fpm.d/www.conf sed -i ’s/user = nobody/user = www-data/g’ /usr/local/php/etc/php-fpm.d/www.conf sed -i ’s/group = nobody/group = www-data/g’ /usr/local/php/etc/php-fpm.d/www.conf /usr/local/bin/php-fpm netstat -nutlp |grep 9000 if [ $? -ne 0 ];then red ‘PHP-FPM start error!!!’ else green ‘PHP-FPM started’ fi}#start executeecho -e “\033[05m\033[35mInstall dependent software…\033[0m"need_rpm &> /dev/nullgreen “Install dependent software…\t\t[ OK ]” echo -e “\033[05m\033[35mStart download apps…\033[0m"down_appecho -e “\033[05m\033[35mStart install nginx and configure it…\033[0m"nginx_install_configureecho -e “\033[05m\033[35mStart install mysql and configure it…\033[0m"mysql_install_configureecho -e “\033[05m\033[35mStart install Libzip and configure it…\033[0m"libzip_install_configureecho -e “\033[05m\033[35mStart install php and configure it…\033[0m"php_install_configure/usr/local/nginx/sbin/nignx -s stop &> /dev/null && /usr/local/nginx/sbin/nginx echo ‘’’<?php phpinfo();?>’’’ >> /usr/local/nginx/html/test.phpcurl http://127.0.0.1/test.phpif [ $? -eq ‘0’];then green ‘LNMP is ok !!!’else red ‘Access PHP Faild!!!‘fi ...

February 13, 2019 · 5 min · jiezi

Bat脚本

在工作中,因为微服务结构,所以每次更新代码都需要重新启动/关闭多个服务,每次手动都比较麻烦,故写了一些bat脚本一键运行/关闭相关服务,以下记录一些相关的bat脚本命令bat命令tasklist 进程列表taskkill 杀死进程start cmd /k “cd D://GoPath && git pull …” 开启一个新的窗口运行命令

January 6, 2019 · 1 min · jiezi

基于深度学习模型Wide&Deep的推荐

本实验选用数据为UCI开源数据集,仅用于学习,请勿商用)Wide&Deep推荐算法出自一篇论文《Wide&Deep Learning for RecommenderSystems》,Wide&Deep由两部分组成,分别是Wide和Deep。先来说wide,表示的是generalized的推荐系统,传统的推荐系统都是通过线性算法基于离散特征来做推荐的。Wide推荐通常是这样的:系统通过获得用户的购物日志数据,包括用户点击哪些商品,购买过哪些商品,然后通过one-hot编码的方式构成离散特征或者通过对业务的理解衍生出一些特征,并进行计算,类似于本系列文章第二篇。这种wide推荐方式有非常多的好处,比如对于大规模的稀疏数据有很好的效果,而且模型的解释性很强。什么叫模型的解释性呢?以逻辑回归为例,每个特征都对应模型中的一个权重值,每个特征的权重值的大小跟这个特征对结果的影响是有关的。那么wide方式同样有很多缺点,比如我们一直强调的,特征衍生需要很多人为操作,需要专家经验,另外这种推荐只对用户操作过的商品有效。接着讲下deep,这里的deep表示的是通过深度学习学习出来的一些向量,这些向量是隐性特征,往往是没有明确可解释性的。这些向量也可以作为特征的一部分参与到训练中。通过deep方式产生的特征会有以下好处,其一可以拟补人为提取特征造成的人力思考维度的限制,试想下一个人可以轻易的思考出二阶乘法的结果,如果是五阶呢?其二这部分特征是深度学习框架自动生成的,无需人力干预。既然Wide和Deep算法各有千秋,那如果可以将两种算法作为组合,那么一定可以生成更有效的推荐场景的模型,本文就介绍如何在PAI-DSW上实现基于Wide&Deep的预测。一、业务场景描述本节使用的是PAI-DSW云端深度学习训练平台和PAI-EAS模型服务平台,使用的是一份开源的基于人的各种背景的统计数据,解决的问题是基于人的各种基础数据预测每个人收入是否会超过50K。本实验的全部代码和数据已经内置于PAI-DSW,只要打开DSW就可以安装下方的教程运行实验。二、数据集介绍数据源:引用UCI开源数据源,https://archive.ics.uci.edu/ml/datasets/Census+Income具体特征字段如下:字段名含义类型描述age对象年龄double对象的年龄大小workclass工作性质string自由职业者、私企、企业人员、政府工作者、无业游民等fnlwgt连续数据double-education学历string学士、说是、博士、11th、10th、1s-4th等等education-num教育年限double教育年限marital-status婚姻状况string单身、未婚、离异等等occupation职业string工程师、农民、销售等等relatonship家庭角色string妻子、父亲、没家庭等等race人种string亚裔、白人、黑人等等sex性别string女性、男性capital-gain连续数据double-capital-loss连续数据double-hours-per-week连续数据double-native-country祖籍国家string美国、哥伦比亚、英格兰、加拿大等等目标字段:income是否超过50k三、数据探索流程首先进入PAI-DSW,找到左侧的Demo文件夹,下载Wide&Deep数据集及代码包。(1)工程描述首先看下整个工程,包含一个census_data文件夹,里面包含一个训练数据和一个测试数据official文件夹是一个工具包census_main.py为训练脚本(2)训练模型打开一个terminal环境,执行python census_main.py –export_dir wide_deep_saved_modelwide_deep_saved_model为输出模型所在的文件夹,训练完在文件目录下会找到相应文件,打开后可以看到checkpoint:把这个checkpoint的号记住。(3)模型预测现在已经生成了模型的checkpoint输出,接下来进入terminal,运行以下脚本:saved_model_cli run –dir wide_deep_saved_model/${模型checkpoint号码}/ –tag_set serve –signature_def=“predict” –input_examples=’${预测数据}‘根据本文的案例可以执行以下脚本拿到预测结果:saved_model_cli run –dir wide_deep_saved_model/1542168326/ –tag_set serve –signature_def=“predict” –input_examples=‘examples=[{“age”:[46.], “education_num”:[10.], “capital_gain”:[7688.], “capital_loss”:[0.], “hours_per_week”:[38.]}, {“age”:[24.], “education_num”:[13.], “capital_gain”:[0.], “capital_loss”:[0.], “hours_per_week”:[50.]}]‘输入了两条预测数据,最终拿到预测结果:输入了两条预测数据,可以得到预测输出,第一条预测结果为1,第二条结果为0,可以通过output key probabilities判断(注:矩阵第一行对应第一个预测结果,第二列0.9599956>第一列0.04000434,所以第一个预测结果是1。同理第二个预测结果是0)。可以通过代码official/wide_deep/census_dataset.py来看具体的特征工程的特征和目标值的构建,目标列>50k时目标值为1,目标列<50k时目标值为0。于是预测结果第一条的人的预测收入为>50k,预测结果第二条的人的预测收入<50k。(4)模型在线部署生成的模型是Tensorflow的标准模型格式,可以通过PAI-EAS将模型部署成Http服务供调用。后续流程可以参考在线预测文档:https://help.aliyun.com/document_detail/92917.html部署成在线服务之后,这样就可以做到模型跟用户自身的业务结合,完成PAI模型训练和业务应用的打通。本文作者:傲海阅读原文本文为云栖社区原创内容,未经允许不得转载。

December 14, 2018 · 1 min · jiezi

JSON数据从OSS迁移到MaxCompute最佳实践

本文为您介绍如何利用DataWorks数据集成将JSON数据从OSS迁移到MaxCompute,并使用MaxCompute内置字符串函数GET_JSON_OBJECT提取JSON信息。数据上传OSS将您的JSON文件重命名后缀为TXT文件,并上传到OSS。本文中使用的JSON文件示例如下。{ “store”: { “book”: [ { “category”: “reference”, “author”: “Nigel Rees”, “title”: “Sayings of the Century”, “price”: 8.95 }, { “category”: “fiction”, “author”: “Evelyn Waugh”, “title”: “Sword of Honour”, “price”: 12.99 }, { “category”: “fiction”, “author”: “J. R. R. Tolkien”, “title”: “The Lord of the Rings”, “isbn”: “0-395-19395-8”, “price”: 22.99 } ], “bicycle”: { “color”: “red”, “price”: 19.95 } }, “expensive”: 10}将applog.txt文件上传到OSS,本文中OSS Bucket位于华东2区。 使用DataWorks导入数据到MaxCompute新增OSS数据源进入DataWorks数据集成控制台,新增OSS类型数据源。 具体参数如下所示,测试数据源连通性通过即可点击完成。Endpoint地址请参见OSS各区域的外网、内网地址,本例中为http://oss-cn-shanghai.aliyun… http://oss-cn-shanghai-internal.aliyuncs.com(由于本文中OSS和DataWorks项目处于同一个region中,本文选用后者,通过内网连接)。 新建数据同步任务在DataWorks上新建数据同步类型节点。 新建的同时,在DataWorks新建一个建表任务,用于存放JSON数据,本例中新建表名为mqdata。 表参数可以通过图形化界面完成。本例中mqdata表仅有一列,类型为string,列名为MQ data。 完成上述新建后,您可以在图形化界面配置数据同步任务参数,如下图所示。选择目标数据源名称为odps_first,选择目标表为刚建立的mqdata。数据来源类型为OSS,Object前缀可填写文件路径及名称。列分隔符使用TXT文件中不存在的字符即可,本文中使用 ^(对于OSS中的TXT格式数据源,Dataworks支持多字符分隔符,所以您可以使用例如 %&%#^$$^%这样很难出现的字符作为列分隔符,保证分割为一列)。 映射方式选择默认的同行映射即可。 点击左上方的切换脚本按钮,切换为脚本模式。修改fileFormat参数为: “fileFormat”:“binary”。该步骤可以保证OSS中的JSON文件同步到MaxCompute之后存在同一行数据中,即为一个字段。其他参数保持不变,脚本模式代码示例如下。{ “type”: “job”, “steps”: [ { “stepType”: “oss”, “parameter”: { “fieldDelimiterOrigin”: “^”, “nullFormat”: “”, “compress”: “”, “datasource”: “OSS_userlog”, “column”: [ { “name”: 0, “type”: “string”, “index”: 0 } ], “skipHeader”: “false”, “encoding”: “UTF-8”, “fieldDelimiter”: “^”, “fileFormat”: “binary”, “object”: [ “applog.txt” ] }, “name”: “Reader”, “category”: “reader” }, { “stepType”: “odps”, “parameter”: { “partition”: “”, “isCompress”: false, “truncate”: true, “datasource”: “odps_first”, “column”: [ “mqdata” ], “emptyAsNull”: false, “table”: “mqdata” }, “name”: “Writer”, “category”: “writer” } ], “version”: “2.0”, “order”: { “hops”: [ { “from”: “Reader”, “to”: “Writer” } ] }, “setting”: { “errorLimit”: { “record”: "" }, “speed”: { “concurrent”: 2, “throttle”: false, “dmu”: 1 } }}完成上述配置后,点击运行接即可。运行成功日志示例如下所示。 获取JSON字段信息在您的业务流程中新建一个ODPS SQL节点。 您可以首先输入 SELECT*from mqdata;语句,查看当前mqdata表中数据。当然这一步及后续步骤,您也可以直接在MaxCompute客户端中输入命令运行。 确认导入表中的数据结果无误后,您可以使用MaxCompute内建字符串函数GET_JSON_OBJECT获取您想要的JSON数据。本例中使用 SELECT GET_JSON_OBJECT(mqdata.MQdata,’$.expensive’) FROM mqdata;获取JSON文件中的 expensive值。如下图所示,可以看到已成功获取数据。 本文作者:付帅阅读原文本文为云栖社区原创内容,未经允许不得转载。 ...

November 15, 2018 · 1 min · jiezi