allow vendord
All checks were successful
Build, Push and Deploy / build-and-push (push) Successful in 3m3s
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 16s
Build, Push and Deploy / deploy-staging (push) Successful in 32s
Build, Push and Deploy / deploy-production (push) Has been skipped
Build, Push and Deploy / cleanup (push) Successful in 1s

This commit is contained in:
2026-03-30 14:54:57 +07:00
parent 66aed7c4e8
commit b5e3a778ce
21316 changed files with 2777892 additions and 13 deletions

View File

@@ -0,0 +1,98 @@
<?php
/*
*
* Copyright 2018 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
namespace Grpc\Gcp;
/**
* ChannelRef is used to record how many active streams the channel has.
* This is a private class
*/
class ChannelRef
{
// $opts has all information except Credentials for creating a Grpc\Channel.
private $opts;
private $channel_id;
private $affinity_ref;
private $active_stream_ref;
private $target;
private $has_deserialized;
private $real_channel;
public function __construct($target, $channel_id, $opts, $affinity_ref=0, $active_stream_ref=0)
{
$this->target = $target;
$this->channel_id = $channel_id;
$this->affinity_ref = $affinity_ref;
$this->active_stream_ref = $active_stream_ref;
$this->opts = $opts;
$this->has_deserialized = new CreatedByDeserializeCheck();
}
public function getRealChannel($credentials)
{
// TODO(ddyihai): remove this check once the serialize handler for
// \Grpc\Channel is implemented(issue https://github.com/grpc/grpc/issues/15870).
if (!$this->has_deserialized->getData()) {
// $real_channel exists and is not created by the deserialization.
return $this->real_channel;
}
// If this ChannelRef is created by deserialization, $real_channel is invalid
// thus needs to be recreated becasue Grpc\Channel don't have serialize and
// deserialize handler.
// Since [target + augments + credentials] will be the same during the recreation,
// it will reuse the underline grpc channel in C extension without creating a
// new connection.
// 'credentials' in the array $opts will be unset during creating the channel.
if (!array_key_exists('credentials', $this->opts)) {
$this->opts['credentials'] = $credentials;
}
$real_channel = new \Grpc\Channel($this->target, $this->opts);
$this->real_channel = $real_channel;
// Set deserialization to false so it won't be recreated within the same script.
$this->has_deserialized->setData(0);
return $real_channel;
}
public function getAffinityRef()
{
return $this->affinity_ref;
}
public function getActiveStreamRef()
{
return $this->active_stream_ref;
}
public function affinityRefIncr()
{
$this->affinity_ref += 1;
}
public function affinityRefDecr()
{
$this->affinity_ref -= 1;
}
public function activeStreamRefIncr()
{
$this->active_stream_ref += 1;
}
public function activeStreamRefDecr()
{
$this->active_stream_ref -= 1;
}
}

113
vendor/google/grpc-gcp/src/Config.php vendored Normal file
View File

@@ -0,0 +1,113 @@
<?php
/*
*
* Copyright 2018 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
namespace Grpc\Gcp;
use Psr\Cache\CacheItemPoolInterface;
/**
* Config is used to enable the support for the channel management.
*/
class Config
{
private $hostname;
private $gcp_call_invoker;
private $cross_script_shmem_enabled;
private $supported_sapis = ['fpm-fcgi', 'cli-server'];
/**
* @param string $target The target API we want to manage the connection.
* @param \Grpc\Gcp\ApiConfig $conf
* @param CacheItemPoolInterface $cacheItemPool A pool for storing configuration and channels
* cross requests within a single worker process.
* @throws \RuntimeException When a failure occurs while attempting to attach to shared memory.
*/
public function __construct($target, $conf = null, ?CacheItemPoolInterface $cacheItemPool = null)
{
if ($conf == null) {
// If there is no configure file, use the default gRPC channel.
$this->gcp_call_invoker = new \Grpc\DefaultCallInvoker();
return;
}
$gcp_channel = null;
$url_host = parse_url($target, PHP_URL_HOST);
$this->hostname = $url_host ? $url_host : $target;
$channel_pool_key = $this->hostname . '.gcp.channel.' . getmypid();
if (!$cacheItemPool) {
$affinity_conf = $this->parseConfObject($conf);
$gcp_call_invoker = new GCPCallInvoker($affinity_conf);
$this->gcp_call_invoker = $gcp_call_invoker;
} else {
$item = $cacheItemPool->getItem($channel_pool_key);
if ($item->isHit()) {
// Channel pool for the $hostname API has already created.
$gcp_call_invoker = unserialize($item->get());
} else {
$affinity_conf = $this->parseConfObject($conf);
// Create GCP channel based on the information.
$gcp_call_invoker = new GCPCallInvoker($affinity_conf);
}
$this->gcp_call_invoker = $gcp_call_invoker;
register_shutdown_function(function ($gcp_call_invoker, $cacheItemPool, $item) {
// Push the current gcp_channel back into the pool when the script finishes.
$item->set(serialize($gcp_call_invoker));
$cacheItemPool->save($item);
}, $gcp_call_invoker, $cacheItemPool, $item);
}
}
/**
* @return \Grpc\CallInvoker The call invoker to be hooked into the gRPC
*/
public function callInvoker()
{
return $this->gcp_call_invoker;
}
/**
* @return string The URI of the endpoint
*/
public function getTarget()
{
return $this->channel->getTarget();
}
private function parseConfObject($conf_object)
{
$config = json_decode($conf_object->serializeToJsonString(), true);
if (isset($config['channelPool'])) {
$affinity_conf['channelPool'] = $config['channelPool'];
}
$aff_by_method = array();
if (isset($config['method'])) {
for ($i = 0; $i < count($config['method']); $i++) {
// In proto3, if the value is default, eg 0 for int, it won't be serialized.
// Thus serialized string may not have `command` if the value is default 0(BOUND).
if (!array_key_exists('command', $config['method'][$i]['affinity'])) {
$config['method'][$i]['affinity']['command'] = 'BOUND';
}
$aff_by_method[$config['method'][$i]['name'][0]] = $config['method'][$i]['affinity'];
}
}
$affinity_conf['affinity_by_method'] = $aff_by_method;
return $affinity_conf;
}
}

