├── .github ├── release-please.yml └── workflows │ └── tests.yml ├── .gitmodules ├── .php_cs.dist ├── LICENSE ├── README.md ├── cloudprober ├── bins │ └── opt │ │ └── grpc_php_plugin ├── cloudprober.cfg ├── codegen.sh ├── composer.json └── grpc_gpc_prober │ ├── firestore_probes.php │ ├── prober.php │ ├── spanner_probes.php │ └── stackdriver_util.php ├── composer.json ├── doc └── gRPC-client-user-guide.md └── src ├── ChannelRef.php ├── Config.php ├── CreatedByDeserializeCheck.php ├── GCPBidiStreamingCall.php ├── GCPCallInvoker.php ├── GCPClientStreamCall.php ├── GCPServerStreamCall.php ├── GCPUnaryCall.php ├── GcpBaseCall.php ├── GcpExtensionChannel.php ├── generated ├── GPBMetadata │ └── GrpcGcp.php └── Grpc │ └── Gcp │ ├── AffinityConfig.php │ ├── AffinityConfig_Command.php │ ├── ApiConfig.php │ ├── ChannelPoolConfig.php │ └── MethodConfig.php └── grpc_gcp.proto /.github/release-please.yml: -------------------------------------------------------------------------------- 1 | releaseType: simple 2 | handleGHRelease: true 3 | primaryBranch: main 4 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Test Suite 2 | on: 3 | push: 4 | branches: 5 | - master 6 | pull_request: 7 | jobs: 8 | test: 9 | runs-on: ubuntu-latest 10 | strategy: 11 | matrix: 12 | php: [ "8.0", "8.1", "8.2", "8.3", "8.4" ] 13 | name: "PHP ${{ matrix.php }} Unit Test" 14 | steps: 15 | - uses: actions/checkout@v2 16 | - uses: codecov/codecov-action@v1 17 | - name: Setup PHP 18 | uses: shivammathur/setup-php@v2 19 | with: 20 | php-version: ${{ matrix.php }} 21 | tools: pecl 22 | extensions: grpc-1.49.0 23 | - name: Install composer dependencies 24 | uses: nick-invision/retry@v1 25 | with: 26 | timeout_minutes: 10 27 | max_attempts: 3 28 | command: composer install 29 | - name: Run PHPUnit 30 | run: vendor/bin/phpunit -v 31 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "third_party/googleapis"] 2 | path = third_party/googleapis 3 | url = https://github.com/googleapis/googleapis.git 4 | -------------------------------------------------------------------------------- /.php_cs.dist: -------------------------------------------------------------------------------- 1 | setRules([ 4 | '@PSR2' => true, 5 | 'concat_space' => ['spacing' => 'one'], 6 | 'no_unused_imports' => true, 7 | 'method_argument_space' => false, 8 | ]) 9 | ->setFinder( 10 | PhpCsFixer\Finder::create() 11 | ->notPath('firestore') 12 | ->in(__DIR__) 13 | ) 14 | ; 15 | 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gRPC for GCP extensions 2 | 3 | Copyright 2018 4 | [The gRPC Authors](https://github.com/grpc/grpc/blob/master/AUTHORS) 5 | 6 | ## About This Repository 7 | 8 | This repo is created to support GCP specific extensions for gRPC. To use the extension features, please refer to [src](src). 9 | 10 | This repo also contains supporting infrastructures such as end2end tests and benchmarks for accessing cloud APIs with gRPC client libraries. 11 | 12 | ## License 13 | 14 | Apache 2.0 - See [LICENSE](LICENSE) for more information. 15 | -------------------------------------------------------------------------------- /cloudprober/bins/opt/grpc_php_plugin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/grpc-gcp-php/e585b7721bbe806ef45b5c52ae43dfc2bff89968/cloudprober/bins/opt/grpc_php_plugin -------------------------------------------------------------------------------- /cloudprober/cloudprober.cfg: -------------------------------------------------------------------------------- 1 | probe { 2 | type: EXTERNAL 3 | name: "spanner" 4 | interval_msec: 1800000 5 | timeout_msec: 30000 6 | targets { dummy_targets {} } # No targets for external probe 7 | external_probe { 8 | mode: ONCE 9 | command: "php grpc_gpc_prober/prober.php --api=spanner" 10 | } 11 | } 12 | 13 | probe { 14 | type: EXTERNAL 15 | name: "firestore" 16 | interval_msec: 1800000 17 | timeout_msec: 30000 18 | targets { dummy_targets {} } # No targets for external probe 19 | external_probe { 20 | mode: ONCE 21 | command: "php grpc_gpc_prober/prober.php --api=firestore" 22 | } 23 | } 24 | 25 | surfacer { 26 | type: STACKDRIVER 27 | name: "stackdriver" 28 | stackdriver_surfacer { 29 | monitoring_url: "custom.googleapis.com/cloudprober/" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /cloudprober/codegen.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | cd "$(dirname "$0")" 3 | 4 | rm -rf google 5 | 6 | for p in $(find ../third_party/googleapis/google -type f -name *.proto); do 7 | protoc \ 8 | --proto_path=../third_party/googleapis \ 9 | --php_out=./ \ 10 | --grpc_out=./ \ 11 | --plugin=protoc-gen-grpc="$(which grpc_php_plugin)" \ 12 | "$p" 13 | done 14 | -------------------------------------------------------------------------------- /cloudprober/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "grpc_gcp_prober", 3 | "description": "grpc cloudprober for PHP", 4 | "require": { 5 | "grpc/grpc": "^v1.15.0", 6 | "google/auth": "^v1.4.0", 7 | "google/cloud": "^v0.86.0" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /cloudprober/grpc_gpc_prober/firestore_probes.php: -------------------------------------------------------------------------------- 1 | setParent($_PARENT_RESOURCE); 20 | $time_start = microtime_float(); 21 | 22 | $client->ListDocuments($list_document_request); 23 | 24 | $lantency = (microtime_float()- $time_start) * 1000; 25 | $metrics['list_documents_latency_ms'] = $lantency; 26 | 27 | } 28 | 29 | $probFunctions = [ 30 | 'documents' => 'document' 31 | ]; 32 | 33 | return $probFunctions; -------------------------------------------------------------------------------- /cloudprober/grpc_gpc_prober/prober.php: -------------------------------------------------------------------------------- 1 | AuthMetadataPlugin($credentials, $request); 28 | $ssl_credentials = Grpc\ChannelCredentials::createSsl(); 29 | $composit_credentials = $grpc->composite_channel_credentials($ssl_credentials, $google_auth_credentials); 30 | return $grpc_gcp->secure_channel($target, $composit_credentials, $kwargs); 31 | } 32 | 33 | function getStubChannel($target){ 34 | $res = $auth->default([$_OAUTH_SCOPE]); 35 | $cred = $res[0]; 36 | return secureAuthorizedChannel($cred, Request(), $target); 37 | }*/ 38 | 39 | function executeProbes($api){ 40 | global $_OAUTH_SCOPE; 41 | global $_SPANNER_TARGET; 42 | global $_FIRESTORE_TARGET; 43 | 44 | global $spanner_probes; 45 | global $firestore_probes; 46 | 47 | $util = new StackdriverUtil($api); 48 | $auth = Google\Auth\ApplicationDefaultCredentials::getCredentials($_OAUTH_SCOPE); 49 | $opts = [ 50 | 'credentials' => Grpc\ChannelCredentials::createSsl(), 51 | 'update_metadata' => $auth->getUpdateMetadataFunc(), 52 | ]; 53 | 54 | if($api == 'spanner'){ 55 | $client = new SpannerGrpcClient($_SPANNER_TARGET, $opts); 56 | $probe_functions = $spanner_probes; 57 | } 58 | else if($api == 'firestore'){ 59 | $client = new FirestoreGrpcClient($_FIRESTORE_TARGET, $opts); 60 | $probe_functions = $firestore_probes; 61 | } 62 | else{ 63 | echo 'grpc not implemented for '.$api; 64 | exit(1); 65 | } 66 | 67 | $total = sizeof($probe_functions); 68 | $success = 0; 69 | $metrics = []; 70 | 71 | # Execute all probes for given api 72 | foreach ($probe_functions as $probe_name => $probe_function) { 73 | try{ 74 | $probe_function($client, $metrics); 75 | $success++; 76 | } 77 | catch(Exception $e){ 78 | $util->reportError($e); 79 | } 80 | 81 | } 82 | 83 | if($success == $total){ 84 | $util->setSuccess(True); 85 | } 86 | 87 | $util->addMetrics($metrics); 88 | $util->outputMetrics(); 89 | 90 | if($success != $total){ 91 | # TODO: exit system 92 | exit(1); 93 | } 94 | 95 | } 96 | 97 | function main(){ 98 | $args = getArgs(); 99 | executeProbes($args['api']); 100 | } 101 | 102 | main(); 103 | -------------------------------------------------------------------------------- /cloudprober/grpc_gpc_prober/spanner_probes.php: -------------------------------------------------------------------------------- 1 | code !== Grpc\STATUS_OK) { 18 | echo "Call did not complete successfully. Status object:\n"; 19 | var_dump($status); 20 | exit(1); 21 | } 22 | } 23 | 24 | function microtime_float() 25 | { 26 | list($usec, $sec) = explode(" ", microtime()); 27 | return ((float)$usec + (float)$sec); 28 | } 29 | 30 | /* 31 | Probes to test session related grpc call from Spanner stub. 32 | 33 | Includes tests against CreateSession, GetSession, ListSessions, and 34 | DeleteSession of Spanner stub. 35 | 36 | Args: 37 | stub: An object of SpannerStub. 38 | metrics: A list of metrics. 39 | 40 | */ 41 | 42 | function sessionManagement($client, &$metrics){ 43 | global $_DATABASE; 44 | 45 | $createSessionRequest = new Google\Cloud\Spanner\V1\CreateSessionRequest(); 46 | $createSessionRequest->setDatabase($_DATABASE); 47 | #Create Session test 48 | #Create 49 | $time_start = microtime_float(); 50 | list($session, $status) = $client->CreateSession($createSessionRequest)->wait(); 51 | 52 | hardAssertIfStatusOk($status); 53 | hardAssert($session !== null, 'Call completed with a null response'); 54 | 55 | $lantency = (microtime_float()- $time_start) * 1000; 56 | $metrics['create_session_latency_ms'] = $lantency; 57 | 58 | #Get Session 59 | $getSessionRequest = new Google\Cloud\Spanner\V1\GetSessionRequest(); 60 | $getSessionRequest->setName($session->getName()); 61 | $time_start = microtime_float(); 62 | $response = $client->GetSession($getSessionRequest); 63 | $response->wait(); 64 | $lantency = (microtime_float() - $time_start) * 1000; 65 | $metrics['get_session_latency_ms'] = $lantency; 66 | 67 | #List session 68 | $listSessionsRequest = new Google\Cloud\Spanner\V1\ListSessionsRequest(); 69 | $listSessionsRequest->setDatabase($_DATABASE); 70 | $time_start = microtime_float(); 71 | $response = $client->ListSessions($listSessionsRequest); 72 | $lantency = (microtime_float() - $time_start) * 1000; 73 | $metrics['list_sessions_latency_ms'] = $lantency; 74 | 75 | #Delete session 76 | $deleteSessionRequest = new Google\Cloud\Spanner\V1\DeleteSessionRequest(); 77 | $deleteSessionRequest->setName($session->getName()); 78 | $time_start = microtime_float(); 79 | $client->deleteSession($deleteSessionRequest); 80 | $lantency = (microtime_float() - $time_start) * 1000; 81 | $metrics['delete_session_latency_ms'] = $lantency; 82 | 83 | } 84 | 85 | /* 86 | Probes to test ExecuteSql and ExecuteStreamingSql call from Spanner stub. 87 | 88 | Args: 89 | stub: An object of SpannerStub. 90 | metrics: A list of metrics. 91 | 92 | */ 93 | function executeSql($client, &$metrics){ 94 | global $_DATABASE; 95 | 96 | $createSessionRequest = new Google\Cloud\Spanner\V1\CreateSessionRequest(); 97 | $createSessionRequest->setDatabase($_DATABASE); 98 | list($session, $status) = $client->CreateSession($createSessionRequest)->wait(); 99 | 100 | hardAssertIfStatusOk($status); 101 | hardAssert($session !== null, 'Call completed with a null response'); 102 | 103 | # Probing ExecuteSql call 104 | $time_start = microtime_float(); 105 | $executeSqlRequest = new Google\Cloud\Spanner\V1\ExecuteSqlRequest(); 106 | $executeSqlRequest->setSession($session->getName()); 107 | $executeSqlRequest->setSql('select * FROM users'); 108 | $result_set = $client->ExecuteSql($executeSqlRequest); 109 | $lantency = (microtime_float() - $time_start) * 1000; 110 | $metrics['execute_sql_latency_ms'] = $lantency; 111 | 112 | // TODO: Error check result_set 113 | 114 | # Probing ExecuteStreamingSql call 115 | $partial_result_set = $client->ExecuteStreamingSql($executeSqlRequest); 116 | 117 | $time_start = microtime_float(); 118 | $first_result = array_values($partial_result_set->getMetadata())[0]; 119 | $lantency = (microtime_float() - $time_start) * 1000; 120 | $metrics['execute_streaming_sql_latency_ms'] = $lantency; 121 | 122 | // TODO: Error Check for sreaming sql first result 123 | 124 | $deleteSessionRequest = new Google\Cloud\Spanner\V1\DeleteSessionRequest(); 125 | $deleteSessionRequest->setName($session->getName()); 126 | $client->deleteSession($deleteSessionRequest); 127 | } 128 | 129 | /* 130 | Probe to test Read and StreamingRead grpc call from Spanner stub. 131 | 132 | Args: 133 | stub: An object of SpannerStub. 134 | metrics: A list of metrics. 135 | */ 136 | 137 | function read($client, &$metrics){ 138 | global $_DATABASE; 139 | 140 | $createSessionRequest = new Google\Cloud\Spanner\V1\CreateSessionRequest(); 141 | $createSessionRequest->setDatabase($_DATABASE); 142 | list($session, $status) = $client->CreateSession($createSessionRequest)->wait(); 143 | 144 | hardAssertIfStatusOk($status); 145 | hardAssert($session !== null, 'Call completed with a null response'); 146 | 147 | # Probing Read call 148 | $time_start = microtime_float(); 149 | $readRequest = new Google\Cloud\Spanner\V1\ReadRequest(); 150 | $readRequest->setSession($session->getName()); 151 | $readRequest->setTable('users'); 152 | $readRequest->setColumns(['username', 'firstname', 'lastname']); 153 | $keyset = new Google\Cloud\Spanner\V1\KeySet(); 154 | $keyset->setAll(True); 155 | $readRequest->setKeySet($keyset); 156 | $result_set = $client->Read($readRequest); 157 | $lantency = (microtime_float() - $time_start) * 1000; 158 | $metrics['read_latency_ms'] = $lantency; 159 | 160 | // TODO: Error Check for result_set 161 | 162 | # Probing StreamingRead call 163 | $partial_result_set = $client->StreamingRead($readRequest); 164 | $time_start = microtime_float(); 165 | $first_result = array_values($partial_result_set->getMetadata())[0]; 166 | $lantency = (microtime_float() - $time_start) * 1000; 167 | $metrics['streaming_read_latency_ms'] = $lantency; 168 | 169 | //TODO: Error Check for streaming read first result 170 | 171 | $deleteSessionRequest = new Google\Cloud\Spanner\V1\DeleteSessionRequest(); 172 | $deleteSessionRequest->setName($session->getName()); 173 | $client->deleteSession($deleteSessionRequest); 174 | } 175 | 176 | /* 177 | Probe to test BeginTransaction, Commit and Rollback grpc from Spanner stub. 178 | 179 | Args: 180 | stub: An object of SpannerStub. 181 | metrics: A list of metrics. 182 | */ 183 | 184 | function transaction($client, &$metrics){ 185 | global $_DATABASE; 186 | 187 | $createSessionRequest = new Google\Cloud\Spanner\V1\CreateSessionRequest(); 188 | $createSessionRequest->setDatabase($_DATABASE); 189 | list($session, $status) = $client->CreateSession($createSessionRequest)->wait(); 190 | 191 | hardAssertIfStatusOk($status); 192 | hardAssert($session !== null, 'Call completed with a null response'); 193 | 194 | $txn_options = new Google\Cloud\Spanner\V1\TransactionOptions(); 195 | $rw = new Google\Cloud\Spanner\V1\TransactionOptions\ReadWrite(); 196 | $txn_options->setReadWrite($rw); 197 | $txn_request = new Google\Cloud\Spanner\V1\BeginTransactionRequest(); 198 | $txn_request->setSession($session->getName()); 199 | $txn_request->setOptions($txn_options); 200 | 201 | # Probing BeginTransaction call 202 | $time_start = microtime_float(); 203 | list($txn, $status) = $client->BeginTransaction($txn_request)->wait(); 204 | $lantency = (microtime_float() - $time_start) * 1000; 205 | $metrics['begin_transaction_latency_ms'] = $lantency; 206 | 207 | hardAssertIfStatusOk($status); 208 | hardAssert($txn !== null, 'Call completed with a null response'); 209 | 210 | # Probing Commit Call 211 | $commit_request = new Google\Cloud\Spanner\V1\CommitRequest(); 212 | $commit_request->setSession($session->getName()); 213 | $commit_request->setTransactionId($txn->getId()); 214 | 215 | $time_start = microtime_float(); 216 | $client->Commit($commit_request); 217 | $latency = (microtime_float() - $time_start) * 1000; 218 | $metrics['commit_latency_ms'] = $lantency; 219 | 220 | # Probing Rollback call 221 | list($txn, $status) = $client->BeginTransaction($txn_request)->wait(); 222 | $rollback_request = new Google\Cloud\Spanner\V1\RollbackRequest(); 223 | $rollback_request->setSession($session->getName()); 224 | $rollback_request->setTransactionId($txn->getId()); 225 | 226 | hardAssertIfStatusOk($status); 227 | hardAssert($txn !== null, 'Call completed with a null response'); 228 | 229 | $time_start = microtime_float(); 230 | $client->Rollback($rollback_request); 231 | $latency = (microtime_float() - $time_start) * 1000; 232 | $metrics['rollback_latency_ms'] = $latency; 233 | 234 | $deleteSessionRequest = new Google\Cloud\Spanner\V1\DeleteSessionRequest(); 235 | $deleteSessionRequest->setName($session->getName()); 236 | $client->deleteSession($deleteSessionRequest); 237 | } 238 | 239 | /* 240 | Probe to test PartitionQuery and PartitionRead grpc call from Spanner stub. 241 | 242 | Args: 243 | stub: An object of SpannerStub. 244 | metrics: A list of metrics. 245 | */ 246 | 247 | function partition($client, &$metrics){ 248 | global $_DATABASE; 249 | global $_TEST_USERNAME; 250 | 251 | $createSessionRequest = new Google\Cloud\Spanner\V1\CreateSessionRequest(); 252 | $createSessionRequest->setDatabase($_DATABASE); 253 | list($session, $status) = $client->CreateSession($createSessionRequest)->wait(); 254 | 255 | hardAssertIfStatusOk($status); 256 | hardAssert($session !== null, 'Call completed with a null response'); 257 | 258 | $txn_options = new Google\Cloud\Spanner\V1\TransactionOptions(); 259 | $ro = new Google\Cloud\Spanner\V1\TransactionOptions\PBReadOnly(); 260 | $txn_options->setReadOnly($ro); 261 | $txn_selector = new Google\Cloud\Spanner\V1\TransactionSelector(); 262 | $txn_selector->setBegin($txn_options); 263 | 264 | #Probing PartitionQuery call 265 | $ptn_query_request = new Google\Cloud\Spanner\V1\PartitionQueryRequest(); 266 | $ptn_query_request->setSession($session->getName()); 267 | $ptn_query_request->setSql('select * FROM users'); 268 | $ptn_query_request->setTransaction($txn_selector); 269 | 270 | $time_start = microtime_float(); 271 | $client->PartitionQuery($ptn_query_request); 272 | $lantency = (microtime_float() - $time_start) * 1000; 273 | $metrics['partition_query_latency_ms'] = $lantency; 274 | 275 | #Probing PartitionRead call 276 | $ptn_read_request = new Google\Cloud\Spanner\V1\PartitionReadRequest(); 277 | $ptn_read_request->setSession($session->getName()); 278 | $ptn_read_request->setTable('users'); 279 | $ptn_read_request->setTransaction($txn_selector); 280 | $keyset = new Google\Cloud\Spanner\V1\KeySet(); 281 | $keyset->setAll(True); 282 | $ptn_read_request->setKeySet($keyset); 283 | $ptn_read_request->setColumns(['username', 'firstname', 'lastname']); 284 | 285 | $time_start = microtime_float(); 286 | $client->PartitionRead($ptn_read_request); 287 | $latency = (microtime_float() - $time_start) * 1000; 288 | $metrics['partition_read_latency_ms'] = $latency; 289 | 290 | # Delete Session 291 | $deleteSessionRequest = new Google\Cloud\Spanner\V1\DeleteSessionRequest(); 292 | $deleteSessionRequest->setName($session->getName()); 293 | $client->deleteSession($deleteSessionRequest); 294 | } 295 | 296 | $PROBE_FUNCTIONS = [ 297 | 'session_management' => 'sessionManagement', 298 | 'execute_sql' => 'executeSql', 299 | 'read' => 'read', 300 | 'transaction' => 'transaction', 301 | 'partition' => 'partition' 302 | ]; 303 | 304 | return $PROBE_FUNCTIONS; 305 | 306 | 307 | 308 | -------------------------------------------------------------------------------- /cloudprober/grpc_gpc_prober/stackdriver_util.php: -------------------------------------------------------------------------------- 1 | api = $api; 18 | $this->metrics = []; 19 | $this->success = FALSE; 20 | $this->err_client = new ReportErrorsServiceClient(); 21 | } 22 | 23 | function addMetric($key, $value){ 24 | $this->matrics[$key] = $value; 25 | } 26 | 27 | function addMetrics($metrics){ 28 | $this->metrics = array_merge($metrics, $this->metrics); 29 | } 30 | 31 | function setSuccess($result){ 32 | $this->success = $result; 33 | } 34 | 35 | function outputMetrics(){ 36 | if ($this->success){ 37 | echo $this->api.'_success 1'."\n"; 38 | } 39 | else{ 40 | echo $this->api.'_success 0'."\n"; 41 | } 42 | foreach ($this->metrics as $key => $value) { 43 | echo $key.' '.$value."\n"; 44 | } 45 | } 46 | 47 | function reportError($err){ 48 | error_log($err); 49 | $projectId = '434076015357'; 50 | $project_name = $this->err_client->projectName($projectId); 51 | 52 | $location = (new SourceLocation()) 53 | ->setFunctionName($this->api); 54 | $context = (new ErrorContext()) 55 | ->setReportLocation($location); 56 | 57 | $error_event = new ReportedErrorEvent(); 58 | $error_event->setMessage('PHPProbeFailure: fails on '.$this->api.' API. Details: '.(string)$err."\n"); 59 | $error_event->setContext($context); 60 | 61 | $this->err_client->reportErrorEvent($project_name, $error_event); 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "google/grpc-gcp", 3 | "description": "gRPC GCP library for channel management", 4 | "license": "Apache-2.0", 5 | "require": { 6 | "php": "^8.0", 7 | "google/protobuf": "^v3.25.3||^4.26.1", 8 | "grpc/grpc": "^v1.13.0", 9 | "google/auth": "^1.3", 10 | "psr/cache": "^1.0.1||^2.0.0||^3.0.0" 11 | }, 12 | "require-dev": { 13 | "phpunit/phpunit": "^9.0", 14 | "google/cloud-spanner": "^1.7" 15 | }, 16 | "autoload": { 17 | "psr-4": { 18 | "Grpc\\Gcp\\": "src/" 19 | }, 20 | "classmap": [ 21 | "src/generated/" 22 | ] 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /doc/gRPC-client-user-guide.md: -------------------------------------------------------------------------------- 1 | # Instructions for create a gRPC client for google cloud services 2 | 3 | ## Overview 4 | 5 | This instruction includes a step by step guide for creating a gRPC 6 | client to test the google cloud service from an empty linux 7 | VM, using GCE ubuntu 16.04 TLS instance. 8 | 9 | The main steps are followed as steps below: 10 | 11 | - Environment prerequisite 12 | - Install protobuf plugin and gRPC-PHP/protobuf extension 13 | - Generate client API from .proto files 14 | - Create the client and send/receive RPC. 15 | 16 | ## Environment Prerequisite 17 | 18 | **Linux** 19 | ```sh 20 | $ [sudo] apt-get install build-essential autoconf libtool pkg-config zip unzip zlib1g-dev 21 | ``` 22 | **PHP** 23 | * `php` 5.5 or above, 7.0 or above 24 | * `pecl` 25 | * `composer` 26 | ```sh 27 | $ [sudo] apt-get install php php-dev 28 | $ curl -sS https://getcomposer.org/installer | php 29 | $ [sudo] mv composer.phar /usr/local/bin/composer 30 | ``` 31 | 32 | ## Install protobuf plugin and gRPC-PHP/protobuf extension 33 | `grpc_php_plugin` is used to generate client API from `*.proto `files. Currently, 34 | The only way to install `grpc_php_plugin` is to build from the gRPC source. 35 | 36 | **Install protobuf, gRPC, which will install the plugin** 37 | ```sh 38 | $ git clone -b $(curl -L https://grpc.io/release) https://github.com/grpc/grpc 39 | $ cd grpc 40 | $ git submodule update --init 41 | # install protobuf 42 | $ cd third_party/protobuf 43 | $ ./autogen.sh && ./configure && make -j8 44 | $ [sudo] make install 45 | $ [sudo] ldconfig 46 | # install gRPC 47 | $ cd ../.. 48 | $ make -j8 49 | $ [sudo] make install 50 | ``` 51 | It will generate `grpc_php_plugin` under `/usr/local/bin`. 52 | 53 | 54 | **Install gRPC-PHP extension** 55 | ```sh 56 | $ [sudo] pecl install protobuf 57 | $ [sudo] pecl install grpc 58 | ``` 59 | It will generate `protobuf.so` and `grpc.so` under PHP's extension directory. 60 | Note gRPC-PHP extension installed by pecl doesn't work on RHEL6 system. 61 | 62 | ## Generate client API from .proto files 63 | The common way to generate the client API is to use `grpc_php_plugin` directly. 64 | Since the plugin won't find the dependency by itself. It works if all your 65 | service proto files and dependent proto files are inside one directory. The 66 | command looks like: 67 | ```sh 68 | $ mkdir $HOME/project 69 | $ protoc --proto_path=./ --php_out=$HOME/project \ 70 | --grpc_out=$HOME/project \ 71 | --plugin=protoc-gen-grpc=./bins/opt/grpc_php_plugin \ 72 | path/to/your/proto_dependency_directory1/*.proto \ 73 | path/to/your/proto_dependency_directory2/*.proto \ 74 | path/to/your/proto_directory/*.proto 75 | 76 | ``` 77 | 78 | Take `Firestore` service under [googleapis github repo](https://github.com/googleapis/googleapis) 79 | for example. The proto files required for generating client API are 80 | ``` 81 | google/api/annotations.proto 82 | google/api/http.proto 83 | google/api/httpbody.proto 84 | google/longrunning/operations.proto 85 | google/rpc/code.proto 86 | google/rpc/error_details.proto 87 | google/rpc/status.proto 88 | google/type/latlng.proto 89 | google/firestore/v1beta1/firestore.proto 90 | google/firestore/v1beta1/common.proto 91 | google/firestore/v1beta1/query.proto 92 | google/firestore/v1beta1/write.proto 93 | google/firestore/v1beta1/document.proto 94 | ``` 95 | Thus the command looks like: 96 | ```sh 97 | $ protoc --proto_path=googleapis --plugin=protoc-gen-grpc=`which grpc_php_plugin` \ 98 | --php_out=./ --grpc_out=./ google/api/annotations.proto google/api/http.proto \ 99 | google/api/httpbody.proto google/longrunning/operations.proto google/rpc/code.proto \ 100 | google/rpc/error_details.proto google/rpc/status.proto google/type/latlng.proto \ 101 | google/firestore/v1beta1/firestore.proto google/firestore/v1beta1/common.proto \ 102 | google/firestore/v1beta1/query.proto google/firestore/v1beta1/write.proto \ 103 | google/firestore/v1beta1/document.proto 104 | ``` 105 | 106 | Since most of cloud services already publish proto files under 107 | [googleapis github repo](https://github.com/googleapis/googleapis), 108 | you can use it's Makefile to generate the client API. 109 | The `Makefile` will help you generate the client API as 110 | well as find the dependencies. The command will simply be: 111 | ```sh 112 | $ cd $HOME 113 | $ mkdir project 114 | $ git clone https://github.com/googleapis/googleapis.git 115 | $ cd googleapis 116 | $ make LANGUAGE=php OUTPUT=$HOME/project 117 | # (It's okay if you see error like Please add 'syntax = "proto3";' 118 | # to the top of your .proto file.) 119 | ``` 120 | The client API library is generated under `$HOME/project`. 121 | Take [`Firestore`](https://github.com/googleapis/googleapis/blob/master/google/firestore/v1beta1/firestore.proto) 122 | as example, the Client API is under 123 | `project/Google/Cloud/Firestore/V1beta1/FirestoreClient.php` depends on your 124 | package name inside .proto file. An easy way to find your client is 125 | ```sh 126 | $ find ./ -name [service_name]Client.php 127 | ``` 128 | 129 | ## Create the client and send/receive RPC. 130 | Now it's time to use the client API to send and receive RPCs. 131 | ```sh 132 | $ cd $HOME/project 133 | ``` 134 | **Install gRPC-PHP composer library** 135 | ```sh 136 | $ vim composer.json 137 | ######## you need to change the path and service namespace. 138 | { 139 | "require": { 140 | "google/cloud": "^0.52.1" 141 | }, 142 | "autoload": { 143 | "psr-4": { 144 | "FireStore\\": "src/", 145 | "Google\\Cloud\\Firestore\\V1beta1\\": "Google/Cloud/Firestore/V1beta1/" 146 | } 147 | } 148 | } 149 | ######## 150 | $ composer install 151 | ``` 152 | **Set credentials file** 153 | ``` sh 154 | $ vim $HOME/key.json 155 | ## Paste you credential file downloaded from your cloud project 156 | ## which you can find in APIs&Services => credentials => create credentials 157 | ## => Service account key => your credentials 158 | $ export GOOGLE_APPLICATION_CREDENTIALS=$HOME/key.json 159 | ``` 160 | 161 | **Implement Service Client** 162 | Take a unary-unary RPC `listDocument` from `FirestoreClient` as example. 163 | Create a file name `ListDocumentClient.php`. 164 | - import library 165 | ``` 166 | require_once __DIR__ . '/vendor/autoload.php'; 167 | use Google\Cloud\Firestore\V1beta1\FirestoreClient; 168 | use Google\Cloud\Firestore\V1beta1\ListDocumentsRequest; 169 | use Google\Auth\ApplicationDefaultCredentials; 170 | ``` 171 | - Google Auth 172 | ``` 173 | $host = "firestore.googleapis.com"; 174 | $credentials = \Grpc\ChannelCredentials::createSsl(); 175 | // WARNING: the environment variable "GOOGLE_APPLICATION_CREDENTIALS" needs to be set 176 | $auth = ApplicationDefaultCredentials::getCredentials(); 177 | $opts = [ 178 | 'credentials' => $credentials, 179 | 'update_metadata' => $auth->getUpdateMetadataFunc(), 180 | ] 181 | ``` 182 | - Create Client 183 | ``` 184 | $firestoreClient = new FirestoreClient($host, $opts); 185 | ``` 186 | - Make and receive RPC call 187 | ``` 188 | $argument = new ListDocumentsRequest(); 189 | $project_id = xxxxxxx; 190 | $argument->setParent("projects/$project_id/databases/(default)/documents"); 191 | list($Response, $error) = $firestoreClient->ListDocuments($argument)->wait(); 192 | ``` 193 | - print RPC response 194 | ``` 195 | $documents = $Response->getDocuments(); 196 | $index = 0; 197 | foreach($documents as $document) { 198 | $index++; 199 | $name = $document->getName(); 200 | echo "=> Document $index: $name\n"; 201 | $fields = $document->getFields(); 202 | foreach ($fields as $name => $value) { 203 | echo "$name => ".$value->getStringValue()."\n"; 204 | } 205 | } 206 | ``` 207 | 208 | - run the script 209 | ```sh 210 | $ php -d extension=grpc.so -d extension=protobuf.so ListDocumentClient.php 211 | ``` 212 | 213 | For different kinds of RPC(unary-unary, unary-stream, stream-unary, stream-stream), 214 | please check [grpc.io PHP part](https://grpc.io/docs/tutorials/basic/php.html#calling-service-methods) 215 | for reference. 216 | 217 | 218 | -------------------------------------------------------------------------------- /src/ChannelRef.php: -------------------------------------------------------------------------------- 1 | target = $target; 41 | $this->channel_id = $channel_id; 42 | $this->affinity_ref = $affinity_ref; 43 | $this->active_stream_ref = $active_stream_ref; 44 | $this->opts = $opts; 45 | $this->has_deserialized = new CreatedByDeserializeCheck(); 46 | } 47 | 48 | public function getRealChannel($credentials) 49 | { 50 | // TODO(ddyihai): remove this check once the serialize handler for 51 | // \Grpc\Channel is implemented(issue https://github.com/grpc/grpc/issues/15870). 52 | if (!$this->has_deserialized->getData()) { 53 | // $real_channel exists and is not created by the deserialization. 54 | return $this->real_channel; 55 | } 56 | // If this ChannelRef is created by deserialization, $real_channel is invalid 57 | // thus needs to be recreated becasue Grpc\Channel don't have serialize and 58 | // deserialize handler. 59 | // Since [target + augments + credentials] will be the same during the recreation, 60 | // it will reuse the underline grpc channel in C extension without creating a 61 | // new connection. 62 | 63 | // 'credentials' in the array $opts will be unset during creating the channel. 64 | if (!array_key_exists('credentials', $this->opts)) { 65 | $this->opts['credentials'] = $credentials; 66 | } 67 | $real_channel = new \Grpc\Channel($this->target, $this->opts); 68 | $this->real_channel = $real_channel; 69 | // Set deserialization to false so it won't be recreated within the same script. 70 | $this->has_deserialized->setData(0); 71 | return $real_channel; 72 | } 73 | 74 | public function getAffinityRef() 75 | { 76 | return $this->affinity_ref; 77 | } 78 | public function getActiveStreamRef() 79 | { 80 | return $this->active_stream_ref; 81 | } 82 | public function affinityRefIncr() 83 | { 84 | $this->affinity_ref += 1; 85 | } 86 | public function affinityRefDecr() 87 | { 88 | $this->affinity_ref -= 1; 89 | } 90 | public function activeStreamRefIncr() 91 | { 92 | $this->active_stream_ref += 1; 93 | } 94 | public function activeStreamRefDecr() 95 | { 96 | $this->active_stream_ref -= 1; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/Config.php: -------------------------------------------------------------------------------- 1 | gcp_call_invoker = new \Grpc\DefaultCallInvoker(); 45 | return; 46 | } 47 | 48 | $gcp_channel = null; 49 | $url_host = parse_url($target, PHP_URL_HOST); 50 | $this->hostname = $url_host ? $url_host : $target; 51 | $channel_pool_key = $this->hostname . '.gcp.channel.' . getmypid(); 52 | 53 | if (!$cacheItemPool) { 54 | $affinity_conf = $this->parseConfObject($conf); 55 | $gcp_call_invoker = new GCPCallInvoker($affinity_conf); 56 | $this->gcp_call_invoker = $gcp_call_invoker; 57 | } else { 58 | $item = $cacheItemPool->getItem($channel_pool_key); 59 | if ($item->isHit()) { 60 | // Channel pool for the $hostname API has already created. 61 | $gcp_call_invoker = unserialize($item->get()); 62 | } else { 63 | $affinity_conf = $this->parseConfObject($conf); 64 | // Create GCP channel based on the information. 65 | $gcp_call_invoker = new GCPCallInvoker($affinity_conf); 66 | } 67 | $this->gcp_call_invoker = $gcp_call_invoker; 68 | register_shutdown_function(function ($gcp_call_invoker, $cacheItemPool, $item) { 69 | // Push the current gcp_channel back into the pool when the script finishes. 70 | $item->set(serialize($gcp_call_invoker)); 71 | $cacheItemPool->save($item); 72 | }, $gcp_call_invoker, $cacheItemPool, $item); 73 | } 74 | } 75 | 76 | /** 77 | * @return \Grpc\CallInvoker The call invoker to be hooked into the gRPC 78 | */ 79 | public function callInvoker() 80 | { 81 | return $this->gcp_call_invoker; 82 | } 83 | 84 | /** 85 | * @return string The URI of the endpoint 86 | */ 87 | public function getTarget() 88 | { 89 | return $this->channel->getTarget(); 90 | } 91 | 92 | private function parseConfObject($conf_object) 93 | { 94 | $config = json_decode($conf_object->serializeToJsonString(), true); 95 | if (isset($config['channelPool'])) { 96 | $affinity_conf['channelPool'] = $config['channelPool']; 97 | } 98 | $aff_by_method = array(); 99 | if (isset($config['method'])) { 100 | for ($i = 0; $i < count($config['method']); $i++) { 101 | // In proto3, if the value is default, eg 0 for int, it won't be serialized. 102 | // Thus serialized string may not have `command` if the value is default 0(BOUND). 103 | if (!array_key_exists('command', $config['method'][$i]['affinity'])) { 104 | $config['method'][$i]['affinity']['command'] = 'BOUND'; 105 | } 106 | $aff_by_method[$config['method'][$i]['name'][0]] = $config['method'][$i]['affinity']; 107 | } 108 | } 109 | $affinity_conf['affinity_by_method'] = $aff_by_method; 110 | return $affinity_conf; 111 | } 112 | 113 | } 114 | -------------------------------------------------------------------------------- /src/CreatedByDeserializeCheck.php: -------------------------------------------------------------------------------- 1 | data = 1; 35 | } 36 | 37 | /** 38 | * @return string 39 | */ 40 | public function serialize() 41 | { 42 | return '0'; 43 | } 44 | 45 | /** 46 | * @return string 47 | */ 48 | public function __serialize() 49 | { 50 | return $this->serialize(); 51 | } 52 | 53 | /** 54 | * @param string $data 55 | */ 56 | public function unserialize($data) 57 | { 58 | $this->data = 1; 59 | } 60 | 61 | /** 62 | * @param string $data 63 | */ 64 | public function __unserialize($data) 65 | { 66 | $this->unserialize($data); 67 | } 68 | 69 | /** 70 | * @param $data 71 | */ 72 | public function setData($data) 73 | { 74 | $this->data = $data; 75 | } 76 | 77 | /** 78 | * @return int 79 | */ 80 | public function getData() 81 | { 82 | return $this->data; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/GCPBidiStreamingCall.php: -------------------------------------------------------------------------------- 1 | _rpcPreProcess($data); 32 | $this->real_call = new \Grpc\BidiStreamingCall($channel_ref->getRealChannel( 33 | $this->gcp_channel->credentials), $this->method, $this->deserialize, $this->options); 34 | $this->real_call->start($this->metadata_rpc); 35 | return $this->real_call; 36 | } 37 | 38 | /** 39 | * Pick a channel and start the call. 40 | * 41 | * @param array $metadata Metadata to send with the call, if applicable 42 | * (optional) 43 | */ 44 | public function start(array $metadata = []) 45 | { 46 | $this->metadata_rpc = $metadata; 47 | } 48 | 49 | /** 50 | * Reads the next value from the server. 51 | * 52 | * @return mixed The next value from the server, or null if there is none 53 | */ 54 | public function read() 55 | { 56 | if (!$this->has_real_call) { 57 | $this->createRealCall(); 58 | $this->has_real_call = true; 59 | } 60 | $response = $this->real_call->read(); 61 | if ($response) { 62 | $this->response = $response; 63 | } 64 | return $response; 65 | } 66 | 67 | /** 68 | * Write a single message to the server. This cannot be called after 69 | * writesDone is called. 70 | * 71 | * @param ByteBuffer $data The data to write 72 | * @param array $options An array of options, possible keys: 73 | * 'flags' => a number (optional) 74 | */ 75 | public function write($data, array $options = []) 76 | { 77 | if (!$this->has_real_call) { 78 | $this->createRealCall($data); 79 | $this->has_real_call = true; 80 | } 81 | $this->real_call->write($data, $options); 82 | } 83 | 84 | /** 85 | * Indicate that no more writes will be sent. 86 | */ 87 | public function writesDone() 88 | { 89 | if (!$this->has_real_call) { 90 | $this->createRealCall(); 91 | $this->has_real_call = true; 92 | } 93 | $this->real_call->writesDone(); 94 | } 95 | 96 | /** 97 | * Wait for the server to send the status, and return it. 98 | * 99 | * @return \stdClass The status object, with integer $code, string 100 | * $details, and array $metadata members 101 | */ 102 | public function getStatus() 103 | { 104 | $status = $this->real_call->getStatus(); 105 | $this->_rpcPostProcess($status, $this->response); 106 | return $status; 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/GCPCallInvoker.php: -------------------------------------------------------------------------------- 1 | affinity_conf = $affinity_conf; 43 | } 44 | 45 | /** 46 | * @param string $hostname 47 | * @param array $opts 48 | * @return GcpExtensionChannel 49 | */ 50 | public function createChannelFactory($hostname, $opts) 51 | { 52 | if ($this->channel) { 53 | // $call_invoker object has already created from previews PHP-FPM scripts. 54 | // Only need to update the $opts including the credentials. 55 | $this->channel->updateOpts($opts); 56 | } else { 57 | $opts['affinity_conf'] = $this->affinity_conf; 58 | $channel = new GcpExtensionChannel($hostname, $opts); 59 | $this->channel = $channel; 60 | } 61 | return $this->channel; 62 | } 63 | 64 | // _getChannel is used for testing only. 65 | public function GetChannel() 66 | { 67 | return $this->channel; 68 | } 69 | 70 | public function UnaryCall($channel, $method, $deserialize, $options) 71 | { 72 | return new GCPUnaryCall($channel, $method, $deserialize, $options); 73 | } 74 | public function ClientStreamingCall($channel, $method, $deserialize, $options) 75 | { 76 | return new GCPClientStreamCall($channel, $method, $deserialize, $options); 77 | } 78 | public function ServerStreamingCall($channel, $method, $deserialize, $options) 79 | { 80 | return new GCPServerStreamCall($channel, $method, $deserialize, $options); 81 | } 82 | public function BidiStreamingCall($channel, $method, $deserialize, $options) 83 | { 84 | return new GCPBidiStreamingCall($channel, $method, $deserialize, $options); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/GCPClientStreamCall.php: -------------------------------------------------------------------------------- 1 | _rpcPreProcess($data); 30 | $this->real_call = new \Grpc\ClientStreamingCall($channel_ref->getRealChannel( 31 | $this->gcp_channel->credentials), $this->method, $this->deserialize, $this->options); 32 | $this->real_call->start($this->metadata_rpc); 33 | return $this->real_call; 34 | } 35 | /** 36 | * Pick a channel and start the call. 37 | * 38 | * @param array $metadata Metadata to send with the call, if applicable 39 | * (optional) 40 | */ 41 | public function start(array $metadata = []) 42 | { 43 | // Postpone first rpc to write function(), where we can pick a channel 44 | // from the channel pool. 45 | $this->metadata_rpc = $metadata; 46 | } 47 | 48 | /** 49 | * Write a single message to the server. This cannot be called after 50 | * wait is called. 51 | * 52 | * @param ByteBuffer $data The data to write 53 | * @param array $options An array of options, possible keys: 54 | * 'flags' => a number (optional) 55 | */ 56 | public function write($data, array $options = []) 57 | { 58 | if (!$this->has_real_call) { 59 | $this->createRealCall($data); 60 | $this->has_real_call = true; 61 | } 62 | $this->real_call->write($data, $options); 63 | } 64 | 65 | /** 66 | * Wait for the server to respond with data and a status. 67 | * 68 | * @return array [response data, status] 69 | */ 70 | public function wait() 71 | { 72 | list($response, $status) = $this->real_call->wait(); 73 | $this->_rpcPostProcess($status, $response); 74 | return [$response, $status]; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/GCPServerStreamCall.php: -------------------------------------------------------------------------------- 1 | real_call = new \Grpc\ServerStreamingCall($channel, $this->method, $this->deserialize, $this->options); 32 | $this->has_real_call = true; 33 | return $this->real_call; 34 | } 35 | 36 | /** 37 | * Pick a channel and start the call. 38 | * 39 | * @param mixed $data The data to send 40 | * @param array $metadata Metadata to send with the call, if applicable 41 | * (optional) 42 | * @param array $options An array of options, possible keys: 43 | * 'flags' => a number (optional) 44 | */ 45 | public function start($argument, $metadata, $options) 46 | { 47 | $channel_ref = $this->_rpcPreProcess($argument); 48 | $this->createRealCall($channel_ref->getRealChannel( 49 | $this->gcp_channel->credentials)); 50 | $this->real_call->start($argument, $metadata, $options); 51 | } 52 | 53 | /** 54 | * @return mixed An iterator of response values 55 | */ 56 | public function responses() 57 | { 58 | $response = $this->real_call->responses(); 59 | // Since the last response is empty for the server streaming RPC, 60 | // the second last one is the last RPC response with payload. 61 | // Use this one for searching the affinity key. 62 | // The same as BidiStreaming. 63 | if ($response) { 64 | $this->response = $response; 65 | } 66 | return $response; 67 | } 68 | 69 | /** 70 | * Wait for the server to send the status, and return it. 71 | * 72 | * @return \stdClass The status object, with integer $code, string 73 | * $details, and array $metadata members 74 | */ 75 | public function getStatus() 76 | { 77 | $status = $this->real_call->getStatus(); 78 | $this->_rpcPostProcess($status, $this->response); 79 | return $status; 80 | } 81 | 82 | /** 83 | * @return mixed The metadata sent by the server 84 | */ 85 | public function getMetadata() 86 | { 87 | return $this->real_call->getMetadata(); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/GCPUnaryCall.php: -------------------------------------------------------------------------------- 1 | real_call = new \Grpc\UnaryCall($channel, $this->method, $this->deserialize, $this->options); 30 | $this->has_real_call = true; 31 | return $this->real_call; 32 | } 33 | 34 | /** 35 | * Pick a channel and start the call. 36 | * 37 | * @param mixed $data The data to send 38 | * @param array $metadata Metadata to send with the call, if applicable 39 | * (optional) 40 | * @param array $options An array of options, possible keys: 41 | * 'flags' => a number (optional) 42 | */ 43 | public function start($argument, $metadata, $options) 44 | { 45 | $channel_ref = $this->_rpcPreProcess($argument); 46 | $real_channel = $channel_ref->getRealChannel($this->gcp_channel->credentials); 47 | $this->createRealCall($real_channel); 48 | $this->real_call->start($argument, $metadata, $options); 49 | } 50 | 51 | /** 52 | * Wait for the server to respond with data and a status. 53 | * 54 | * @return array [response data, status] 55 | */ 56 | public function wait() 57 | { 58 | list($response, $status) = $this->real_call->wait(); 59 | $this->_rpcPostProcess($status, $response); 60 | return [$response, $status]; 61 | } 62 | 63 | /** 64 | * @return mixed The metadata sent by the server 65 | */ 66 | public function getMetadata() 67 | { 68 | return $this->real_call->getMetadata(); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/GcpBaseCall.php: -------------------------------------------------------------------------------- 1 | gcp_channel = $channel; 65 | $this->method = $method; 66 | $this->deserialize = $deserialize; 67 | $this->options = $options; 68 | $this->_affinity = null; 69 | 70 | if (isset($this->gcp_channel->affinity_conf['affinity_by_method'][$method])) { 71 | $this->_affinity = $this->gcp_channel->affinity_conf['affinity_by_method'][$method]; 72 | } 73 | } 74 | 75 | /** 76 | * Pick a ChannelRef from the channel pool based on the request and 77 | * the affinity config. 78 | * 79 | * @param mixed $argument Requests. 80 | * 81 | * @return ChannelRef 82 | */ 83 | protected function _rpcPreProcess($argument) 84 | { 85 | $this->affinity_key = null; 86 | if ($this->_affinity) { 87 | $command = $this->_affinity['command']; 88 | if ($command == self::BOUND || $command == self::UNBIND) { 89 | $this->affinity_key = $this->getAffinityKeyFromProto($argument); 90 | } 91 | } 92 | $this->channel_ref = $this->gcp_channel->getChannelRef($this->affinity_key); 93 | $this->channel_ref->activeStreamRefIncr(); 94 | return $this->channel_ref; 95 | } 96 | 97 | /** 98 | * Update ChannelRef when RPC finishes. 99 | * 100 | * @param \stdClass $status The status object, with integer $code, string 101 | * $details, and array $metadata members 102 | * @param mixed $response Response. 103 | */ 104 | protected function _rpcPostProcess($status, $response) 105 | { 106 | if ($this->_affinity) { 107 | $command = $this->_affinity['command']; 108 | if ($command == self::BIND) { 109 | if ($status->code != \Grpc\STATUS_OK) { 110 | return; 111 | } 112 | $affinity_key = $this->getAffinityKeyFromProto($response); 113 | $this->gcp_channel->bind($this->channel_ref, $affinity_key); 114 | } elseif ($command == self::UNBIND) { 115 | $this->gcp_channel->unbind($this->affinity_key); 116 | } 117 | } 118 | $this->channel_ref->activeStreamRefDecr(); 119 | } 120 | 121 | /** 122 | * Get the affinity key based on the affinity config. 123 | * 124 | * @param mixed $proto Objects may contain the affinity key. 125 | * 126 | * @return string Affinity key. 127 | */ 128 | protected function getAffinityKeyFromProto($proto) 129 | { 130 | if ($this->_affinity) { 131 | $names = $this->_affinity['affinityKey']; 132 | $names_arr = explode(".", $names); 133 | foreach ($names_arr as $name) { 134 | $getAttrMethod = 'get' . ucfirst($name); 135 | $proto = call_user_func_array(array($proto, $getAttrMethod), array()); 136 | } 137 | return $proto; 138 | } 139 | echo "Cannot find the field in the proto\n"; 140 | } 141 | 142 | /** 143 | * @return mixed The metadata sent by the server 144 | */ 145 | public function getMetadata() 146 | { 147 | if (!$this->has_real_call) { 148 | $this->createRealCall(); 149 | $this->has_real_call = true; 150 | } 151 | return $this->real_call->getMetadata(); 152 | } 153 | 154 | /** 155 | * @return mixed The trailing metadata sent by the server 156 | */ 157 | public function getTrailingMetadata() 158 | { 159 | if (!$this->has_real_call) { 160 | $this->createRealCall(); 161 | $this->has_real_call = true; 162 | } 163 | return $this->real_call->getTrailingMetadata(); 164 | } 165 | 166 | /** 167 | * @return string The URI of the endpoint 168 | */ 169 | public function getPeer() 170 | { 171 | if (!$this->has_real_call) { 172 | $this->createRealCall(); 173 | $this->has_real_call = true; 174 | } 175 | return $this->real_call->getPeer(); 176 | } 177 | 178 | /** 179 | * Cancels the call. 180 | */ 181 | public function cancel() 182 | { 183 | if (!$this->has_real_call) { 184 | $this->has_real_call = true; 185 | $this->createRealCall(); 186 | } 187 | $this->real_call->cancel(); 188 | } 189 | 190 | /** 191 | * Serialize a message to the protobuf binary format. 192 | * 193 | * @param mixed $data The Protobuf message 194 | * 195 | * @return string The protobuf binary format 196 | */ 197 | protected function _serializeMessage($data) 198 | { 199 | return $this->real_call->_serializeMessage($data); 200 | } 201 | 202 | /** 203 | * Deserialize a response value to an object. 204 | * 205 | * @param string $value The binary value to deserialize 206 | * 207 | * @return mixed The deserialized value 208 | */ 209 | protected function _deserializeResponse($value) 210 | { 211 | return $this->real_call->_deserializeResponse($value); 212 | } 213 | 214 | /** 215 | * Set the CallCredentials for the underlying Call. 216 | * 217 | * @param CallCredentials $call_credentials The CallCredentials object 218 | */ 219 | public function setCallCredentials($call_credentials) 220 | { 221 | $this->call->setCredentials($call_credentials); 222 | } 223 | } 224 | -------------------------------------------------------------------------------- /src/GcpExtensionChannel.php: -------------------------------------------------------------------------------- 1 | channel_refs; 44 | } 45 | 46 | /** 47 | * @param string $hostname 48 | * @param array $opts Options to create a \Grpc\Channel and affinity config 49 | */ 50 | public function __construct($hostname = null, $opts = array()) 51 | { 52 | if ($hostname == null || !is_array($opts)) { 53 | throw new \InvalidArgumentException("Expected hostname is empty"); 54 | } 55 | $this->max_size = 10; 56 | $this->max_concurrent_streams_low_watermark = 100; 57 | if (isset($opts['affinity_conf'])) { 58 | if (isset($opts['affinity_conf']['channelPool'])) { 59 | if (isset($opts['affinity_conf']['channelPool']['maxSize'])) { 60 | $this->max_size = $opts['affinity_conf']['channelPool']['maxSize']; 61 | } 62 | if (isset($opts['affinity_conf']['channelPool']['maxConcurrentStreamsLowWatermark'])) { 63 | $this->max_concurrent_streams_low_watermark = 64 | $opts['affinity_conf']['channelPool']['maxConcurrentStreamsLowWatermark']; 65 | } 66 | } 67 | $this->affinity_by_method = $opts['affinity_conf']['affinity_by_method']; 68 | $this->affinity_conf = $opts['affinity_conf']; 69 | } 70 | $this->target = $hostname; 71 | $this->affinity_key_to_channel_ref = array(); 72 | $this->channel_refs = array(); 73 | $this->updateOpts($opts); 74 | // Initiate a Grpc\Channel at the beginning in order to keep the same 75 | // behavior as the Grpc. 76 | $channel_ref = $this->getChannelRef(); 77 | $channel_ref->getRealChannel($this->credentials); 78 | } 79 | 80 | /** 81 | * @param array $opts Options to create a \Grpc\Channel 82 | */ 83 | public function updateOpts($opts) 84 | { 85 | if (isset($opts['credentials'])) { 86 | $this->credentials = $opts['credentials']; 87 | } 88 | unset($opts['affinity_conf']); 89 | unset($opts['credentials']); 90 | $this->options = $opts; 91 | $this->is_closed = false; 92 | } 93 | 94 | /** 95 | * Bind the ChannelRef with the affinity key. This is a private method. 96 | * 97 | * @param ChannelRef $channel_ref 98 | * @param string $affinity_key 99 | * 100 | * @return ChannelRef 101 | */ 102 | public function bind($channel_ref, $affinity_key) 103 | { 104 | if (!array_key_exists($affinity_key, $this->affinity_key_to_channel_ref)) { 105 | $this->affinity_key_to_channel_ref[$affinity_key] = $channel_ref; 106 | } 107 | $channel_ref->affinityRefIncr(); 108 | return $channel_ref; 109 | } 110 | 111 | /** 112 | * Unbind the affinity key. This is a private method. 113 | * 114 | * @param string $affinity_key 115 | * 116 | * @return ChannelRef 117 | */ 118 | public function unbind($affinity_key) 119 | { 120 | $channel_ref = null; 121 | if (array_key_exists($affinity_key, $this->affinity_key_to_channel_ref)) { 122 | $channel_ref = $this->affinity_key_to_channel_ref[$affinity_key]; 123 | $channel_ref->affinityRefDecr(); 124 | } 125 | unset($this->affinity_key_to_channel_ref[$affinity_key]); 126 | return $channel_ref; 127 | } 128 | 129 | 130 | public function cmp_by_active_stream_ref($a, $b) 131 | { 132 | return $a->getActiveStreamRef() - $b->getActiveStreamRef(); 133 | } 134 | 135 | /** 136 | * Pick or create a ChannelRef from the pool by affinity key. 137 | * 138 | * @param string $affinity_key 139 | * 140 | * @return ChannelRef 141 | */ 142 | public function getChannelRef($affinity_key = null) 143 | { 144 | if ($affinity_key) { 145 | if (array_key_exists($affinity_key, $this->affinity_key_to_channel_ref)) { 146 | return $this->affinity_key_to_channel_ref[$affinity_key]; 147 | } 148 | return $this->getChannelRef(); 149 | } 150 | usort($this->channel_refs, array($this, 'cmp_by_active_stream_ref')); 151 | 152 | if (count($this->channel_refs) > 0 && $this->channel_refs[0]->getActiveStreamRef() < 153 | $this->max_concurrent_streams_low_watermark) { 154 | return $this->channel_refs[0]; 155 | } 156 | $num_channel_refs = count($this->channel_refs); 157 | if ($num_channel_refs < $this->max_size) { 158 | // grpc_target_persist_bound stands for how many channels can be persisted for 159 | // the same target in the C extension. It is possible that the user use the pure 160 | // gRPC and this GCP extension at the same time, which share the same target. In this case 161 | // pure gRPC channel may occupy positions in C extension, which deletes some channels created 162 | // by this GCP extension. 163 | // If that happens, it won't cause the script failure because we saves all arguments for creating 164 | // a channel instead of a channel itself. If we watch to fetch a GCP channel already deleted, 165 | // it will create a new channel. The only cons is the latency of the first RPC will high because 166 | // it will establish the connection again. 167 | if (!isset($this->options['grpc_target_persist_bound']) || 168 | $this->options['grpc_target_persist_bound'] < $this->max_size) { 169 | $this->options['grpc_target_persist_bound'] = $this->max_size; 170 | } 171 | $cur_opts = array_merge($this->options, 172 | ['grpc_gcp_channel_id' => $num_channel_refs]); 173 | $channel_ref = new ChannelRef($this->target, $num_channel_refs, $cur_opts); 174 | array_unshift($this->channel_refs, $channel_ref); 175 | } 176 | return $this->channel_refs[0]; 177 | } 178 | 179 | /** 180 | * Get the connectivity state of the channel 181 | * 182 | * @param bool $try_to_connect try to connect on the channel 183 | * 184 | * @return int The grpc connectivity state 185 | * @throws \InvalidArgumentException 186 | */ 187 | public function getConnectivityState($try_to_connect = false) 188 | { 189 | // Since getRealChannel is creating a PHP Channel object. However in gRPC, when a Channel 190 | // object is closed, we only mark this Object to be invalid. Thus, we need a global variable 191 | // to mark whether this GCPExtensionChannel is close or not. 192 | if ($this->is_closed) { 193 | throw new \RuntimeException("Channel has already been closed"); 194 | } 195 | $ready = 0; 196 | $idle = 0; 197 | $connecting = 0; 198 | $transient_failure = 0; 199 | $shutdown = 0; 200 | foreach ($this->channel_refs as $channel_ref) { 201 | $state = $channel_ref->getRealChannel($this->credentials)->getConnectivityState($try_to_connect); 202 | switch ($state) { 203 | case \Grpc\CHANNEL_READY: 204 | $ready += 1; 205 | break 2; 206 | case \Grpc\CHANNEL_FATAL_FAILURE: 207 | $shutdown += 1; 208 | break; 209 | case \Grpc\CHANNEL_CONNECTING: 210 | $connecting += 1; 211 | break; 212 | case \Grpc\CHANNEL_TRANSIENT_FAILURE: 213 | $transient_failure += 1; 214 | break; 215 | case \Grpc\CHANNEL_IDLE: 216 | $idle += 1; 217 | break; 218 | } 219 | } 220 | if ($ready > 0) { 221 | return \Grpc\CHANNEL_READY; 222 | } elseif ($idle > 0) { 223 | return \Grpc\CHANNEL_IDLE; 224 | } elseif ($connecting > 0) { 225 | return \Grpc\CHANNEL_CONNECTING; 226 | } elseif ($transient_failure > 0) { 227 | return \Grpc\CHANNEL_TRANSIENT_FAILURE; 228 | } elseif ($shutdown > 0) { 229 | return \Grpc\CHANNEL_SHUTDOWN; 230 | } 231 | } 232 | 233 | /** 234 | * Watch the connectivity state of the channel until it changed 235 | * 236 | * @param int $last_state The previous connectivity state of the channel 237 | * @param Timeval $deadline_obj The deadline this function should wait until 238 | * 239 | * @return bool If the connectivity state changes from last_state 240 | * before deadline 241 | * @throws \InvalidArgumentException 242 | */ 243 | public function watchConnectivityState($last_state, $deadline_obj = null) 244 | { 245 | if ($deadline_obj == null || !is_a($deadline_obj, '\Grpc\Timeval')) { 246 | throw new \InvalidArgumentException(""); 247 | } 248 | // Since getRealChannel is creating a PHP Channel object. However in gRPC, when a Channel 249 | // object is closed, we only mark this Object to be invalid. Thus, we need a global variable 250 | // to mark whether this GCPExtensionChannel is close or not. 251 | if ($this->is_closed) { 252 | throw new \RuntimeException("Channel has already been closed"); 253 | } 254 | $state = 0; 255 | foreach ($this->channel_refs as $channel_ref) { 256 | $state = $channel_ref->getRealChannel($this->credentials)->watchConnectivityState($last_state, $deadline_obj); 257 | } 258 | return $state; 259 | } 260 | 261 | /** 262 | * Get the endpoint this call/stream is connected to 263 | * 264 | * @return string The URI of the endpoint 265 | */ 266 | public function getTarget() 267 | { 268 | return $this->target; 269 | } 270 | 271 | /** 272 | * Close the channel 273 | */ 274 | public function close() 275 | { 276 | foreach ($this->channel_refs as $channel_ref) { 277 | $channel_ref->getRealChannel($this->credentials)->close(); 278 | } 279 | $this->is_closed = true; 280 | } 281 | } 282 | -------------------------------------------------------------------------------- /src/generated/GPBMetadata/GrpcGcp.php: -------------------------------------------------------------------------------- 1 | internalAddGeneratedFile(hex2bin( 19 | "0ac9030a0e677270635f6763702e70726f746f1208677270632e67637022" . 20 | "670a09417069436f6e66696712310a0c6368616e6e656c5f706f6f6c1802" . 21 | "2001280b321b2e677270632e6763702e4368616e6e656c506f6f6c436f6e" . 22 | "66696712270a066d6574686f6418e9072003280b32162e677270632e6763" . 23 | "702e4d6574686f64436f6e66696722690a114368616e6e656c506f6f6c43" . 24 | "6f6e66696712100a086d61785f73697a6518012001280d12140a0c69646c" . 25 | "655f74696d656f7574180220012804122c0a246d61785f636f6e63757272" . 26 | "656e745f73747265616d735f6c6f775f77617465726d61726b1803200128" . 27 | "0d22490a0c4d6574686f64436f6e666967120c0a046e616d651801200328" . 28 | "09122b0a08616666696e69747918e9072001280b32182e677270632e6763" . 29 | "702e416666696e697479436f6e6669672285010a0e416666696e69747943" . 30 | "6f6e66696712310a07636f6d6d616e6418022001280e32202e677270632e" . 31 | "6763702e416666696e697479436f6e6669672e436f6d6d616e6412140a0c" . 32 | "616666696e6974795f6b6579180320012809222a0a07436f6d6d616e6412" . 33 | "090a05424f554e44100012080a0442494e441001120a0a06554e42494e44" . 34 | "1002620670726f746f33" 35 | )); 36 | 37 | static::$is_initialized = true; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/generated/Grpc/Gcp/AffinityConfig.php: -------------------------------------------------------------------------------- 1 | grpc.gcp.AffinityConfig 11 | */ 12 | class AffinityConfig extends \Google\Protobuf\Internal\Message 13 | { 14 | /** 15 | * The affinity command applies on the selected gRPC methods. 16 | * 17 | * Generated from protobuf field .grpc.gcp.AffinityConfig.Command command = 2; 18 | */ 19 | private $command = 0; 20 | /** 21 | * The field path of the affinity key in the request/response message. 22 | * For example: "f.a", "f.b.d", etc. 23 | * 24 | * Generated from protobuf field string affinity_key = 3; 25 | */ 26 | private $affinity_key = ''; 27 | 28 | public function __construct() 29 | { 30 | \GPBMetadata\GrpcGcp::initOnce(); 31 | parent::__construct(); 32 | } 33 | 34 | /** 35 | * The affinity command applies on the selected gRPC methods. 36 | * 37 | * Generated from protobuf field .grpc.gcp.AffinityConfig.Command command = 2; 38 | * @return int 39 | */ 40 | public function getCommand() 41 | { 42 | return $this->command; 43 | } 44 | 45 | /** 46 | * The affinity command applies on the selected gRPC methods. 47 | * 48 | * Generated from protobuf field .grpc.gcp.AffinityConfig.Command command = 2; 49 | * @param int $var 50 | * @return $this 51 | */ 52 | public function setCommand($var) 53 | { 54 | GPBUtil::checkEnum($var, \Grpc\Gcp\AffinityConfig_Command::class); 55 | $this->command = $var; 56 | 57 | return $this; 58 | } 59 | 60 | /** 61 | * The field path of the affinity key in the request/response message. 62 | * For example: "f.a", "f.b.d", etc. 63 | * 64 | * Generated from protobuf field string affinity_key = 3; 65 | * @return string 66 | */ 67 | public function getAffinityKey() 68 | { 69 | return $this->affinity_key; 70 | } 71 | 72 | /** 73 | * The field path of the affinity key in the request/response message. 74 | * For example: "f.a", "f.b.d", etc. 75 | * 76 | * Generated from protobuf field string affinity_key = 3; 77 | * @param string $var 78 | * @return $this 79 | */ 80 | public function setAffinityKey($var) 81 | { 82 | GPBUtil::checkString($var, true); 83 | $this->affinity_key = $var; 84 | 85 | return $this; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/generated/Grpc/Gcp/AffinityConfig_Command.php: -------------------------------------------------------------------------------- 1 | Grpc\Gcp\AffinityConfig\Command 9 | */ 10 | class AffinityConfig_Command 11 | { 12 | /** 13 | * The annotated method will be required to be bound to an existing session 14 | * to execute the RPC. The corresponding will be 15 | * used to find the affinity key from the request message. 16 | * 17 | * Generated from protobuf enum BOUND = 0; 18 | */ 19 | const BOUND = 0; 20 | /** 21 | * The annotated method will establish the channel affinity with the channel 22 | * which is used to execute the RPC. The corresponding 23 | * will be used to find the affinity key from the 24 | * response message. 25 | * 26 | * Generated from protobuf enum BIND = 1; 27 | */ 28 | const BIND = 1; 29 | /** 30 | * The annotated method will remove the channel affinity with the channel 31 | * which is used to execute the RPC. The corresponding 32 | * will be used to find the affinity key from the 33 | * request message. 34 | * 35 | * Generated from protobuf enum UNBIND = 2; 36 | */ 37 | const UNBIND = 2; 38 | } 39 | -------------------------------------------------------------------------------- /src/generated/Grpc/Gcp/ApiConfig.php: -------------------------------------------------------------------------------- 1 | grpc.gcp.ApiConfig 11 | */ 12 | class ApiConfig extends \Google\Protobuf\Internal\Message 13 | { 14 | /** 15 | * The channel pool configurations. 16 | * 17 | * Generated from protobuf field .grpc.gcp.ChannelPoolConfig channel_pool = 2; 18 | */ 19 | private $channel_pool = null; 20 | /** 21 | * The method configurations. 22 | * 23 | * Generated from protobuf field repeated .grpc.gcp.MethodConfig method = 1001; 24 | */ 25 | private $method; 26 | 27 | public function __construct() 28 | { 29 | \GPBMetadata\GrpcGcp::initOnce(); 30 | parent::__construct(); 31 | } 32 | 33 | /** 34 | * The channel pool configurations. 35 | * 36 | * Generated from protobuf field .grpc.gcp.ChannelPoolConfig channel_pool = 2; 37 | * @return \Grpc\Gcp\ChannelPoolConfig 38 | */ 39 | public function getChannelPool() 40 | { 41 | return $this->channel_pool; 42 | } 43 | 44 | /** 45 | * The channel pool configurations. 46 | * 47 | * Generated from protobuf field .grpc.gcp.ChannelPoolConfig channel_pool = 2; 48 | * @param \Grpc\Gcp\ChannelPoolConfig $var 49 | * @return $this 50 | */ 51 | public function setChannelPool($var) 52 | { 53 | GPBUtil::checkMessage($var, \Grpc\Gcp\ChannelPoolConfig::class); 54 | $this->channel_pool = $var; 55 | 56 | return $this; 57 | } 58 | 59 | /** 60 | * The method configurations. 61 | * 62 | * Generated from protobuf field repeated .grpc.gcp.MethodConfig method = 1001; 63 | * @return \Google\Protobuf\Internal\RepeatedField 64 | */ 65 | public function getMethod() 66 | { 67 | return $this->method; 68 | } 69 | 70 | /** 71 | * The method configurations. 72 | * 73 | * Generated from protobuf field repeated .grpc.gcp.MethodConfig method = 1001; 74 | * @param \Grpc\Gcp\MethodConfig[]|\Google\Protobuf\Internal\RepeatedField $var 75 | * @return $this 76 | */ 77 | public function setMethod($var) 78 | { 79 | $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Grpc\Gcp\MethodConfig::class); 80 | $this->method = $arr; 81 | 82 | return $this; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/generated/Grpc/Gcp/ChannelPoolConfig.php: -------------------------------------------------------------------------------- 1 | grpc.gcp.ChannelPoolConfig 11 | */ 12 | class ChannelPoolConfig extends \Google\Protobuf\Internal\Message 13 | { 14 | /** 15 | * The max number of channels in the pool. 16 | * 17 | * Generated from protobuf field uint32 max_size = 1; 18 | */ 19 | private $max_size = 0; 20 | /** 21 | * The idle timeout (seconds) of channels without bound affinity sessions. 22 | * 23 | * Generated from protobuf field uint64 idle_timeout = 2; 24 | */ 25 | private $idle_timeout = 0; 26 | /** 27 | * The low watermark of max number of concurrent streams in a channel. 28 | * New channel will be created once it get hit, until we reach the max size 29 | * of the channel pool. 30 | * 31 | * Generated from protobuf field uint32 max_concurrent_streams_low_watermark = 3; 32 | */ 33 | private $max_concurrent_streams_low_watermark = 0; 34 | 35 | public function __construct() 36 | { 37 | \GPBMetadata\GrpcGcp::initOnce(); 38 | parent::__construct(); 39 | } 40 | 41 | /** 42 | * The max number of channels in the pool. 43 | * 44 | * Generated from protobuf field uint32 max_size = 1; 45 | * @return int 46 | */ 47 | public function getMaxSize() 48 | { 49 | return $this->max_size; 50 | } 51 | 52 | /** 53 | * The max number of channels in the pool. 54 | * 55 | * Generated from protobuf field uint32 max_size = 1; 56 | * @param int $var 57 | * @return $this 58 | */ 59 | public function setMaxSize($var) 60 | { 61 | GPBUtil::checkUint32($var); 62 | $this->max_size = $var; 63 | 64 | return $this; 65 | } 66 | 67 | /** 68 | * The idle timeout (seconds) of channels without bound affinity sessions. 69 | * 70 | * Generated from protobuf field uint64 idle_timeout = 2; 71 | * @return int|string 72 | */ 73 | public function getIdleTimeout() 74 | { 75 | return $this->idle_timeout; 76 | } 77 | 78 | /** 79 | * The idle timeout (seconds) of channels without bound affinity sessions. 80 | * 81 | * Generated from protobuf field uint64 idle_timeout = 2; 82 | * @param int|string $var 83 | * @return $this 84 | */ 85 | public function setIdleTimeout($var) 86 | { 87 | GPBUtil::checkUint64($var); 88 | $this->idle_timeout = $var; 89 | 90 | return $this; 91 | } 92 | 93 | /** 94 | * The low watermark of max number of concurrent streams in a channel. 95 | * New channel will be created once it get hit, until we reach the max size 96 | * of the channel pool. 97 | * 98 | * Generated from protobuf field uint32 max_concurrent_streams_low_watermark = 3; 99 | * @return int 100 | */ 101 | public function getMaxConcurrentStreamsLowWatermark() 102 | { 103 | return $this->max_concurrent_streams_low_watermark; 104 | } 105 | 106 | /** 107 | * The low watermark of max number of concurrent streams in a channel. 108 | * New channel will be created once it get hit, until we reach the max size 109 | * of the channel pool. 110 | * 111 | * Generated from protobuf field uint32 max_concurrent_streams_low_watermark = 3; 112 | * @param int $var 113 | * @return $this 114 | */ 115 | public function setMaxConcurrentStreamsLowWatermark($var) 116 | { 117 | GPBUtil::checkUint32($var); 118 | $this->max_concurrent_streams_low_watermark = $var; 119 | 120 | return $this; 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/generated/Grpc/Gcp/MethodConfig.php: -------------------------------------------------------------------------------- 1 | grpc.gcp.MethodConfig 11 | */ 12 | class MethodConfig extends \Google\Protobuf\Internal\Message 13 | { 14 | /** 15 | * A fully qualified name of a gRPC method, or a wildcard pattern ending 16 | * with .*, such as foo.bar.A, foo.bar.*. Method configs are evaluated 17 | * sequentially, and the first one takes precedence. 18 | * 19 | * Generated from protobuf field repeated string name = 1; 20 | */ 21 | private $name; 22 | /** 23 | * The channel affinity configurations. 24 | * 25 | * Generated from protobuf field .grpc.gcp.AffinityConfig affinity = 1001; 26 | */ 27 | private $affinity = null; 28 | 29 | public function __construct() 30 | { 31 | \GPBMetadata\GrpcGcp::initOnce(); 32 | parent::__construct(); 33 | } 34 | 35 | /** 36 | * A fully qualified name of a gRPC method, or a wildcard pattern ending 37 | * with .*, such as foo.bar.A, foo.bar.*. Method configs are evaluated 38 | * sequentially, and the first one takes precedence. 39 | * 40 | * Generated from protobuf field repeated string name = 1; 41 | * @return \Google\Protobuf\Internal\RepeatedField 42 | */ 43 | public function getName() 44 | { 45 | return $this->name; 46 | } 47 | 48 | /** 49 | * A fully qualified name of a gRPC method, or a wildcard pattern ending 50 | * with .*, such as foo.bar.A, foo.bar.*. Method configs are evaluated 51 | * sequentially, and the first one takes precedence. 52 | * 53 | * Generated from protobuf field repeated string name = 1; 54 | * @param string[]|\Google\Protobuf\Internal\RepeatedField $var 55 | * @return $this 56 | */ 57 | public function setName($var) 58 | { 59 | $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::STRING); 60 | $this->name = $arr; 61 | 62 | return $this; 63 | } 64 | 65 | /** 66 | * The channel affinity configurations. 67 | * 68 | * Generated from protobuf field .grpc.gcp.AffinityConfig affinity = 1001; 69 | * @return \Grpc\Gcp\AffinityConfig 70 | */ 71 | public function getAffinity() 72 | { 73 | return $this->affinity; 74 | } 75 | 76 | /** 77 | * The channel affinity configurations. 78 | * 79 | * Generated from protobuf field .grpc.gcp.AffinityConfig affinity = 1001; 80 | * @param \Grpc\Gcp\AffinityConfig $var 81 | * @return $this 82 | */ 83 | public function setAffinity($var) 84 | { 85 | GPBUtil::checkMessage($var, \Grpc\Gcp\AffinityConfig::class); 86 | $this->affinity = $var; 87 | 88 | return $this; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/grpc_gcp.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2018 gRPC authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | syntax = "proto3"; 16 | 17 | package grpc.gcp; 18 | 19 | message ApiConfig { 20 | // The channel pool configurations. 21 | ChannelPoolConfig channel_pool = 2; 22 | 23 | // The method configurations. 24 | repeated MethodConfig method = 1001; 25 | } 26 | 27 | message ChannelPoolConfig { 28 | // The max number of channels in the pool. 29 | uint32 max_size = 1; 30 | // The idle timeout (seconds) of channels without bound affinity sessions. 31 | uint64 idle_timeout = 2; 32 | // The low watermark of max number of concurrent streams in a channel. 33 | // New channel will be created once it get hit, until we reach the max size 34 | // of the channel pool. 35 | uint32 max_concurrent_streams_low_watermark = 3; 36 | } 37 | 38 | message MethodConfig { 39 | // A fully qualified name of a gRPC method, or a wildcard pattern ending 40 | // with .*, such as foo.bar.A, foo.bar.*. Method configs are evaluated 41 | // sequentially, and the first one takes precedence. 42 | repeated string name = 1; 43 | 44 | // The channel affinity configurations. 45 | AffinityConfig affinity = 1001; 46 | } 47 | 48 | message AffinityConfig { 49 | enum Command { 50 | // The annotated method will be required to be bound to an existing session 51 | // to execute the RPC. The corresponding will be 52 | // used to find the affinity key from the request message. 53 | BOUND = 0; 54 | // The annotated method will establish the channel affinity with the channel 55 | // which is used to execute the RPC. The corresponding 56 | // will be used to find the affinity key from the 57 | // response message. 58 | BIND = 1; 59 | // The annotated method will remove the channel affinity with the channel 60 | // which is used to execute the RPC. The corresponding 61 | // will be used to find the affinity key from the 62 | // request message. 63 | UNBIND = 2; 64 | } 65 | // The affinity command applies on the selected gRPC methods. 66 | Command command = 2; 67 | // The field path of the affinity key in the request/response message. 68 | // For example: "f.a", "f.b.d", etc. 69 | string affinity_key = 3; 70 | } 71 | --------------------------------------------------------------------------------