Rental Application (React & Spring boot Microservice) - 16 : rental-service(2)

#1 service, repository

RentalService, RentalRepository를 구현하도록 하겠습니다.

  • RentalService
package com.microservices.rentalservice.service;

import com.microservices.rentalservice.dto.RentalDto;

public interface RentalService {
    RentalDto createRental(RentalDto rentalDto);

    RentalDto getRentalByRentalId(String rentalId);

    Iterable<RentalDto> getRentalsByOwner(String owner);

    Iterable<RentalDto> getRentalsByBorrower(String borrower);

    RentalDto deleteRental(String rentalId);
}
  • RentalServiceImpl
package com.microservices.rentalservice.service;

import com.microservices.rentalservice.dto.RentalDto;
import com.microservices.rentalservice.entity.RentalEntity;
import com.microservices.rentalservice.repository.RentalRepository;
import com.microservices.rentalservice.util.DateUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.transaction.Transactional;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

@Service
@Slf4j
public class RentalServiceImpl implements RentalService {
    private RentalRepository rentalRepository;

    @Autowired
    public RentalServiceImpl(RentalRepository rentalRepository) {
        this.rentalRepository = rentalRepository;
    }

    @Transactional
    @Override
    public RentalDto createRental(RentalDto rentalDto) {
        log.info("Rental Service's Service Layer :: Call createRental write Method!");

        RentalEntity rentalEntity = RentalEntity.builder()
                                                .rentalId(UUID.randomUUID().toString())
                                                .postId(rentalDto.getPostId())
                                                .price(rentalDto.getPrice())
                                                .owner(rentalDto.getOwner())
                                                .borrower(rentalDto.getBorrower())
                                                .startDate(rentalDto.getStartDate())
                                                .endDate(rentalDto.getEndDate())
                                                .createdAt(DateUtil.dateNow())
                                                .build();

        rentalRepository.save(rentalEntity);

        return RentalDto.builder()
                        .rentalId(rentalEntity.getRentalId())
                        .postId(rentalEntity.getPostId())
                        .price(rentalEntity.getPrice())
                        .owner(rentalEntity.getOwner())
                        .borrower(rentalEntity.getBorrower())
                        .startDate(rentalEntity.getStartDate())
                        .endDate(rentalEntity.getEndDate())
                        .createdAt(rentalEntity.getCreatedAt())
                        .build();
    }

    @Transactional
    @Override
    public RentalDto getRentalByRentalId(String rentalId) {
        log.info("Rental Service's Service Layer :: Call getRentalByRentalId Method!");

        RentalEntity rentalEntity = rentalRepository.findByRentalId(rentalId);

        return RentalDto.builder()
                        .rentalId(rentalEntity.getRentalId())
                        .postId(rentalEntity.getPostId())
                        .price(rentalEntity.getPrice())
                        .owner(rentalEntity.getOwner())
                        .borrower(rentalEntity.getBorrower())
                        .startDate(rentalEntity.getStartDate())
                        .endDate(rentalEntity.getEndDate())
                        .createdAt(rentalEntity.getCreatedAt())
                        .build();
    }

    @Transactional
    @Override
    public Iterable<RentalDto> getRentalsByOwner(String owner) {
        log.info("Rental Service's Service Layer :: Call getRentalsByOwner Method!");

        Iterable<RentalEntity> rentals = rentalRepository.findAllByOwner(owner);
        List<RentalDto> rentalList = new ArrayList<>();

        rentals.forEach(v -> {
            rentalList.add(RentalDto.builder()
                                    .rentalId(v.getRentalId())
                                    .postId(v.getPostId())
                                    .price(v.getPrice())
                                    .owner(v.getOwner())
                                    .borrower(v.getBorrower())
                                    .startDate(v.getStartDate())
                                    .endDate(v.getEndDate())
                                    .createdAt(v.getCreatedAt())
                                    .build());
        });

        return rentalList;
    }

    @Transactional
    @Override
    public Iterable<RentalDto> getRentalsByBorrower(String borrower) {
        log.info("Rental Service's Service Layer :: Call getRentalsByBorrower Method!");

        Iterable<RentalEntity> rentals = rentalRepository.findAllByBorrower(borrower);
        List<RentalDto> rentalList = new ArrayList<>();

        rentals.forEach(v -> {
            rentalList.add(RentalDto.builder()
                                    .rentalId(v.getRentalId())
                                    .postId(v.getPostId())
                                    .price(v.getPrice())
                                    .owner(v.getOwner())
                                    .borrower(v.getBorrower())
                                    .startDate(v.getStartDate())
                                    .endDate(v.getEndDate())
                                    .createdAt(v.getCreatedAt())
                                    .build());
        });

        return rentalList;
    }

    @Transactional
    @Override
    public RentalDto deleteRental(String rentalId) {
        log.info("Rental Service's Service Layer :: Call deleteRental Method!");

        RentalEntity rentalEntity = rentalRepository.findByRentalId(rentalId);

        rentalRepository.delete(rentalEntity);

        return RentalDto.builder()
                        .rentalId(rentalEntity.getRentalId())
                        .build();
    }
}
  • RentalRepository
package com.microservices.rentalservice.repository;