View File

@@ -0,0 +1,84 @@
<?php
/*
*
* Copyright 2018 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
namespace Grpc\Gcp;
/**
* DeserializeCheck is used to check whether _ChannelRef is created by deserialization or not.
* If it is, $real_channel is invalid thus we need to recreate it using $opts.
* If not, we can use $real_channel directly instead of creating a new one.
* It is useful to handle 'force_new' channel option.
* This is a private class
*/
class CreatedByDeserializeCheck implements \Serializable
{
// TODO(ddyihai): remove it once the serialzer handler for \Grpc\Channel is implemented.
private $data;
public function __construct()
{
$this->data = 1;
}
/**
* @return string
*/
public function serialize()
{
return '0';
}
/**
* @return string
*/
public function __serialize()
{
return $this->serialize();
}
/**
* @param string $data
*/
public function unserialize($data)
{
$this->data = 1;
}
/**
* @param string $data
*/
public function __unserialize($data)
{
$this->unserialize($data);
}
/**
* @param $data
*/
public function setData($data)
{
$this->data = $data;
}
/**
* @return int
*/
public function getData()
{
return $this->data;
}
}

View File

@@ -0,0 +1,108 @@
<?php
/*
*
* Copyright 2018 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
namespace Grpc\Gcp;
/**
* Represents an active call that allows for sending and recieving messages
* in streams in any order.
*/
class GCPBidiStreamingCall extends GcpBaseCall
{
private $response = null;
protected function createRealCall($data = null)
{
$channel_ref = $this->_rpcPreProcess($data);
$this->real_call = new \Grpc\BidiStreamingCall($channel_ref->getRealChannel(
$this->gcp_channel->credentials), $this->method, $this->deserialize, $this->options);
$this->real_call->start($this->metadata_rpc);
return $this->real_call;
}
/**
* Pick a channel and start the call.
*
* @param array $metadata Metadata to send with the call, if applicable
* (optional)
*/
public function start(array $metadata = [])
{
$this->metadata_rpc = $metadata;
}
/**
* Reads the next value from the server.
*
* @return mixed The next value from the server, or null if there is none
*/
public function read()
{
if (!$this->has_real_call) {
$this->createRealCall();
$this->has_real_call = true;
}
$response = $this->real_call->read();
if ($response) {
$this->response = $response;
}
return $response;
}
/**
* Write a single message to the server. This cannot be called after
* writesDone is called.
*
* @param ByteBuffer $data The data to write
* @param array $options An array of options, possible keys:
* 'flags' => a number (optional)
*/
public function write($data, array $options = [])
{
if (!$this->has_real_call) {
$this->createRealCall($data);
$this->has_real_call = true;
}
$this->real_call->write($data, $options);
}
/**
* Indicate that no more writes will be sent.
*/
public function writesDone()
{
if (!$this->has_real_call) {
$this->createRealCall();
$this->has_real_call = true;
}
$this->real_call->writesDone();
}
/**
* Wait for the server to send the status, and return it.
*
* @return \stdClass The status object, with integer $code, string
* $details, and array $metadata members
*/
public function getStatus()
{
$status = $this->real_call->getStatus();
$this->_rpcPostProcess($status, $this->response);
return $status;
}
}

View File

@@ -0,0 +1,86 @@
<?php
/*
*
* Copyright 2018 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
namespace Grpc\Gcp;
/**
* GCPCallInvoker updates the channel pool(GcpExtensionChannel) for
* each RPC. The idea is:
* Before the RPC starts, pick a channel from the channel pool:
* - if the RPC is bound to a channel, use that channel.
* - if the RPC doesn't bound to a channel, use the one with minimum active streams.
* After the RPC finishes, update the active stream ref count.
* - if the RPC is defined as bind, bind the channel with corresponding key like
* spanner session name.
* - if the RPC is defined as unbind, unbind the channel with the key.
*/
class GCPCallInvoker implements \Grpc\CallInvoker
{
private $channel;
private $affinity_conf;
/**
* @param array $affinity_conf Store the affinity config for process each RPC.
*/
public function __construct($affinity_conf)
{
$this->affinity_conf = $affinity_conf;
}
/**
* @param string $hostname
* @param array $opts
* @return GcpExtensionChannel
*/
public function createChannelFactory($hostname, $opts)
{
if ($this->channel) {
// $call_invoker object has already created from previews PHP-FPM scripts.
// Only need to update the $opts including the credentials.
$this->channel->updateOpts($opts);
} else {
$opts['affinity_conf'] = $this->affinity_conf;
$channel = new GcpExtensionChannel($hostname, $opts);
$this->channel = $channel;
}
return $this->channel;
}
// _getChannel is used for testing only.
public function GetChannel()
{
return $this->channel;
}
public function UnaryCall($channel, $method, $deserialize, $options)
{
return new GCPUnaryCall($channel, $method, $deserialize, $options);
}
public function ClientStreamingCall($channel, $method, $deserialize, $options)
{
return new GCPClientStreamCall($channel, $method, $deserialize, $options);
}
public function ServerStreamingCall($channel, $method, $deserialize, $options)
{
return new GCPServerStreamCall($channel, $method, $deserialize, $options);
}
public function BidiStreamingCall($channel, $method, $deserialize, $options)
{
return new GCPBidiStreamingCall($channel, $method, $deserialize, $options);
}
}

