1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

サーバーにruby、クライアントにPHPでgRPCを頑張る

Last updated at Posted at 2021-01-07

##はじめに
google謹製のRPCフレームワークであるgRPCを使って、サーバーにruby、クライアントにPHPを使って、異なる言語間を共通のI/FでAPI通信してみたいと思います。

異なる言語間としているのはgRPCのメリットであるIDL定義によるI/Fの共通化の醍醐味を味わってみたいというより、PHPでgRPCサーバの構築ができないという現実に対する条件反射のようなものですが、結果的に醍醐味もやってくるので、味わってみたいと思います。

クライアントにPHPを使う場合、サーバー側はどうもgoで紹介しているところが多いので、ここではrubyを使ってみます(はい、go知らないの確定)。

概要はここで書くまでもないので、さっそく。
ここではAPIとして、ドメインを指定すると有効期限を取ってくる、というものを書いてみます。

INPUT: FQDN
OUTPUT: yyyy/mm/dd hh:ii:ss

API(gRPC): 定義したI/Oで共通I/Fを用意
クライアント(PHP): サーバを通じてAPIにFQDNをリクエスト
サーバ(ruby): クライアントからリクエストされたFQDNからSSL証明書の有効期限を調べてAPI経由で日付をレスポンス

##事前作業

pecl install grpc
pecl install protobuf
vi ()/php.ini
	extension=grpc.so
	extension=protobuf.so

gem install grpc
gem install grpc-tools

mkdir ~/projects/grpc_trial/
cd ~/projects/grpc_trial/

composer require grpc/grpc
composer require google/protobuf

mkdir {protos,php,ruby}

##実作業

vi protos/checkexpires.proto
    (後述)

grpc_tools_ruby_protoc -I ./protos --ruby_out=./ruby --grpc_out=./ruby ./protos/checkexpires.proto
ls ruby/ | grep checkexpires
	checkexpires_pb.rb
	checkexpires_services_pb.rb

protoc --proto_path=./protos --php_out=./php --grpc_out=./php ./protos/checkexpires.proto --plugin=protoc-gen-grpc=/usr/local/bin/grpc_php_plugin
ls php/Checkexpires/
	CheckReply.php  CheckRequest.php  GetSslClient.php

vi checkexpires.rb
    (後述)

vi checkexpires.php
    (後述)

ruby ./checkexpires.rb &
php ./checkexpires.php 
php checkexpires.php example.com
	2021/12/25 23:59:59
checkexpires.proto
syntax = "proto3";

package checkexpires;

import "google/protobuf/timestamp.proto";

service GetSsl {
  rpc getExpire (CheckRequest) returns (CheckReply) {}
}

message CheckRequest {
  string fqdn = 1;
}

message CheckReply {
  google.protobuf.Timestamp timestamp = 1;
}
checkexpires.rb
this_dir = File.expand_path(File.dirname(__FILE__))
lib_dir = File.join(this_dir, 'ruby')
$LOAD_PATH.unshift(lib_dir) unless $LOAD_PATH.include?(lib_dir)

require 'time'
require 'grpc'
require 'checkexpires_services_pb'

class CheckexpiresServer < Checkexpires::GetSsl::Service
  def get_expire(check_req, _unused_call)
    end_at = `openssl s_client -connect #{check_req.fqdn}:443 </dev/null 2>/dev/null|openssl x509 -text | grep "Not After"`
    end_at = end_at.split(' : ').pop
    end_at = end_at.gsub("\n", '')
    end_at = Time.parse(end_at).to_i
    end_at = Time.at(end_at)
    Checkexpires::CheckReply.new(timestamp: end_at)
  end
end

def main
  s = GRPC::RpcServer.new
  s.add_http2_port('localhost:50051', :this_port_is_insecure)
  s.handle(CheckexpiresServer)
  s.run_till_terminated_or_interrupted([1, 'int', 'SIGQUIT'])
end

main
checkexpires.php
<?php
require dirname(__FILE__).'/vendor/autoload.php';
require dirname(__FILE__).'/php/Checkexpires/GetSslClient.php';
require dirname(__FILE__).'/php/Checkexpires/CheckReply.php';
require dirname(__FILE__).'/php/Checkexpires/CheckRequest.php';
require dirname(__FILE__).'/php/GPBMetadata/Checkexpires.php';
$server = 'localhost:50051'; //checkexpires.rb

if (is_null($argv[1] ?? null)) {
    echo "need to input fqdn\n";
    exit(1);
}

try {
    $client = new Checkexpires\GetSslClient($server, [
        'credentials' => Grpc\ChannelCredentials::createInsecure(),
    ]);
    $request = new Checkexpires\CheckRequest();
    $request->setFqdn($argv[1]);
    list($reply, $status) = $client->getExpire($request)->wait();

    if (($status->code ?? null) === 0) {
        $ts = $reply->getTimestamp()->getSeconds();
        echo date('Y/m/d H:i:s', $ts);
        exit(0);
    }
} catch (Exception $e) {
    echo $e->getMessage();
    exit(1);
}

##補足
せっかく異なる言語で共通I/Fを定義しているのですから、それぞれのコンパイラも一個に共通化してしまいましょう。

vi ~/.bashrc
	function protoc2() {
	    grpc_tools_ruby_protoc -I ./protos --ruby_out=./ruby --grpc_out=./ruby ./protos/$@ && protoc --proto_path=./protos --php_out=./php --grpc_out=./php ./protos/$@ --plugin=protoc-gen-grpc=/usr/local/bin/grpc_php_plugin
	}

. ~/.bashrc
cd ~/projects/grpc_trial/
protoc2 checkexpires.proto

これで、ひとつのprotoファイルでrubyとPHPそれぞれの共通I/Fが準備できました。
あとはクライアント側とサーバ側にそれぞれ固有な処理を書いていけばいい感じですね。
頑張れそうです。

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?