Pub/Sub 메시지 쓰기 및 응답

리전 ID

REGION_ID는 앱을 만들 때 선택한 리전을 기준으로 Google에서 할당하는 축약된 코드입니다. 일부 리전 ID는 일반적으로 사용되는 국가 및 주/도 코드와 비슷하게 표시될 수 있지만 코드는 국가 또는 주/도와 일치하지 않습니다. 2020년 2월 이후에 생성된 앱의 경우 REGION_ID.r이 App Engine URL에 포함됩니다. 이 날짜 이전에 만든 기존 앱의 경우 URL에서 리전 ID는 선택사항입니다.

리전 ID에 대해 자세히 알아보세요.

Pub/Sub는 애플리케이션 사이에서 안정적인 다대다 비동기 메시징 기능을 제공합니다. 게시자 애플리케이션은 메시지를 주제로 보낼 수 있으며 다른 애플리케이션은 주제를 구독하여 메시지를 수신할 수 있습니다.

이 문서에서는 Cloud 클라이언트 라이브러리를 사용하여 Python 3 앱에서 Pub/Sub 메시지를 전송 및 수신하는 방법을 설명합니다.

기본 요건

  • App Engine에서 Python 3용 'Hello, World!'의 안내를 따라 환경과 프로젝트를 설정하고 App Engine Python 3 앱의 구조에 대해 알아봅니다.
  • 이 문서에 설명된 샘플 애플리케이션을 실행할 때 필요하므로 프로젝트 ID를 기록해 둡니다.

    샘플 앱 클론

    샘플 앱을 로컬 머신에 복사한 후 pubsub 디렉터리로 이동합니다.

    git clone https://github.com/GoogleCloudPlatform/python-docs-samples
    cd python-docs-samples/appengine/standard_python3/pubsub
    

    주제 및 구독 만들기

    주제 및 구독을 만듭니다. 이때 Pub/Sub 서버가 요청을 전송할 엔드포인트를 지정해야 합니다.

    gcloud pubsub topics create YOUR_TOPIC_NAME
    gcloud pubsub subscriptions create YOUR_SUBSCRIPTION_NAME \
        --topic YOUR_TOPIC_NAME \
        --push-endpoint \
        https://YOUR_PROJECT_ID.REGION_ID.r.appspot.com/pubsub/push?token=YOUR_TOKEN \
        --ack-deadline 10
    

    YOUR_TOKEN을 무작위의 비밀 토큰으로 바꿉니다. 내보내기 엔드포인트에서 이 토큰을 사용해 요청을 확인합니다.

    환경 변수 설정

    app.yaml 파일을 수정하여 프로젝트 ID, 주제, 확인 토큰의 환경 변수를 설정합니다.

    env_variables:
      PUBSUB_TOPIC: '<YOUR_TOPIC>'
      # This token is used to verify that requests originate from your
      # application. It can be any sufficiently random string.
      PUBSUB_VERIFICATION_TOKEN: '<YOUR_VERIFICATION_TOKEN>'

    코드 검토

    샘플 앱은 Cloud 클라이언트 라이브러리를 사용합니다.

    샘플 앱은 app.yaml 파일에 설정된 값을 사용하여 환경 변수를 구성합니다. 푸시 요청 핸들러는 이러한 값을 사용하여 요청이 Pub/Sub에서 수신되었으며 신뢰할 수 있는 소스가 보낸 것인지 확인합니다.

    app.config['PUBSUB_VERIFICATION_TOKEN'] = \
        os.environ['PUBSUB_VERIFICATION_TOKEN']
    app.config['PUBSUB_TOPIC'] = os.environ['PUBSUB_TOPIC']
    

    샘플 앱은 전역 목록을 유지 관리하여 이 인스턴스에서 수신된 메시지를 저장합니다.

    MESSAGES = []
    
    receive_messages_handler() 메서드는 푸시된 메시지를 수신하여 MESSAGES 전역 목록에 추가합니다.

    @app.route('/push-handlers/receive_messages', methods=['POST'])
    def receive_messages_handler():
        # Verify that the request originates from the application.
        if (request.args.get('token', '') !=
                current_app.config['PUBSUB_VERIFICATION_TOKEN']):
            return 'Invalid request', 400
    
        # Verify that the push request originates from Cloud Pub/Sub.
        try:
            # Get the Cloud Pub/Sub-generated JWT in the "Authorization" header.
            bearer_token = request.headers.get('Authorization')
            token = bearer_token.split(' ')[1]
            TOKENS.append(token)
    
            # Verify and decode the JWT. `verify_oauth2_token` verifies
            # the JWT signature, the `aud` claim, and the `exp` claim.
            # Note: For high volume push requests, it would save some network
            # overhead if you verify the tokens offline by downloading Google's
            # Public Cert and decode them using the `google.auth.jwt` module;
            # caching already seen tokens works best when a large volume of
            # messages have prompted a single push server to handle them, in which
            # case they would all share the same token for a limited time window.
            claim = id_token.verify_oauth2_token(token, requests.Request(),
                                                 audience='example.com')
    
            # IMPORTANT: you should validate claim details not covered by signature
            # and audience verification above, including:
            #   - Ensure that `claim["email"]` is equal to the expected service
            #     account set up in the push subscription settings.
            #   - Ensure that `claim["email_verified"]` is set to true.
    
            CLAIMS.append(claim)
        except Exception as e:
            return 'Invalid token: {}\n'.format(e), 400
    
        envelope = json.loads(request.data.decode('utf-8'))
        payload = base64.b64decode(envelope['message']['data'])
        MESSAGES.append(payload)
        # Returning any 2xx status indicates successful receipt of the message.
        return 'OK', 200

    index() 메서드는 App Engine 웹 앱과 상호작용하여 새 메시지를 게시하고 수신된 메시지를 표시합니다.

    @app.route('/', methods=['GET', 'POST'])
    def index():
        if request.method == 'GET':
            return render_template('index.html', messages=MESSAGES, tokens=TOKENS,
                                   claims=CLAIMS)
    
        data = request.form.get('payload', 'Example payload').encode('utf-8')
    
        # Consider initializing the publisher client outside this function
        # for better latency performance.
        publisher = pubsub_v1.PublisherClient()
        topic_path = publisher.topic_path(app.config['GOOGLE_CLOUD_PROJECT'],
                                          app.config['PUBSUB_TOPIC'])
        future = publisher.publish(topic_path, data)
        future.result()
        return 'OK', 200

    로컬에서 샘플 실행

    로컬에서 실행 시 Google Cloud CLI를 사용하여 Google Cloud API 사용을 위한 인증을 제공할 수 있습니다. 기본 요건의 설명대로 환경을 설정했다면 이 인증을 제공하는 gcloud init 명령어를 이미 실행한 것입니다.

    종속 항목을 가상 환경에 설치하는 것이 좋습니다.

    Mac OS/Linux

    1. 격리된 Python 환경을 만듭니다.
      python3 -m venv env
      source env/bin/activate
    2. 현재 위치가 샘플 코드가 있는 디렉터리가 아니면 hello_world 샘플 코드가 포함된 디렉터리로 이동합니다. 그런 후 종속 항목을 설치합니다.
      cd YOUR_SAMPLE_CODE_DIR
      pip install -r requirements.txt

    Windows

    PowerShell을 사용하여 Python 패키지를 실행합니다.

    1. PowerShell 설치 위치를 찾습니다.
    2. PowerShell 바로가기를 마우스 오른쪽 버튼으로 클릭하고 관리자 권한으로 시작합니다.
    3. 격리된 Python 환경을 만듭니다.
      python -m venv env
      .\env\Scripts\activate
    4. 프로젝트 디렉터리로 이동하여 종속 항목을 설치합니다. 현재 위치가 샘플 코드가 있는 디렉터리가 아니면 hello_world 샘플 코드가 포함된 디렉터리로 이동합니다. 그런 후 종속 항목을 설치합니다.
      cd YOUR_SAMPLE_CODE_DIR
      pip install -r requirements.txt

    그런 다음 애플리케이션을 시작하기 전에 환경 변수를 설정합니다.

    export GOOGLE_CLOUD_PROJECT=[your-project-id]
    export PUBSUB_VERIFICATION_TOKEN=[your-verification-token]
    export PUBSUB_TOPIC=[your-topic]
    python main.py
    

    푸시 알림 시뮬레이션

    애플리케이션은 로컬에서 메시지를 보낼 수 있지만 로컬에서 푸시 메시지를 받지는 못합니다. 하지만 로컬 푸시 알림 엔드포인트에 HTTP 요청을 전송하면 푸시 메시지를 시뮬레이션할 수 있습니다. 샘플에는 sample_message.json 파일이 포함되어 있습니다.

    curl 또는 httpie 클라이언트를 사용하여 HTTP POST 요청을 보낼 수 있습니다.

    curl -i --data @sample_message.json "localhost:8080/push-handlers/receive_messages?token=[your-token]"
    

    또는

    http POST ":8080/push-handlers/receive_messages?token=[your-token]" < sample_message.json
    

    응답:

    HTTP/1.0 200 OK
    Content-Length: 2
    Content-Type: text/html; charset=utf-8
    Date: Mon, 10 Aug 2015 17:52:03 GMT
    Server: Werkzeug/0.10.4 Python/2.7.10
    
    OK
    

    요청이 완료되면 localhost:8080을 새로고침하고 수신 메시지 목록에서 메시지를 확인할 수 있습니다.

    App Engine에서 실행

    gcloud 명령줄 도구로 App Engine에 데모 앱을 배포하려면 app.yaml 파일이 있는 디렉터리에서 다음 명령어를 실행합니다.

    gcloud app deploy
    

    이제 https://PROJECT_ID.REGION_ID.r.appspot.com에서 애플리케이션에 액세스할 수 있습니다. 양식을 사용하여 메시지를 제출할 수 있지만 애플리케이션에서 어떤 인스턴스가 알림을 수신하는지 보장하지는 못합니다. 여러 메시지를 전송하고 페이지를 새로 고쳐 수신된 메시지를 확인할 수 있습니다.