View File

@@ -0,0 +1,76 @@
<?php
/*
*
* Copyright 2018 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
namespace Grpc\Gcp;
/**
* Represents an active call that sends a stream of messages and then gets
* a single response.
*/
class GCPClientStreamCall extends GcpBaseCall
{
protected function createRealCall($data = null)
{
$channel_ref = $this->_rpcPreProcess($data);
$this->real_call = new \Grpc\ClientStreamingCall($channel_ref->getRealChannel(
$this->gcp_channel->credentials), $this->method, $this->deserialize, $this->options);
$this->real_call->start($this->metadata_rpc);
return $this->real_call;
}
/**
* Pick a channel and start the call.
*
* @param array $metadata Metadata to send with the call, if applicable
* (optional)
*/
public function start(array $metadata = [])
{
// Postpone first rpc to write function(), where we can pick a channel
// from the channel pool.
$this->metadata_rpc = $metadata;
}
/**
* Write a single message to the server. This cannot be called after
* wait is called.
*
* @param ByteBuffer $data The data to write
* @param array $options An array of options, possible keys:
* 'flags' => a number (optional)
*/
public function write($data, array $options = [])
{
if (!$this->has_real_call) {
$this->createRealCall($data);
$this->has_real_call = true;
}
$this->real_call->write($data, $options);
}
/**
* Wait for the server to respond with data and a status.
*
* @return array [response data, status]
*/
public function wait()
{
list($response, $status) = $this->real_call->wait();
$this->_rpcPostProcess($status, $response);
return [$response, $status];
}
}

View File

@@ -0,0 +1,89 @@
<?php
/*
*
* Copyright 2018 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
namespace Grpc\Gcp;
/**
* Represents an active call that sends a single message and then gets a
* stream of responses.
*/
class GCPServerStreamCall extends GcpBaseCall
{
private $response = null;
protected function createRealCall($channel)
{
$this->real_call = new \Grpc\ServerStreamingCall($channel, $this->method, $this->deserialize, $this->options);
$this->has_real_call = true;
return $this->real_call;
}
/**
* Pick a channel and start the call.
*
* @param mixed $data The data to send
* @param array $metadata Metadata to send with the call, if applicable
* (optional)
* @param array $options An array of options, possible keys:
* 'flags' => a number (optional)
*/
public function start($argument, $metadata, $options)
{
$channel_ref = $this->_rpcPreProcess($argument);
$this->createRealCall($channel_ref->getRealChannel(
$this->gcp_channel->credentials));
$this->real_call->start($argument, $metadata, $options);
}
/**
* @return mixed An iterator of response values
*/
public function responses()
{
$response = $this->real_call->responses();
// Since the last response is empty for the server streaming RPC,
// the second last one is the last RPC response with payload.
// Use this one for searching the affinity key.
// The same as BidiStreaming.
if ($response) {
$this->response = $response;
}
return $response;
}
/**
* Wait for the server to send the status, and return it.
*
* @return \stdClass The status object, with integer $code, string
* $details, and array $metadata members
*/
public function getStatus()
{
$status = $this->real_call->getStatus();
$this->_rpcPostProcess($status, $this->response);
return $status;
}
/**
* @return mixed The metadata sent by the server
*/
public function getMetadata()
{
return $this->real_call->getMetadata();
}
}

View File

@@ -0,0 +1,70 @@
<?php
/*
*
* Copyright 2018 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
namespace Grpc\Gcp;
/**
* Represents an active call that sends a single message and then gets a
* single response.
*/
class GCPUnaryCall extends GcpBaseCall
{
protected function createRealCall($channel)
{
$this->real_call = new \Grpc\UnaryCall($channel, $this->method, $this->deserialize, $this->options);
$this->has_real_call = true;
return $this->real_call;
}
/**
* Pick a channel and start the call.
*
* @param mixed $data The data to send
* @param array $metadata Metadata to send with the call, if applicable
* (optional)
* @param array $options An array of options, possible keys:
* 'flags' => a number (optional)
*/
public function start($argument, $metadata, $options)
{
$channel_ref = $this->_rpcPreProcess($argument);
$real_channel = $channel_ref->getRealChannel($this->gcp_channel->credentials);
$this->createRealCall($real_channel);
$this->real_call->start($argument, $metadata, $options);
}
/**
* Wait for the server to respond with data and a status.
*
* @return array [response data, status]
*/
public function wait()
{
list($response, $status) = $this->real_call->wait();
$this->_rpcPostProcess($status, $response);
return [$response, $status];
}
/**
* @return mixed The metadata sent by the server
*/
public function getMetadata()
{
return $this->real_call->getMetadata();
}
}

View File

