Step Functions용 미들웨어: Amazon S3에서 자동으로 페이로드 저장 및 로드
지난 몇 달간 새로 지은 도서관, middy-store를 소개하고 싶습니다. 나는 이 아이디어를 한동안 숙고했고, 1년여 전에 내가 열었던 이 기능 요청으로 돌아갔습니다. middy-store는 Amazon S3 또는 기타 서비스와 같은 Store에서 페이로드를 자동으로 저장하고 로드하는 Middy용 미들웨어입니다.
동기 부여
AWS 서비스에는 알아야 할 특정 제한 사항이 있습니다. 예를 들어, AWS Lambda의 페이로드 제한은 동기 호출의 경우 6MB, 비동기 호출의 경우 256KB입니다. AWS Step Functions에서는 UTF-8로 인코딩된 문자열로 최대 256KB의 데이터 입력 또는 출력 크기를 허용합니다. 데이터를 반환할 때 이 제한을 초과하면 악명 높은 States.DataLimitExceeded 예외가 발생합니다.
이 제한에 대한 일반적인 해결 방법은 페이로드 크기를 확인하고 Amazon S3와 같은 영구 스토리지에 임시 저장하는 것입니다. 그런 다음 S3에 대한 객체 URL 또는 ARN을 반환합니다. 다음 Lambda는 입력에 URL 또는 ARN이 있는지 확인하고 S3에서 페이로드를 로드합니다. 상상할 수 있듯이 이로 인해 Amazon S3에서 페이로드를 저장하고 로드하기 위한 많은 상용구 코드가 생성되며, 이는 모든 Lambda에서 반복되어야 합니다.
페이로드의 일부만 S3에 저장하고 나머지는 그대로 두려는 경우 이는 더욱 번거롭습니다. 예를 들어 Step Functions로 작업할 때 페이로드에는 직접 액세스해야 하는 Choice 또는 Map과 같은 상태에 대한 제어 흐름 데이터가 포함될 수 있습니다. 이는 첫 번째 Lambda가 부분 페이로드를 S3에 저장하고 다음 Lambda가 S3에서 부분 페이로드를 로드하여 나머지 페이로드와 병합해야 함을 의미합니다. 이를 위해서는 유형이 여러 기능에서 일관되게 유지되어야 하며 이는 물론 오류가 발생하기 쉽습니다.
작동 원리
middy-store는 Middy를 위한 미들웨어입니다. 이는 Lambda 함수에 연결되어 있으며 Lambda 호출 중에 두 번 호출됩니다. 즉, Lambda 핸들러()가 실행되기 전 및 후입니다. 핸들러가 실행되기 전에 입력을 받고 실행이 완료된 후 핸들러로부터 출력을 받습니다.
더 쉽게 따라갈 수 있도록 성공적인 호출 후 출력부터 시작하겠습니다. middy-store는 handler() 함수에서 출력(페이로드)을 수신하고 크기를 확인합니다. 크기를 계산하기 위해 페이로드가 객체인 경우 문자열화하고 Buffer.byteLength()를 사용하여 UTF-8로 인코딩된 문자열 크기를 계산합니다. 크기가 구성 가능한 특정 임계값보다 큰 경우 페이로드는 Amazon S3와 같은 저장소에 저장됩니다. 그런 다음 저장된 페이로드(예: S3 URL 또는 ARN)에 대한 참조가 원래 출력 대신 출력으로 반환됩니다.
이제 이 출력을 입력으로 수신하는 다음 Lambda 함수(예: 상태 머신에서)를 살펴보겠습니다. 이번에는 handler()가 호출되기 전 입력을 살펴보겠습니다. middy-store는 핸들러에 대한 입력을 수신하고 저장된 페이로드에 대한 참조를 검색합니다. 페이로드를 찾으면 저장소에서 페이로드가 로드되고 핸들러에 대한 입력으로 반환됩니다. 핸들러는 페이로드가 직접 전달된 것처럼 사용합니다.
다음은 middy-store의 작동 방식을 보여주는 예입니다.
/* ./src/functions/handler1.ts */ export const handler1 = middy() .use( middyStore({ stores: [new S3Store({ /* S3 options */ })], }) ) .handler(async (input) => { // Return 1MB of random data as a base64 encoded string as output return randomBytes(1024 * 1024).toString('base64'); }); /* ./src/functions/handler2.ts */ export const handler2 = middy() .use( middyStore({ stores: [new S3Store({ /* S3 options */ })], }) ) .handler(async (input) => { // Print the size of the input return console.log(`Size: ${Buffer.from(input, "base64").byteLength / 1024 / 1024} MB`); }); /* ./src/workflow.ts */ // First Lambda returns a large output // It automatically uploads the data to S3 const output1 = await handler1({}); // Output is a reference to the S3 object: { "@middy-store": "s3://bucket/key"} console.log(output1); // Second Lambda receives the output as input // It automatically downloads the data from S3 const output2 = await handler2(output1);
매장이란 무엇입니까?
일반적으로 스토어는 Amazon S3 또는 기타 영구 스토리지 시스템과 같은 임의의 페이로드를 저장하고 로드할 수 있는 서비스입니다. DynamoDB와 같은 데이터베이스도 스토어 역할을 할 수 있습니다. Store는 Lambda 핸들러로부터 페이로드를 수신하고 이를 직렬화(객체인 경우)한 후 영구 스토리지에 저장합니다. 다음 Lambda 핸들러에 페이로드가 필요할 때 Store는 스토리지에서 페이로드를 로드하고 역직렬화한 후 반환합니다.
middy-store는 모든 Store가 구현해야 하는 StoreInterface 인터페이스를 통해 Store와 상호 작용합니다. 인터페이스는 페이로드를 저장하는 canStore() 및 store() 함수와 페이로드를 로드하는 canLoad() 및 load() 함수를 정의합니다.
interface StoreInterface<TPayload = unknown, TReference = unknown> { name: string; canLoad: (args: LoadArgs<unknown>) => boolean; load: (args: LoadArgs<TReference | unknown>) => Promise<TPayload>; canStore: (args: StoreArgs<TPayload>) => boolean; store: (args: StoreArgs<TPayload>) => Promise<TReference>; }
canStore()는 Store가 특정 페이로드를 저장할 수 있는지 확인하는 경비원 역할을 합니다. 페이로드와 해당 바이트 크기를 수신하고 페이로드가 저장소의 최대 크기 제한 내에 맞는지 확인합니다. 예를 들어 DynamoDB가 지원하는 스토어의 최대 항목 크기는 400KB인 반면, S3 스토어는 저장할 수 있는 페이로드 크기에 사실상 제한이 없습니다.
store() receives a payload and stores it in its underlying storage system. It returns a reference to the payload, which is a unique identifier to identify the stored payload within the underlying service. For example, the Amazon S3 Store uses an S3 URI in the format s3://
/ as a reference, while other Amazon services might use ARNs. canLoad() acts like a filter to check if the Store can load a certain reference. It receives the reference to a stored payload and checks if it's a valid identifier for the underlying storage system. For example, the Amazon S3 Store checks if the reference is a valid S3 URI, while a DynamoDB Store would check if it's a valid ARN.
load() receives the reference to a stored payload and loads the payload from storage. Depending on the Store, the payload will be deserialized into its original type according to the metadata that was stored alongside it. For example, a payload of type application/json will get parsed back into a JSON object, while a plain string of type text/plain will remain unaltered.
Single and Multiple Stores
Most of the time, you will only need one Store, like Amazon S3, which can effectively store any payload. However, middy-store lets you work with multiple Stores at the same time. This can be useful if you want to store different types of payloads in different Stores. For example, you might want to store large payloads in S3 and small payloads in DynamoDB.
middy-store accepts an Array
On the other hand, when middy-store runs after the handler and the output is larger than the maximum allowed size, it will iterate over the Stores and call canStore() for each Store. The first Store that returns true will be used to store the payload with store().
Therefore, it is important to note that the order of the Stores in the array is important.
References
When a payload is stored in a Store, middy-store will return a reference to the stored payload. The reference is a unique identifier to find the stored payload in the Store. The value of the identifier depends on the Store and its configuration. For example, the Amazon S3 Store will use an S3 URI by default. However, it can also be configured to return other formats like an ARN arn:aws:s3:::
The output from the handler after middy-store will contain the reference to the stored payload:
/* Output with reference */ { "@middy-store": "s3://bucket/key" }
middy-store embeds the reference from the Store in the output as an object with a key "@middy-store". This allows middy-store to quickly find all references when the next Lambda function is called and load the payloads from the Store before the handler runs. In case you are wondering, middy-store recursively iterates through the input object and searches for the "@middy-store" key. That means the input can contain multiple references, even from different Stores, and middy-store will find and load them.
Selecting a Payload
By default, middy-store will store the entire output of the handler as a payload in the Store. However, you can also select only a part of the output to be stored. This is useful for workflows like AWS Step Functions, where you might need some of the data for control flow, e.g., a Choice state.
middy-store accepts a selector in its storingOptions config. The selector is a string path to the relevant value in the output that should be stored.
Here's an example:
const output = { a: { b: ['foo', 'bar', 'baz'], }, }; export const handler = middy() .use( middyStore({ stores: [new S3Store({ /* S3 options */ })], storingOptions: { selector: '', /* select the entire output as payload */ // selector: 'a'; /* selects the payload at the path 'a' */ // selector: 'a.b'; /* selects the payload at the path 'a.b' */ // selector: 'a.b[0]'; /* selects the payload at the path 'a.b[0]' */ // selector: 'a.b[*]'; /* selects the payloads at the paths 'a.b[0]', 'a.b[1]', 'a.b[2]', etc. */ } }) ) .handler(async () => output); await handler({});
The default selector is an empty string (or undefined), which selects the entire output as a payload. In this case, middy-store will return an object with only one property, which is the reference to the stored payload.
/* selector: '' */ { "@middy-store": "s3://bucket/key" }
The selectors a, a.b, or a.b[0] select the value at the path and store only this part in the Store. The reference to the stored payload will be inserted at the path in the output, thereby replacing the original value.
/* selector: 'a' */ { a: { "@middy-store": "s3://bucket/key" } } /* selector: 'a.b' */ { a: { b: { "@middy-store": "s3://bucket/key" } } } /* selector: 'a.b[0]' */ { a: { b: [ { "@middy-store": "s3://bucket/key" }, 'bar', 'baz' ] } }
A selector ending with [*] like a.b[*] acts like an iterator. It will select the array at a.b and store each element in the array in the Store separately. Each element will be replaced with the reference to the stored payload.
/* selector: 'a.b[*]' */ { a: { b: [ { "@middy-store": "s3://bucket/key" }, { "@middy-store": "s3://bucket/key" }, { "@middy-store": "s3://bucket/key" } ] } }
Size Limit
middy-store will calculate the size of the entire output returned from the handler. The size is calculated by stringifying the output, if it's not already a string, and calculating the UTF-8 encoded size of the string in bytes. It will then compare this size to the configured minSize in the storingOptions config. If the output size is equal to or greater than the minSize, it will store the output or a part of it in the Store.
export const handler = middy() .use( middyStore({ stores: [new S3Store({ /* S3 options */ })], storingOptions: { minSize: Sizes.STEP_FUNCTIONS, /* 256KB */ // minSize: Sizes.LAMBDA_SYNC, /* 6MB */ // minSize: Sizes.LAMBDA_ASYNC, /* 256KB */ // minSize: 1024 * 1024, /* 1MB */ // minSize: Sizes.ZERO, /* 0 */ // minSize: Sizes.INFINITY, /* Infinity */ // minSize: Sizes.kb(512), /* 512KB */ // minSize: Sizes.mb(1), /* 1MB */ } }) ) .handler(async () => output); await handler({});
middy-store provides a Sizes helper with some predefined limits for Lambda and Step Functions. If minSize is not specified, it will use Sizes.STEP_FUNCTIONS with 256KB as the default minimum size. The Sizes.ZERO (equal to the number 0) means that middy-store will always store the payload in a Store, ignoring the actual output size. On the other hand, Sizes.INFINITY (equal to Math.POSITIVE_INFINITY) means that it will never store the payload in a Store.
Stores
Currently, there is only one Store implementation for Amazon S3, but I'm planning to implement a Store backed by DynamoDB and DAX. DynamoDB, with its Time-To-Live (TTL) feature, provides a great option for short-term payloads that only need to exist during the execution of a workflow like Step Functions.
Amazon S3
The middy-store-s3 package provides a store implementation for Amazon S3. It uses the official @aws-sdk/client-s3 package to interact with S3.
import { middyStore } from 'middy-store'; import { S3Store } from 'middy-store-s3'; const handler = middy() .use( middyStore({ stores: [ new S3Store({ config: { region: "us-east-1" }, bucket: "bucket", key: () => randomUUID(), format: "arn", }), ], }), ) .handler(async (input) => { return { /* ... */ }; });
The S3Store only requires a bucket where the payloads are being stored. The key is optional and defaults to randomUUID(). The format configures the style of the reference that is returned after a payload is stored. The supported formats include arn, object, or one of the URL formats from the amazon-s3-url package. It's important to note that S3Store can load any of these formats; the format config only concerns the returned reference. The config is the S3 client configuration and is optional. If not set, the S3 client will resolve the config (credentials, region, etc.) from the environment or file system.
Custom Store
A new Store can be implemented as a class or a plain object, as long as it provides the required functions from the StoreInterface interface.
Here's an example of a Store to store and load payloads as base64 encoded data URLs:
import { StoreInterface, middyStore } from 'middy-store'; const base64Store: StoreInterface<string, string> = { name: "base64", /* Reference must be a string starting with "data:text/plain;base64," */ canLoad: ({ reference }) => { return ( typeof reference === "string" && reference.startsWith("data:text/plain;base64,") ); }, /* Decode base64 string and parse into object */ load: async ({ reference }) => { const base64 = reference.replace("data:text/plain;base64,", ""); return Buffer.from(base64, "base64").toString(); }, /* Payload must be a string or an object */ canStore: ({ payload }) => { return typeof payload === "string" || typeof payload === "object"; }, /* Stringify object and encode as base64 string */ store: async ({ payload }) => { const base64 = Buffer.from(JSON.stringify(payload)).toString("base64"); return `data:text/plain;base64,${base64}`; }, }; const handler = middy() .use( middyStore({ stores: [base64Store], storingOptions: { minSize: Sizes.ZERO, /* Always store the data */ } }), ) .handler(async (input) => { /* Random text with 100 words */ return `Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.`; }); const output = await handler(null, context); /* Prints: { '@middy-store': 'data:text/plain;base64,IkxvcmVtIGlwc3VtIGRvbG9yIHNpdC...' } */ console.log(output);
This example is the perfect way to try middy-store, because it doesn't rely on external resources like an S3 bucket. You will find it in the repository at examples/custom-store and should be able to run it locally.
Contributions and Feedback
I've been tinkering with the API design for a while, and it's definitely not stable yet. I would love to get feedback on the current state as well as suggestions for changes or improvements. If you are eager to contribute to this project, please go ahead and submit feature requests or pull requests.
zirkelc
/
middy-store
Middleware for Step Functions: Automatically Store and Load Payloads
Middleware middy-store
middy-store is a middleware for Lambda that automatically stores and loads payloads from and to a Store like Amazon S3 or potentially other services.
Installation
You will need @middy/core >= v5 to use middy-store Please be aware that the API is not stable yet and might change in the future. To avoid accidental breaking changes, please pin the version of middy-store and its sub-packages in your package.json to an exact version.
npm install --save-exact @middy/core middy-store middy-store-s3
Motivation
AWS services have certain limits that one must be aware of. For example, AWS Lambda has a payload limit of 6MB for synchronous invocations and 256KB for asynchronous invocations. AWS Step Functions allows for a maximum input or output size of 256KB of data as a UTF-8 encoded string. If you exceed this limit when returning data, you will encounter the infamous States.DataLimitExceeded exception.
The usual workaround for this…
위 내용은 Step Functions용 미들웨어: Amazon S3에서 자동으로 페이로드 저장 및 로드의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

Video Face Swap
완전히 무료인 AI 얼굴 교환 도구를 사용하여 모든 비디오의 얼굴을 쉽게 바꾸세요!

인기 기사

뜨거운 도구

메모장++7.3.1
사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전
중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기
강력한 PHP 통합 개발 환경

드림위버 CS6
시각적 웹 개발 도구

SublimeText3 Mac 버전
신 수준의 코드 편집 소프트웨어(SublimeText3)

Python은 부드러운 학습 곡선과 간결한 구문으로 초보자에게 더 적합합니다. JavaScript는 가파른 학습 곡선과 유연한 구문으로 프론트 엔드 개발에 적합합니다. 1. Python Syntax는 직관적이며 데이터 과학 및 백엔드 개발에 적합합니다. 2. JavaScript는 유연하며 프론트 엔드 및 서버 측 프로그래밍에서 널리 사용됩니다.

웹 개발에서 JavaScript의 주요 용도에는 클라이언트 상호 작용, 양식 검증 및 비동기 통신이 포함됩니다. 1) DOM 운영을 통한 동적 컨텐츠 업데이트 및 사용자 상호 작용; 2) 사용자가 사용자 경험을 향상시키기 위해 데이터를 제출하기 전에 클라이언트 확인이 수행됩니다. 3) 서버와의 진실한 통신은 Ajax 기술을 통해 달성됩니다.

실제 세계에서 JavaScript의 응용 프로그램에는 프론트 엔드 및 백엔드 개발이 포함됩니다. 1) DOM 운영 및 이벤트 처리와 관련된 TODO 목록 응용 프로그램을 구축하여 프론트 엔드 애플리케이션을 표시합니다. 2) Node.js를 통해 RESTFULAPI를 구축하고 Express를 통해 백엔드 응용 프로그램을 시연하십시오.

보다 효율적인 코드를 작성하고 성능 병목 현상 및 최적화 전략을 이해하는 데 도움이되기 때문에 JavaScript 엔진이 내부적으로 작동하는 방식을 이해하는 것은 개발자에게 중요합니다. 1) 엔진의 워크 플로에는 구문 분석, 컴파일 및 실행; 2) 실행 프로세스 중에 엔진은 인라인 캐시 및 숨겨진 클래스와 같은 동적 최적화를 수행합니다. 3) 모범 사례에는 글로벌 변수를 피하고 루프 최적화, Const 및 Lets 사용 및 과도한 폐쇄 사용을 피하는 것이 포함됩니다.

Python과 JavaScript는 커뮤니티, 라이브러리 및 리소스 측면에서 고유 한 장점과 단점이 있습니다. 1) Python 커뮤니티는 친절하고 초보자에게 적합하지만 프론트 엔드 개발 리소스는 JavaScript만큼 풍부하지 않습니다. 2) Python은 데이터 과학 및 기계 학습 라이브러리에서 강력하며 JavaScript는 프론트 엔드 개발 라이브러리 및 프레임 워크에서 더 좋습니다. 3) 둘 다 풍부한 학습 리소스를 가지고 있지만 Python은 공식 문서로 시작하는 데 적합하지만 JavaScript는 MDNWebDocs에서 더 좋습니다. 선택은 프로젝트 요구와 개인적인 이익을 기반으로해야합니다.

