かぐや姫方式(gRPC技術):工事中

かぐや姫方式(gRPC技術)について、ご紹介します。

かぐや姫はペルシャ王女だった

日本書記のかぐや姫に関する記述

「かぐや姫」は、以下の二つの本に全く同じことが書いてあって、 ササン朝ペルシャの最後の王子であるペーローズの娘なんだそうです。

  1. 孫崎紀子著「かぐや姫誕生の謎」(現代書館)
  2. 小林恵子著「本当は怖ろしい万葉集~壬申の乱編」(祥伝社黄金文庫)

日本書記には、この「かぐや姫」に関して、以下の記述があるそうです。

  • 654年夏4月 吐火羅国男二人女二人舎衛女一人風に被いて日向に流れ来たり
  • 657年秋7月 都貨羅国男二人女四人筑紫に漂い泊まれり
  • 659年3月 吐火羅人、妻舎衛婦人とともに来けり
  • 660年秋7月 都火羅人、乾豆波斯達阿、本土に帰えらむと西海之路に入なむ
  • 675年春正月 舎衛の女、堕羅の女、薬及び珍に異しき等物を捧げて進る

吐火羅(トカラ)国は、ペルシャの北東にありペーローズ達が逃げ込んだ場所です。

上記の達阿がペーローズであり、舎衛女がその妻で、堕羅(ダラ)の女がその娘で つまり「かぐや姫」だと言う訳ですね。

大津皇子とかぐや姫との悲恋

「本当は怖ろしい万葉集」によると「かぐや姫」は、天武天皇の息子である 大津皇子と草壁王子の二人から求婚され、大津皇子の方を選んだとか。

日本書記や万葉集では、「かぐや姫」は「山辺皇女」と言う名前で登場するそうです。

大津皇子は、天智天皇の娘である大田皇女と天武天皇の間にできた子供です。

ところが、大津皇子の愛人であった「山辺皇女」を父親の天武天皇が奪ったのです。

これで、大津皇子は父親の天武天皇に恨みを持つようになったのだそうです。

683年に大津皇子は天皇になったそうですが、天武天皇に逆らっていたため、 686年7月に失脚し、10月に反逆罪で死刑になりました。

その死刑の場所が「磐余の池」と呼ばれる池で、溺死と言う刑なんだそうです。

皇族には体を傷つけない溺死刑が普通なんだそうです。

そして、大津皇子の愛人であった「山辺皇女」は、大津皇子の後を追って、 「磐余の池」に身を投げて死んだのだそうです。

藤原不比等は、かぐや姫により出世した

問題は、「山辺皇女」を父親の天武天皇が奪った時に派遣された使いが、 例のセコイ「藤原不比等」であったことです。

竹取物語に「庫持の皇子」と登場するのが「藤原不比等」ですね。

大津皇子の愛人であった「山辺皇女」は、色々難題を「藤原不比等」に 突きつけて天武天皇の後宮になるのを防ごうとした訳ですね。

セコイ「藤原不比等」は懐に「干飯」をこっそり一杯詰め込んで 「山辺皇女」つまり、「かぐや姫」の家を訪問し、「私は、山辺皇女を連れて、 天武天皇のところに戻るまでは、何も食べないで、ここで待っております」と カッコいいことを言って陰では懐の「干飯」をボリボリ食っていたらしいです。

しかし、困ったちゃうなーの「山辺皇女」も当時の権力者に逆らうのは難しく、 イヤイヤながらもスケベ・ジジー天武天皇の後宮になったのだそうです。

この時の、「藤原不比等」の行動が、天武天皇の信頼を得たそうで、 以後の藤原道長などに繋がる藤原氏の繁栄の元になったらしいです。

つまり、「藤原不比等」は「かぐや姫」のお陰で出世できた訳ですね。

「かぐや姫」と大津皇子との悲恋をブチ壊すことで出世できた訳です。

竹取物語は、藤原不比等を告発する書

竹取物語は、それを告発する本なのでしょうね。

「かぐや姫」はペルシャ人ですから、さぞかし美人だったと思います。

「かぐや姫」を書いたのは、菅原文時(899-981)と言う菅原道真の孫です。

菅原文時は文章博士であったから、当然古今東西の書物には、詳しかった。

菅原文時は、祖父菅原道真が藤原氏のために大宰府に左遷されことと、 200年前の大津皇子が反逆罪で水死刑になり、その後を追って死んだ ペルシャ人の愛人である「山辺皇女」の話を結び付けて、藤原氏を告発したかったに違いないですね。

