Two-way communication (python) flow-based way of grpc

grpc Introduction

grpc is Google's open source-based communications framework set rpc realized ( official website has more complete definition). Before they get to know grpc, we must first understand what rpc Yes. Here is rpc define their own understanding, if not, Wang said:

rpc officially known as remote procedure calls . I am here to be understood as a remote function call that a native program calls a function in another machine program. Because calling is not the same machine, you need remote access operations.

In contrast to the remote procedure call is a "short-range Procedure Call" (Haha, her own played). In fact, the program implementation and calls on the same machine. For example, learned object-oriented language (e.g., java) can be interpreted as: a method implements a class, then the instances of another program in a new class (object), and invokes the method. The remote procedure call is equivalent to a machine implemented method of a class, the new machine to another object of this class, if it wants to call this method, it is necessary to implement a machine with a class method of communication. At this point we can say class that implements the method of the machine to the server, new objects to the client machine.

communication grpc

grpc with http communication as a means of communication is also based on "request-response" mode. Client request, the server responds. For more introduction grpc can refer to the official website. Said the following about four kinds of communication grpc [see official website ], according to different business scenarios can be divided into:

1. The client single request, the server response time:

// Obtains the feature at a given position.
rpc GetFeature(Point) returns (Feature) {}

2. The client first request, the streaming server response (in fact equivalent to a plurality of data back to the client end)

// Obtains the Features available within the given Rectangle.  Results are
// streamed rather than returned at once (e.g. in a response message with a
// repeated field), as the rectangle may cover a large area and contain a
// huge number of features.
rpc ListFeatures(Rectangle) returns (stream Feature) {}

3. The client streaming request, the server response time

// Accepts a stream of Points on a route being traversed, returning a
// RouteSummary when traversal is completed.
rpc RecordRoute(stream Point) returns (RouteSummary) {}

4. The streaming client requests streaming server response

// Accepts a stream of RouteNotes sent while a route is being traversed,
// while receiving other RouteNotes (e.g. from other users).
rpc RouteChat(stream RouteNote) returns (stream RouteNote) {}

Know after four of communication, back to the main issue, we have to use these four ways of communication to achieve the client and server communicate with each other. To achieve mutual communication, here I think there are two:

  1. The client and server each both a client and a server
    this way feel is most easily achieved. I.e. between the client and server implementations a "request-response model", so when the client side active communication service is a normal response to the request, the server when it is active at this time communication client becomes client requests. Such external opinion can communicate with each other between the two machines.
    The implementation manner to establish a communication channel two. The disadvantage is that to achieve the two sets of communication code.
  2. The client and server communicate directly with each other
    that we know grpc is based on the response to the request, the client request, the server response. How to let the server take the initiative to request that client communication do? In fact, we can use grpc the service end of the second or fourth streaming response . Principle is to allow the client to send a blank message to the server to let the server know (the equivalent of clients registered on the server side), and then streaming server to respond. Streaming will not respond because all of a sudden back to finish, we can put in the middle of the server to send messages to clients added to the flow, so that the flow of batches of messages to the client.
    In the external view of the client and the server can communicate with each other. However, this disadvantage is that the service communicate with each other are melted mixed into a piece.

Implementation

It says the two methods of communicating with each other and four kinds of communication grpc. The second realization method used here and the second communication grpc be implemented using the Python programming language implementation.
grpc protobuf employed to define and transmit data. Therefore, the data communication is defined by proto file. About proto syntax can refer to the documentation
first establish the following directories:

│  contact_client.py
│  contact_server.py
|
├─contact
│  │  contact.proto
│  │ 
│  │  __init__.py

contact.proto: data communication section defines
contact_client.py: client code
contact_server.py: server code

contact.proto reads as follows:

syntax = "proto3";

// 定义一个服务
service Contact {
    // 客户端通信给服务端,通信方式可以随意选择,这里我选择第4种通信方式
    rpc sendStatus (stream ClientMsg) returns (stream Result);
    // 客户端发送一个空消息给服务端,服务端就能给客户端通信了
    rpc getTask (Empty) returns (stream ServerMsg);
    // 客户端接受完服务端消息处理完后,再告诉服务端。这个tellResult也可以不要,看具体需求
    rpc tellResult (stream Result) returns (Empty);
}