개발 환경에서 Python과 JavaScript의 선택이 모두 중요합니다. 1) Python의 개발 환경에는 Pycharm, Jupyternotebook 및 Anaconda가 포함되어 있으며 데이터 과학 및 빠른 프로토 타이핑에 적합합니다. 2) JavaScript의 개발 환경에는 Node.js, VScode 및 Webpack이 포함되어 있으며 프론트 엔드 및 백엔드 개발에 적합합니다. 프로젝트 요구에 따라 올바른 도구를 선택하면 개발 효율성과 프로젝트 성공률이 향상 될 수 있습니다.

C와 C는 주로 통역사와 JIT 컴파일러를 구현하는 데 사용되는 JavaScript 엔진에서 중요한 역할을합니다. 1) C는 JavaScript 소스 코드를 구문 분석하고 추상 구문 트리를 생성하는 데 사용됩니다. 2) C는 바이트 코드 생성 및 실행을 담당합니다. 3) C는 JIT 컴파일러를 구현하고 런타임에 핫스팟 코드를 최적화하고 컴파일하며 JavaScript의 실행 효율을 크게 향상시킵니다.

Python은 데이터 과학 및 자동화에 더 적합한 반면 JavaScript는 프론트 엔드 및 풀 스택 개발에 더 적합합니다. 1. Python은 데이터 처리 및 모델링을 위해 Numpy 및 Pandas와 같은 라이브러리를 사용하여 데이터 과학 및 기계 학습에서 잘 수행됩니다. 2. 파이썬은 간결하고 자동화 및 스크립팅이 효율적입니다. 3. JavaScript는 프론트 엔드 개발에 없어서는 안될 것이며 동적 웹 페이지 및 단일 페이지 응용 프로그램을 구축하는 데 사용됩니다. 4. JavaScript는 Node.js를 통해 백엔드 개발에 역할을하며 전체 스택 개발을 지원합니다.
