Localstack SNS 主题未将消息正文转发到订阅的 HTTP 端点

问题描述 投票:0回答:1

问题

在 LocalStack 容器中运行的 SNS 主题将向在单独容器中运行的订阅 HTTP 端点发送通知,但 API 收到

{}
而不是发送到 SNS 的消息。

重现步骤

为了简洁起见,我排除了

my-api
Dockerfile 和应用程序代码。您可以填写任何 Web 服务器(FastAPI、Express 等),只要它侦听端口 3000,并且具有
POST /events
端点即可。对我来说,该 API 现在没有什么特别的事情发生,它只是记录收到的事件。

  1. 创建以下
    docker-compose.yml
    文件
version: '3.8'

services:
  my-api:
    build:
      context: .
    ports:
      - 3000:3000
    volumes:
      - ./src:/opt/my-api/src

  localstack:
    image: localstack/localstack
    container_name: localstack
    ports:
      - 4566:4566
    environment:
      - SERVICES=sns
      - AWS_DEFAULT_REGION=us-east-1
    volumes:
      - localstack-data:/var/lib/localstack
      # https://docs.localstack.cloud/references/init-hooks/
      - ./localstack-init-scripts/:/etc/localstack/init/ready.d/

volumes:
  localstack-data:

  1. 创建
    localstack-init-scripts/init.py
    用于初始化 LocalStack 容器的 Python 脚本。
import boto3
import requests

# SNS Client
sns = boto3.client('sns', endpoint_url='http://localhost:4566', region_name='us-east-1')

# Create Topic
topic_arn = sns.create_topic(Name='local-events')['TopicArn']
print('Topic Arn:', topic_arn)

#Subscribe HTTP endpoint to Topic
subscription_arn = sns.subscribe(
    TopicArn=topic_arn,
    Protocol='http',
    Endpoint='http://host.docker.internal:3000/events',
    # I've also tried Endpoint='http://my-api:3000/events',
    ReturnSubscriptionArn=True
)['SubscriptionArn']
print('Subscription Arn:', subscription_arn)

# Get the Subscription Token and Confirm the Subscription
token_res = requests.get(f'http://localhost:4566/_aws/sns/subscription-tokens/{subscription_arn}').json()
print('Token Res:', token_res)
confirmation_status_code = sns.confirm_subscription(
    TopicArn=topic_arn,
    Token=token_res['subscription_token']
).get('ResponseMetadata', {}).get('HTTPStatusCode', 500)

confirmed = True if confirmation_status_code == 200 else False 
print('Subscription Confirmed:', confirmed)

  1. 启动 Docker 服务
docker compose up
  1. local-events
    主题发送测试通知
aws --endpoint-url http://localhost:4566 sns publish \
  --topic-arn arn:aws:sns:us-east-1:000000000000:local-events \
  --message '{ "msg": "This is a Test Message" }'

结果

运行上述命令后,您应该看到类似以下内容的 docker 日志

localstack                    | 2024-10-01T01:38:29.993  INFO --- [et.reactor-0] localstack.request.aws     : AWS sns.CreateTopic => 200
localstack                    | Topic Arn: arn:aws:sns:us-east-1:000000000000:local-events
localstack                    | 2024-10-01T01:38:29.997  INFO --- [et.reactor-0] localstack.request.aws     : AWS sns.Subscribe => 200
localstack                    | Subscription Arn: arn:aws:sns:us-east-1:000000000000:local-events:e98cb2ad-565c-4160-a77c-6a4bdd21350f
localstack                    | 2024-10-01T01:38:30.001  INFO --- [et.reactor-0] localstack.request.http    : GET /_aws/sns/subscription-tokens/arn:aws:sns:us-east-1:000000000000:local-events:e98cb2ad-565c-4160-a77c-6a4bdd21350f => 200
localstack                    | Token Res: {'subscription_token': '75732d656173742d312f8c8e1e8b8c8e1e8b8c8e1e8b8c8e1e8b8c8e1e8b8c8e', 'subscription_arn': 'arn:aws:sns:us-east-1:000000000000:local-events:e98cb2ad-565c-4160-a77c-6a4bdd21350f'}
localstack                    | 2024-10-01T01:38:30.004  INFO --- [et.reactor-0] localstack.request.aws     : AWS sns.ConfirmSubscription => 200
localstack                    | Subscription Confirmed: True
localstack                    | Ready.
my-api-1                      | Received event {}

故障排除步骤~~到目前为止~~

  1. 执行到 Localstack 容器并运行
    curl -XPOST http://host.docker.internal:3000/events -d '{ "msg": "curl test message" }'
    我得到了预期的响应,并在 docker 日志中看到以下内容
my-api-1                      | Received event { '{ "msg": "curl test message" }': '' }
  1. 通过运行
    awslocal sns get-subscription-attributes --subscription-arn "<subscription-arn-from-logs>"
    确认主题已订阅,并通过
     "ConfirmationWasAuthenticated": "true"
    "PendingConfirmation": "false"
  2. 获得响应

参考文献

docker-compose amazon-sns localstack
1个回答
0
投票

我已经遇到这个问题了。

问题似乎与 LocalStack 在发送到 HTTP 端点时如何处理 SNS 消息格式有关。这里有一些可以尝试的事情:

  1. 消息格式: SNS 通常将消息包装在 JSON 结构中。尝试修改您的 API 以期望这种格式:

    @app.post("/events")
    async def receive_event(event: dict):
        message = event.get('Message', '{}')
        print(f"Received message: {message}")
        # Parse message if it's JSON
        try:
            message_json = json.loads(message)
            print(f"Parsed message: {message_json}")
        except json.JSONDecodeError:
            print("Message is not JSON")
    
  2. 原始消息传递: 为订阅启用原始消息传递。将其添加到您的

    init.py

    sns.set_subscription_attributes(
        SubscriptionArn=subscription_arn,
        AttributeName='RawMessageDelivery',
        AttributeValue='true'
    )
    
  3. 网络配置: 确保您的容器可以通信。尝试使用服务名称而不是

    host.docker.internal
    :

    Endpoint='http://my-api:3000/events'
    

    您可能需要向您的

    docker-compose.yml
    添加网络:

    services:
      my-api:
        # ... other config ...
        networks:
          - mynetwork
      localstack:
        # ... other config ...
        networks:
          - mynetwork
    
    networks:
      mynetwork:
    
  4. 调试: 在您的 API 中添加更多日志记录以查看完整请求:

    @app.post("/events")
    async def receive_event(request: Request):
        body = await request.body()
        headers = request.headers
        print(f"Received headers: {headers}")
        print(f"Received body: {body}")
    
  5. 本地堆栈版本: 确保您使用的是最新版本的 LocalStack。旧版本可能存在错误。

  6. 消息结构: 发布时,尝试包含

    MessageStructure
    参数:

    aws --endpoint-url http://localhost:4566 sns publish \
      --topic-arn arn:aws:sns:us-east-1:000000000000:local-events \
      --message '{"default": "{ \"msg\": \"This is a Test Message\" }"}' \
      --message-structure json
    

如果这些都不能解决问题,则可能是 LocalStack 的 SNS 实现中的错误。在这种情况下,您可以考虑在 LocalStack GitHub repository 上提出问题。

© www.soinside.com 2019 - 2024. All rights reserved.