본문 바로가기
카테고리 없음

마이크로서비스 개발 3

by ^..^v 2022. 3. 17.
728x90
반응형

3.2 예약 마이크로서비스 코드 구현 (P260)

파이썬 플라스크와 레디스 데이터 저장소를 사용해 구현

파이썬/플라스크 스택을 위한 ms-python-flask-template 템플릿을 사용

 

깃 템플릿 복제

https://github.com/inadarei/ms-python-flask-template

 

 

 

 

로컬 개발 환경으로 복제

c:\msur> git clone https://github.com/myanjini/ms-reservations.git

 

 

예약 마이크로서비스 OAS(OpenAPI Spec) 확인

OAS 업데이트

C:\msur\ms-reservations\docs\api.yml 파일의 내용을 앞에서 설계한 내용(https://github.com/implementing-microservices/ms-reservations/blob/master/docs/api.yml)으로 업데이트

 

redoc을 이용해서 OAS 문서를 읽어서 디플로이

c:\msur\ms-reservations\docs> make start PWD=c:/msur/ms-reservations/docs
docker run -d --rm --name ms-python-docs -p 3939:80 -v c:/msur/ms-reservations/docs/api.yml:/usr/share/nginx/html/swagger.yaml -e SPEC_URL=swagger.yaml redocly/redoc:v2.0.0-rc.8-1
b0f0428bfcc125f236c9e4fa0956b353959fcd3ea26e7300886d89870afa371c
"server started at: http://0.0.0.0:3939"

 

HTML 템플릿으로 렌더링된 결과 확인

http://127.127.127.127:3939/



마이크로서비스 구현 1. 예약

users 엔드포인트에 대한 매핑을 reservations 엔드포인트에 대한 매핑으로 변경

C:\msur\ms-reservations\service.py

         … (생략) …

# # For more sophisticated forms in Flask, see:
# # https://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-iii-web-forms
# @app.route('/users', defaults={'user_id': ""}, methods=['POST'])
# @app.route('/users/<user_id>', methods=['POST'])
# def update_user(user_id):
#     """Endpoint that creates or saves user in Redis database"""
#     # Note 'force=True' ignores mime-type=app/json requirement default in Flask
#     user = request.get_json(force=True)

#     resp = handlers.save_user(user, user_id)
#     return jsonify(resp)


@app.route('/reservations', methods=['PUT'])
def reserve():
    """Endpoint that reserves a seat for a customer"""
    json_body = request.get_json(force=True)
    resp = handlers.reserve(json_body)
    if (resp.get("status") == "success"):
        return jsonify(resp)
    else:
        return Response(
            json.dumps(resp),
            status=403,
            mimetype='application/json'
        )

         … (생략) …

 

매핑 처리기 수정

C:\msur\ms-reservations\src\handlers.py

         … (생략) …

# def save_user(user, user_id=""):
#     """Save User route callback"""
#     if user_id == "":
#         user_id = str(uuid.uuid4())
#     log.info("Saving USER_ID: %s", user_id)

#     user = json.dumps(user)

#     return model.save_user(user, user_id)


def reserve(json_body):
    """Save reservation callback"""
    return model.save_reservation(json_body)

         … (생략) …

 

모델 수정 - 데이터베이스에 실제 저장을 구현

레디스 CLI로 실행한 hsetnx 명령어를 파이썬으로 구현

C:\msur\ms-reservations\src\model.py

         … (생략) …

# def save_user(user, user_id):
#     """Saves user into redis database"""
#     try:
#         this.redis_conn.set(user_id, user)
#     except redis.RedisError:
#         response = {
#             "completion" : {"status": "error"}
#         }
#         log.info("Error while saving in Redis", exc_info=True)
#     else:
#         response = {
#             "completion": {
#                 "status": "success",
#                 "user_id" : user_id
#             }
#         }

#     return response


def save_reservation(reservation):
    """Saves reservation into Redis database"""
    seat_num = reservation['seat_num']
    try:
        result = this.redis_conn.hsetnx(
            this.tblprefix + reservation['flight_id'],
            seat_num,
            reservation['customer_id'])
    except redis.RedisError:
        response = {
            "error": f"Unexpected error reserving {seat_num}"
        }
        log.error(f"Unexpected error reserving {seat_num}", exc_info=True)
    else:
        if result == 1:
            response = {
                "status": "success",
            }
        else:
            response = {
                "error": f"Could not complete reservation for {seat_num}",
                "description": "Seat already reserved. Cannot double-book"
            }
    return response

         … (생략) …

 

테이블 수준 접두사를 선언

C:\msur\ms-reservations\src\model.py

         … (생략) …

# this is a pointer to the module object instance itself.
# pylint: disable=invalid-name
this = sys.modules[__name__]
this.tblprefix = "flights:"
this.redis_conn = redis.Redis(host=REDIS_HOST, port=REDIS_PORT,
                              db=REDIS_DB, password=REDIS_PWD,
                              decode_responses=True)
         … (생략) …

 

 

마이스크서비스 테스트 1. 예약

docker-compose.yaml 파일 수정

C:\msur\ms-reservations\docker-compose.yml

version: '3.4'

services:
  ms-template-microservice:
    container_name: ms-template-microservice
    build:
      context: .
    restart: always
    volumes:
      - ./:/app
    ports:
      - 7701:5000
    env_file:
      - ./database-dev.env
    environment:
      FLASK_ENV: development
    links:
      - ms-redis
    command: ./wait-for.sh -t 60 ms-redis:6379 -- gunicorn -b 0.0.0.0:5000 --reload -w 1 service:app

  ms-redis:
    container_name: ms-redis
    image: redis:6-alpine
    restart: always
    command: redis-server /usr/local/etc/redis/redis.conf --requirepass 4n_ins3cure_P4ss
    ### you only need to host-map this port if you have an app (DB GUI Editor?)
    ### on host that needs access to the Redis DB. Otherwise, keep it commented.
    #ports:
    #  - "6379:6379"
    expose:
      - 6379
    volumes:
      - local_redis_data:/data
#     - $PWD/redis.conf:/usr/local/etc/redis/redis.conf
      - ./redis.conf:/usr/local/etc/redis/redis.conf

    environment:
      - REDIS_REPLICATION_MODE=master

volumes:
  local_redis_data:

 

쉘 스크립트 개행문자 변경

C:\msur\ms-reservations\wait-for.sh 파일의 개행문자를 LF로 변경

 

마이크로서비스 실행

c:\msur\ms-reservations> make start
docker-compose -p ms-workspace-demo up -d
Creating network "ms-workspace-demo_default" with the default driver
Creating ms-redis ... done
Creating ms-template-microservice ... done

 

예약 테스트

C:\msur\ms-reservations> curl --header "Content-Type:application/json" --request PUT --data "{\"seat_num\":\"12B\",\"flight_id\":\"werty\",\"customer_id\":\"dfdg\"}" http://127.127.127.127:7701/reservations
{
  "status": "success"
}

 

중복 예약 시 오류 반환 확인

C:\msur\ms-reservations> curl --header "Content-Type:application/json" --request PUT --data "{\"seat_num\":\"12B\",\"flight_id\":\"werty\",\"customer_id\":\"dfdg\"}" http://127.127.127.127:7701/reservations
{"error": "Could not complete reservation for 12B", "description": "Seat already reserved. Cannot double-book"}

C:\msur\ms-reservations> curl --header "Content-Type:application/json" --request PUT --data "{\"seat_num\":\"12B\",\"flight_id\":\"werty\",\"customer_id\":\"jkfl\"}" http://127.127.127.127:7701/reservations
{"error": "Could not complete reservation for 12B", "description": "Seat already reserved. Cannot double-book"}

 

 

마이크로서비스 구현 2. 예약 검색

예약 검색 엔드포인트 구현

C:\msur\ms-reservations\service.py

          … (생략) …

@app.route('/reservations', methods=['GET'])
def reservations():
    """ Get Reservations Endpoint"""
    flight_id = request.args.get('flight_id')
    resp = handlers.get_reservations(flight_id)
    return jsonify(resp)

          … (생략) …

 

예약 검색 핸들러 구현

C:\msur\ms-reservations\src\handlers.py

          … (생략) …

def get_reservations(flight_id):
    """Get reservations callback"""
    return model.get_reservations(flight_id)

          … (생략) …

 

예약 검색 모델 구현

C:\msur\ms-reservations\src\model.py

          … (생략) …

def get_reservations(flight_id):
    """List of reservations for a flight, from Redis database"""
    try:
        key = this.tblprefix + flight_id
        reservations = this.redis_conn.hgetall(key)
    except redis.RedisError:
        response = {
            "error": "Cannot retrieve reservations"
        }
        log.error("Error retrieving reservations from Redis",
                  exc_info=True)
    else:
        response = reservations
        return response

 

 

마이크로서비스 테스트 2. 예약 조회

C:\msur\ms-reservations> curl -v http://127.127.127.127:7701/reservations?flight_id=werty
*   Trying 127.127.127.127:7701...
* Connected to 127.127.127.127 (127.127.127.127) port 7701 (#0)
> GET /reservations?flight_id=werty HTTP/1.1
> Host: 127.127.127.127:7701
> User-Agent: curl/7.79.1
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Server: gunicorn/20.0.4
< Date: Sat, 05 Mar 2022 23:14:17 GMT
< Connection: close
< Content-Type: application/json
< Content-Length: 20
<
{
  "12B": "dfdg"
}
* Closing connection 0

 

 

서비스 중지 및 깃허브 등록

c:\msur\ms-reservations> make stop

c:\msur\ms-reservations> git add .
c:\msur\ms-reservations> git commit -m "예약 서비스 구현"
c:\msur\ms-reservations> git push

 

728x90
반응형

댓글