서버리스, 람다, SQS를 이용한 프로젝트2

문한성·2023년 5월 25일
0

부트캠프

목록 보기
95/123
post-thumbnail

프로젝트 개요

AWS 클라우드 환경을 기반으로 하는  느슨하게 연결된 (loosely coupled) 어플리케이션 아키텍처에 대한 이해


프로젝트 요구사항 및 시나리오

프로젝트 명 : <자동 재고 확보 시스템> 을 위한 MSA 구성


Day 2

목표

메시지 큐의 Pub/Sub 패턴과 Producer/Consumer 패턴의 차이를 이해한다

DB와 서버와의 통신이 가능하도록 연결한다

특정 상황에서 SNS, SQS로 메세지가 전달되도록 시스템을 구성한다

SQS에 들어온 메세지를 레거시 시스템(Factory API)으로 전달하는 시스템을 구성한다

레거시 시스템(Factory API)의 콜백 대상이되는 리소스를 생성해 데이터베이스에 접근 할 수 있게 한다


Step 1 : Lambda 서버 (Sales API) - DB 연결

1. 제공 받은 레포지토리 구조 확인 (sales-api 디렉토리)

2. 데이터 베이스 연결 (mysql -> 따로 구성되어 있음) - local 테스트

( 구성되어있는 데이터 베이스에 접근하여 개인별 데이터 베이스 리소스에 table을 생성하고 값 추가)

  • mysql 설치

// MacOS mysql 설치% brew install mysql

// mysql 설치 유무 확인% mysql -V

  • mysql 접속 (로그인)

mysql -h <RDS 데이터 베이스 경로> -u <username입력> -p <password입력>

// -p 는 password 를 사용한다는 뜻

  • database 목록 확인

mysql> show databases;

  • 원하는 데이터 베이스 진입

mysql> use <데이터 베이스 명>

  • 데이터 베이스 내 테이블 확인

mysql> show tables;

3. 데이터 베이스 값 입력

  • 제공 받은 init.sql 내 쿼리문 실행

=> BIN_TO_UUID(factory_id), BIN_TO_UUID(ad_id) 값 확인하여 수정

-- RDS에 생성한 데이터베이스 리소스에 아래 table들을 생성하고, 값을 추가해넣으세요.
CREATE TABLE `product` (
    `product_id` BINARY(16)  NOT NULL ,
    -- http://mdr.tta.or.kr/item/1036/property/sku`sku` varchar(200)  NOT NULL ,
    -- Field documentation comment 3
    `name` varchar(200)  NOT NULL ,
    `price` int  NOT NULL ,
    `stock` int  NOT NULL ,
    `factory_id` BINARY(16)  NOT NULL ,
    `ad_id` BINARY(16)  NOT NULL ,
    PRIMARY KEY (
        `product_id`
    ),
    CONSTRAINT `uc_product_sku` UNIQUE (
        `sku`
    ),
    CONSTRAINT `uc_product_name` UNIQUE (
        `name`
    )
);

CREATE TABLE `factory` (
    `factory_id` BINARY(16)  NOT NULL ,
    -- http://mdr.tta.or.kr/item/1036/property/sku`identifier` varchar(200)  NOT NULL ,
    -- Field documentation comment 3
    `name` varchar(200)  NOT NULL ,
    `manager_email` varchar(200)  NOT NULL ,
    `API` varchar(200)  NOT NULL ,
    PRIMARY KEY (
        `factory_id`
    ),
    CONSTRAINT `uc_factory_name` UNIQUE (
        `name`
    )
);

CREATE TABLE `advertisement` (
    `ad_id` BINARY(16)  NOT NULL ,
    `status` boolean  NOT NULL ,
    `manager_email` varchar(200)  NOT NULL ,
    PRIMARY KEY (
        `ad_id`
    )
);

ALTER TABLE `product` ADD CONSTRAINT `fk_product_factory_id` FOREIGN KEY(`factory_id`)
REFERENCES `factory` (`factory_id`);

ALTER TABLE `product` ADD CONSTRAINT `fk_product_ad_id` FOREIGN KEY(`ad_id`)
REFERENCES `advertisement` (`ad_id`);

INSERT INTO factory(factory_id, identifier, name, manager_email, api) VALUES(UUID_TO_BIN(UUID()),'FF-500293','부산도너츠 공장', 'dob_factory@codeatates.com', '');

INSERT INTO advertisement(ad_id, status, manager_email) VALUES(UUID_TO_BIN(UUID()),true, 'dob_ad@codeatates.com');

SELECT
    BIN_TO_UUID(factory_id) as factory_id
FROM factory;
-- <출력값 1>

SELECT
    BIN_TO_UUID(ad_id) as ad_id
FROM advertisement;
-- <출력값 2>

INSERT INTO product(product_id, sku, name, price, stock, factory_id, ad_id)
VALUES(UUID_TO_BIN(UUID()),'CP-502101','부산도너츠', 19900, 3, UUID_TO_BIN("<출력값 1>"),
UUID_TO_BIN('<출력값 2>'));
  • product 테이블 내 물품의 stock 확인