かぐや姫こと「山辺皇女」の時代

西暦 和暦 事件
655 幸徳天皇5年夏4月 吐火羅国男二人女二人舎衛女一人風に被いて日向に流れ来たれり
657 斉明天皇3年秋7月 都貨羅国男二人女四人筑紫に漂い泊まれり。言さく、「臣等、初め海見島に漂い泊れりに」ともうす。
659 斉明天皇5年3月 吐火羅人、妻舎衛婦人と共に来けり
660 斉明天皇6年秋7月 都火羅人乾豆波斯達阿、本土に帰えらむと送使を求ぎ曰さ「願わくは、後に大国に朝らむ。所以に、妻をとどめて表とす。」もうす。 乃ち数十人と西海之路に入りぬ
675 天武天皇4年春正月 大学寮の諸々の学生・陰陽寮・下薬寮及び舎衛の女・堕羅の女・百済の王善光・新羅の仕丁等、薬及び珍に異しき等物を捧げて進る
686 朱鳥元年10月2日 友人の川島皇子の密告により、謀反の意有りとされて大津皇子を逮捕。翌日に磐余にある訳語田の自邸から磐余池にて水死刑。享年24。

盂蘭盆会

「ウラボン」とは仏教用語でもなく、サンスクリット語でもない。

これはペルシア語由来が正しいようだ。同じ季節にイラン・イラクなどは祖霊祭をやる。

もちろんイスラム以前の風習だろう。

盂蘭盆の語源は「ウランバナ」ではある。けれどそれは膨大なサンスクリット文献に出てこない言葉である。むしろ日本の仏教者がウラボンから造作した言葉だろう。

宗蜜『盂蘭盆経疎』(9世紀)には「盂蘭」=西域としている。

古代イラン語に”Ulavan”がある。

そもそもは「天則を回復した者の月」”Artavanam”に由来する東イラン方言である。

これはイスラム圏の古い正月祖霊祭りである「ラマダン」の夏季の祭に当たる。

天則とは中国の女帝則天武后も使っている言葉。天の、つまり「自然の摂理」=神であろう。

かぐや姫を偲びながら「gRPC」のお勉強と実装に励もう

我々も雲上人の「かぐや姫」を偲びながら「gRPC」のお勉強と実装に励み 「かぐや姫方式」を確立したいものです。

南阿弥陀仏・・・

えっ、「gRPC」って何かって?それは次のタブで・・

gRPC技術とは

雲上人の「かぐや姫」を偲びながら「gRPC技術」に励み「かぐや姫方式」を確立ましょう。

gRPC技術とは

HTTP/2を標準でサポートしたRPCフレームワークで、2015年にGoogleが発表しました。 デフォルトで対応しているProtocolBufferをgRPC用に書いた上で、サポートしている言語に書き出しを行うと、異なる言語のサーバー間でもある程度型堅牢に通信を行うことができます。 GoogleではプロトコルをProtoclBufferで書いているそうです。


ProtocolBuffer

こちらは構造化されたデータ・フォーマットであり、構造化の仕方はGolangに似ています。 gRPCではmessageという構造体と、serviceというRPC部分の実装を記述します。 Proto3(version3)からはJSONフォーマットへのデコードもサポートするようになったので、もしgRPCではなく通常のREST Apiで送信したい場合はそのようにフォーマットするといいかと思います。

gRPC技術で電話が作れちゃう?

gRPC技術は、HTTP/2を標準でサポートしたRPCフレームワークですが、HTTP/2と従来のHTTP/1.1の大きな違いは、ストリーミングをサポートしていることです。

ストリーミングは、最近、流行りのAIスピーカの「Google Home」「Amazon Echo」やインターネットテレビの「AbemaTV」などで 使用されている技術です。

ストリーミングを使うと、音声、音楽、動画などをリアルタイムに相手に送ることができます。 WebのForm画面で音声、音楽、動画などのファイルをバッチ的に一方向に送るのとは違いますから、 電話のように、全二重な双方向通信が可能で会話ができる技術です。

gRPC技術を使ったPythonサンプル例

gRPC技術を使ったPythonサンプル例を紹介します。

GRCP Python Quickstartからの引用です。

IDL(helloworld.proto)

.protoファイルにシリアライズするデータ構造を定義します。メッセージタイプという形式に従って、それぞれの構造を下記のように記述します。