@@ -0,0 +1,223 @@
<?php
/*
*
* Copyright 2018 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
namespace Grpc\Gcp;
abstract class GcpBaseCall
{
const BOUND = 'BOUND';
const UNBIND = 'UNBIND';
const BIND = 'BIND';
protected $gcp_channel;
// It has the Grpc\Channel and related ref_count information for this RPC.
protected $channel_ref;
// If this RPC is 'UNBIND', use it instead of the one from response.
protected $affinity_key;
// Array of [affinity_key, command]
protected $_affinity;
// Information needed to create Grpc\Call object when the RPC starts.
protected $method;
protected $argument;
protected $metadata;
protected $options;
protected $deserialize;
// In GCP extension, it is when a RPC calls "start", we pick a channel.
// Thus we need to save the $me
protected $metadata_rpc = array();
// first_rpc is used to check whether the first request is sent for client
// streaming RPC.
protected $has_real_call = null;
protected $real_call;
/**
* Create a new Call wrapper object.
*
* @param Channel $channel The channel to communicate on
* @param string $method The method to call on the
* remote server
* @param callback $deserialize A callback function to deserialize
* the response
* @param array $options Call options (optional)
*/
public function __construct($channel, $method, $deserialize, $options)
{
$this->gcp_channel = $channel;
$this->method = $method;
$this->deserialize = $deserialize;
$this->options = $options;
$this->_affinity = null;
if (isset($this->gcp_channel->affinity_conf['affinity_by_method'][$method])) {
$this->_affinity = $this->gcp_channel->affinity_conf['affinity_by_method'][$method];
}
}
/**
* Pick a ChannelRef from the channel pool based on the request and
* the affinity config.
*
* @param mixed $argument Requests.
*
* @return ChannelRef
*/
protected function _rpcPreProcess($argument)
{
$this->affinity_key = null;
if ($this->_affinity) {
$command = $this->_affinity['command'];
if ($command == self::BOUND || $command == self::UNBIND) {
$this->affinity_key = $this->getAffinityKeyFromProto($argument);
}
}
$this->channel_ref = $this->gcp_channel->getChannelRef($this->affinity_key);
$this->channel_ref->activeStreamRefIncr();
return $this->channel_ref;
}
/**
* Update ChannelRef when RPC finishes.
*
* @param \stdClass $status The status object, with integer $code, string
* $details, and array $metadata members
* @param mixed $response Response.
*/
protected function _rpcPostProcess($status, $response)
{
if ($this->_affinity) {
$command = $this->_affinity['command'];
if ($command == self::BIND) {
if ($status->code != \Grpc\STATUS_OK) {
return;
}
$affinity_key = $this->getAffinityKeyFromProto($response);
$this->gcp_channel->bind($this->channel_ref, $affinity_key);
} elseif ($command == self::UNBIND) {
$this->gcp_channel->unbind($this->affinity_key);
}
}
$this->channel_ref->activeStreamRefDecr();
}
/**
* Get the affinity key based on the affinity config.
*
* @param mixed $proto Objects may contain the affinity key.
*
* @return string Affinity key.
*/
protected function getAffinityKeyFromProto($proto)
{
if ($this->_affinity) {
$names = $this->_affinity['affinityKey'];
$names_arr = explode(".", $names);
foreach ($names_arr as $name) {
$getAttrMethod = 'get' . ucfirst($name);
$proto = call_user_func_array(array($proto, $getAttrMethod), array());
}
return $proto;
}
echo "Cannot find the field in the proto\n";
}
/**
* @return mixed The metadata sent by the server
*/
public function getMetadata()
{
if (!$this->has_real_call) {
$this->createRealCall();
$this->has_real_call = true;
}
return $this->real_call->getMetadata();
}
/**
* @return mixed The trailing metadata sent by the server
*/
public function getTrailingMetadata()
{
if (!$this->has_real_call) {
$this->createRealCall();
$this->has_real_call = true;
}
return $this->real_call->getTrailingMetadata();
}
/**
* @return string The URI of the endpoint
*/
public function getPeer()
{
if (!$this->has_real_call) {
$this->createRealCall();
$this->has_real_call = true;
}
return $this->real_call->getPeer();
}
/**
* Cancels the call.
*/
public function cancel()
{
if (!$this->has_real_call) {
$this->has_real_call = true;
$this->createRealCall();
}
$this->real_call->cancel();
}
/**
* Serialize a message to the protobuf binary format.
*
* @param mixed $data The Protobuf message
*
* @return string The protobuf binary format
*/
protected function _serializeMessage($data)
{
return $this->real_call->_serializeMessage($data);
}
/**
* Deserialize a response value to an object.
*
* @param string $value The binary value to deserialize
*
* @return mixed The deserialized value
*/
protected function _deserializeResponse($value)
{
return $this->real_call->_deserializeResponse($value);
}
/**
* Set the CallCredentials for the underlying Call.
*
* @param CallCredentials $call_credentials The CallCredentials object
*/
public function setCallCredentials($call_credentials)
{
$this->call->setCredentials($call_credentials);
}
}

View File

