PageRenderTime 52ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/bitrix/modules/pull/lib/protobuftransport.php

https://gitlab.com/neuser/bitrix-core
PHP | 349 lines | 260 code | 54 blank | 35 comment | 25 complexity | 54724cc132455998977d8498514255ef MD5 | raw file
  1. <?php
  2. namespace Bitrix\Pull;
  3. use Bitrix\Main\Config\Option;
  4. use Bitrix\Main\SystemException;
  5. use Bitrix\Main\Text\BinaryString;
  6. use Bitrix\Main\Type\DateTime;
  7. use Bitrix\Main\Web\HttpClient;
  8. use Bitrix\Pull\Protobuf;
  9. use Protobuf\MessageCollection;
  10. class ProtobufTransport
  11. {
  12. protected $hits = 0;
  13. protected $bytes = 0;
  14. /**
  15. * @param array $messages Messages to send to the pull server.
  16. */
  17. public static function sendMessages(array $messages)
  18. {
  19. if(!Config::isProtobufUsed())
  20. {
  21. throw new SystemException("Sending messages in protobuf format is not supported by queue server");
  22. }
  23. $protobufMessages = static::convertMessages($messages);
  24. $requests = static::createRequests($protobufMessages);
  25. $requestBatches = static::createRequestBatches($requests);
  26. $queueServerUrl = \CHTTP::urlAddParams(Config::getPublishUrl(), ["binaryMode" => "true"]);
  27. foreach ($requestBatches as $requestBatch)
  28. {
  29. $urlWithSignature = $queueServerUrl;
  30. $httpClient = new HttpClient(["streamTimeout" => 1]);
  31. $bodyStream = $requestBatch->toStream();
  32. if(\CPullOptions::IsServerShared())
  33. {
  34. $signature = \CPullChannel::GetSignature($bodyStream->getContents());
  35. $urlWithSignature = \CHTTP::urlAddParams($urlWithSignature, ["signature" => $signature]);
  36. }
  37. $httpClient->disableSslVerification();
  38. $httpClient->query(HttpClient::HTTP_POST, $urlWithSignature, $bodyStream);
  39. }
  40. return true;
  41. }
  42. /**
  43. * Returns online status for each known channel in the list of private channel ids.
  44. * @param array $channels Array of private channel ids.
  45. * @return array Return online status for known channels in format [channelId => bool].
  46. */
  47. public static function getOnlineChannels(array $channels)
  48. {
  49. $result = [];
  50. $maxChannelsPerRequest = \CPullOptions::GetMaxChannelsPerRequest();
  51. $channelBatches = [];
  52. $currentChannelBatch = 0;
  53. $requestsInChannelBatch = 0;
  54. foreach ($channels as $channelId)
  55. {
  56. $channel = new Protobuf\ChannelId();
  57. $channel->setId(hex2bin($channelId));
  58. $channel->setIsPrivate(true);
  59. $requestsInChannelBatch++;
  60. if($requestsInChannelBatch >= $maxChannelsPerRequest)
  61. {
  62. $currentChannelBatch++;
  63. $requestsInChannelBatch = 1;
  64. }
  65. $channelBatches[$currentChannelBatch][] = $channel;
  66. }
  67. $requests = [];
  68. foreach ($channelBatches as $channelBatchNumber => $channelBatch)
  69. {
  70. $channelsStatsRequest = new Protobuf\ChannelStatsRequest();
  71. $channelsStatsRequest->setChannelsList(new MessageCollection($channelBatch));
  72. $request = new Protobuf\Request();
  73. $request->setChannelStats($channelsStatsRequest);
  74. $requests[] = $request;
  75. }
  76. $queueServerUrl = \CHTTP::urlAddParams(Config::getPublishUrl(), ["binaryMode" => "true"]);
  77. $requestBatches = static::createRequestBatches($requests);
  78. foreach ($requestBatches as $requestBatch)
  79. {
  80. $http = new HttpClient();
  81. $http->disableSslVerification();
  82. $urlWithSignature = $queueServerUrl;
  83. $bodyStream = $requestBatch->toStream();
  84. if(\CPullOptions::IsServerShared())
  85. {
  86. $signature = \CPullChannel::GetSignature($bodyStream->getContents());
  87. $urlWithSignature = \CHTTP::urlAddParams($urlWithSignature, ["signature" => $signature]);
  88. }
  89. $binaryResponse = $http->post($urlWithSignature, $bodyStream);
  90. if($http->getStatus() != 200)
  91. {
  92. return [];
  93. }
  94. if(BinaryString::getLength($binaryResponse) == 0)
  95. {
  96. return [];
  97. }
  98. try
  99. {
  100. $responseBatch = Protobuf\ResponseBatch::fromStream($binaryResponse);
  101. }
  102. catch (\Exception $e)
  103. {
  104. return [];
  105. }
  106. $responses = $responseBatch->getResponsesList();
  107. $response = $responses[0];
  108. if(!($response instanceof Protobuf\Response))
  109. {
  110. return[];
  111. }
  112. if ($response->hasChannelStats())
  113. {
  114. $stats = $response->getChannelStats();
  115. /** @var Protobuf\ChannelStats $channel */
  116. foreach ($stats->getChannelsList() as $channel)
  117. {
  118. if($channel->getIsOnline())
  119. {
  120. $channelId = bin2hex($channel->getId());
  121. $result[$channelId] = true;
  122. }
  123. }
  124. }
  125. }
  126. return $result;
  127. }
  128. /**
  129. * @param array $messages
  130. * @return Protobuf\IncomingMessage[]
  131. */
  132. protected static function convertMessages(array $messages)
  133. {
  134. $result = [];
  135. foreach ($messages as $message)
  136. {
  137. $event = $message['event'];
  138. if(!is_array($message['channels']) || count($message['channels']) == 0 || !isset($event['module_id']) || !isset($event['command']))
  139. {
  140. continue;
  141. }
  142. $result = array_merge($result, static::convertMessage($message['channels'], $event));
  143. }
  144. return $result;
  145. }
  146. /**
  147. * @param array $channels
  148. * @param array $event
  149. *
  150. * @return Protobuf\IncomingMessage[]
  151. */
  152. protected static function convertMessage(array $channels, array $event)
  153. {
  154. $result = [];
  155. $extra = is_array($event['extra']) ? $event['extra'] : [];
  156. $extra['server_time'] = $extra['server_time'] ?: new DateTime();
  157. $extra['server_time_unix'] = $extra['server_time_unix'] ?: microtime(true);
  158. $extra['server_name'] = Option::get('main', 'server_name', $_SERVER['SERVER_NAME']);
  159. $extra['revision_web'] = PULL_REVISION_WEB;
  160. $extra['revision_mobile'] = PULL_REVISION_MOBILE;
  161. $body = Common::jsonEncode(array(
  162. 'module_id' => $event['module_id'],
  163. 'command' => $event['command'],
  164. 'params' => $event['params'] ?: [],
  165. 'extra' => $extra
  166. ));
  167. // for statistics
  168. $messageType = "{$event['module_id']}_{$event['command']}";
  169. $messageType = preg_replace("/[^\w]/", "", $messageType);
  170. $maxChannelsPerRequest = \CPullOptions::GetMaxChannelsPerRequest();
  171. $receivers = [];
  172. foreach ($channels as $channel)
  173. {
  174. $receiver = new Protobuf\Receiver();
  175. $receiver->setIsPrivate(true);
  176. $receiver->setId(hex2bin($channel));
  177. $receivers[] = $receiver;
  178. if(count($receivers) === $maxChannelsPerRequest)
  179. {
  180. $message = new Protobuf\IncomingMessage();
  181. $message->setReceiversList(new MessageCollection($receivers));
  182. $message->setExpiry($event['expiry']);
  183. $message->setBody($body);
  184. $message->setType($messageType); // for statistics
  185. $result[] = $message;
  186. $receivers = [];
  187. }
  188. }
  189. if(count($receivers) > 0)
  190. {
  191. $message = new Protobuf\IncomingMessage();
  192. $message->setReceiversList(new MessageCollection($receivers));
  193. $message->setExpiry($event['expiry']);
  194. $message->setBody($body);
  195. $result[] = $message;
  196. }
  197. return $result;
  198. }
  199. /**
  200. * @param Protobuf\Request[] $requests
  201. * @return Protobuf\RequestBatch[]
  202. */
  203. protected static function createRequestBatches(array $requests)
  204. {
  205. $result = [];
  206. foreach ($requests as $request)
  207. {
  208. $batch = new Protobuf\RequestBatch();
  209. $batch->addRequests($request);
  210. $result[] = $batch;
  211. }
  212. return $result;
  213. }
  214. /**
  215. * @param Protobuf\IncomingMessage[] $messages
  216. * @return Protobuf\Request[]
  217. */
  218. protected static function createRequests(array $messages)
  219. {
  220. $result = [];
  221. $maxPayload = \CPullOptions::GetMaxPayload() - 200;
  222. $maxMessages = \CPullOptions::GetMaxMessagesPerRequest();
  223. $currentMessageBatch = [];
  224. $currentBatchSize = 0;
  225. foreach ($messages as $message)
  226. {
  227. $messageSize = static::getMessageSize($message);
  228. if($currentBatchSize + $messageSize >= $maxPayload || count($currentMessageBatch) >= $maxMessages)
  229. {
  230. // finalize current request and start a new one
  231. $incomingMessagesRequest = new Protobuf\IncomingMessagesRequest();
  232. $incomingMessagesRequest->setMessagesList(new MessageCollection($currentMessageBatch));
  233. $request = new Protobuf\Request();
  234. $request->setIncomingMessages($incomingMessagesRequest);
  235. $result[] = $request;
  236. $currentMessageBatch = [];
  237. $messageSize = 0;
  238. }
  239. // add the request to the current batch
  240. $currentMessageBatch[] = $message;
  241. $currentBatchSize += $messageSize;
  242. }
  243. if(count($currentMessageBatch) > 0)
  244. {
  245. $incomingMessagesRequest = new Protobuf\IncomingMessagesRequest();
  246. $incomingMessagesRequest->setMessagesList(new MessageCollection($currentMessageBatch));
  247. $request = new Protobuf\Request();
  248. $request->setIncomingMessages($incomingMessagesRequest);
  249. $result[] = $request;
  250. }
  251. return $result;
  252. }
  253. /**
  254. * @param Protobuf\IncomingMessage $message
  255. * @param $maxReceivers
  256. * @return Protobuf\IncomingMessage[]
  257. */
  258. protected static function splitReceivers(Protobuf\IncomingMessage $message, $maxReceivers)
  259. {
  260. $receivers = $message->getReceiversList();
  261. if(count($receivers) <= $maxReceivers)
  262. {
  263. return [$message];
  264. }
  265. $result = [];
  266. $currentReceivers = [];
  267. foreach ($receivers as $receiver)
  268. {
  269. if(count($currentReceivers) == $maxReceivers)
  270. {
  271. $subMessage = new Protobuf\IncomingMessage();
  272. $subMessage->setBody($message->getBody());
  273. $subMessage->setExpiry($message->getExpiry());
  274. $subMessage->setReceiversList(new MessageCollection($currentReceivers));
  275. $result[] = $subMessage;
  276. $currentReceivers = [];
  277. }
  278. $currentReceivers[] = $receiver;
  279. }
  280. if(count($currentReceivers) > 0)
  281. {
  282. $subMessage = new Protobuf\IncomingMessage();
  283. $subMessage->setBody($message->getBody());
  284. $subMessage->setExpiry($message->getExpiry());
  285. $subMessage->setReceiversList(new MessageCollection($currentReceivers));
  286. $result[] = $subMessage;
  287. }
  288. return $result;
  289. }
  290. protected static function getMessageSize(Protobuf\IncomingMessage $message)
  291. {
  292. $config = \Protobuf\Configuration::getInstance();
  293. return $message->serializedSize($config->createComputeSizeContext());
  294. }
  295. }