본문 바로가기
DB/MongoDB

[MongoDB] MongoDB Reactive - Exception opening socket

by lucas_owner 2024. 7. 27.

 

MongoDB - Exception Opening Socket 

 

Error Log

 

아주 오래전, MongoDB Reactive 를 활용하여, 특정 API의 로그들을 별도로 Reactive 하게 적재하는 기능을 추가하기 위해

`data-mongodb-reactive` 를 사용하여 간단한 CRUD, Test 까지만 만들어두고 MongoDB를 홈서버로 이전하면서 다시 서버를 기동시켰을때 발생한 Exception 이었다.

 

당시에 mongoDB 를 로그로 적재하는 DB 로 선택한 이유는 크게 다음과 같다. 

1. Document 형식의 DB 이기에 RDB 보다 로그를 적재하기 적합(데이터 포맷에 대한 신경 X, 데이터 전처리과정 X)
2. Main DB 인 RDB 에 로그 적재로인한 불필요한 리소스 사용 방지
3. TTL 설정으로 적절한 생명주기 관리 가능

 

implementation 'org.springframework.boot:spring-boot-starter-data-mongodb-reactive'
implementation 'org.springframework.boot:spring-boot-starter-data-mongodb'

* 당시 사용하던 Libs


 

개발환경

  1. Java 11
  2. Spring Boot 2.7.9
  3. Docker
  4. MongoDB 5.0 

MongoDB 는, Local PC 가 아닌 홈서버의 Docker Container 로 Running 상태이고, 외부접속 및 통신에는 문제가 전혀 없는상태.


당시에 간단하게 CRUD 테스트만을 진행했었기에 가장 기본적인 설정만 되어있었다.

설정은 다음과 같았다.. 

 

● application.yml

# application.yml
spring:
  data:
    mongodb:
      host: 192.168.35.135
      port: 17017
      username: test1
      password: test1234
      database: test
      authentication-database: admin

 

● Main Class

// Main Application Class
@SpringBootApplication
@EnableReactiveMongoRepositories
public class MongoApplication extends AbstractReactiveMongoConfiguration {

    public static void main(String[] args) {
        SpringApplication.run(MongoApplication.class, args);
    }

    @Override
    protected String getDatabaseName() {
        return "test";
    }
}

 

● Controller, Repository

// Repository
@Repository
public interface MemberRepository extends ReactiveMongoRepository<Member_info, String> {

//    List<Member_info> findByEmailContains(String keyWord);

}


// -------------- Controller --------------
@RestController
@RequiredArgsConstructor
@Slf4j
@RequestMapping("/api/test/")
public class MongoFirstController {

    private final MemberRepository repository;

    /**
     * Post - save
     * @param inDTO
     * @return
     */
    @PostMapping("v1/insert/member")
    public Mono<Member_info> insertMember(@RequestBody MemberDTO inDTO) {
        log.info("Controller Request Param >>>> {}", inDTO);

        Mono<Member_info> save = repository.save(inDTO.toEntity());

        log.info("Controller Return Param >>>> {}", save.subscribe(System.out::println));
        // MessageConverter -> Java Obj to JSON Translate
        return save;
    }

    /**
     * FindAll - Get
     * @return
     */
    @GetMapping("v1/findall/member")
    public Flux<Member_info> findAll() {

        Flux<Member_info> all = repository.findAll();

        log.info("Controller Return Param >>>> {}", all.subscribe(System.out::println));
        return all;
    }

    /**
     * Update
     * @param inDTO
     * @param id
     * @return
     */
    @PutMapping("v1/update/member")
    public Mono<Member_info> updateMember(@RequestBody MemberDTO inDTO, @RequestParam String id) {
        inDTO.set_id(id);
        Member_info member_info = inDTO.toEntity();

        Mono<Member_info> save = repository.save(member_info);
        log.info("Controller Return Param >>>> {}", save.subscribe(System.out::println));

        return save;
    }

    /**
     * Delete
     * @param id
     * @return
     */
    @DeleteMapping("v1/delete/member")
    public int deleteMember(@RequestParam String id) {
        Mono<Void> voidMono = repository.findById(id).flatMap(repository::delete);
        log.info("Delete Log : {}", voidMono.subscribe(System.out::println));
        return 1;
    }


}

 

별다른 설정또한 많이 존재하지 않았던 상태이고, Opening Socket Excpetion 이 계속 발생하고 있었기에 