@@ -0,0 +1,281 @@
<?php
/*
*
* Copyright 2018 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
namespace Grpc\Gcp;
/**
* GcpExtensionChannel maintains an array of channels for certain API.
*/
class GcpExtensionChannel
{
public $max_size;
public $max_concurrent_streams_low_watermark;
public $target;
public $options;
public $affinity_by_method;
public $affinity_key_to_channel_ref;
public $channel_refs;
public $credentials;
public $affinity_conf;
private $is_closed;
/**
* @return array An array of ChannelRefs created for certain API.
*/
public function getChannelRefs()
{
return $this->channel_refs;
}
/**
* @param string $hostname
* @param array $opts Options to create a \Grpc\Channel and affinity config
*/
public function __construct($hostname = null, $opts = array())
{
if ($hostname == null || !is_array($opts)) {
throw new \InvalidArgumentException("Expected hostname is empty");
}
$this->max_size = 10;
$this->max_concurrent_streams_low_watermark = 100;
if (isset($opts['affinity_conf'])) {
if (isset($opts['affinity_conf']['channelPool'])) {
if (isset($opts['affinity_conf']['channelPool']['maxSize'])) {
$this->max_size = $opts['affinity_conf']['channelPool']['maxSize'];
}
if (isset($opts['affinity_conf']['channelPool']['maxConcurrentStreamsLowWatermark'])) {
$this->max_concurrent_streams_low_watermark =
$opts['affinity_conf']['channelPool']['maxConcurrentStreamsLowWatermark'];
}
}
$this->affinity_by_method = $opts['affinity_conf']['affinity_by_method'];
$this->affinity_conf = $opts['affinity_conf'];
}
$this->target = $hostname;
$this->affinity_key_to_channel_ref = array();
$this->channel_refs = array();
$this->updateOpts($opts);
// Initiate a Grpc\Channel at the beginning in order to keep the same
// behavior as the Grpc.
$channel_ref = $this->getChannelRef();
$channel_ref->getRealChannel($this->credentials);
}
/**
* @param array $opts Options to create a \Grpc\Channel
*/
public function updateOpts($opts)
{
if (isset($opts['credentials'])) {
$this->credentials = $opts['credentials'];
}
unset($opts['affinity_conf']);
unset($opts['credentials']);
$this->options = $opts;
$this->is_closed = false;
}
/**
* Bind the ChannelRef with the affinity key. This is a private method.
*
* @param ChannelRef $channel_ref
* @param string $affinity_key
*
* @return ChannelRef
*/
public function bind($channel_ref, $affinity_key)
{
if (!array_key_exists($affinity_key, $this->affinity_key_to_channel_ref)) {
$this->affinity_key_to_channel_ref[$affinity_key] = $channel_ref;
}
$channel_ref->affinityRefIncr();
return $channel_ref;
}
/**
* Unbind the affinity key. This is a private method.
*
* @param string $affinity_key
*
* @return ChannelRef
*/
public function unbind($affinity_key)
{
$channel_ref = null;
if (array_key_exists($affinity_key, $this->affinity_key_to_channel_ref)) {
$channel_ref = $this->affinity_key_to_channel_ref[$affinity_key];
$channel_ref->affinityRefDecr();
}
unset($this->affinity_key_to_channel_ref[$affinity_key]);
return $channel_ref;
}
public function cmp_by_active_stream_ref($a, $b)
{
return $a->getActiveStreamRef() - $b->getActiveStreamRef();
}
/**
* Pick or create a ChannelRef from the pool by affinity key.
*
* @param string $affinity_key
*
* @return ChannelRef
*/
public function getChannelRef($affinity_key = null)
{
if ($affinity_key) {
if (array_key_exists($affinity_key, $this->affinity_key_to_channel_ref)) {
return $this->affinity_key_to_channel_ref[$affinity_key];
}
return $this->getChannelRef();
}
usort($this->channel_refs, array($this, 'cmp_by_active_stream_ref'));
if (count($this->channel_refs) > 0 && $this->channel_refs[0]->getActiveStreamRef() <
$this->max_concurrent_streams_low_watermark) {
return $this->channel_refs[0];
}
$num_channel_refs = count($this->channel_refs);
if ($num_channel_refs < $this->max_size) {
// grpc_target_persist_bound stands for how many channels can be persisted for
// the same target in the C extension. It is possible that the user use the pure
// gRPC and this GCP extension at the same time, which share the same target. In this case
// pure gRPC channel may occupy positions in C extension, which deletes some channels created
// by this GCP extension.
// If that happens, it won't cause the script failure because we saves all arguments for creating
// a channel instead of a channel itself. If we watch to fetch a GCP channel already deleted,
// it will create a new channel. The only cons is the latency of the first RPC will high because
// it will establish the connection again.
if (!isset($this->options['grpc_target_persist_bound']) ||
$this->options['grpc_target_persist_bound'] < $this->max_size) {
$this->options['grpc_target_persist_bound'] = $this->max_size;
}
$cur_opts = array_merge($this->options,
['grpc_gcp_channel_id' => $num_channel_refs]);
$channel_ref = new ChannelRef($this->target, $num_channel_refs, $cur_opts);
array_unshift($this->channel_refs, $channel_ref);
}
return $this->channel_refs[0];
}
/**
* Get the connectivity state of the channel
*
* @param bool $try_to_connect try to connect on the channel
*
* @return int The grpc connectivity state
* @throws \InvalidArgumentException
*/
public function getConnectivityState($try_to_connect = false)
{
// Since getRealChannel is creating a PHP Channel object. However in gRPC, when a Channel
// object is closed, we only mark this Object to be invalid. Thus, we need a global variable
// to mark whether this GCPExtensionChannel is close or not.
if ($this->is_closed) {
throw new \RuntimeException("Channel has already been closed");
}
$ready = 0;
$idle = 0;
$connecting = 0;
$transient_failure = 0;
$shutdown = 0;
foreach ($this->channel_refs as $channel_ref) {
$state = $channel_ref->getRealChannel($this->credentials)->getConnectivityState($try_to_connect);
switch ($state) {
case \Grpc\CHANNEL_READY:
$ready += 1;
break 2;
case \Grpc\CHANNEL_FATAL_FAILURE:
$shutdown += 1;
break;
case \Grpc\CHANNEL_CONNECTING:
$connecting += 1;
break;
case \Grpc\CHANNEL_TRANSIENT_FAILURE:
$transient_failure += 1;
break;
case \Grpc\CHANNEL_IDLE:
$idle += 1;
break;
}
}
if ($ready > 0) {
return \Grpc\CHANNEL_READY;
} elseif ($idle > 0) {
return \Grpc\CHANNEL_IDLE;
} elseif ($connecting > 0) {
return \Grpc\CHANNEL_CONNECTING;
} elseif ($transient_failure > 0) {
return \Grpc\CHANNEL_TRANSIENT_FAILURE;
} elseif ($shutdown > 0) {
return \Grpc\CHANNEL_SHUTDOWN;
}
}
/**
* Watch the connectivity state of the channel until it changed
*
* @param int $last_state The previous connectivity state of the channel
* @param Timeval $deadline_obj The deadline this function should wait until
*
* @return bool If the connectivity state changes from last_state
* before deadline
* @throws \InvalidArgumentException
*/
public function watchConnectivityState($last_state, $deadline_obj = null)
{
if ($deadline_obj == null || !is_a($deadline_obj, '\Grpc\Timeval')) {
throw new \InvalidArgumentException("");
}
// Since getRealChannel is creating a PHP Channel object. However in gRPC, when a Channel
// object is closed, we only mark this Object to be invalid. Thus, we need a global variable
// to mark whether this GCPExtensionChannel is close or not.
if ($this->is_closed) {
throw new \RuntimeException("Channel has already been closed");
}
$state = 0;
foreach ($this->channel_refs as $channel_ref) {
$state = $channel_ref->getRealChannel($this->credentials)->watchConnectivityState($last_state, $deadline_obj);
}
return $state;
}
/**
* Get the endpoint this call/stream is connected to
*
* @return string The URI of the endpoint
*/
public function getTarget()
{
return $this->target;
}
/**
* Close the channel
*/
public function close()
{
foreach ($this->channel_refs as $channel_ref) {
$channel_ref->getRealChannel($this->credentials)->close();
}
$this->is_closed = true;
}
}

