티스토리 뷰

반응형
  • 스프링 웹플럭스 프로젝트 만들기
  • 넌리액티브 마이크로서비스를 마이그레이션하기

 

스프링 웹플럭스란?

Netty라는 새 애플리케이션 서버를 사용해 리액티브 마이크로서비스를 만들 수 있는 컴포넌트입니다.

리액티브 스트림 패턴을 구현하기 위해 리액터 프레임 워크를 광범위하게 사용합니다.

 

 

 

스프링 웹플럭스 프로젝트 생성하기

https://start.spring.io/

 

 

 

 

위와 같이 세팅을 하고 애플리케이션을 다운로드합니다.

 

 

이제 상단의 실행을 클릭하거나

 

Terminal에서 아래 작업을 실행합니다.

 

mvnw spring-boot:run

 

그러면 실행이 되고 로그에 아래와 같이 표시되는 것을 확인하여 네티를 이용해 요청을 처리할 준비를 합니다.

 

 

 

네티

Netty는 넌블로킹 I/O 작업을 수행할 수 있게 하는 Client-Server 프레임워크를 만들려는 아이디어를 가진 JBoss에 의해 개발되었습니다. 이런 기능을 위해 리액터 패턴의 메시지 기반 구현을 사용합니다.

 

Netty를 사용하면 마이크로서비스에서 더 많은 부하를 처리할 수 있어서 효과적입니다.

 

 

 

 

정적 콘텐츠

네티를 사용해 정적 콘텐츠를 제공할 수 있으며, 넌블로킹 IO를 통해 리액티브하게 제공됩니다.

 

정적 콘텐츠를 테스트하기 위해서 resources 안에 public 폴더를 생성하고 index.html을 생성합니다.

 

 

 

index.html 안에 내용은 아래와 같이 작성합니다.

 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Hello World!</title>
</head>
<body>
정적 콘텐츠 입니다.
</body>
</html>

 

이제 실행을 하고 http://localhost:8081/index.html 에 접속을 하면 아래와 같이 뜹니다.

정적 콘텐츠 입니다.

 

이 콘텐츠는 이전과 마찬가지로 제공되었지만 블로킹 작업이 아닌 리액티브하게 처리됩니다.

 

블로킹 IO에서는 서버의 페이지를 요청하면 모든 페이지 내용을 읽고 이를 요청한 클라이언트로 페이지를 전송하기 시작합니다.

넌블로킹 IO에서는 서버가 페이지를 읽기 시작하고 데이터를 가져오는 즉시 정보를 보냅니다. 그리고 파일에서 데이터를 더 읽고 클라이언트로 데이터를 다시 보냅니다. 실제로 요청한 페이지의 읽기 또는 보내기를 차단하지 않고 데이터를 요청하는 클라이언트에게 동일한 페이지 또는 다른 페이지의 데이터 전송을 시작할 수 있습니다.

 

 

 

Rest Api

리액티브한 코드를 작성하기 전에 기본 rest api 코드를 작성하겠습니다.

 

application.yml

server:
  port: 8081

spring:
  jackson:
    default-property-inclusion: NON_NULL

 

Customer.kt

data class Customer(
        val id: Int = 0,
        val name: String = "",
        val telephone: Telephone? = null
)

data class Telephone(
        val countryCode: String = "",
        val telephoneNumber: String = ""
)

 

CustomerService.kt

interface CustomerService {
    fun getCustomer(id: Int) : Customer?
    fun searchCustomer(nameFilter: String) : List<Customer>
}

 

CustomerServiceImpl.kt

@Component
class CustomerServiceImpl : CustomerService {

    companion object {
        val customerList = arrayOf(
                Customer(1, "임리프"),
                Customer(2, "차"),
                Customer(3, "영달", Telephone("+82", "12341234"))
        )
    }

    val customers = ConcurrentHashMap<Int, Customer>(customerList.associateBy(Customer::id))

    override fun getCustomer(id: Int): Customer? = customers[id]

    override fun searchCustomer(nameFilter: String): List<Customer> =
            customers.filter {
                it.value.name.contains(nameFilter, true)
            }.map(Map.Entry<Int, Customer>::value).toList()
}

 

CustomerController.kt

@RestController
class CustomerController(@Autowired private val customerService: CustomerService) {

    @GetMapping(value = ["/customer/{id}"])
    fun getCustomer(@PathVariable id: Int) = customerService.getCustomer(id)

    @GetMapping(value = ["/customers"])
    fun getCustomers(@RequestParam(required = false, defaultValue = "") nameFilter: String) = customerService.searchCustomer(nameFilter)
}

 

 

http://localhost:8081/customers 입력 시에 잘 동작하는 것을 확인할 수 있습니다.

[
    {
        "id": 1,
        "name": "임리프"
    },
    {
        "id": 2,
        "name": "차"
    },
    {
        "id": 3,
        "name": "영달",
        "telephone": {
            "countryCode": "+82",
            "telephoneNumber": "12341234"
        }
    }
]

 

 

 

블로킹은 리액티브가 아니다.

넌리액티브 마이크로서비스를 리액티브하게 쉽게 전환할 수 있다는 것을 알 수 있었습니다. 하지만 위에서 구현한 서비스는 완전한 리액티브가 아닙니다. 위에서 작성한 코드는 클라이언트가 URL을 호출하면 서비스를 호출해 Customer을 얻거나 검색을 합니다. 작업이 끝나면 JSON으로 결과를 직렬화하고, 이후에 값을 반환합니다. 즉, 위에서 작성한 코드는 블로킹 작업입니다. 따라서 리액티브 서비스가 되도록 하려면 코드를 수정해야 됩니다.

반응형
댓글