홈서버로 이관한 MongoDB 와 Spring 이 통신을 못하고 있는 상태로 보였었다.

실제로, Server 기동시에 Exception 뿐만 아니라, Test API 요청시 No server chosen by com.mongodb.reactivestreams
Log 가 찍히고 있었기 때문.

 

API Log

 

 

No server chosen by com.mongodb.reactivestreams

해당 에러에 대해 Jdbc Driver, Version 호환, 방화벽문제, 인증문제 관련된 문서와 글들만 존재한 상태였다. 

관련 글들의 이슈로만 본다면, DB Connection 또한 연결이 되지 않아야 하는게 정상이었지만 DB Connection은 정상적으로 연결이 되는걸 확인했기에, 위에서 언급했던 문제들과는 관련이 없었다고 생각했다.

 

Connection 정상 연결

 

 

제일 의심가는 설정이었던,, Main Class 를 확인해 보았다

// Main Application Class
@SpringBootApplication
@EnableReactiveMongoRepositories
public class MongoApplication extends AbstractReactiveMongoConfiguration {

    public static void main(String[] args) {
        SpringApplication.run(MongoApplication.class, args);
    }

    @Override
    protected String getDatabaseName() {
        return "test";
    }
}

 

@EnableReactiveMongoRepositories

해당 어노테이션은 `ReactiveMongoRepository` 와 같은 리액티브 레포지토리를 사용, 비동기적인 상호작용

기본설정 제공, 리액티브 기능자동구성 및 커스터마이즈를 지원해주는 어노테이션이었다. 

다만, 제거후에도 같은 에러가 발생 했었고, 커스터마이즈를 하지 않았다면 오히려 자동으로 설정을 해주기에 영향도가 없을것으로 생각했다.

 

extends AbstractReactiveMongoConfiguration

문제는 바로 `AbstractReactiveMongoConfiguration` 이었다.

해당 Class는 extends 를 하게되면 필수적으로 해야되는 @Override 가 하나 존재한다.

@Override
    protected String getDatabaseName() {
        return "test";
    }

 

바로 해당 메서드이고, 단순하게 DatabaseName 을 Return 하는것이기에 다른 메서드들을 추가적으로 살펴보지않은것

 

Override List

내부에 Override 항목을 살펴보면 reactiveMongoClient() 라는 메서드가 존재한다. 

`MongoClient`를 설정하는데 사용이 되는 메서드이며, MongoClient 는 MongoDB와의 연결을 관리하는 주체이다..

 

Redis 또한, 연결을 위한 추가 Configuration 을 추가하여 ConnectionFactory 설정을 해줬었던걸 잊고 있었다..

(etc. Template, Factory) 

 

나는 해당 프로젝트에서 별도의 MongoClient 설정을 추가해주지 않았기에 yml 파일의 설정으로는 Spring boot -> MongoDB 에 커넥션을 잡지 못한것이었다. 

 

@Override
    public MongoClient reactiveMongoClient() {
        return MongoClients.create(MongoClientSettings.builder()
                .applyConnectionString(new com.mongodb.ConnectionString("mongodb://@192.168.35.135:17017/test?authSource=admin&authMechanism=SCRAM-SHA-1"))
                .build());
    }

 

별도의 Configuration 을 설정할 필요가 없다면, Main Class 에 MongoClient 를 설정해주는 

reactiveMongoClient() 를 @Override 하여 커넥션을 추가, 허용해주면 해결이 가능하다.

 

 

기본적으로 Spring Boot 는 동기 MongoDB Client 자동설정을 제공한다.(Spring-boot-starter-data-mongodb)

'spring.data.mongdb' 하위의 속성들을 Server Start 시 인식, Clinet 설정을 해주지만

 

Exception이 발생했던, Reactive-mongodb 의 경우에는 비동기적인 데이터 접근을 제공하고 사용하는 방식이기에 동기적인 Client 설정과는 다르게, 비동기적인 MongoClient 를 설정해줘야 한다. 

com.mongodb.reactivestreams

하위의 설정과, 'spring.data.mongodb' 와는 다름. 

 

AbstractReactiveMongoConfiguration 는 결국 확장해서 사용하는 순간 Client 설정을 명시적으로 제공할 수 있고

관련 기능을 통해 Spring Data MongoDB Reactive 기능을 사용할 수 있었던 것.

반응형

댓글