View File

@@ -0,0 +1,39 @@
<?php
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: grpc_gcp.proto
namespace GPBMetadata;
class GrpcGcp
{
public static $is_initialized = false;
public static function initOnce()
{
$pool = \Google\Protobuf\Internal\DescriptorPool::getGeneratedPool();
if (static::$is_initialized == true) {
return;
}
$pool->internalAddGeneratedFile(hex2bin(
"0ac9030a0e677270635f6763702e70726f746f1208677270632e67637022" .
"670a09417069436f6e66696712310a0c6368616e6e656c5f706f6f6c1802" .
"2001280b321b2e677270632e6763702e4368616e6e656c506f6f6c436f6e" .
"66696712270a066d6574686f6418e9072003280b32162e677270632e6763" .
"702e4d6574686f64436f6e66696722690a114368616e6e656c506f6f6c43" .
"6f6e66696712100a086d61785f73697a6518012001280d12140a0c69646c" .
"655f74696d656f7574180220012804122c0a246d61785f636f6e63757272" .
"656e745f73747265616d735f6c6f775f77617465726d61726b1803200128" .
"0d22490a0c4d6574686f64436f6e666967120c0a046e616d651801200328" .
"09122b0a08616666696e69747918e9072001280b32182e677270632e6763" .
"702e416666696e697479436f6e6669672285010a0e416666696e69747943" .
"6f6e66696712310a07636f6d6d616e6418022001280e32202e677270632e" .
"6763702e416666696e697479436f6e6669672e436f6d6d616e6412140a0c" .
"616666696e6974795f6b6579180320012809222a0a07436f6d6d616e6412" .
"090a05424f554e44100012080a0442494e441001120a0a06554e42494e44" .
"1002620670726f746f33"
));
static::$is_initialized = true;
}
}

View File

@@ -0,0 +1,87 @@
<?php
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: grpc_gcp.proto
namespace Grpc\Gcp;
use Google\Protobuf\Internal\GPBUtil;
/**
* Generated from protobuf message <code>grpc.gcp.AffinityConfig</code>
*/
class AffinityConfig extends \Google\Protobuf\Internal\Message
{
/**
* The affinity command applies on the selected gRPC methods.
*
* Generated from protobuf field <code>.grpc.gcp.AffinityConfig.Command command = 2;</code>
*/
private $command = 0;
/**
* The field path of the affinity key in the request/response message.
* For example: "f.a", "f.b.d", etc.
*
* Generated from protobuf field <code>string affinity_key = 3;</code>
*/
private $affinity_key = '';
public function __construct()
{
\GPBMetadata\GrpcGcp::initOnce();
parent::__construct();
}
/**
* The affinity command applies on the selected gRPC methods.
*
* Generated from protobuf field <code>.grpc.gcp.AffinityConfig.Command command = 2;</code>
* @return int
*/
public function getCommand()
{
return $this->command;
}
/**
* The affinity command applies on the selected gRPC methods.
*
* Generated from protobuf field <code>.grpc.gcp.AffinityConfig.Command command = 2;</code>
* @param int $var
* @return $this
*/
public function setCommand($var)
{
GPBUtil::checkEnum($var, \Grpc\Gcp\AffinityConfig_Command::class);
$this->command = $var;
return $this;
}
/**
* The field path of the affinity key in the request/response message.
* For example: "f.a", "f.b.d", etc.
*
* Generated from protobuf field <code>string affinity_key = 3;</code>
* @return string
*/
public function getAffinityKey()
{
return $this->affinity_key;
}
/**
* The field path of the affinity key in the request/response message.
* For example: "f.a", "f.b.d", etc.
*
* Generated from protobuf field <code>string affinity_key = 3;</code>
* @param string $var
* @return $this
*/
public function setAffinityKey($var)
{
GPBUtil::checkString($var, true);
$this->affinity_key = $var;
return $this;
}
}