import com.microservices.rentalservice.entity.RentalEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface RentalRepository extends JpaRepository<RentalEntity, Long> {
    RentalEntity findByRentalId(String rentalId);

    Iterable<RentalEntity> findAllByOwner(String owner);

    Iterable<RentalEntity> findAllByBorrower(String borrower);
}

코드 자체는 post-service와 비슷하고 메서드에 대한 설명은 이전 포스트에 RentalController를 구현하면서 설명을 했으니 참고하시길 바라겠습니다.

#2 kafka

rental-service는 post-service와 메시지 기반으로 통신을 할 서비스입니다. 시나리오는 다음과 같습니다.

1) 게시글을 통해 대여에 관한 수락이 된다면, rental-service에서는 대여에 관한 정보를 생성합니다.

2) 대여를 취소한다면, rental-service에서는 대여에 관한 정보를 취소합니다.

간단하게 생각해본 시나리오지만 메시지 기반으로 통신할 때는 여러 장점들이 존재합니다. 우선 Apache Kafka에 대해 알아보겠습니다.

Apache Kafka는 오픈 소스 메시지 브로커 프로젝트입니다.

위 사진처럼 service-x ~ z까지가 service-a ~ c에 요청을 하고있다고 가정해보겠습니다. 모든 요청들이 잘 처리되어진다면 최고의 상황이겠지만, 실제 상황에서는 service-a의 서버 다운, service-b의 과도한 처리로 인한 응답의 늦어짐 등등이 있겠죠. 이런 것들을 개선하기 위해 도입된 것이 메시지 브로커이고 대표적인 오픈소스 프로젝트가 Apache Kafka입니다.

위 사진처럼 중간에 kafka 메시지 브로커를 놓아주게 되면 다음과 같은 제가 생각하는 카프카를 사용했을 때 장점인 시나리오들을 처리할 수 있습니다.

1) service-a의 서버가 다운이 되어도 kafka에 메시지를 보내놓는다면 kafka를 구독하고 있는 service-a는 서버가 다시 구동이 되었을 때, 자신에게 온 메시지를 읽어서 요청을 처리할 수 있습니다.

2) service-b는 현재 처리할 요청들이 너무 많아 당장 요청을 받기 어렵습니다. 그래서 현재 요청들을 처리하고 그 다음에 다른 요청들을 처리하기 위해 kafka를 구독하고 순서대로 요청들을 처리할 수 있습니다.

3) service-z는 service-b, service-c에게 요청을 하고 싶습니다. 만약 각각 직접 요청을 해야한다면 service-z는 2번의 요청을 해야하고 2번의 응답을 받아야하죠. 하지만 kafka 메시지 브로커에 메시지를 보내고 b, c가 브로커를 구독하는 상황이라면 service-z는 한 번의 요청과 2번의 응답만 받으면 됩니다. 즉, 2번의 요청 -> 1번의 요청이라는 자원 낭비를 줄일 수 있죠.

Apache Kafka의 장점은 다음과 같습니다.

1) Low Latencty: 낮은 지연 시간을 갖습니다. 즉, 하나의 메시지를 보낼 때 이동하는데 걸리는 시간이 낮다는 의미이죠.

2) High Throughput: 높은 처리량을 갖습니다.

3) Fault Tolerance: 클러스터 내 노드 중 일부에서 결함이 생겨도 정상적 혹은 부분적으로 노드를 실행할 수 있게 합니다.

이 외에도 많은 장점들이 존재합니다.

#3 kafka 설치

kafka_2.13-2.8.0.tgz파일을 다운받고 MyRentalKafka라는 디렉토리를 만들어 압축 해제하도록 하겠습니다.


그리고 다음 명령어를 통해 Kafka Connect를 설치하고 압축해제 해주도록 하겠습니다.

curl -O http://packages.confluent.io/archive/6.1/confluent-community-6.1.0.tar.gz
tar xvf confluent-community-6.1.0.tar.gz

https://docs.confluent.io/kafka-connect-jdbc/current/index.html (아래 사진의 Download and extract the ZIP file) 로 이동해서 파일을 다운받고 kafka-connect-jdbc를 압축해제하도록 하겠습니다.

그리고 제 기준 /home/biuea/Desktop/MyRentalKafka/confluentinc-kafka-connect-jdbc-10.2.1/lib 경로를 복사해서 /home/biuea/Desktop/MyRentalKafka/confluent-6.1.0/etc/kafka/connect-distributed.properties 설정파일에 들어가 맨 하단에 있는 plugin.path를 다음과 같이 plugin.path=/home/biuea/Desktop/MyRentalKafka/confluentinc-kafka-connect-jdbc-10.2.1/lib 로 바꿔주도록 하겠습니다.

마지막으로 홈 디렉토리의 /home/biuea/.m2/repository/org/mariadb/jdbc/mariadb-java-client/2.7.2 경로로 가서 cp ./mariadb-java-client-2.7.2.jar /home/biuea/Desktop/MyRentalKafka/confluent-6.1.0/share/java/kafka로 파일을 복사하도록 하겠습니다.

이로써 kafka를 사용하기 위한 파일들을 다운받았고 다음 포스트에서는 이를 이용해서 post-service와 rental-service 간 메시지 통신을 진행해보겠습니다.

참고

인프런: Spring Cloud로 개발하는 마이크로서비스 애플리케이션(MSA) - 이도원

좋은 웹페이지 즐겨찾기