public function testAllInOne() { // ²âÊÔ´òÓ¡µ½ÆÁÄ» BigpipeLog::debug('[%s:%u][%s][debug]', __FILE__, __LINE__, __FUNCTION__); BigpipeLog::trace('[%s:%u][%s][trace]', __FILE__, __LINE__, __FUNCTION__); BigpipeLog::notice('[%s:%u][%s][notice]', __FILE__, __LINE__, __FUNCTION__); BigpipeLog::monitor('[%s:%u][%s][monitor]', __FILE__, __LINE__, __FUNCTION__); BigpipeLog::warning('[%s:%u][%s][warning]', __FILE__, __LINE__, __FUNCTION__); BigpipeLog::fatal('[%s:%u][%s][fatal]', __FILE__, __LINE__, __FUNCTION__); // ²âÊÔ²»Êä³ö´òÓ¡ÐÅÏ¢log configure $conf = new BigpipeLogConf(); $conf->severity = BigpipeLogSeverity::FATAL; $conf->disable_ostream = true; $fatal_log = sprintf('[%s:%u][%s][fatal message]', __FILE__, __LINE__, __FUNCTION__); $warning_Log = sprintf('[%s:%u][%s][warning]', __FILE__, __LINE__, __FUNCTION__); $this->assertTrue(BigpipeLog::init($conf)); BigpipeLog::fatal('%s', $fatal_log); $msg = BigpipeLog::get_last_error_message(); BigpipeLog::warning('%s', $warning_Log); $this->assertEquals($msg, BigpipeLog::get_last_error_message()); $new_fatal_log = sprintf('[%s:%u][%s][fatal new message]', __FILE__, __LINE__, __FUNCTION__); $this->assertTrue($msg == BigpipeLog::get_last_error_message()); // ²âÊÔclose BigpipeLog::close(); }
/** * 生成unique id * @return string unique id */ public static function get_session_id($pipe_name) { $hostip = '127.0.0.0'; // default host ip ##$host = gethostname(); // can not be used before php 5.3 $host = php_uname('n'); if (false === $host) { BigpipeLog::warning('[%s:%u][%s][no host name. use default value: %s]', __FILE__, __LINE__, __FUNCTION__, $hostip); } else { $hostip = gethostbyname($host); if (false === $hostip) { BigpipeLog::warning('[%s:%u][%s][no ip on host use default local ip: 127.0.0.0][host name:%s]', __FILE__, __LINE__, __FUNCTION__, $host); } } // session格式为 // hostip + 精确得到1天半的时间戳 + pid + pipe_name $timestamp = time() >> 17; // 精确到1天半的时间戳 $uid = sprintf('%s-%u-%u-%s', $hostip, $timestamp, getmypid(), $pipe_name); return $uid; }
/** * 将message package 写入buffer<p> * @param binary $buff: 传入一个buffer的引用 * @return true on success or false on failure */ public function store(&$buff) { if ($this->is_empty()) { BigpipeLog::warning('[store error][empty message package]'); return false; } $buff .= pack("L1", $this->_count); $buff .= $this->_data; return true; }
/** * 删除一个stop状态的queue * @param string $name : name of the queue * @param string $token : token of the queue * @param array $meta_params : meta配置信息 * @return boolean */ public static function delete_queue($name, $token, $meta_params) { // 参数检查 if (false === is_array($meta_params) || true === empty($name) || true === empty($token)) { BigpipeLog::fatal("[%s:%u][%s][invalid params]", __FILE__, __LINE__, __FUNCTION__); return false; } $meta = true === self::$unittest ? self::$stub_meta : self::_init_meta($meta_params); if (false === $meta) { BigpipeLog::fatal("[%s:%u][%s][fail to init meta]", __FILE__, __LINE__, __FUNCTION__); return false; } // 读取queue entry信息 $full_entry = sprintf('queue/%s', $name); $stat = array('version' => 'for test'); //仅在测试时有用 $queue_info = $meta->get_entry($full_entry, $stat); if (false === $queue_info) { BigpipeLog::fatal("[%s:%u][%s][can not access to queue under /%s]", __FILE__, __LINE__, __FUNCTION__, $full_entry); return false; } if ($token != $queue_info['token']) { BigpipeLog::fatal("[%s:%u][%s][wrong token][entry:%s]", __FILE__, __LINE__, __FUNCTION__, $full_entry); return false; } $status = $queue_info['status']; if (BigpipeQueueStatus::STOPPED != $status) { // 只删除stop状态的queue node BigpipeLog::warning("[%s:%u][%s][please stop the queue before delete it][entry:%s][status:%s]", __FILE__, __LINE__, __FUNCTION__, $full_entry, BigpipeQueueStatus::to_string($status)); return false; } $curr_version = $stat['version']; $queue_info['status'] = BigpipeQueueStatus::STOPPED; // change status of a queue $ret = $meta->delete_entry($full_entry); if (false === $ret) { BigpipeLog::fatal("[%s:%u][%s][fail to delete the queue][name:%s][ver:%u]", __FILE__, __LINE__, __FUNCTION__, $name, $curr_version); return false; } BigpipeLog::notice("[%s:%u][%s][queue is deleted][name:%s]", __FILE__, __LINE__, __FUNCTION__, $name); return true; }
/** * 从zk中读取一个node并,反序列化node value数组 * @return mixed value of a node. <p> * return value converted into associative arrays on success. <p> * return false on failure */ public function deserialize($buff) { // unpack head $this->_content = $buff; $head_arr = unpack("C1ver/C1len/S1flags/L1bdlen", $this->_content); $this->_version = $head_arr['ver']; $this->_length = $head_arr['len']; $this->_flags = $head_arr['flags']; $this->_body_len = $head_arr['bdlen']; $obj = json_decode(substr($this->_content, $this->_length), true); if (null === $obj) { BigpipeLog::warning("[%s:%u][%s][fail to decode meta node][ver:%d][flags:%u][len:%u]", __FILE__, __LINE__, __FUNCTION__, $this->_version, $this->_flags, $this->_body_len); $obj = false; } else { BigpipeLog::notice("[%s:%u][%s][meta node header][ver:%d][flags:%u][len:%u]", __FILE__, __LINE__, __FUNCTION__, $this->_version, $this->_flags, $this->_body_len); } return $obj; }
public static function get_private_method(&$obj, $method_name) { $ret = version_compare(PHP_VERSION, '5.3.0', '>=') ? true : false; if (true === $ret) { // 使用5.3.0的功能 $method = new ReflectionMethod($obj, $method_name); $method->setAccessible(true); // >=php5.3可用 return $method; } else { BigpipeLog::warning('unsported function in PHP < 5.3.0'); } return $ret; }
/** * 从config array中读取配置<p> * 不支持嵌套config array, 既config中嵌套config array * @param array $conf_arr * @return true on success or false on failure */ protected static function _array_to_object($conf_arr, &$elem) { try { $obj = new ReflectionClass($elem); $fields = $obj->getProperties(); foreach ($fields as $fld) { if (isset($conf_arr[$fld->name])) { $val = $conf_arr[$fld->name]; if (true === is_array($val)) { // 不允许用本函数给配置项赋一个array BigpipeLog::warning('[%s:%u][%s][can set an array to the config element][name:%s]', __FILE__, __LINE__, __FUNCTION__, $fld->name); return false; } else { $fld->setValue($elem, $val); } } else { // 检查是否有默认配置 $val = $fld->getValue($elem); if (null === $val) { // 配置没有默认值,必须在配置文件里给出 BigpipeLog::warning('[%s:%u][%s][missing configure item][name:%s]', __FILE__, __LINE__, __FUNCTION__, $fld->name); return false; } } // end of check defulat value } // end of traverse each fields of 'this' object } catch (Exception $e) { BigpipeLog::warning('[%s:%u][%s][fail to load configure][error message:%s]', __FILE__, __LINE__, __FUNCTION__, $e->getMessage()); return false; } return true; }
/** * 从传入buff中读取command type * @param binary string $buff:传入协议流 * @return int32_t command type on success or UNKNOWN_TYPE on failure */ public static function get_command_type($buff) { if (2 > strlen($buff)) { BigpipeLog::warning('[fail to get command type]'); return MetaAgentFrameType::UNKNOWN_TYPE; } $ai = unpack("l1int", $buff); $type = $ai["int"]; return $type; }
/** * List the children of the given path, i.e. the name of the directories * within the current node, if any * @param string $path the path to the node * @return array the subpaths within the given node */ public function get_children($path) { if (false === $this->_inited) { BigpipeLog::warning("[%s:%u][%s][uninited]", __FILE__, __LINE__, __FUNCTION__); return false; } if (strlen($path) > 1 && preg_match('@/$@', $path)) { // remove trailing / $path = substr($path, 0, -1); } return $this->_zk->getChildren($path); }
/** * Uninitialize the meta on meta agent * @param $meta_name : string of meta name * @return void type */ private function _uninit_meta($meta_name) { if (null == $this->meta_name) { return; // 不需要释放meta } // create uninit_meta_command $cmd = new UninitMetaFrame(); $cmd->meta_name = $this->meta_name; // send $res_body = $this->_request($cmd); if (null === $res_body) { $this->meta_name = null; BigpipeLog::warning('[uninit_meta error][%s][%s]', $this->last_error_message, $cmd->last_error_message()); $this->last_error_message = "_uninit_meta no ack"; return; } // parse ack $ack = new UninitMetaAckFrame(); if (!$ack->load($res_body)) { $this->last_error_message = '_uninit_meta error ack'; $this->meta_name = null; BigpipeLog::warning('[%s:%u][%s][ack error][%s][%s]', __FILE__, __LINE__, __FUNCTION__, $this->last_error_message, $cmd->last_error_message()); return; } $this->meta_name = null; }
/** * Check if there is a frame to read * @param $timeo: 用户指定的超时等待值,如果无指定则读取配置(单位:毫秒) * @return number */ public function is_readable($timeo_ms) { if (empty($timeo_ms) || $timeo_ms <= 0) { // 参数错误 BigpipeLog::warning("[is_readable][invalid parameter]"); return BigpipeErrorCode::INVALID_PARAM; } $timeo_s = floor($timeo_ms / 1000); $timeo_us = $timeo_ms % 1000 * 1000; $ret = $this->_socket->is_readable($timeo_s, $timeo_us); if (c_socket::ERROR == $ret) { $msg = $this->_socket->__get('socket_error'); BigpipeLog::warning("[peek to read error][{$msg}]"); $ret = BigpipeErrorCode::ERROR_CONNECTION; } else { if (c_socket::TIMEOUT == $ret) { BigpipeLog::warning("[peek to read time out]"); $ret = BigpipeErrorCode::TIMEOUT; } else { $ret = BigpipeErrorCode::READABLE; } } return $ret; }
/** * 接收返回消息, 判断发布是否成功 * @return BigpipePubResult on success or false on failure */ private function _check_ack($receipt_id, &$send_result) { // 接收CONNECTED $res_body = $this->_stomp_adapter->receive(); if (null === $res_body) { return false; } // parse ACK $ack = new BStompAckFrame(); if (!$ack->load($res_body)) { BigpipeLog::warning('[stomp parse ack frame error][cmd_type:%d][err_msg:]', $ack->command_type, $ack->last_error_message()); return false; } if ($ack->session_message_id != $this->_session_msg_id) { BigpipeLog::warning('[check session message id error][send:%u][ack:%u]', $this->_session_msg_id, $ack->session_message_id); return false; } if ($ack->receipt_id != $receipt_id) { BigpipeLog::warning('[check receipt id error][send:%u][ack:%u]', $receipt_id, $ack->receipt_id); return false; } // 填充result $send_result = new BigpipePubResult(); $send_result->error_no = $ack->status; $send_result->pipelet_id = $this->_pipelet_id; $send_result->pipelet_msg_id = $ack->topic_message_id; $send_result->session_msg_id = $ack->session_message_id; return $send_result; }
/** * 从read buffer读取一个message package * @return false on failure or BigpageMessage on success */ private function _receive() { $obj = false; do { $res_body = $this->_stomp_adapter->receive(); if (null === $res_body) { continue; // 直接重订阅 } // 接收成功,读取数据 $msg = new BStompMessageFrame(); if (!$msg->load($res_body)) { // message包问题 BigpipeLog::warning('[%s:%u][%s][receive msg error][%s]', __FILE__, __LINE__, __FUNCTION__, $msg->last_error_message()); continue; } // 读取msg包 if (-1 != $this->_pipelet_msg_id && $msg->topic_message_id < $this->_pipelet_msg_id) { BigpipeLog::warning('[%s:%u][%s][received different start point error][recv: %u][req: %u]', __FILE__, __LINE__, __FUNCTION__, $msg->topic_message_id, $this->_pipelet_msg_id); continue; } $msg_body = $msg->message_body; if (empty($msg_body) || false === $msg_body) { continue; // 接收message失败,failover } // message 接收成功,返回ack if (BStompClientAckType::AUTO == $this->_client_ack_type) { // 发送ack包 $ack = new BStompAckFrame(); $ack->receipt_id = $msg->receipt_id; $ack->topic_message_id = $msg->topic_message_id; $ack->destination = $msg->destination; $ack->ack_type = BStompIdAckType::TOPIC_ID; if (!$this->_stomp_adapter->send($ack)) { // message接收成功,但是ack发送失败,可以不用理会 // 因为下次receive时,会进入failover BigpipeLog::warning('[%s:%u][%s][fail to ack message]', __FILE__, __LINE__, __FUNCTION__); } } // 处理message if ($this->_enable_checksum) { $sign = creat_sign_mds64($msg_body); if ($sign[2] != $msg->cur_checksum) { // checksum校验失败, 进入failover BigpipeLog::warning('[%s:%u][%s][message package checksum error][orig:%u][curr:%u][name:%s][msg_id:%u]', __FILE__, __LINE__, __FUNCTION__, $msg->cur_checksum, $sign[2], $this->_get_stripe_name(), $msg->topic_message_id); continue; } } if (!$this->_package->load($msg_body, $msg->topic_message_id)) { // 接收的包有问题, 进入failover BigpipeLog::warning('[%s:%u][%s][message package error][name:%s][msg_id:%u]', __FILE__, __LINE__, __FUNCTION__, $this->_get_stripe_name(), $msg->topic_message_id); continue; } $obj = $this->_package->pop(); // 必须能成功取一条message if (false === $obj) { // 包中的内容有问,进入failover BigpipeLog::warning('[%s:%u][%s][empty message package][name:%s][msg_id:%u]', __FILE__, __LINE__, __FUNCTION__, $this->_get_stripe_name(), $msg->topic_message_id); continue; } $this->_active(); // 设置活跃状态 $this->_pipelet_msg_id = $msg->topic_message_id + 1; if ($this->_get_end_pos() < $this->_pipelet_msg_id) { // 已接收到stripe末尾,更换新stripe // 主动更新订阅, 如果更新失败也没关系,下次receive时可以处理 // 这里进入flush_subscribe不需要sleep $this->_fo_sleep_time = 0; if ($this->_flush_subscribe()) { $this->_active(); // } } break; // 成功收到message 跳出循环 } while ($this->_flush_subscribe()); return $obj; }
/** * 向终端发送CONNECT命令 */ private function _stomp_connect() { // 发送CONNECT $cmd = new BStompConnectFrame(); $cmd->role = $this->role; $cmd->session_id = $this->session_id; $cmd->topic_name = $this->topic_name; if (!$this->send($cmd)) { BigpipeLog::warning("[stomp connect error]"); return false; } // 接收CONNECTED $res_body = $this->receive(); if (null === $res_body) { BigpipeLog::warning("[stomp receive connected error]"); return false; } // parse CONNECTED $ack = new BStompConnectedFrame(); if (!$ack->load($res_body)) { BigpipeLog::warning('[stomp parse connected frame error][cmd_type:' . $ack->command_type . '][msg:' . $ack->last_error_message() . ']'); return false; } // 更新session id和session message id $this->session_id = $ack->session_id; $this->session_message_id = $ack->session_message_id + 1; return true; }
/** * 检查路径下的node是否存在,如不存在打印错误日志 * @param string $node_path : node path in meta * @return true on success or false on failure */ private function _exists($node_path) { if (false === $this->_zk->exists($node_path)) { BigpipeLog::warning("[invalid meta node][%s]", $node_path); return false; } return true; }