// Copyright 2015 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";

option java_multiple_files = true;
option java_package = "io.grpc.examples.helloworld";
option java_outer_classname = "HelloWorldProto";
option objc_class_prefix = "HLW";

package helloworld;

// The greeting service definition.
service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {}
  // Sends another greeting(追加)
  rpc SayHelloAgain (HelloRequest) returns (HelloReply) {}
}

// The request message containing the user's name.
message HelloRequest {
  string name = 1;
}

// The response message containing the greetings
message HelloReply {
  string message = 1;
}
サービスの定義

上記のIDLでは、まず、「Greeter」と言うサービスを定義しています。

「Greeter」と言うサービスの中には、「SayHello」と「SayHelloAgain」の二つのRPCメソッドがあります。

「SayHello」と言うRPCメソッドは、クライアントからサーバへ要求時には「HelloRequest」と言うメッセージ形式を使用します。 サーバからの応答時には「HelloReply」と言うメッセージ形式を使用します。

メッセージの定義

上記のIDLでは、「HelloRequest」と「HelloReplay」と言うメッセージ形式を定義しています。

「HelloRequest」と言うメッセージ形式は、「name」と言う「string」データだけです。

「HelloReply」と言うメッセージ形式も、「message」と言う「string」データだけです。

IDLのコンパイル

helloworld.protoがある場所で、以下のコマンド(protoc)を実行するとスタブが生成されます。

$ python3 -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. helloworld.proto

スタブと言うのは、以下の「helloworld_pb2.py」と「helloworld_pb2_grpc.py」のことのようです。

サーバアプリ(greeter_server.py)やクライアントアプリ(greeter_client.py)は、これをimportして使用します。

「helloworld_pb2.py」と「helloworld_pb2_grpc.py」の違いは、調査中です。

生成モジュール1(helloworld_pb2.py)

HelloRequestは、クライアントの要求データのシリアライズ処理を行います。

HelloReplyは、サーバの応答データのシリアライズ処理を行います。

# Generated by the protocol buffer compiler.  DO NOT EDIT!
# source: helloworld.proto

import sys
_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1'))
from google.protobuf import descriptor as _descriptor
from google.protobuf import message as _message
from google.protobuf import reflection as _reflection
from google.protobuf import symbol_database as _symbol_database
from google.protobuf import descriptor_pb2
# @@protoc_insertion_point(imports)

_sym_db = _symbol_database.Default()




DESCRIPTOR = _descriptor.FileDescriptor(
  name='helloworld.proto',
  package='helloworld',
  syntax='proto3',
  serialized_pb=_b('\n\x10helloworld.proto\x12\nhelloworld\"\x1c\n\x0cHelloRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\"\x1d\n\nHelloReply\x12\x0f\n\x07message\x18\x01 \x01(\t2\x8e\x01\n\x07Greeter\x12>\n\x08SayHello\x12\x18.helloworld.HelloRequest\x1a\x16.helloworld.HelloReply\"\x00\x12\x43\n\rSayHelloAgain\x12\x18.helloworld.HelloRequest\x1a\x16.helloworld.HelloReply\"\x00\x42\x36\n\x1bio.grpc.examples.helloworldB\x0fHelloWorldProtoP\x01\xa2\x02\x03HLWb\x06proto3')
)




_HELLOREQUEST = _descriptor.Descriptor(
  name='HelloRequest',
  full_name='helloworld.HelloRequest',
  filename=None,
  file=DESCRIPTOR,
  containing_type=None,
  fields=[
    _descriptor.FieldDescriptor(
      name='name', full_name='helloworld.HelloRequest.name', index=0,
      number=1, type=9, cpp_type=9, label=1,
      has_default_value=False, default_value=_b("").decode('utf-8'),
      message_type=None, enum_type=None, containing_type=None,
      is_extension=False, extension_scope=None,
      options=None),
  ],
  extensions=[
  ],
  nested_types=[],
  enum_types=[
  ],
  options=None,
  is_extendable=False,
  syntax='proto3',
  extension_ranges=[],
  oneofs=[
  ],
  serialized_start=32,
  serialized_end=60,
)


