GRPC commençant par Python

Aperçu

gRPC est un framework RPC (Remoto Protocol Call) open source moderne et performant qui peut être exécuté dans n'importe quel environnement. Il présente l'avantage de pouvoir sérialiser les données à l'aide de tampons de protocole et de réaliser une communication à haut débit. Il est compatible avec différentes langues et plates-formes, et le streaming bidirectionnel utilisant http / 2 est possible. Vous pouvez simplement définir des services (données et fonctions à communiquer) à l'aide de Protocol Buffers et clarifier les spécifications de l'API.

git: k-washi/stereophonic-Sound-System/proto

L'article gRPC de Golang versin peut être trouvé dans GRPC commençant par Golang.

Contenu

Cette section décrit comment utiliser gRPC pour répondre à une demande d'un client avec la position (x, y, z) d'un objet approprié calculé par le serveur. À l'origine, c'est un système qui génère un son stéréophonique en fonction de la position du personnage, et a été créé pour transmettre des informations de position.

Installation de la bibliothèque

pip install grpcio-tools==1.26.0

Définition du protocole

Définissez les positions x, y, z comme Pos dans le fichier .proto comme indiqué ci-dessous. Ensuite, Position devient un protocole (convention de communication) comprenant des informations de localisation. D'autre part, le protocole Msg est défini comme résultat de l'émission d'un message demandant des informations de localisation au côté serveur ou de l'émission d'informations de localisation. De plus, PositionReq et PositionPub sont définis comme des fonctions utilisées pour la communication. PositionReq est une fonction qui envoie Msg au serveur et reçoit des informations de Position du serveur, et PositionPub est une fonction qui envoie des informations de Position au serveur et reçoit Msg. Dans cet article, nous expliquerons à l'aide d'un exemple utilisant PositionReq.

proto/position.proto


syntax = "proto3";

package posXYZ;
option go_package="posXYZpb";

message Pos {
  float x = 1;
  float y = 2;
  float z = 3;
}

//client streaming
message Position{
  Pos position = 1;
  int32 status = 2;
  string msg = 3;
}

message Msg{
  int32 status = 1;
  string msg = 2;
}


service PositionService{
  
  rpc PositionReq(Msg) returns (Position) {};
  rpc PositionPub(Position) returns (Msg) {};
}

Conversion en tampon de protocole pour python

Comme décrit dans l'article gRPC de Golang versin gRPC commençant par Golang, même s'il est converti en un tampon de protocole pour golang, il ressemble à cet article. Même si vous convertissez en tampon de protocole pour python, la conversion sera basée sur la définition de protocole ci-dessus. En convertissant vers ce tampon de protocole, la structure de chaque message défini dans chaque langue et le document de définition (programme) comprenant la fonction de service sont émis.

Python peut être converti en exécutant le programme suivant.

proto/codegen.py


from grpc.tools import protoc

protoc.main(
    (
        '',
        '-I.',
        '--python_out=.',
        '--grpc_python_out=.',
        './proto/position.proto',
    )
)
python ./proto/codegen.py

Suite à la commande ci-dessus, position_pb2.py qui définit le message défini dans le fichier de protocole et position_pb2_grpc.py qui définit la fonction utilisée pour la communication gRPC sont générés.

Implémentation côté serveur

Ici, implémentez un serveur qui reçoit le message de demande et émet Position (informations de localisation). L'information de position émise ici est une position qui tourne tout en maintenant une distance de 1 m de (x, y, z) = (0, 0, 0).