mysql> select * from product;//만약 물품 구매 테스트 후 stock이 0 이 된다면 // mysql> UPDATE product SET stock = 3;

  • .env 파일 구성 (.gitignore 에 추가) ==> AWS Lambda 생성 시 환경변수로 등록해도 됨

HOSTNAME=<RDS 데이터 베이스 입력>

USERNAME=<id 입력>

PASSWORD=<password 입력>

DATABASE=<깃허브 유저아이디>

4. cURL 테스트

  • 새 터미널을 열고 npm 실행 (server 실행 - port : 8080)

% npm install% npm start

  • curl 명령어를 통하여 GET, POST 메소드 실행 및 결과 확인
  • $ curl -X GET localhost:8080/product/donut응답 : {"product_id":"","name":"부산도너츠", "price":19900,"stock":3,"BIN_TO_UUID(factory_id)":"","BIN_TO_UUID(ad_id)":"~~"}
  • $ curl -X POST localhost:8080/checkout첫번째 응답 : {"message":"구매 완료! 남은 재고: 2"}두번째 응답 : {"message":"구매 완료! 남은 재고: 1"}세번째 응답 : {"message":"구매 완료! 남은 재고: 0"}네번째 응답 : {"message":"구매 실패! 남은 재고: 0"}오류{"message":"데이터베이스 연결 오류"} 라는 오류가 뜸 - .env를 상위 디렉토리에 저장하여 생긴 오류

5. 배포하기

% sls deploy     // 배포하기 // 람다 함수 생성 확인


Step 2 : "재고없음" 메시지 전달 시스템 구성

1. SNS 토픽 생성 (stock_empty) - DB에 재고가 없을경우 재고가 없다는 알림을 위함

  • SNS 생성 (표준)

  • 람다 함수 내 대상 추가 (SNS 추가)

  • SQS 생성 및 SNS 연결

SNS 구독

2. 소스코드 수정 및 SQS 동작 확인

  • handler.js 소스 코드 수정 (구매 실패 else 문에 삽입)
const now = new Date().toString()
const message = `도너츠 재고가 없습니다. 제품을 생산해주세요! \n메시지 작성 시각: ${now}`
const params = {
  Message: message,
  Subject: '도너츠 재고 부족',
  MessageAttributes: {
    MessageAttributeProductId: {
      StringValue: product.product_id,
      DataType: "String",
    },
    MessageAttributeFactoryId: {
      StringValue: req.body.MessageAttributeFactoryId,
      DataType: "String",
    },
  },
  TopicArn: process.env.TOPIC_ARN
}

const result = await sns.publish(params).promise()
  • .env 내 TOPIC_ARN 추가
TOPIC_ARN=<SNS 엔드포인트>
  • % sls deploy (재배포)
  • cURL 을 통해 모든 재고 소진

% curl --location --request POST 'https://API_GATE_ID.execute-api.ap-northeast-2.amazonaws.com/checkout' --header 'Content-Type: application/json' --data-raw '{ "MessageGroupId": "stock-empty-group", "subject": "부산도너츠 재고 부족", "message": "재고 부족", "MessageAttributeProductId": "CP-502101", "MessageAttributeFactoryId": "FF-500293"}'

오류

{"message":"Internal Server Error"} 응답이 오고 AWS 콘솔 내 테스트에서 handler or export 문제가 있다는 오류가 출력될 경우

소스 코드 내 아래 소스 작성 되어 있는지 확인 => 테스트 하다가 지워버렸었음

module.exports.handler = serverless(app);

module.exports.app = app;

  • SQS 사용 가능한 메시지 증가


Step 3: 메시지를 Factory API로 전송하는 Lambda 구성 및 DLQ 추가

1. serverless 템플릿 생성 (starter) - stock_lambda 용

% serverless

// starter 템플릿 생성// 배포 확인 시 n 으로 진행

  • .yml 파일 수정 -> 리전 추가
provider:
  name: aws
  runtime: nodejs18.x
  region: ap-northeast-2
  • index.js 수정
function delay(time) {
  return new Promise(resolve => setTimeout(resolve, time));
}

exports.handler = async (event) => {
  await delay(50000)
  console.log(event)
  return event
}
  • % serverless deploy (재배포)

2. SQS DLQ 생성 - 배달 못한 편지 대기열 활성화 - SQS에 연결

  • 최대 수신 회수 = 메시지를 제대로 소비하지 못했을때 다시 시도할 수 있는 회수

3. 추가로 생성된 람다에 트리거 추가 (SQS) + 권한 추가 (SQS FULL Access)

4. DLQ 동작확인 - Delay 함수 추가한것 활용

  • cURL 을 통한 재고 소진 시 SQS를 통해 람다 함수가 메시지를 소비하고자 한다.

  • 하지만 Delay 를 50초 정도로 주었고 SQS 의 기본 표시 제한 시간 보다 길기때문에 메시지가 소비되지 못하고 DLQ로 넘어가게 된다.

profile
기록하고 공유하려고 노력하는 DevOps 엔지니어

0개의 댓글