_HELLOREPLY = _descriptor.Descriptor(
  name='HelloReply',
  full_name='helloworld.HelloReply',
  filename=None,
  file=DESCRIPTOR,
  containing_type=None,
  fields=[
    _descriptor.FieldDescriptor(
      name='message', full_name='helloworld.HelloReply.message', index=0,
      number=1, type=9, cpp_type=9, label=1,
      has_default_value=False, default_value=_b("").decode('utf-8'),
      message_type=None, enum_type=None, containing_type=None,
      is_extension=False, extension_scope=None,
      options=None),
  ],
  extensions=[
  ],
  nested_types=[],
  enum_types=[
  ],
  options=None,
  is_extendable=False,
  syntax='proto3',
  extension_ranges=[],
  oneofs=[
  ],
  serialized_start=62,
  serialized_end=91,
)

DESCRIPTOR.message_types_by_name['HelloRequest'] = _HELLOREQUEST
DESCRIPTOR.message_types_by_name['HelloReply'] = _HELLOREPLY
_sym_db.RegisterFileDescriptor(DESCRIPTOR)

HelloRequest = _reflection.GeneratedProtocolMessageType('HelloRequest', (_message.Message,), dict(
  DESCRIPTOR = _HELLOREQUEST,
  __module__ = 'helloworld_pb2'
  # @@protoc_insertion_point(class_scope:helloworld.HelloRequest)
  ))
_sym_db.RegisterMessage(HelloRequest)

HelloReply = _reflection.GeneratedProtocolMessageType('HelloReply', (_message.Message,), dict(
  DESCRIPTOR = _HELLOREPLY,
  __module__ = 'helloworld_pb2'
  # @@protoc_insertion_point(class_scope:helloworld.HelloReply)
  ))
_sym_db.RegisterMessage(HelloReply)


DESCRIPTOR.has_options = True
DESCRIPTOR._options = _descriptor._ParseOptions(descriptor_pb2.FileOptions(), _b('\n\033io.grpc.examples.helloworldB\017HelloWorldProtoP\001\242\002\003HLW'))

_GREETER = _descriptor.ServiceDescriptor(
  name='Greeter',
  full_name='helloworld.Greeter',
  file=DESCRIPTOR,
  index=0,
  options=None,
  serialized_start=94,
  serialized_end=236,
  methods=[
  _descriptor.MethodDescriptor(
    name='SayHello',
    full_name='helloworld.Greeter.SayHello',
    index=0,
    containing_service=None,
    input_type=_HELLOREQUEST,
    output_type=_HELLOREPLY,
    options=None,
  ),
  _descriptor.MethodDescriptor(
    name='SayHelloAgain',
    full_name='helloworld.Greeter.SayHelloAgain',
    index=1,
    containing_service=None,
    input_type=_HELLOREQUEST,
    output_type=_HELLOREPLY,
    options=None,
  ),
])
_sym_db.RegisterServiceDescriptor(_GREETER)

DESCRIPTOR.services_by_name['Greeter'] = _GREETER

# @@protoc_insertion_point(module_scope)
生成モジュール2(helloworld_pb2_gprc.py)

「GreerStub」クラスは、クライアント用のスタブです。

「GreerServicer」クラスは、サーバ用のサービス提供者です。

「add_GreeterServicer_to_serve」メソッドは、サービス提供者にサービスメニューを追加します。

# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
import grpc

import helloworld_pb2 as helloworld__pb2


class GreeterStub(object):
  """The greeting service definition.
  """

  def __init__(self, channel):
    """Constructor.

    Args:
      channel: A grpc.Channel.
    """
    self.SayHello = channel.unary_unary(
        '/helloworld.Greeter/SayHello',
        request_serializer=helloworld__pb2.HelloRequest.SerializeToString,
        response_deserializer=helloworld__pb2.HelloReply.FromString,
        )
    self.SayHelloAgain = channel.unary_unary(
        '/helloworld.Greeter/SayHelloAgain',
        request_serializer=helloworld__pb2.HelloRequest.SerializeToString,
        response_deserializer=helloworld__pb2.HelloReply.FromString,
        )


class GreeterServicer(object):
  """The greeting service definition.
  """

  def SayHello(self, request, context):
    """Sends a greeting
    """
    context.set_code(grpc.StatusCode.UNIMPLEMENTED)
    context.set_details('Method not implemented!')
    raise NotImplementedError('Method not implemented!')

  def SayHelloAgain(self, request, context):
    """Sends another greeting
    """
    context.set_code(grpc.StatusCode.UNIMPLEMENTED)
    context.set_details('Method not implemented!')
    raise NotImplementedError('Method not implemented!')