View File

@@ -0,0 +1,38 @@
<?php
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: grpc_gcp.proto
namespace Grpc\Gcp;
/**
* Protobuf enum <code>Grpc\Gcp\AffinityConfig\Command</code>
*/
class AffinityConfig_Command
{
/**
* The annotated method will be required to be bound to an existing session
* to execute the RPC. The corresponding <affinity_key_field_path> will be
* used to find the affinity key from the request message.
*
* Generated from protobuf enum <code>BOUND = 0;</code>
*/
const BOUND = 0;
/**
* The annotated method will establish the channel affinity with the channel
* which is used to execute the RPC. The corresponding
* <affinity_key_field_path> will be used to find the affinity key from the
* response message.
*
* Generated from protobuf enum <code>BIND = 1;</code>
*/
const BIND = 1;
/**
* The annotated method will remove the channel affinity with the channel
* which is used to execute the RPC. The corresponding
* <affinity_key_field_path> will be used to find the affinity key from the
* request message.
*
* Generated from protobuf enum <code>UNBIND = 2;</code>
*/
const UNBIND = 2;
}

View File

@@ -0,0 +1,84 @@
<?php
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: grpc_gcp.proto
namespace Grpc\Gcp;
use Google\Protobuf\Internal\GPBUtil;
/**
* Generated from protobuf message <code>grpc.gcp.ApiConfig</code>
*/
class ApiConfig extends \Google\Protobuf\Internal\Message
{
/**
* The channel pool configurations.
*
* Generated from protobuf field <code>.grpc.gcp.ChannelPoolConfig channel_pool = 2;</code>
*/
private $channel_pool = null;
/**
* The method configurations.
*
* Generated from protobuf field <code>repeated .grpc.gcp.MethodConfig method = 1001;</code>
*/
private $method;
public function __construct()
{
\GPBMetadata\GrpcGcp::initOnce();
parent::__construct();
}
/**
* The channel pool configurations.
*
* Generated from protobuf field <code>.grpc.gcp.ChannelPoolConfig channel_pool = 2;</code>
* @return \Grpc\Gcp\ChannelPoolConfig
*/
public function getChannelPool()
{
return $this->channel_pool;
}
/**
* The channel pool configurations.
*
* Generated from protobuf field <code>.grpc.gcp.ChannelPoolConfig channel_pool = 2;</code>
* @param \Grpc\Gcp\ChannelPoolConfig $var
* @return $this
*/
public function setChannelPool($var)
{
GPBUtil::checkMessage($var, \Grpc\Gcp\ChannelPoolConfig::class);
$this->channel_pool = $var;
return $this;
}
/**
* The method configurations.
*
* Generated from protobuf field <code>repeated .grpc.gcp.MethodConfig method = 1001;</code>
* @return \Google\Protobuf\Internal\RepeatedField
*/
public function getMethod()
{
return $this->method;
}
/**
* The method configurations.
*
* Generated from protobuf field <code>repeated .grpc.gcp.MethodConfig method = 1001;</code>
* @param \Grpc\Gcp\MethodConfig[]|\Google\Protobuf\Internal\RepeatedField $var
* @return $this
*/
public function setMethod($var)
{
$arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Grpc\Gcp\MethodConfig::class);
$this->method = $arr;
return $this;
}
}

View File

@@ -0,0 +1,122 @@
<?php
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: grpc_gcp.proto
namespace Grpc\Gcp;
use Google\Protobuf\Internal\GPBUtil;
/**
* Generated from protobuf message <code>grpc.gcp.ChannelPoolConfig</code>
*/
class ChannelPoolConfig extends \Google\Protobuf\Internal\Message
{
/**
* The max number of channels in the pool.
*
* Generated from protobuf field <code>uint32 max_size = 1;</code>
*/
private $max_size = 0;
/**
* The idle timeout (seconds) of channels without bound affinity sessions.
*
* Generated from protobuf field <code>uint64 idle_timeout = 2;</code>
*/
private $idle_timeout = 0;
/**
* The low watermark of max number of concurrent streams in a channel.
* New channel will be created once it get hit, until we reach the max size
* of the channel pool.
*
* Generated from protobuf field <code>uint32 max_concurrent_streams_low_watermark = 3;</code>
*/
private $max_concurrent_streams_low_watermark = 0;
public function __construct()
{
\GPBMetadata\GrpcGcp::initOnce();
parent::__construct();
}
/**
* The max number of channels in the pool.
*
* Generated from protobuf field <code>uint32 max_size = 1;</code>
* @return int
*/
public function getMaxSize()
{
return $this->max_size;
}
/**
* The max number of channels in the pool.
*
* Generated from protobuf field <code>uint32 max_size = 1;</code>
* @param int $var
* @return $this
*/
public function setMaxSize($var)
{
GPBUtil::checkUint32($var);
$this->max_size = $var;
return $this;
}
/**
* The idle timeout (seconds) of channels without bound affinity sessions.
*
* Generated from protobuf field <code>uint64 idle_timeout = 2;</code>
* @return int|string
*/
public function getIdleTimeout()
{
return $this->idle_timeout;
}
/**
* The idle timeout (seconds) of channels without bound affinity sessions.
*
* Generated from protobuf field <code>uint64 idle_timeout = 2;</code>
* @param int|string $var
* @return $this
*/
public function setIdleTimeout($var)
{
GPBUtil::checkUint64($var);
$this->idle_timeout = $var;
return $this;
}
/**
* The low watermark of max number of concurrent streams in a channel.
* New channel will be created once it get hit, until we reach the max size
* of the channel pool.
*
* Generated from protobuf field <code>uint32 max_concurrent_streams_low_watermark = 3;</code>
* @return int
*/
public function getMaxConcurrentStreamsLowWatermark()
{
return $this->max_concurrent_streams_low_watermark;
}
/**
* The low watermark of max number of concurrent streams in a channel.
* New channel will be created once it get hit, until we reach the max size
* of the channel pool.
*
* Generated from protobuf field <code>uint32 max_concurrent_streams_low_watermark = 3;</code>
* @param int $var
* @return $this
*/
public function setMaxConcurrentStreamsLowWatermark($var)
{
GPBUtil::checkUint32($var);
$this->max_concurrent_streams_low_watermark = $var;
return $this;
}
}