message ClientMsg {
    string msg = 1;
}

message ServerMsg {
    string task = 1;
}

message Empty {

}

message Result {
    string ret = 1;
}

In the contact folder run the command:

python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. contact.proto

Automatically generates contact_pb2.py and contact_pb2_grpc.py two files in the contact directory. Down is to achieve a specific communication, the first client to the server message:
contact_server.py achieve specific code in the code:

    # 注意服务端的具体实现函数是在类里面
    def sendStatus(self, request_iterator, context):
        for note in request_iterator:
            yield contact_pb2.Result(
                result=f"服务端接收到消息:{note.msg}"
            )

contact_client.py code is:

# 先制造一些客户端能发送的数据
def make_some_data():
    for i in range(15):
        num = random.randint(1, 20)
        yield contact_pb2.ClientMsg(msg=f"数据:{num}")
        
        
def send_status(stub):
    try:
        while True:
            status_response = stub.sendStatus(make_some_data())
            for ret in status_response:
                print(ret.result)
            time.sleep(60)
    except Exception as e:
        print(f'err in send_status:{e}')
        return

The above code to achieve the function of the client-side active communication service. We can see a server to achieve specific code, and then call the customer care specific function and end server communication, and finally the data returned from the server for processing.
The active communication client server can be understood as a way: give the client sends the server a message telling me online server, then the server can send data to the client, and the client and then finally I received notification server What are your data. Specific code:
Server-side code:

import logging
import random
import time
from concurrent import futures

import grpc
from contact import contact_pb2_grpc
from contact import contact_pb2

# 在类初试化的时候定义了一个列表self.tasks来充当任务队列
def getTask(self, request_iterator, context):
    print("服务端已接收到客户端上线通知,开始发送任务给客户端\n")
    last_index = 0
    while True:
        print("服务端开始发送任务给客户端了。。。。。。\n")
        while len(self.tasks) > last_index:
            n = self.tasks[last_index]
            last_index += 1
            yield n
            print(f'服务端发送给了客户端任务:{n.task}##########\n')
            
       # 顺便制造些服务端的任务数据用来填充到任务队列里面
        for i in range(10):
            num = random.randint(100, 200)
            self.tasks.append(contact_pb2.ServerMsg(
                task=f"任务:{num}"
            ))
        time.sleep(40)

def tellResult(self, request_iterator, context):
    for response in request_iterator:
        print(f"我已经知道客户端接收到我发过去的任务:{response.ret}")

    return contact_pb2.Empty()

client-side code

import logging
import random
import threading
import time

import grpc
from contact import contact_pb2
from contact import contact_pb2_grpc


# 接收服务端发送过来的任务
def get_task(stub):
    try:
        for task in stub.getTask(contact_pb2.Empty()):
            print(f"客户端已接收到服务端任务:{task.task}\n")
            # 顺便再告诉服务端我已经接收到你发的任务,你不用担心我没接收到它
            yield contact_pb2.Result(
                ret=f"客户端接收到任务:{task.task}"
            )
    except Exception as e:
        print(f'err:{e}')
        return
        
# 客户端再通知服务端我接收到你的消息了
def tell_result(stub):
   result = get_task(stub)
    stub.tellResult(result)
    
def run():
    with grpc.insecure_channel('localhost:50051') as channel:
        stub = contact_pb2_grpc.ContactStub(channel)
        while True:
            try:
                threading.Thread(target=send_status, args=(stub,), daemon=True).start()
                tell_result(stub)
            except grpc.RpcError as e:
                print(f"server connected out, please retry:{e.code()},{e.details()}")
            except Exception as e:
                print(f'unknown err:{e}')
            finally:
                time.sleep(2)


if __name__ == '__main__':
    run()

to sum up

Seen from the above, the active communication server to the client, not escape grpc request response mode. The above code is to achieve a method of communicating with each other, but not both as a client and a server that is simple.

Guess you like

Origin www.cnblogs.com/jack-233/p/12001215.html