Lambda에서 수주 연결한 이야기(와 Apex의 이야기라든지)

7623 단어 Apex람다Node.js

요구 사항 정보



정기적으로 API를 통해 데이터를 검색하고 좋은 느낌으로 성형하고 RDS에 저장하고 싶습니다.

구성도




왜 이렇게 되었다.

개발 환경



Apex v0.13.0
Node.js v4.3.2

환경 구축



Node.js



nvm 사용하고 있습니다만, anyenv라든지 물론 OK입니다.

nvm 도입


$ git clone git://github.com/creationix/nvm.git ~/.nvm

.bash_profile에 다음을 씁니다.
# nvm
if [[ -s ~/.nvm/nvm.sh ]] ; then source ~/.nvm/nvm.sh ; fi

source로 로드
$ source ~/.bash_profile

node.js 설치



Lambda에서는 v0.10.36 또는 v4.3.2 밖에 사용할 수 없기 때문에.
참고) htps : // / cs. 아 ws. 아마존. 이 m / 그럼 _ jp / ぁ mb다 / ㅁ st / dg / 쿤 t - 쏘 r d ゔ ぇ r 시온 s. HTML
$ nvm install v4.3.2
$ node -v
v4.3.2

Apex 소개



Apex 설치


$ curl https://raw.githubusercontent.com/apex/apex/master/install.sh | sh

자격 증명을 위한 파일 준비


$ cat ~/.aws/config
[default]
output = json
region = ap-northeast-1
$ cat ~/.aws/credentials
[default]
aws_access_key_id = [AWS_ACCESS_KEY]
aws_secret_access_key = [AWS_SECRET_KEY]

파일 구성


├── common
│   ├── index.js
│   ├── lib
│   └── package.json
├── functions
│   ├── function1
│   │   ├── driver.js
│   │   ├── index.js
│   │   └── package.json
│   ├── function2
│   │   ├── driver.js
│   │   ├── index.js
│   │   └── package.json
│   ├── function2
(略)
├── project.development.json
├── project.json
├── project.local.json
└── project.staging.json

구성안에 대해서는
Apex에서 환경 변수를 전환하는 이야기
에 쓴 env마다 분리한다 & 공통계를
서버리스 아키텍처에서 Lambda Function의 분할 단위에 대해 생각하는 # 공통 처리
같이 정리하는 느낌으로하고 있습니다.

나중에 로컬 실행을 위해
AWS Lambda 함수를 개발할 때 Node.js 버전에주의를 # 로컬에서 Lambda 함수를 실행하고 싶습니다.
를 참고로 driver.js 만들고 있습니다만, 이 근처는 gulp

마침내 주제 : 구성에 대한 이야기



처음에는



정기 실행으로
  • API 두드려 데이터 가져오기
  • 가져온 데이터를 병합하여 RDS에 돌입

  • 라고 하는 Lambda를 1개 만들면 좋겠다, 라고.

    이런 느낌으로 생각했는데요.
    예상보다 필요한 요청수가 많아, 보통으로 던지면 실행시간이 5분(Lambda의 상한)을 여유로 넘어 버리는 슬픈 사태에.

    어쩔 수 없기 때문에


  • S3에 json 파일을 넣고 그것을 트리거하고 lambda를 시작합니다.
  • 일정한 수의 요청을 처리하면 다음 페이지의 분을 S3에 put
  • 그것을 트리거에 동일한 lambda가 별도로 시작
  • 이하 2.와 3.의 반복

  • 같은 방향으로 전환했습니다.
    여러가지 끝 접혀 있습니다만, 이런 느낌으로 실장.
    exports.handler = (event, context, callback) => {
      co(function *() {
        let target = get_payload(event.Records[0].s3.object.key);
    
        ~~~(中略)~~~
    
        if (target.page < total_page) {
          target.page = target.page + 1;
          result = yield put_payload(prefix, target);
        }
      });
    });
    
    function get_payload = (key) => {
      let params = {
        'Bucket': config.s3.bucket_name,
        'Key': key
      };
      return new Promise((resolve) => {
        s3.getObject(params, (err, data) => {
          if (err) {
            console.error(err);
            reject(err);
          } else {
            resolve(JSON.parse(data.Body.toString());
          }
        });
      });
    };
    
    function put_payload = (prefix, target) => {
      let body = JSON.stringify(target);
      let filename = 'payload.' + target.key + '.' + target.page + '.json';
      let params = {
        'Bucket': config.s3.bucket_name,
        'Key': prefix + filename,
        'Body': body.toString('base64'),
      };
      return new Promise((resolve, reject) => {
        s3.putObject(params, (err) => {
          if(err) {
            console.log(err);
            reject(false);
          } else {
            resolve(true);
          }
        });
      });
    };
    

    그리고는 트리거를 설정.
    ※ management console의 스크린 샷 붙이고 있습니다만, terraform로 설정하는 편이 좋을까.







    뭐하는거야?





    이런 구성이 완성되었다는 것입니다.
    아마 S3가 아니라 DynamoDB 사용하는 것이 더 좋을 것입니다.
    혹은 Step Functions 사용한다든가, 같은 태스크의 반복해서 할 수 있었던 것입니다. . . ?

    브레이커(후술의 부분)의 장치에 대해서



    이것, 뭔가 잘못되면 연장 루프가 계속되게 되는 무서운 구조로 해.
    그 해결책으로
  • 처리 시작시에 차단기 파일이 S3에 있는지 확인
  • 대상 파일이 있으면 그 시점에서 처리 종료

  • 하는 것으로, 폭주해도 멈출 수 있도록 하고 있습니다.
    체크하는 곳의 구현은 이런 느낌으로.
    exports.handler = (event, context, callback) => {
      co(function *() {
        let breaker_key = 'collector-2';
        let breaker = yield get_breaker(breaker_key);
        if (breaker && breaker.status) {
          console.log('Close breaker, stopped.');
          callback(null, true);
        }
        console.log('check breaker finished, OK.');
    
        (以下処理が続く)
    

    실패로 판단하고 다시 달리지 않도록 성공시 callback 반환합니다.
    S3에서 가져오는 곳은
    function get_breaker = (target) => {
      let prefix = config.breaker_prefix;
      let filename = 'payload.' + target + '.json';
      let params = {
        'Bucket': config.s3.bucket_name,
        'Key': prefix + filename,
      };
      return new Promise((resolve) => {
        s3.getObject(params, (err, data) => {
          if (err) {
            resolve({'target': target, 'status': false});
          } else {
            let objectBody = data.Body.toString();
            resolve(JSON.parse(objectBody));
          }
        });
      });
    };
    

    파일이 없으면 “브레이커는 발동하고 있지 않다”라고 간주하도록 하고 있습니다.
    브레이커용의 파일 두는 처리도 Lambda로 만들고 있으므로,
    $ echo -n '{"target": "collector-1", "status": "break"}' | apex invoke -e development circuit-breaker
    

    라든지 수중에서 실행하면, 브레이커 발동.
    해제할 때도 비슷한 느낌.
    덧붙여 이 변도 gulp 나름으로 이하 약.

    그건 그렇고



    응용으로 더욱 키와인 것도 만들고 있거나.

    수주련 패턴과 마음대로 명명하고 있습니다.

    요약



    PHP 쓰고 당초 듣고 있었습니다만, 왠지 Node.js를 쓰고 있고, 지금은 Python으로 재작성하라고 합니다.

    좋은 웹페이지 즐겨찾기