View File

@@ -0,0 +1,90 @@
<?php
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: grpc_gcp.proto
namespace Grpc\Gcp;
use Google\Protobuf\Internal\GPBUtil;
/**
* Generated from protobuf message <code>grpc.gcp.MethodConfig</code>
*/
class MethodConfig extends \Google\Protobuf\Internal\Message
{
/**
* A fully qualified name of a gRPC method, or a wildcard pattern ending
* with .*, such as foo.bar.A, foo.bar.*. Method configs are evaluated
* sequentially, and the first one takes precedence.
*
* Generated from protobuf field <code>repeated string name = 1;</code>
*/
private $name;
/**
* The channel affinity configurations.
*
* Generated from protobuf field <code>.grpc.gcp.AffinityConfig affinity = 1001;</code>
*/
private $affinity = null;
public function __construct()
{
\GPBMetadata\GrpcGcp::initOnce();
parent::__construct();
}
/**
* A fully qualified name of a gRPC method, or a wildcard pattern ending
* with .*, such as foo.bar.A, foo.bar.*. Method configs are evaluated
* sequentially, and the first one takes precedence.
*
* Generated from protobuf field <code>repeated string name = 1;</code>
* @return \Google\Protobuf\Internal\RepeatedField
*/
public function getName()
{
return $this->name;
}
/**
* A fully qualified name of a gRPC method, or a wildcard pattern ending
* with .*, such as foo.bar.A, foo.bar.*. Method configs are evaluated
* sequentially, and the first one takes precedence.
*
* Generated from protobuf field <code>repeated string name = 1;</code>
* @param string[]|\Google\Protobuf\Internal\RepeatedField $var
* @return $this
*/
public function setName($var)
{
$arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::STRING);
$this->name = $arr;
return $this;
}
/**
* The channel affinity configurations.
*
* Generated from protobuf field <code>.grpc.gcp.AffinityConfig affinity = 1001;</code>
* @return \Grpc\Gcp\AffinityConfig
*/
public function getAffinity()
{
return $this->affinity;
}
/**
* The channel affinity configurations.
*
* Generated from protobuf field <code>.grpc.gcp.AffinityConfig affinity = 1001;</code>
* @param \Grpc\Gcp\AffinityConfig $var
* @return $this
*/
public function setAffinity($var)
{
GPBUtil::checkMessage($var, \Grpc\Gcp\AffinityConfig::class);
$this->affinity = $var;
return $this;
}
}

View File

@@ -0,0 +1,70 @@
// Copyright 2018 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
syntax = "proto3";
package grpc.gcp;
message ApiConfig {
// The channel pool configurations.
ChannelPoolConfig channel_pool = 2;
// The method configurations.
repeated MethodConfig method = 1001;
}
message ChannelPoolConfig {
// The max number of channels in the pool.
uint32 max_size = 1;
// The idle timeout (seconds) of channels without bound affinity sessions.
uint64 idle_timeout = 2;
// The low watermark of max number of concurrent streams in a channel.
// New channel will be created once it get hit, until we reach the max size
// of the channel pool.
uint32 max_concurrent_streams_low_watermark = 3;
}
message MethodConfig {
// A fully qualified name of a gRPC method, or a wildcard pattern ending
// with .*, such as foo.bar.A, foo.bar.*. Method configs are evaluated
// sequentially, and the first one takes precedence.
repeated string name = 1;
// The channel affinity configurations.
AffinityConfig affinity = 1001;
}
message AffinityConfig {
enum Command {
// The annotated method will be required to be bound to an existing session
// to execute the RPC. The corresponding <affinity_key_field_path> will be
// used to find the affinity key from the request message.
BOUND = 0;
// The annotated method will establish the channel affinity with the channel
// which is used to execute the RPC. The corresponding
// <affinity_key_field_path> will be used to find the affinity key from the
// response message.
BIND = 1;
// The annotated method will remove the channel affinity with the channel
// which is used to execute the RPC. The corresponding
// <affinity_key_field_path> will be used to find the affinity key from the
// request message.
UNBIND = 2;
}
// The affinity command applies on the selected gRPC methods.
Command command = 2;
// The field path of the affinity key in the request/response message.
// For example: "f.a", "f.b.d", etc.
string affinity_key = 3;
}