認証のないパブリックエンドポイント用にAWS APIゲートウェイをセットアップしています。 LambdaをトリガーするWebSocketに接続します。
https://pypi.org/project/websocket_client/ でPythonのwebsocket-client
libとの接続を作成していました。
接続が約10%の時間失敗し、負荷が増加すると悪化することに気づきました。私の一般的なAPIゲートウェイ設定でYour current account level throttling rate is 10000 requests per second with a burst of 5000 requests.
と表示されているので、スロットルが表示されるはずの場所が見つかりません。これは、1秒あたりわずか2〜3件のリクエストで問題がかなり頻繁にトリガーされるという点とは異なります。
一方、失敗の応答は{u'message': u'Forbidden', u'connectionId': u'Z2Jp-dR5vHcCJkg=', u'requestId': u'Z2JqAEJRvHcFzvg='}
のようになります
CloudWatchログの洞察を調べ、接続IDとリクエストIDを検索しました。 APIゲートウェイのロググループでは、どちらのIDでも結果が見つかりません。それでも、websocket接続で起動する私のLambdaを検索すると、その接続IDのログが表示されます。ログには、すべてが正常に実行されていることが示されています。ラムダは、起動するMySQLクエリを実行するだけです。
ラムダが期待どおりに動作しているにもかかわらず、なぜ禁止の応答が返されるのですか?
(getting message:forbidden reply from AWS API gateway )にある既存の質問は、一部のプライベートエンドポイントに対して常に禁止されている場合に対処するようです。私のユースケースに沿ったものは何もありません。
[〜#〜]更新[〜#〜]
これはlocust.io
、または毎秒接続するために使用しているpythonに関連していると思います。私は https://www.npmjs.com/package/wscat をマシンにインストールし、接続とクローズをできるだけ速く繰り返しています。 Forbidden
メッセージが表示されません。接続方法がForbidden
メッセージをランダムに吐き出す方法がわからないので、混乱するだけです。
class SocketClient(object):
def __init__(self, Host):
self.Host = Host
self.session_id = uuid4().hex
def connect(self):
self.ws = websocket.WebSocket()
self.ws.settimeout(10)
self.ws.connect(self.Host)
events.quitting += self.on_close
data = self.attach_session({})
return data
def attach_session(self, payload):
message_id = uuid4().hex
start_time = time.time()
e = None
try:
print("Sending payload {}".format(payload))
data = self.send_with_response(payload)
assert data['mykey']
except AssertionError as exp:
e = exp
except Exception as exp:
e = exp
self.ws.close()
self.connect()
elapsed = int((time.time() - start_time) * 1000)
if e:
events.request_failure.fire(request_type='sockjs', name='send',
response_time=elapsed, exception=e)
else:
events.request_success.fire(request_type='sockjs', name='send',
response_time=elapsed,
response_length=0)
return data
def send_with_response(self, payload):
json_data = json.dumps(payload)
g = gevent.spawn(self.ws.send, json_data)
g.get(block=True, timeout=2)
g = gevent.spawn(self.ws.recv)
result = g.get(block=True, timeout=10)
json_data = json.loads(result)
return json_data
def on_close(self):
self.ws.close()
class ActionsTaskSet(TaskSet):
@task
def streams(self):
response = self.client.connect()
logger.info("Connect Response: {}".format(response))
class WSUser(Locust):
task_set = ActionsTaskSet
min_wait = 1000
max_wait = 3000
def __init__(self, *args, **kwargs):
super(WSUser, self).__init__(*args, **kwargs)
self.client = SocketClient('wss://mydomain.amazonaws.com/endpoint')
更新2
以前は存在しなかった1種類のログであるアクセスログを有効にしました。私のラムダが問題なく常に200を取得していることがわかります。 403は、実際のMESSAGE
にヒットしないeventType
routeKey
からのものです。それがどこから来るのかはわかりませんが、その答えを見つけることでこれを解決できます。
ENIの問題がないことも確認できました。
私の例のペイロードは空です。 APIは、routeKeyを認識するために$request.body.action
を使用するように構成されています。接続すると、デフォルトの$connect
ルートが機能します。
私の体に適切なaction
を追加すると、403はなくなりました。これが解決策です。基本的に、接続と切断のアクションから200の応答を受け取りましたが、ペイロードのないメッセージが通過するたびに403を受け取りました。
VPC関連の制限に達している可能性があります。 https://winterwindsoftware.com/scaling-lambdas-inside-vpc/ を参照してください。 ENIが不足しているようです。関数を別のVPCに移動してみてください。ラムダの各呼び出しにはどのくらいの時間がかかりますか?そして、ラムダ語で書かれた言語は何ですか?