def add_GreeterServicer_to_server(servicer, server):
  rpc_method_handlers = {
      'SayHello': grpc.unary_unary_rpc_method_handler(
          servicer.SayHello,
          request_deserializer=helloworld__pb2.HelloRequest.FromString,
          response_serializer=helloworld__pb2.HelloReply.SerializeToString,
      ),
      'SayHelloAgain': grpc.unary_unary_rpc_method_handler(
          servicer.SayHelloAgain,
          request_deserializer=helloworld__pb2.HelloRequest.FromString,
          response_serializer=helloworld__pb2.HelloReply.SerializeToString,
      ),
  }
  generic_handler = grpc.method_handlers_generic_handler(
      'helloworld.Greeter', rpc_method_handlers)
  server.add_generic_rpc_handlers((generic_handler,))

サーバ側アプリ(greeter_server.py)

「Greeter」クラス

サーバ側アプリは、「Greeter」と言うクライアントアプリから呼ばれる「helloworld_pb2_grpc.GreeterServicer」の継承クラスを持ちます。

「Greeter」クラスには、「SayHello」と「SayHelloAgain」の二つのメソッドを持ちます。

「SayHello」メソッドでは、「HellowReply」で「message」にデータを格納してシリアライズを行います。

「SayHelloAgain」メソッドでも、「HellowReply」で「message」にデータを格納してシリアライズを行います。

「serve」メソッド

サーバ側アプリは、「serve」と言うサーバ処理を行うメソッドを持ちます。

「serve」メソッドでは、「grpc」の「server」メソッドを使用して「grpc」スレッド(server)を生成します。

「serve」メソッドでは、「helloworld_pb2」の「add_GreeterSevicer_to_server」メソッドを使用して「Greeter」クラスを登録します。

「serve」メソッドでは、「server」が「ポート50051」を使うよう指示して、「server」を「start」メソッドで起動します。

# Copyright 2015 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.

"""The Python implementation of the GRPC helloworld.Greeter server."""

from concurrent import futures
import time

import grpc

import helloworld_pb2
import helloworld_pb2_grpc

APP = 'greeter_server.py'

_ONE_DAY_IN_SECONDS = 60 * 60 * 24


class Greeter(helloworld_pb2_grpc.GreeterServicer):

  def SayHello(self, request, context):
    print(APP + ' message = ' + 'Hello, %s!' % request.name)
    return helloworld_pb2.HelloReply(message='Hello, %s!' % request.name)

  def SayHelloAgain(self, request, context):
    return helloworld_pb2.HelloReply(message='Hello again, %s!' % request.name)


def serve():
  server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
  helloworld_pb2_grpc.add_GreeterServicer_to_server(Greeter(), server)
  server.add_insecure_port('[::]:50051')
  server.start()
  try:
    while True:
      time.sleep(_ONE_DAY_IN_SECONDS)
  except KeyboardInterrupt:
    server.stop(0)

if __name__ == '__main__':
  serve()

クライアント側アプリ(greeter_client.py)

「run」メソッド

クライアント側アプリは、クライアント処理を行う「run」メソッドを持ちます。

「run」メソッドでは、「grpc」の「insecure_channel」メソッドで、サーバのURLを指定して、チャネルを生成します。

「run」メソッドでは、チャネルを指定して、「helloworld_pb2_grpc」の「GreeterStub」メソッドで、スタブを生成します。

「run」メソッドでは、スタブを使用して、サーバ側の「HelloRequest」メソッドを呼び出して、サーバに送信して、応答(response)を受け取ります。 この時、送信パラメータ「name」には'you'を指定します。応答(response)の「message」が受信デ-タです。

「run」メソッドでは、続いて、スタブを使用して、サーバ側の「HelloRequestAgain」メソッドを呼び出して、サーバに送信して、応答(response)を受け取ります。 この時、パラメータ「name」には'you'を指定します。応答(response)の「message」が受信デ-タです。

# Copyright 2015 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.

"""The Python implementation of the GRPC helloworld.Greeter client."""

from __future__ import print_function

import grpc

import helloworld_pb2
import helloworld_pb2_grpc


def run():
  channel = grpc.insecure_channel('localhost:50051')
  stub = helloworld_pb2_grpc.GreeterStub(channel)
  response = stub.SayHello(helloworld_pb2.HelloRequest(name='you'))
  print("Greeter client received: " + response.message)

  response = stub.SayHelloAgain(helloworld_pb2.HelloRequest(name='you'))
  print("Greeter client received: " + response.message)

if __name__ == '__main__':
  run()


森の小径2