不同项目组之间经常使用接口通信,整理常用的几种方式如下。
一 . Curl模拟Http请求
最常用的通信方式,可以获取或改动少量数据,也可以让对方进行某种操作,响应时间限制很短。
接口文档:
调用代码:
/**
* @brief 调用在线培训接口
* @note
* @author jichenghan
* @param $p_aInput
* @date 2020-11-05
* @return array
*/
public function getApi($p_aInput)
{
$aApiParams = [];
$aApiParams['userid'] = $p_aInput['userid'];
$aApiParams['campids'] = $p_aInput['campids'];
$aApiParams['timespan'] = time();
ksort($aApiParams);
$sApiValue = '';
foreach ($aApiParams as $sValue)
{
$sApiValue .= $sValue;
}
/* 生成签名和参数 */
$aApiParams['key']= MD5(MD5($sApiValue.KEY));
$aRequest = [
'type' => 1,
'url' => URL,
'method' => 'POST',
'param' => $aApiParams
];
$sResult = $this->oHttpRequest->send($aRequest);
$aResult = json_decode($sResult, true);
return $aResult;
}
基类代码:
$this->oCurl = curl_init();
$sQuery = http_build_query($p_aInput['param']);
//设置参数
curl_setopt($this->oCurl, CURLOPT_URL, $p_aInput['url']);
curl_setopt($this->oCurl, CURLOPT_POST, true);
curl_setopt($this->oCurl, CURLOPT_HEADER, 0);
curl_setopt($this->oCurl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($this->oCurl, CURLOPT_POSTFIELDS, $sQuery);
$mResult = curl_exec($this->oCurl));
curl_close($this->oCurl);
return $mResult;
二. Socket通讯
一般用于传参数量很多和返回结果量很大、响应时间较长的情况,如下调用大数据接口返回搜索结果。
调用代码:
/**
* @brief 调用大数据接口 通用方法
* @note
* @author jichenghan
* @date 2021-01-15
* @param array $p_aInput 参数数组
* @return string 返回结果
*/
protected function getResultFromBigData($p_aInput)
{
$sOprType = chr(26); //间隔字符
$sFieldchar = '|'; //操作符
$sSendStr = '';
foreach ($p_aInput as $aSendvalue)
{
$sSendStr .= $sOprType;
if (is_array($aSendvalue))
{
$sSendStr .= implode($sFieldchar, $aSendvalue);
}
else
{
$sSendStr .= $aSendvalue;
}
}
$sSendStr = substr($sSendStr, 1);
$iBuflen = mb_strlen($sSendStr, '8bit');//byte字节长度
$sBreqlen = pack('N', $iBuflen); //int转字节(大端)
$sSocketSendParams = [];
$sSocketSendParams['sendstr'] = $sBreqlen . $sSendStr;
$sSocketSendParams['serverip'] = IP;
$sSocketSendParams['serverport'] = PORT;
$sSocketSendParams['resultlen'] = 4;
$sSearchResult = $this->oSocket->getResultFromSocket($sSocketSendParams);
$aSearchResult = unpack('N*', $sSearchResult);
return $aSearchResult;
}
基类代码:
/*创建socket*/
$rSocket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
try
{
/*判断socket是否创建成功*/
if ($rSocket)
{
/*设置超时*/
socket_set_option($rSocket, SOL_SOCKET, SO_RCVTIMEO, ['sec' => 10, 'usec' => 0]);
socket_set_option($rSocket, SOL_SOCKET, SO_SNDTIMEO, ['sec' => 10, 'usec' => 0]);
/*设置关闭时立即关闭,不等待*/
$aSetLinger = [];
$aSetLinger['l_onoff'] = 1;
$aSetLinger['l_linger'] = 0;
socket_set_option($rSocket, SOL_SOCKET, SO_LINGER, $aSetLinger);
/*连接socket*/
$bSocketConnect = @socket_connect($rSocket, $sServerIp, $sServerPort);
if ($bSocketConnect)
{
/*发送*/
$iSent = socket_write($rSocket, $sSendStr, strlen($sSendStr));
/*返回字符串*/
$sReceiveStr = '';
/*发送失败*/
if ($iSent === false)
{
$q_sFailMsg = '发送失败';
}
else
{
$sReceiveStr = socket_read($rSocket, $sResultLen);
if ($p_iReadType == 2)
{
$iReturnLength = unpack('N', $sReceiveStr)[1];//获取有用信息字符串长度
$sReceiveStr = '';
$iLength = $iReturnLength;
/* 设置for循环,防止进入死循环 */
for ($iLoop=0; $iLength> 0 && $iLoop < 1000; $iLoop++)
{
$sReceiveStrTmp = @socket_read($rSocket, 1024);
$sReceiveStr .= $sReceiveStrTmp;
$iLength -= strlen($sReceiveStrTmp);
}
$sReceiveStr= substr($sReceiveStr, 0, $iReturnLength);
}
elseif ($p_iReadType == 3)
{
$sReceiveStrTmp = '';
while ($sReceiveStrTmp = socket_read($rSocket, $sResultLen))
{
$sReceiveStr .= $sReceiveStrTmp;
}
}
elseif ($p_iReadType == 4)
{
$iReturnLength = unpack('V', $sReceiveStr)[1];//小端字节序
$sReceiveStr = '';
$iLength = $iReturnLength;
/* 设置for循环,防止进入死循环 */
for ($iLoop=0; $iLength> 0 && $iLoop < 1000; $iLoop++)
{
$sReceiveStrTmp = @socket_read($rSocket, 1024);
$sReceiveStr .= $sReceiveStrTmp;
$iLength -= strlen($sReceiveStrTmp);
}
$sReceiveStr = substr($sReceiveStr, 0, $iReturnLength);
if (strlen($sReceiveStr) != $iReturnLength)
{
$sReceiveStr = false;
$q_sFailMsg = '返回字节长度不正确';
}
}
/*关闭socket*/
socket_close($rSocket);
/* 操作成功,返回结果 */
return $sReceiveStr;
}
}
/*连接失败*/
else
{
$q_sFailMsg = '连接失败';
}
}
/*socket创建失败*/
else
{
$q_sFailMsg = 'socket创建失败';
}
}
catch (Exception $e)
{
$q_sFailMsg = 'Caught exception:' . $e->getMessage();
}
二. 生成XML文件
多用于数据量较大但改动很少的场景,比如页面显示列表,类似于缓存的作用。
public function getXML()
{
$xml = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n";
$xml .= "<Report date=\".date("Y-m-d").\">\r\n";
$space = " ";
foreach ($Arr as $ptypekey => $ptype)
{
$xml .= $space."<PTYPE$ptypekey>\r\n";
foreach ($ptype as $num => $ptypevalue)
{
$xml .= $space.$space."<Record>\r\n";
foreach ($ptypevalue as $key => $value)
{
if ($key == 'id')
{
continue;
}
$value = iconv("gbk", "utf-8//IGNORE", $value);
$xml .= $space.$space.$space."<$key>".($value)."</$key>\r\n";
}
$xml .= $space.$space."</Record>\r\n";
}
$xml .= $space."</PTYPE$ptypekey>\r\n";
}
$xml .= "</Report>";
$filedir = PATH;
if (!is_dir($filedir)) {
mkdir($filedir,0777,true); // 如果不存在则创建
}
$fileName = $filedir."/ehire.xml";
$myfile = @fopen($fileName, 'w');
$Result = fwrite($myfile, $xml);
fclose($myfile);
if ($Result === false) {
return '写文件失败';
}
$fileNameTmp = $filedir."/ehire".date("YmdHis").".xml";
copy($fileName, $fileNameTmp);
return '成功';
}
生成xml文件:
四. 定时任务
操作同一张表,A项目组下任务,B项目组定时读取,进行操作同时改变状态。
定时任务 Mission.sh:
#!/bin/sh
. /etc/profile
procNum=$(ps -ef|grep Mission.php|grep -v grep |wc -l)
if [ "$procNum" -lt "1" ];then
/user/bin/php /www/shell/mission/Mission.php >> /www/shell/mission/log.out
fi
调用文件 Mission.php:
set_time_limit( 0 );
@ini_set('memory_limit','500M');
//操作代码
五. 消息队列
比方法四有更好的实时性和容错性,这个后面我单独详解。