首页
留言
友情链接
壁纸
更多
关于
Search
1
dockerfile怎么执行shell脚本(利用tail -f /dev/null命令防止container启动后退出)
4,930 阅读
2
channel常见的异常总结
4,256 阅读
3
支付宝支付配置开发流程
1,400 阅读
4
HTTP 协议中的Content-Encoding
1,246 阅读
5
Laravel底层原理(二) —— 契约(Contracts)
938 阅读
PHP
composer
laravel
swoole
Docker
Linux
Go
随笔
mysql
nginx
Search
标签搜索
gopher
Docker
PHP
dockerfile
通道
go
defer
alipay
支付
git
phpstorm
IDEA
web安全
漏洞
socket
Royal
累计撰写
35
篇文章
累计收到
0
条评论
首页
栏目
PHP
composer
laravel
swoole
Docker
Linux
Go
随笔
mysql
nginx
页面
留言
友情链接
壁纸
关于
搜索到
35
篇与
Royal
的结果
2023-07-07
FFI扩展调用C的so动态库
什么是FFI? 此扩展允许在纯PHP中加载共享库(.DLL或.so),调用C函数以及访问C数据结构,而无需深入了解Zend扩展API。这个扩展需要安装 libffi libffi-devel库,编译安装步骤具体步骤就不说了(phpize、configure、make、make install四部曲),另外此扩展要求PHP版本必须是(PHP 7 >= 7.4.0, PHP 8),安装完通过php -m就可以看到下面的模块。FFI的API都有哪些?FFI::addr— 创建指向C数据的非托管指针FFI::alignof— 获取对齐方式FFI::arrayType— 动态构造一个新的C数组类型FFI::cast— 执行C类型转换FFI::cdef— 创建一个新的FFI对象FFI::free— 释放非托管数据结构FFI::isNull— 检查FFI \ CData是否为空指针FFI::load— 从C头文件加载C声明FFI::memcmp— 比较内存区域FFI::memcpy— 将一个存储区复制到另一个FFI::memset— 填充内存区域FFI::new— 创建一个C数据结构FFI::scope— 在预加载期间使用解析的C声明实例化FFI对象FFI::sizeof— 获取C数据或类型的大小FFI::string— 从内存区域创建一个PHP字符串FFI::type— 从C声明创建FFI \ CType对象FFI::typeof— 获取FFI \ CData的FFI \ CTypeFFI怎么用? 直接上例子,下面就是通过一个实例来说明FFI怎么调用C的动态库函数。首先通过FFI::cdef创建一个FFI对象,定义我们动态库中的常量、结构体、函数、以及通过地址加载我们的动态库。 define('ECCref_MAX_BITS', 512); define('ECCref_MAX_LEN', (ECCref_MAX_BITS + 7) / 8); define("SGD_SM3", 0x00000001); $this->ffi = FFI::cdef(<<<EOH // 定义结构体 typedef struct ECCrefPublicKey_st{ unsigned int bits; unsigned char *x; unsigned char *y; } ECCrefPublicKey; // 定义打开设备函数 int SDF_OpenDevice(void **phDeviceHandle); // 定义创建会话句柄函数 int SDF_OpenSession(void *hDeviceHandle, void **phSessionHandle); // 定义生成随机数函数 int SDF_GenerateRandom(void *hSessionHandle, unsigned int uiLength, unsigned char *pucRandom); // 定义杂凑运算初始化函数 int SDF_HashInit(void *hSessionHandle, unsigned int uiAlgID, ECCrefPublicKey *pucPublicKey, unsigned char *pucID, unsigned int uiIDLength); EOH , '/home/sangshuaidong/sdk/libcsapi.so');#加载动态库然后,我们在方法中进行调用c函数。public function openDevice() { $deviceHandlePtr = FFI::new("void*[1]"); $result = $this->ffi->SDF_OpenDevice($deviceHandlePtr); if ($result == 0) { $this->deviceHandle = $deviceHandlePtr[0]; return true; } else { return false; } } public function openSession() { $sessionHandlePtr = FFI::new("void*[1]"); $result = $this->ffi->SDF_OpenSession($this->deviceHandle, FFI::addr($sessionHandlePtr)); if ($result == 0) { $this->sessionHandle = $sessionHandlePtr[0]; return true; } else { return false; } } // 获取16位随机数 public function generateRandom($length) { $randomBuffer = FFI::new("unsigned char[$length]"); $result = $this->ffi->SDF_GenerateRandom($this->sessionHandle, $length, $randomBuffer); if ($result == 0) { $randomHex = bin2hex(FFI::string($randomBuffer, $length)); return $randomHex; } else { return false; } } // hash杂凑运算初始化 public function hashInit($algID = "SGD_SM3", $length = 0) { $result = 0; if ($algID === 'SGD_SM3') { $publicKey =$this->ffi->new("ECCrefPublicKey"); $publicKey->bits = 0; $unsignedCharType = FFI::arrayType(FFI::type('unsigned char'), [ECCref_MAX_LEN]); $publicKey->x = FFI::cast('unsigned char*', FFI::new($unsignedCharType)); $publicKey->y = FFI::cast('unsigned char*', FFI::new($unsignedCharType)); $result = $this->ffi->SDF_HashInit( $this->sessionHandle, SGD_SM3, FFI::addr($publicKey), FFI::addr(FFI::new("unsigned char")), $length ); } else { // 其他算法的处理,根据实际情况进行设置 } if ($result == 0) { return true; } else { return false; } } 最后,我们通过执行可执行程序可以看到成功输出!
2023年07月07日
233 阅读
0 评论
1 点赞
2023-06-21
线上服务怎么部署golang应用程序
编译我们的golang项目并生成可执行程序(二进制文件)go build main.go我们需要考虑将应用程序作为系统服务运行。为此,我们需要编写一个systemd服务文件。在/etc/systemd/system/目录中,使用vim或nano等编辑器创建一个新文件,文件名称为devcloud.service,当然您也可以为其取其他的名称在devcloud.service文件中,添加以下内容:[Unit] Description=devcloud After=syslog.target After=network.target [Service] # Modify these two values and uncomment them if you have # repos with lots of files and get an HTTP error 500 because # of that ### #LimitMEMLOCK=infinity LimitNOFILE=100000 LimitNPROC=100000 Type=forking # 如果要启动的命令是一个daemon进程, 这里的值设置为forking User=root Group=root # WorkingDirectory,程序的运行目录 WorkingDirectory=/home/goproject/src # ExecStart,程序的启动命令,必须为绝对路径,若WorkingDirectory有变动,ExecStart路径对应也要变动 ExecStart=/home/goproject/src/main Restart=always ExecStop=/bin/kill -9 $(pidof main) # Some distributions may not support these hardening directives. If you cannot start the service due # to an unknown option, comment out the ones not supported by your version of systemd. # ProtectSystem=full # PrivateDevices=yes # PrivateTmp=yes # NoNewPrivileges=true [Install] WantedBy=multi-user.target注释:常见的Type类型有以下这些:在Systemd配置文件中,常见的Type选项的取值有以下几种类型:simple:默认类型。当该服务以Type=simple启动时,Systemd会认为启动命令是简单的,不会进一步检查进程的状态。Systemd仅仅启动命令,并认为启动完成后服务已经就绪。forking:当服务启动时,Systemd会认为启动命令会派生(fork)出一个子进程,并认为服务在子进程就绪后才完成启动。此类型的服务会在主进程退出后仍然继续运行。oneshot:适用于只执行一次任务的服务。当服务启动时,Systemd会等待命令执行完成,然后认为服务已经完成启动。dbus:适用于通过DBus启动的服务。notify:适用于服务启动完成后向Systemd发送通知的情况。idle:适用于服务在空闲状态时启动的情况。exec:仅执行一次命令,不会认为服务在命令执行完成后就绪。以上是常见的Type选项的取值类型。根据您的需求和服务的特性,选择适合的Type选项来定义服务的启动类型。保存文件后,使用以下命令启动新的系统服务并将其添加到自动启动中:systemctl start devcloud systemctl enable devcloud执行以上命令后,您的服务应该已经在Linux服务器上成功运行并自动启动,可以使用以下命令查看服务启动状态:systemctl status devcloud重启/停止服务systemctl restart devcloud systemctl stop devcloud
2023年06月21日
203 阅读
0 评论
0 点赞
2022-10-11
SSH通过免密登录实现程序远程批量部署
需求最近在部署程序的时候发现,一共三个语言,6台设备,每台设备3个模块,每个模块3种语言,总共6台设备下来要部署54个程序,工作重复繁琐不说,很容易出现漏部署或者错误,想到了使用一键脚本部署程序的方式,把程序的tar包和shell脚本分别通过统一入口放置,在入口处执行脚本,分别把tar包和执行脚本远程发送到对应的机器和对应的模块上去执行,这样就不用一个一个模块上传程序,再操作,因此想到了一种方式ssh免密登录的方式,通过一台机器去连接到其他所有要部署的机器上,把程序传输过去,前提是这些机器必须是在同一网段内,如果是在一个交换机下面,必须是在同一vlan下面。好了废话不多说,接下去第一步实现免密登录。实现免密登录免密登录很简单,网上一搜很容易就能知道怎么做,我们看下过程:客户端生成公私钥此客户端就是我们要统一放置程序的地方,需要部署的机器我们称之为服务端,本地客户端生成公私钥:(一路回车默认即可)ssh-keygen上面这个命令会在用户目录.ssh文件夹下创建公私钥cd ~/.ssh ls上传公钥到服务端我们随机选择一台机器作为服务端,然后把第一步生成的公钥复制一份传到服务端,然后把公钥内容写入到authorized_keys文件内cat id_rsa.pub >authorized_keys可以看到客户端写入到服务器的 id_rsa.pub (公钥)内容。测试下免密登录ssh root@10.21.4.155第一次需要输入yes才能访问!可以看到我们的免密登录功能做好了,剩下的就是怎么把我们的程序,脚本发送过去,批量执行的问题了。实现一键部署我们做了一个ci.sh的一键部署脚本,所有的程序tar包和部署脚本放在了deploy目录,执行ci.sh就可以了#!/bin/bash set -e ####################################################################### # #4、#5、#6的相应模块的调试口ip left2="10.21.4.121" left2_2="10.21.4.122" right2="10.21.4.124" schedule2="10.21.4.123" executor2_5="10.21.4.125" executor2_6="10.21.4.126" executor2_7="10.21.4.127" left3="10.21.4.131" left3_2="10.21.4.132" right3="10.21.4.134" schedule3="10.21.4.133" executor3_5="10.21.4.135" executor3_6="10.21.4.136" executor3_7="10.21.4.137" # 部署的时候指定执行体被复制到板卡的哪个目录 function help_info() { echo "help" } ####################################################################### script_dir=$(cd "$(dirname "$0")"; pwd) script_name=$(basename $0) script_path=$script_dir/$script_name real_script_path=$(readlink -f `which $script_path`) real_script_dir=$(dirname $real_script_path) image_dir=$real_script_dir/deploy wip="30.30.30.23" bip="192.168.1.46" machine_num=0 ip="0.0.0.0" isw=0 isb=0 isdeploy=0 com=0 lange=0 short_opts="b:w:n:i:dc:l:h" ARGS=`getopt -o ${short_opts} -n 'switch.sh' -- "$@"` if [[ $? != 0 ]]; then echo "Terminating..." exit 1 fi #将规范化后的命令行参数分配至位置参数($1,$2,...) eval set -- "${ARGS}" while true do case "$1" in -w) isw=1 wip=$2 shift 2 ;; -b) isb=1 bip=$2 shift 2 ;; -n) machine_num=$2 shift 2 ;; -d) isdeploy=1 shift 1 ;; -c) com=$2 #echo $com shift 2 ;; -l) lange=$2 #echo $lange shift 2 ;; -h) help_info shift 1 ;; --) module=$2 shift 2 break ;; *) echo "Internal error!" exit 1 ;; esac done ############################################################ #处理剩余的无法识别的参数 unknown_arg=$@ for arg in $unknown_arg do echo "unknown argument $arg" done if [[ ${#unknown_arg} -ne 0 ]];then exit 127 fi ############################################################ function deploy() { #将命令行指定的密码机号和模块号映射成具体的模块ip,存放在executor变量中 local executor=$(eval echo \$executor$1_$2) local language=$3 #echo $executor if [[ $language = "java" ]];then ssh -t root@$executor "systemctl stop scheduleClient" if [[ $2 -eq 5 ]];then scp -r $image_dir/java/deploy-java-com$2.sh root@$executor:/home/ scp -r $image_dir/java/de_ht_nt.tar root@$executor:/home/ ssh -t root@$executor "cd /home/ && chmod u+x deploy-java-com$2.sh && ./deploy-java-com$2.sh" elif [[ $2 -eq 6 ]];then scp -r $image_dir/java/deploy-java-com$2.sh root@$executor:/home/ scp -r $image_dir/java/ub_ng_nt.tar root@$executor:/home/ ssh -t root@$executor "cd /home/ && chmod u+x deploy-java-com$2.sh && ./deploy-java-com$2.sh" elif [[ $2 -eq 7 ]];then scp -r $image_dir/java/deploy-java-com$2.sh root@$executor:/home/ scp -r $image_dir/java/ce_ht_nt.tar root@$executor:/home/ ssh -t root@$executor "cd /home/ && chmod u+x deploy-java-com$2.sh && ./deploy-java-com$2.sh" fi ssh -t root@$executor "systemctl start scheduleClient" elif [[ $language = "php" ]];then ssh -t root@$executor "systemctl stop scheduleClient" if [[ $2 -eq 5 ]];then scp -r $image_dir/php/deploy-php-com$2.sh root@$executor:/home/ scp -r $image_dir/php/centos_apache_ci.tar root@$executor:/home/ ssh -t root@$executor "cd /home/ && chmod u+x deploy-php-com$2.sh && ./deploy-php-com$2.sh" elif [[ $2 -eq 6 ]];then scp -r $image_dir/php/deploy-php-com$2.sh root@$executor:/home/ scp -r $image_dir/php/debian_nginx_ci.tar root@$executor:/home/ ssh -t root@$executor "cd /home/ && chmod u+x deploy-php-com$2.sh && ./deploy-php-com$2.sh" elif [[ $2 -eq 7 ]];then scp -r $image_dir/php/deploy-php-com$2.sh root@$executor:/home/ scp -r $image_dir/php/ubuntu_apache_ci.tar root@$executor:/home/ ssh -t root@$executor "cd /home/ && chmod u+x deploy-php-com$2.sh && ./deploy-php-com$2.sh" fi ssh -t root@$executor "systemctl start scheduleClient" elif [[ $language == "python" ]];then ssh -t root@$executor "systemctl stop scheduleClient" if [[ $2 -eq 5 ]];then scp -r $image_dir/python/deploy-python-com$2.sh root@$executor:/home/ scp -r $image_dir/python/cm_un_env_v1.tar root@$executor:/home/ ssh -t root@$executor "cd /home/ && chmod u+x deploy-python-com$2.sh && ./deploy-python-com$2.sh" elif [[ $2 -eq 6 ]];then scp -r $image_dir/python/deploy-python-com$2.sh root@$executor:/home/ scp -r $image_dir/python/cm_ca_env_v1.tar root@$executor:/home/ ssh -t root@$executor "cd /home/ && chmod u+x deploy-python-com$2.sh && ./deploy-python-com$2.sh" elif [[ $2 -eq 7 ]];then scp -r $image_dir/python/deploy-python-com$2.sh root@$executor:/home/ scp -r $image_dir/python/cm_dn_env_v1.tar root@$executor:/home/ ssh -t root@$executor "cd /home/ && chmod u+x deploy-python-com$2.sh && ./deploy-python-com$2.sh" fi ssh -t root@$executor "systemctl start scheduleClient" elif [[ $language -eq 0 ]];then ssh -t root@$executor "systemctl stop scheduleClient" #部署php if [[ $2 -eq 5 ]];then scp -r $image_dir/php/deploy-php-com$2.sh root@$executor:/home/ scp -r $image_dir/php/centos_apache_ci.tar root@$executor:/home/ ssh -t root@$executor "cd /home/ && chmod u+x deploy-php-com$2.sh && ./deploy-php-com$2.sh" elif [[ $2 -eq 6 ]];then scp -r $image_dir/php/deploy-php-com$2.sh root@$executor:/home/ scp -r $image_dir/php/debian_nginx_ci.tar root@$executor:/home/ ssh -t root@$executor "cd /home/ && chmod u+x deploy-php-com$2.sh && ./deploy-php-com$2.sh" elif [[ $2 -eq 7 ]];then scp -r $image_dir/php/deploy-php-com$2.sh root@$executor:/home/ scp -r $image_dir/php/ubuntu_apache_ci.tar root@$executor:/home/ ssh -t root@$executor "cd /home/ && chmod u+x deploy-php-com$2.sh && ./deploy-php-com$2.sh" fi #部署python if [[ $2 -eq 5 ]];then scp -r $image_dir/python/deploy-python-com$2.sh root@$executor:/home/ scp -r $image_dir/python/cm_un_env_v1.tar root@$executor:/home/ ssh -t root@$executor "cd /home/ && chmod u+x deploy-python-com$2.sh && ./deploy-python-com$2.sh" elif [[ $2 -eq 6 ]];then scp -r $image_dir/python/deploy-python-com$2.sh root@$executor:/home/ scp -r $image_dir/python/cm_ca_env_v1.tar root@$executor:/home/ ssh -t root@$executor "cd /home/ && chmod u+x deploy-python-com$2.sh && ./deploy-python-com$2.sh" elif [[ $2 -eq 7 ]];then scp -r $image_dir/python/deploy-python-com$2.sh root@$executor:/home/ scp -r $image_dir/python/cm_dn_env_v1.tar root@$executor:/home/ ssh -t root@$executor "cd /home/ && chmod u+x deploy-python-com$2.sh && ./deploy-python-com$2.sh" fi #部署java if [[ $2 -eq 5 ]];then scp -r $image_dir/java/deploy-java-com$2.sh root@$executor:/home/ scp -r $image_dir/java/de_ht_nt.tar root@$executor:/home/ ssh -t root@$executor "cd /home/ && chmod u+x deploy-java-com$2.sh && ./deploy-java-com$2.sh" elif [[ $2 -eq 6 ]];then scp -r $image_dir/java/deploy-java-com$2.sh root@$executor:/home/ scp -r $image_dir/java/ub_ng_nt.tar root@$executor:/home/ ssh -t root@$executor "cd /home/ && chmod u+x deploy-java-com$2.sh && ./deploy-java-com$2.sh" elif [[ $2 -eq 7 ]];then scp -r $image_dir/java/deploy-java-com$2.sh root@$executor:/home/ scp -r $image_dir/java/ce_ht_nt.tar root@$executor:/home/ ssh -t root@$executor "cd /home/ && chmod u+x deploy-java-com$2.sh && ./deploy-java-com$2.sh" fi ssh -t root@$executor "systemctl start scheduleClient" fi } #machine_num、com、lange function exe() { local mchi_num=$1 local com_num=$2 local lang=$3 local all_mchi_num="2 3" local mchi_isexist=`expr index "$all_mchi_num" $mchi_num` #isexit为0,不存在,非0存在 local all_com_num="5 6 7" local com_isexist=`expr index "$all_com_num" "$com_num"` if [[ $mchi_num == "all" ]];then for i in $all_mchi_num do for((j=5;j<=7;j++)) do deploy $i $j 0 #echo $i $j done done elif [[ $mchi_isexist -ne 0 && $com_isexist -ne 0 ]];then deploy $mchi_num $com_num $lange #echo $mchi_num $com_num $lange elif [[ $mchi_isexist -ne 0 && $com_num -eq 0 ]];then #echo $mchi_num $com_num $lange deploy $mchi_num 5 $lange deploy $mchi_num 6 $lange deploy $mchi_num 7 $lange fi } function left() { echo "function left" } function sche() { echo "function sche" } function cmn() { local mchi_num=$1 local com_num=$2 local executor=$(eval echo \$executor$1_$2) local left=$(eval echo \$left$1) local left2=$(eval echo \$left$1_2) local schedule=$(eval echo \$schedule$1) local right=$(eval echo \$right$1) local all_mchi_num="2 3" local mchi_num_isexist=`expr index "$all_mchi_num" $mchi_num` #isexit为0,不存在,非0存在 if [[ $mchi_num_isexist -eq 0 ]];then echo "nothing done!!!" exit 127 fi if [[ $com_num -eq 1 ]];then scp -r $image_dir/cmnmod/web_left.tar root@$left:/home/ scp -r $image_dir/cmnmod/web_left.sh root@$left:/home/ ssh -t root@$left "cd /home/ && chmod a+x web_left.sh && ./web_left.sh" elif [[ $com_num -eq 2 ]];then scp -r $image_dir/cmnmod/csm_left.tar root@$left2:/home/ scp -r $image_dir/cmnmod/csm_left.sh root@$left2:/home/ ssh -t root@$left2 "cd /home/ && chmod a+x csm_left.sh && ./csm_left.sh" elif [[ $com_num -eq 3 ]];then scp -r $image_dir/cmnmod/dispatch.tar root@$schedule:/home/ scp -r $image_dir/cmnmod/dispatch.sh root@$schedule:/home/ ssh -t root@$schedule "cd /home/ && chmod a+x dispatch.sh && ./dispatch.sh" elif [[ $com_num -eq 4 ]];then scp -r $image_dir/cmnmod/right.tar root@$right:/home/ scp -r $image_dir/cmnmod/right.sh root@$right:/home/ ssh -t root@$right "cd /home/ && chmod a+x right.sh && ./right.sh" elif [[ $com_num -eq 5 ]];then scp -r $image_dir/cmnmod/executor1.sh root@$executor:/home/ scp -r $image_dir/cmnmod/executor1.tar root@$executor:/home/ ssh -t root@$executor "cd /home/ && chmod a+x executor1.sh && ./executor1.sh" elif [[ $com_num -eq 6 ]];then scp -r $image_dir/cmnmod/executor2.sh root@$executor:/home/ scp -r $image_dir/cmnmod/executor2.tar root@$executor:/home/ ssh -t root@$executor "cd /home/ && chmod a+x executor2.sh && ./executor2.sh" elif [[ $com_num -eq 7 ]];then scp -r $image_dir/cmnmod/executor3.sh root@$executor:/home/ scp -r $image_dir/cmnmod/executor3.tar root@$executor:/home/ ssh -t root@$executor "cd /home/ && chmod a+x executor3.sh && ./executor3.sh" elif [[ $com -eq 0 ]];then scp -r $image_dir/cmnmod/web_left.tar root@$left:/home/ scp -r $image_dir/cmnmod/web_left.sh root@$left:/home/ ssh -t root@$left "cd /home/ && chmod a+x web_left.sh && ./web_left.sh" scp -r $image_dir/cmnmod/csm_left.tar root@$left2:/home/ scp -r $image_dir/cmnmod/csm_left.sh root@$left2:/home/ ssh -t root@$left2 "cd /home/ && chmod a+x csm_left.sh && ./csm_left.sh" scp -r $image_dir/cmnmod/dispatch.tar root@$schedule:/home/ scp -r $image_dir/cmnmod/dispatch.sh root@$schedule:/home/ ssh -t root@$schedule "cd /home/ && chmod a+x dispatch.sh && ./dispatch.sh" scp -r $image_dir/cmnmod/right.tar root@$right:/home/ scp -r $image_dir/cmnmod/right.sh root@$right:/home/ ssh -t root@$right "cd /home/ && chmod a+x right.sh && ./right.sh" executor=$(eval echo \$executor$1_5) scp -r $image_dir/cmnmod/executor1.sh root@$executor:/home/ scp -r $image_dir/cmnmod/executor1.tar root@$executor:/home/ ssh -t root@$executor "cd /home/ && chmod a+x executor1.sh && ./executor1.sh" executor=$(eval echo \$executor$1_6) scp -r $image_dir/cmnmod/executor2.sh root@$executor:/home/ scp -r $image_dir/cmnmod/executor2.tar root@$executor:/home/ ssh -t root@$executor "cd /home/ && chmod a+x executor2.sh && ./executor2.sh" executor=$(eval echo \$executor$1_7) scp -r $image_dir/cmnmod/executor3.sh root@$executor:/home/ scp -r $image_dir/cmnmod/executor3.tar root@$executor:/home/ ssh -t root@$executor "cd /home/ && chmod a+x executor3.sh && ./executor3.sh" fi } if [[ $module == "left" ]];then echo "function left" elif [[ $module == "right" ]];then echo "right" elif [[ $module == "cmn" ]];then cmn $machine_num $com elif [[ $module == "sch" ]];then echo "sche" elif [[ $module == "exe" ]];then exe $machine_num $com $lange fi
2022年10月11日
194 阅读
0 评论
0 点赞
2022-09-15
记一次线上socket_read的坑
先来看下问题:测试的时候发现三个执行体只有php的执行体所在的板卡CPU占用率居高不下,java和python的则正常,由于服务正在被多人测试,导致登录界面加载很慢,验证码刷新不出等问题。排查:通过排查发现,左括号输出日志,报了8080超时,8080端口为php的服务,而此时拟态大屏在频繁发送数据请求,而右括号服务没有正常响应,导致执行体的客户端一直在等待接收数据,因为执行体作为TCP客户端跟右括号建立通信时需要接收指定的长度才会断开连接,但是使用了socket_set_nonblock非阻塞模式设置,所以如果没有接收到数据就会跳出外层for循环,一直持续4分钟的时间,这样就会导致cpu资源被for循环全部占用。我们先来看下有问题的代码:我们知道socket_read,socket_recv,socket_accept均为默认的阻塞模式,什么是阻塞模式,就是当程序运行到此函数时,会一直读取服务端发来的数据,直到接收满2048个字节的(最大设置65535)长度才会继续往下运行,否则就停留在此处,交由操作系统和底层网络接管程序,一直监听,直到超时为止,所以问题就来了,在这个代码中由于把read设置成了socket_set_nonblock($socket);非阻塞模式,也就是说当运行到这里时,由于右括号服务已经挂了所以不会发送数据,自然而然也不会读到数据,由于又没有超时设置,所以很快就往下继续运行了,跳出本次循环,继续下次循环,这个时间是很短暂的,也就是说当有括号不发数据时,执行体一直在运行for循环,自然而然cpu就飙上去了,直到手动设置的超时4分钟结束才中断连接,也就是这4分钟内其他服务都不能访问!!虽然CPU升高是由于右括号的服务异常,继而导致php执行体服务运行不正常最终导致的,但是我们还是要尽量避免由于别的模块或者服务不正常导致自己的服务出问题继而瘫痪整个系统的情况。要尽量把异常缩小在可控范围内。解决知道问题所在了,那么就很好解决了,可以通过以下方式进行修改:设置为非阻塞模式,并增加读取数据超时时间设置;socket_set_option($socket,SOL_SOCKET,SO_RCVTIMEO,array("sec"=>10, "usec"=>0 ) );记住:一定要注释掉非阻塞改为阻塞,不然即使加了阻塞超时设置也不会生效,因为本质是非阻塞,非阻塞就是死循环。增加多路复用阻塞:假如我一定要用非阻塞设置该怎么办呢?办法很多,那就是在read前面加多路复用阻塞:参数 描述read 指向一组等待可读性检查的套接字write 指向一组等待可写性检查的套接字except 指向一组等待错误检查的套接字tv_sec 用来设置select()的等待时间,秒tv_usec两者组成了 用来设置select()的等待时间,微妙这种方式也很好理解,就相当于在read非阻塞模式前加了阻塞判断,select来监听read的数据。当读取出来数据为空时会一直读,直到10s为止,可以理解为比sleep()更高级点,PHP的socket_select函数也是调用系统的select函数实现的。PHP中socket_select()函数传入的read和write数组是引用传入的,所以每次调用socket_select()后read和write或者except数组中会包含最新的可以使用的资源数组。传入的是要监视的,而调用socket_select后得到的是可以用的。多路是指多个客户端连接socket,复用就是指复用少数几个进程,多路复用本身依然隶属于同步通信方式,只是表现出的结果看起来像异步,这点值得注意.目前多路复用有三种常用的方案,依次是:select,最早的解决方案poll,算是select的升级版epoll,目前的最终解决版,解决c10k问题的功臣使用其他方式其实该问题的本质还是解决死循环过程中CPU过高的问题,只不过我们改为阻塞,就避免了程序死循环过程中被过多占用CPU资源的问题,我们其实可以直接暴利一点使用sleep(),判断当接收不到数据时 就sleep来释放资源,不过这种方式过于暴利,会影响性能,你要知道循环一次sleep1毫秒 循环10000次就是10s 不可想象!!总结 通过以上总结,我们在使用socket通信时,如果对并发量要求不高,尽量使用阻塞模式,由于我们使用了负载均衡,所以对单机并发量不是特别要求。阻塞的socket函数在调用send,recv,connect,accept等函数时,如果特定的条件不满足,就会阻塞其调用线程直至超时,非阻塞的socket恰恰相反。非阻塞模式一般用于需要支持高并发多QPS的场景(如服务器程序),但是正如前文所述,这种模式让程序的执行流和控制逻辑变得复杂;相反,阻塞模式逻辑简单,程序结构简单明了,常用于一些特殊场景中。应用场景一:某程序需要临时发送一个文件,文件分段发送,每发送一段,对端都会给予一个响应,该程序可以单独开一个任务线程,在这个任务线程函数里面,使用先send后recv再send再recv的模式,每次send和recv都是阻塞模式的。应用场景二:A端与B端之间的通信只有问答模式,即A端每发送给B端一个请求,B端比定会给A端一个响应,除此之外,B端不会向A端推送任何数据,此时A端就可以采用阻塞模式,在每次send完请求后,都可以直接使用阻塞式的recv函数接收应答包。
2022年09月15日
273 阅读
0 评论
1 点赞
2022-08-12
git教程及开发规范
Git简介 git是一种源码管理系统(source code management,缩写为SCM)。它对当前文件提供版本管理功能,核心思想是对当前文件建立一个对象数据库(object database),将历史版本信息存放在这个数据库中,git是目前最好用的版本控制系统(没有之一)。集中式和分布式 版本控制系统又分为集中式和分布式两种,我们熟悉的CVS及SVN都是属于集中式的版本控制系统,而git则是分布式版本控制系统,集中式和分布式版本控制系统有什么区别?集中式版本控制系统,版本库是集中存放在中央服务器的(如图一),而干活的时候,用的都是自己的电脑,所以要先从中央服务器取得最新的版本,然后开始干活,干完活了,再把自己的活推送给中央服务器。中央服务器就好比是一个图书馆,你要改一本书,必须先从图书馆借出来,然后回到家自己改,改完了,再放回图书馆。分布式版本控制系统与集中式版本控制系统有何不同呢?首先,分布式版本控制系统根本没有“中央服务器”(如图二),每个人的电脑上都是一个完整的版本库,这样,你工作的时候,就不需要联网了,因为版本库就在你自己的电脑上。既然每个人电脑上都有一个完整的版本库,那多个人如何协作呢?比方说你在自己电脑上改了文件A,你的同事也在他的电脑上改了文件A,这时,你们俩之间只需把各自的修改推送给对方,就可以互相看到对方的修改了。在实际使用分布式版本控制系统的时候,其实很少在两人之间的电脑上推送版本库的修改,因为可能你们俩不在一个局域网内,两台电脑互相访问不了,也可能今天你的同事病了,他的电脑压根没有开机。因此,分布式版本控制系统通常也有一台充当“中央服务器”的电脑,但这个服务器的作用仅仅是用来方便“交换”大家的修改,没有它大家也一样干活,只是交换修改不方便而已。和集中式版本控制系统相比,分布式版本控制系统的安全性要高很多,因为每个人电脑里都有完整的版本库,某一个人的电脑坏掉了不要紧,随便从其他人那里复制一个就可以了。而集中式版本控制系统的中央服务器要是出了问题,所有人都没法干活了。本文重点讲下分布式版本控制系统git的使用。Git安装 Git可以在Linux、Unix、Mac和Windows这几大平台上正常安装运行。安装git可以直接去官网下载安装最新的安装程序:https://git-scm.com/downloads,安装步骤这里就不再详细说。Git工作区、暂存区、版本库的工作流程 我们每天使用 git ,但是很多命令记不住。一般来说,日常使用只要记住下图(如图三)6个命令,就可以了。但是熟练使用,恐怕要记住60~100个命令,首先来了解几个概念:Workspace:工作区,就是你在电脑里能看到的目录,包括你在IDE中开发项目进行文件的添加、修改、删除等操作。Index / Stage:暂存区,英文叫stage或index。一般存放在.git目录下的index 文件(.git/index)中(如图四),所以我们把暂存区有时也叫作索引(index)。暂存区指将工作区中的操作完成小阶段的存储,是版本库的一部分。Repository:仓库区(或本地仓库),仓库区表示个人开发的一个小阶段的完成,仓库区记录的各版本是可以查看并回退的和暂存区不同的是暂存区一旦提交就再也没有了。Remote:远程仓库,简单来说就是我们搭建的git服务器gitlab或者gitee或者github代码托管平台等,存储每个成员的编码记录。用命令的方式解释下各个区是如何工作的:工作区与暂存区当对工作区修改(或新增)的文件执行git add命令时,暂存区的目录树被更新,同时工作区修改(或新增)的文件内容被写入到对象库中的一个新的对象中,而该对象的ID被记录在暂存区的文件索引中。工作区到暂存区常用命令如下:git add [file1] [file2] ... # 添加一个或多个指定文件到暂存区 git add [dir] # 添加指定目录到暂存区包括子目录git add . # 添加当前目录的所有文件到暂存区git rm [file] # 同时删除工作区和暂存区的文件git rm --cached [file] # 删除暂存区的文件并保留工作区的文件git checkout . # 用暂存区全部文件替换工作区的文件git checkout -- # 用暂存区指定文件替换工作区的文件git checkout这个操作很危险,会清除工作区中未添加到暂存区中的改动。暂存区与仓库区当执行提交操作(git commit)时,暂存区的目录树写到版本库(对象库)中,master 分支会做相应的更新。即 master 指向的目录树就是提交时暂存区的目录树。暂存到仓库区常用命令如下:git commit -m “本次提交说明” # 提交暂存区文件到本地仓库中 git reset HEAD # 移除添加到暂存区的文件,暂存区的目录树会被重写,被 master 分支指向的目录树所替换,但是工作区不受影响git reset HEAD^ # 去掉上一次的提交变成add之前的状态git reset --soft HEAD^ # 去掉上一次的提交变成add之后,commit之前的状态版本库与远程仓库当执行提交操作(git push)时,会把本地仓库中文件提交到远程仓库中,常用命令如下:git pull # 取回远程仓库的变化,并与本地分支合并git push # 上传本地指定分支到远程仓库创建版本库 了解完工作区、暂存区和本地仓库的关系之后,我们知道暂存区和本地仓库都是版本库的一部分。什么是版本库呢?版本库又名仓库,英文名repository,你可以简单理解成一个目录,这个目录里面的所有文件都可以被Git管理起来,每个文件的修改、删除,Git都能跟踪,以便任何时刻都可以追踪历史,或者在将来某个时刻可以“还原”。下面我们开始git的实际操作。第一步:需要创建版本库,非常简单,直接创建一个空目录,进入空目录里面即可(以linux操作系统为例):$ mkdir testgit$ cd testgit$ pwd/root/gittest第二步:通过git init 命令把这个目录变成git可以管理的仓库;仓库建好了,当前目录下会多一个.git的目录,这个目录就是用来跟踪管理版本库的,我们也可以在其他路径使用git init <目录名称>来指定目录作为git仓库。第三步:在工作区新建文件并把文件添加到仓库;$ touch readme.txt$ vi readme.txt$ cat readme.txt hello git切记,新建文件一定要放到gittest目录(子目录)中,因为这是一个git仓库,放到其他地方git再厉害也找不到这个文件。第四步:用命令git add告诉Git,把文件添加到仓库; git add readme.txt第五步:用命令git commit告诉Git,把文件提交到本地仓库;$ git commit -m “测试git”-m:后面输入的是本次提交的说明,建议我们在开发过程中要养成习惯,输入的内容要有意义,这样能够从历史记录中轻易找到方便我们回退版本;1 file changed:1个文件被改动(我们新添加的readme.txt文件);1 insertions:插入了1行内容(readme.txt有1行内容hello git);除了以上信息之外,我们发现GIT需要我们设置名称和邮箱地址,因为Git是分布式版本控制系统,所以,每个机器都必须自报家门:你的名字和Email地址。Git的配置文件为.git目录下的config文件,常用命令如下:git config --list # 显示当前的Git配置 git config [--global] user.name "[name]" # 设置提交时的用户信息git config [--global] user.email "[email address]" # 设置提交时的邮箱地址版本回退 我们现在已经知道了怎么修改文件,并且能够把修改后的文件提交到git版本库,然后我们不停的修改文件,不停的提交,通过git log命令我们可以查到我们都做了哪些提交。$ git log通过以上命令我们可以看到从最近到最远提交的日志记录,一共提交了3次,最近的一次是“测试git3 修改hello git 2023”,上一次是“测试git2 修改hello git 2022”,第一次是“测试git”,可以试试加上--pretty=oneline参数打印一次:这样是不是看得更清楚?第一段代表commit id为版本号,和SVN不一样,Git的commit id不是1,2,3……递增的数字,而是一个SHA1计算出来的一个非常大的数字,用十六进制表示,而且你看到的commit id和我的肯定不一样,以你自己的为准。为什么commit id需要用这么一大串数字表示呢?因为Git是分布式的版本控制系统,后面我们还要研究多人在同一个版本库里工作,如果大家都用1,2,3……作为版本号,那肯定就冲突了。第二段就很好理解了,代表我们提交说明,每次修改的内容是什么一目了然。假如我们现在有个需求,需要把reademe.txt回退到上一个版本,也就是测试git2 输出hello git2的哪个版本,怎么做?在git中,用HEAD表示当前版本,上一个版本是HEAD^,上上一个版本HEAD^^,往上100个版本是HEAD~100,现在回退到上一版本可以使用如下命令:$ git reset --hard HEAD^可以看到readme.txt的内容已经回退到上一版本并输出hello git 2022.假如我们现在需求变了!!需要回退到2023的版本怎么办呢?我们打印log发现2023的日志没了,这时候怎么办呢?我们使用git reflog命令来打印所有版本的commit id然后查询到git 2023的版本号为8b2cd52,再执行以下命令可以看到回退到2023的版本了。$ git reset --hard 8b2cd52Git版本回退,常用命令如下:git log --pretty=oneline # 显示历史版本 git reset --hard HEAD^ # 回退到上一版本git reflog # HEAD所指向的一个顺序的提交列表git reset --hard # 回退到指定版本号撤销修改 场景1:当你改乱了工作区某个文件的内容,想直接丢弃工作区的修改时,用命令:$ git checkout -- file场景2:当你不但改乱了工作区某个文件的内容,还添加到了暂存区时,想丢弃修改,分两步:$ git reset HEAD 表示把暂存区的修改撤销掉,回到场景1$ git checkout -- file 表示把工作区的修改直接撤销掉场景3:已经提交了不合适的修改到版本库时,想要撤销本次提交,参考上节版本回退。删除文件 假如我们在工作区中删除了某个文件,会发生什么呢?现在你有两个选择,一是确实要从版本库中删除该文件,那就用命令git rm删掉,并且git commit:现还有一种情况是删错了,因为版本库里还有呢,所以可以很轻松地把误删的文件恢复到最新版本:$ git checkout -- 表示用版本库里的版本替换工作区的版本Git删除,常用命令如下:git rm # git rm就是删文件,并且把删文件的修改提交到暂存区,相当于rm删文件后,git add 提交,保存修改 添加、推送、删除远程仓 现在的情景是,你已经在本地创建了一个Git仓库后,你的代码只能在你的电脑上看到,不能跟其他同事协作开发,这时候就需要有一个远程仓库来同步我们的本地仓库,既可以作为备份,也可以让其他人通过远程仓进行协作,这就是我们开篇提到的分布式版本控制系统通常也有一台充当“中央服务器”,来交换大家的修改。第一步:添加远程仓,以gitlab为例介绍:$ git remote add origin git@172.30.1.24:webhex/cipher-php.git远程库的名字就叫origin,是可以更换的,下一步就可以把本地库的所有内容推送到远程库上了:$ git push -u origin master我们第一次推送master分支时,加上了-u参数,Git不但会把本地的master分支内容推送的远程新的master分支,还会把本地的master分支和远程的master分支关联起来,在以后的推送或者拉取时就可以简化命令。如果想删除远程库,可以使用如下命令(使用前,建议先用git remote -v查看远程库信息):$ git remote -v 查看远程库$ git remote rm origin 删除名字是origin的远程库克隆远程仓 现在我们知道了,先在本地建立版本库,然后添加远程仓,最后实现工作区到版本库再到远程仓的协作开发,那假如我们现在从零开发,最好的方式先在gitlab、gitee或者github等建立了远程仓,怎么实现关联呢?我们只需要把远程库克隆到我们本地就能在本地同步一个本地版本仓。$ git clone git@172.30.1.24:webhex/cipher-php.git 然后我们就可以在生成的目录里面添加文件、修改文件、删除文件操作,然后如果有多个人协作开发,那么每个人各自从远程克隆一份就可以了。分支管理 git版本库创建后会默认创建一个master分支,这个分支也叫主分支。当我们需要多人协作最后合并代码,或者我们需要有测试环境、生产环境、BUG修复环境等等,这时候一个主分支已经不够用了。一开始的时候,master分支是一条线,Git用master指向最新的提交,再用HEAD指向master,就能确定当前分支,以及当前分支的提交点:每次提交,master分支都会向前移动一步,这样,随着你不断提交,master分支的线也越来越长。当我们创建新的分支,例如dev时,Git新建了一个指针叫dev,指向master相同的提交,再把HEAD指向dev,就表示当前分支在dev上:不过,从现在开始,对工作区的修改和提交就是针对dev分支了,比如新提交一次后,dev指针往前移动一步,而master指针不变:假如我们在dev上的工作完成了,就可以把dev合并到master上。Git怎么合并呢?最简单的方法,就是直接把master指向dev的当前提交,就完成了合并:所以Git合并分支也很快!就改改指针,工作区内容也不变!合并完分支后,甚至可以删除dev分支。删除dev分支就是把dev指针给删掉,删掉后,我们就剩下了一条master分支:Git分支管理,常用命令如下:git checkout -b dev # git checkout命令加上-b参数表示创建并切换,相当于以下两条命令:git branch dev # 创建dev分支git checkout dev # 切换到dev分支git branch # git branch命令会列出所有分支,当前分支前面会标一个*号。git merge dev # 把dev分支合并到当前所在的分支git branch -d dev # 删除dev分支git switch -c dev # 创建切换分支同git checkout -b dev一样git switch dev # 切换到dev分支同git checkout dev分支管理策略 在实际开发中,我们应该按照几个基本原则进行分支管理:首先,master分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活;那在哪干活呢?干活都在dev分支上,也就是说,dev分支是不稳定的,到某个时候,比如1.0版本发布时,再把dev分支合并到master上,在master分支发布1.0版本;你和你的小伙伴们每个人都在dev分支上干活,每个人都有自己的分支,时不时地往dev分支上合并就可以了。所以,团队合作的分支看起来就像这样:合并分支时,加上--no-ff参数就可以用普通模式合并,合并后的历史有分支,能看出来曾经做过合并,而fast forward合并就看不出来曾经做过合并。 BUG分支 软件开发中,bug就像家常便饭一样。有了bug就需要修复,在Git中,由于分支是如此的强大,所以,每个bug都可以通过一个新的临时分支来修复,修复后,合并分支,然后将临时分支删除。当你接到一个修复一个代号101的bug的任务时,很自然地,你想创建一个分支issue-101来修复它,但是,等等,当前正在dev上进行的工作还没有提交:并不是你不想提交,而是工作只进行到一半,还没法提交,预计完成还需1天时间。但是,必须在两个小时内修复该bug,怎么办?Git还提供了一个stash功能,可以把当前工作现场“储藏”起来,等以后恢复现场后继续工作: git stash 首先确定要在哪个分支上修复bug,假定需要在master分支上修复,就从master创建临时分支:$ git checkout master$ git checkout -b issue-101Switched to a new branch 'issue-101'现在修复bug,改完文件后,然后提交。修复完成后再次切换到master主分支,并完成合并,删除issue-101分支:$ git switch masterSwitched to branch 'master'$ git merge --no-ff -m "merged bug fix 101" issue-101现在,是时候接着回到dev分支干活了!用git stash list命令看看有哪些工作区:$ git stash list用git stash apply恢复,但是恢复后,stash内容并不删除,你需要用git stash drop来删除;另一种方式是用git stash pop,恢复的同时把stash内容也删了:在master分支上修复了bug后,我们要想一想,dev分支是早期从master分支分出来的,所以,这个bug其实在当前dev分支上也存在。那怎么在dev分支上修复同样的bug?重复操作一次,提交不就行了?有木有更简单的方法?有!同样的bug,要在dev上修复,我们只需要把bug分支提交的commit id找到这个提交所做的修改“复制”到dev分支。注意:我们只想复制那一个commit id这个提交所做的修改,并不是把整个master分支merge过来。Git专门提供了一个cherry-pick命令,让我们能复制一个特定的提交到当前分支:$ git cherry-pick 多人协作 多人协作的工作模式通常是这样:1.首先,可以试图用git push origin 推送自己的修改;2.如果推送失败,则因为远程分支比你的本地更新,需要先用git pull试图合并;3.如果合并有冲突,则解决冲突,并在本地提交;4.没有冲突或者解决掉冲突后,再用git push origin 推送就能成功!5.如果git pull提示no tracking information,则说明本地分支和远程分支的链接关系没有创建,用命令git branch --set-upstream-to origin/;6.在本地创建和远程分支对应的分支,使用git checkout -b branch-name origin/branch-name,本地和远程分支的名称最好一致。标签管理 在Git中打标签非常简单,首先,切换到需要打标签的分支上:$ git tag v1.0可以用命令git tag查看所有标签:$ git tagv1.0默认标签是打在最新提交的commit上的。有时候,如果忘了打标签,比如,现在已经是周五了,但应该在周一打的标签没有打,怎么办?方法是找到历史提交的commit id,然后打上就可以了:$ git tag v0.9 注意,标签不是按时间顺序列出,而是按字母排序的。可以用git show 查看标签信息:还可以创建带有说明的标签,用-a指定标签名,-m指定说明文字:$ git tag -a v0.1 -m "version 0.1 released" 如果标签打错了,也可以删除:$ git tag -d v0.1因为创建的标签都只存储在本地,不会自动推送到远程。所以,打错的标签可以在本地安全删除。如果要推送某个标签到远程,使用命令git push origin :$ git push origin v1.0或者,一次性推送全部尚未推送到远程的本地标签:$ git push origin --tags如果标签已经推送到远程,要删除远程标签就麻烦一点,先从本地删除:$ git tag -d v0.9然后,从远程删除。删除命令也是push,但是格式如下:$ git push origin :refs/tags/v0.9Git标签管理,常用命令如下:git tag # 用于新建一个标签,默认为HEAD,也可以指定一个commit idgit tag -a -m "blablabla..." # 可以指定标签信息git tag # 查看所有标签git push origin # 可以推送一个本地标签git push origin --tags # 可以推送全部未推送过的本地标签git tag -d # 可以删除一个本地标签git push origin :refs/tags/ # 可以删除一个远程标签git产生冲突的情况汇总 本地拉取:1)当远端有更新,本地库没有变化,拉取操作远端会覆盖本地库(远端版本高于本地)2)当远端修改,本地库也有修改,拉取操作会产生冲突文件(远端版本和本地版本冲突)3)当本地库修改,而远端没有变化,拉取不会产生变化(本地版本高于远端版本)4)当本地库版本低于远端版本,则无法推送,必须先拉取在操作,否则报错分支合并:1)当资源分支版本高于目标分支时,合并,资源分支同名文件会覆盖目标分支;2)当目标分支版本高于资源分支时,合并,提示Already up-to-date.(已经更新),目标分支内容不会变化;3)当目标分支和资源分支同名文件都有改动时,会报冲突,需手动解决; 参考资料 git官方教程https://www.runoob.com/git/git-tutorial.html阮一峰git工作流程https://www.ruanyifeng.com/blog/2015/12/git-workflow.html廖雪峰git教程https://www.liaoxuefeng.com/wiki/896043488029600/
2022年08月12日
232 阅读
0 评论
0 点赞
1
2
...
7