Concernant configInit pour les paramètres non liés à gRPC et logger pour la journalisation, mon article précédent Parameter setting by python configparser, [Python logging Veuillez vous référer à Comment utiliser le module.

La classe PositionServer surcharge le PositionServerServer défini dans position_pb2_grpc.py et définit la fonction PositionReq qui renvoie des informations de position en réponse à une demande. Cette valeur de retour, Position, utilise celle définie dans position_pb2.py. En outre, cette classe définit également des fonctions qui stockent et produisent des informations de localisation x, y et z, et gère les informations de localisation.

La classe Server est une classe qui résume le traitement effectué par le côté serveur de gRPC. Par conséquent, il a une instance de la classe PosotionServer comme variable. La fonction de démarrage définit le processus pour démarrer le serveur gRPC et la fonction d'arrêt définit le processus pour arrêter le serveur gRPC. Étant donné que ces processus de démarrage et d'arrêt sont des phrases fixes, le flux de base ne change dans aucun programme.

Dans le processus principal, le serveur est ouvert en exécutant la fonction de démarrage et le processus côté serveur de gRPC est terminé. Ici, afin de changer les informations de localisation toutes les heures, les informations de localisation émises par posServer.pubPos (x, y, z) sont écrasées.

proto/server.py


import os
import sys

sys.path.append(os.path.join(os.path.dirname(__file__), '..'))

# ------------

from utils.config import configInit
Conf = configInit()
logger = Conf.setLogger(__name__)

# ------------
#grpc

import grpc
from proto.position_pb2 import *
from proto import position_pb2_grpc

# ------------

from concurrent import futures

class PositionService(position_pb2_grpc.PositinServiceServicer):
  def __init__(self):
    self.x = 1.
    self.y = 0.
    self.z = 0.

  def PositionReq(self, request, context):
    try:
      is_success = 0
    except:
      is_success = -1
    return Position(
      position = Pos(
        x = self.x, y = self.y, z = self.z
      ),
      status = is_success,
      msg = "character position"
    )

  def pubPos(self, x, y, z):
    self.x, self.y, self.z = x, y, z
  
  def getPos(self, x, y, z):
    return self.x, self.y, self.z

class Server():
  def __init__(self):
    self.posServer = PositionService()

  def start(self):
    
    self.server = grpc.server(futures.ThreadPoolExecutor(max_workers=3))
    position_pb2_grpc.add_PositinServiceServicer_to_server(
      self.posServer, self.server
    )

    self.server.add_insecure_port(Conf.PosServer)
    self.server.start()
    logger.info("Start server {0}".format(Conf.PosServer))

  def stop(self):
    self.server.stop(0)

if __name__ == "__main__":
  import time
  import numpy as np
  server = Server()
  server.start()
  
  z = 0.
  azimuth = 0.
  aziShift = 5* np.pi / 180.

  def azi2pos(azimuth):
    x = np.cos(azimuth)
    y = np.sin(azimuth)
    return x, y

  try:
    while True:
      time.sleep(0.1)
      azimuth += aziShift
      x,y = azi2pos(azimuth)
      server.posServer.pubPos(x,y,z)

  except Exception as e:
    logger.critical(e)
    server.stop()

Implémentation côté client

L'implémentation côté client est implémentée dans la classe posClient. Démarrez le traitement du client avec la fonction ouverte. Notez que position_pb2_grpc.PositinServiceStub contient les fonctions que le client exécute. Par conséquent, dans la fonction posRequest, Msg peut être envoyé par la fonction self.stub.PositionReq et les informations du serveur peuvent être obtenues comme valeur de retour. Après cela, chaque fois que vous exécutez posRequest () dans le processus principal, vous pouvez communiquer avec le côté serveur et obtenir les informations de localisation.

proto/client.py


mport os
import sys

sys.path.append(os.path.join(os.path.dirname(__file__), '..'))

# ------------

from utils.config import configInit
Conf = configInit()
logger = Conf.setLogger(__name__)

# ------------
#grpc

import grpc
from proto.position_pb2 import *
from proto import position_pb2_grpc

class posClient():
  def __init__(self):
    self.x = 0.
    self.y = 0.
    self.z = 0.

  def posRequest(self):
    request = Msg(
      status = 0,
      msg = "request pos"
    )
    res = self.stub.PositionReq(request)
    if res.status == 0:
      logger.info("PositionRes {0}, {1}, x:{2}, y:{3}, z:{4}".format(res.status, res.msg, res.position.x, res.position.y, res.position.z))
      self.x, self.y, self.z = res.position.x, res.position.y, res.position.z
      return True
    
    logger.error("Position Response Error")
    return False

  
  def open(self):
    self.channel = grpc.insecure_channel(Conf.PosClient)
    self.stub = position_pb2_grpc.PositinServiceStub(self.channel)
    logger.info("Open position client channel: {0}".format(Conf.PosClient))

  def close(self):
    self.channel.close()
  
  def getPos(self):
    return self.x, self.y, self.z
  
if __name__ == "__main__":
  import time
  posCl = posClient()
  posCl.open()
  while True:
    time.sleep(1)
    try:
      ok = posCl.posRequest()
      if ok:
        x, y, z = posCl.getPos()
        logger.info("{0}, {1}, {2}".format(x, y, z))
    except Exception as e:
      logger.error("client error {0}".format(e))
  posCl.close()

Résumé

Avec ce qui précède, les informations de localisation peuvent être échangées avec gRPC par python. Je pense que gRPC sera une technologie qui sera utilisée dans de nombreuses situations à l'avenir, car elle présente des avantages tels que faciliter la création de microservices écrits dans plusieurs langues. Veuillez essayer.

Recommended Posts

GRPC commençant par Python
Python à partir de Windows 7
Apprentissage amélioré à partir de Python
J'ai essayé gRPC avec Python
Python commençant par Hello world!
Analyse de données à partir de python (visualisation de données 1)
Analyse de données à partir de python (visualisation de données 2)
FizzBuzz en Python3
Grattage avec Python
Grattage avec Python
Python avec Go
Twilio avec Python
Intégrer avec Python
Jouez avec 2016-Python
AES256 avec python
Testé avec Python
python commence par ()
avec syntaxe (Python)
Bingo avec python
Zundokokiyoshi avec python
Excel avec Python
Micro-ordinateur avec Python
Cast avec python
Communiquez entre Elixir et Python avec gRPC
Commerce système à partir de Python3: investissement à long terme
"Commerce du système à partir de Python3" lecture du mémo
Efficacité commerciale à partir de zéro avec Python
Analyse de données à partir de python (pré-traitement des données-apprentissage automatique)
"Première recherche élastique" commençant par un client python
Communication série avec Python
Zip, décompressez avec python
Django 1.11 a démarré avec Python3.6
Python avec eclipse + PyDev.
Communication de socket avec Python
Analyse de données avec python 2
Grattage en Python (préparation)
Essayez de gratter avec Python.
Apprendre Python avec ChemTHEATER 03
Recherche séquentielle avec Python
Apprentissage automatique à partir de Python Personal Memorandum Part2
"Orienté objet" appris avec python
Manipuler yaml avec python
Résolvez AtCoder 167 avec python
Communication série avec python
[Python] Utiliser JSON avec Python
Apprendre Python avec ChemTHEATER 05-1
Apprenez Python avec ChemTHEATER
Exécutez prepDE.py avec python3
1.1 Premiers pas avec Python
Collecter des tweets avec Python
Binarisation avec OpenCV / Python
3. 3. Programmation IA avec Python
Méthode Kernel avec Python
Apprentissage automatique à partir de Python Personal Memorandum Part1
Grattage avec Python + PhantomJS
Conduisez WebDriver avec python
[Python] Redirection avec CGIHTTPServer
Analyse vocale par python
Pensez à yaml avec python
Utiliser Kinesis avec Python
Utiliser DynamoDB avec Python