https://velog.io/@kshired/Spring-Kafka-ErrorHandlingDeserializer

 

Spring Kafka ErrorHandlingDeserializer

Kafka consumer를 사용하다보면, JsonDeserializer를 많이 사용하게 됩니다.하지만 가끔 정의한 schema와 다른 value가 의도치않게 들어오는 경우가 생기는데 ( 이벤트 수기 발행이라던지, 프로듀서 쪽에서

velog.io

 

ErrorHandlingDeserializer 의 동작방식은 위 페이지에서 자세하게 잘 정리해주셨습니다. 
요번에 해당 옵션을 적용하면서, 
갑자기 warn log가 엄청나게 쌓이는 현상이 발생했는데요, 
적용했던 내용, 문제 원인과 조치 방법을 공유하려 합니다. 


적용 내용

	@Bean
	public ConcurrentKafkaListenerContainerFactory<String, Object> consumerKafkaFactory() {
		ConcurrentKafkaListenerContainerFactory<String, Object> factory = new ConcurrentKafkaListenerContainerFactory<>();
		factory.setConsumerFactory(new DefaultKafkaConsumerFactory<>(consumerConfigs()));
		factory.setRecordMessageConverter(new StringJsonMessageConverter());
		return factory;
	}

	@Bean
	public Map<String, Object> consumerConfigs() {
		Map<String, Object> props = new HashMap<>();
		props.put(ConsumerConfig.GROUP_ID_CONFIG, "group-id");
		props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "bootstrap-server:9092"));
		props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "auto-offset-reset");
		props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, ErrorHandlingDeserializer.class);
		props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, ErrorHandlingDeserializer.class);
		props.put(ErrorHandlingDeserializer.KEY_DESERIALIZER_CLASS, StringDeserializer.class);
		props.put(ErrorHandlingDeserializer.VALUE_DESERIALIZER_CLASS, JsonDeserializer.class);
		return props;
	}


위 처럼, Deserializer 설정을 추가 한 뒤, Bean 등록을 해주었습니다. 

해당 옵션이 켜지게 되면, consumer 는 checkNullKeyForExceptions 옵션이 true로 설정됩니다.

이 옵션은 ErrorHandlingDeserializer 를 사용하는 한, 변경 false 로 변경 할 수 없습니다. 

			this.checkNullKeyForExceptions = this.containerProperties.isCheckDeserExWhenKeyNull()
					|| ErrorHandlingUtils.checkDeserializer(KafkaMessageListenerContainer.this.consumerFactory,
							consumerProperties, false,
							applicationContext == null
									? getClass().getClassLoader()
									: applicationContext.getClassLoader());

 

문제 상황

운영 부서 특성상, kafka record 발행 시, key 값을 optional 로 발행해주고 있었는데요, 
순서가 중요한 토픽 -> key 값에 id 

순서가 중요하지 않은 토픽 -> key 값에 null
ErrorHandlingDeserializer 의 코드 상, null 값 record 가 들어왔을 때 warn 로그를 찍어주고있습니다. 

private void invokeOnMessage(final ConsumerRecord<K, V> cRecord) {

			if (cRecord.value() instanceof DeserializationException ex) {
				throw ex;
			}
			if (cRecord.key() instanceof DeserializationException ex) {
				throw ex;
			}
			if (cRecord.value() == null && this.checkNullValueForExceptions) {
				checkDeser(cRecord, SerializationUtils.VALUE_DESERIALIZER_EXCEPTION_HEADER);
			}
			if (cRecord.key() == null && this.checkNullKeyForExceptions) {
				checkDeser(cRecord, SerializationUtils.KEY_DESERIALIZER_EXCEPTION_HEADER);
			}
			doInvokeOnMessage(cRecord);
			if (this.nackSleepDurationMillis < 0 && !this.isManualImmediateAck) {
				ackCurrent(cRecord);
			}
		}

 

SereializationUtils 코드 상, header 가 DeserializationExceptionHeader 아닐 시, 로깅합니다. 

@Nullable
	public static DeserializationException getExceptionFromHeader(final ConsumerRecord<?, ?> record,
			String headerName, LogAccessor logger) {

		Header header = record.headers().lastHeader(headerName);
		if (!(header instanceof DeserializationExceptionHeader)) {
			// 여기 부분이 찍힘
			logger.warn(
					() -> String.format("Foreign deserialization exception header in (%s) ignored; possible attack?",
							KafkaUtils.format(record)));
			return null;
		}
		if (header != null) {
			byte[] value = header.value();
			DeserializationException exception = byteArrayToDeserializationException(logger, header);
			if (exception != null) {
				Headers headers = new RecordHeaders(record.headers().toArray());
				headers.remove(headerName);
				exception.setHeaders(headers);
			}
			return exception;
		}
		return null;
	}

 

null 키로 역직렬화 된 레코드 정보가 있지만, commit 과정을 넘어가는 것에 대한 로깅으로 보시면 되겠습니다.

저희 부서에서는 warning 로그 도 저장소에 쌓고있는데요. 
이 코드가 배포되면서 급격하게 사용량이 증가하기 시작하였습니다.  😭

 

해결

따라서  log4j상에서 해당 패키지를 exclude 시킬지, key deserializer 에는 ErrorHandlingDeserializer 를 적용하지 않을지 고민하다가, 후자로 결정하여 적용하였습니다. 

HTTP Transfer-Encoding

HTTP 통신시 인코딩 형태를 지정하는 설정 값. 헤더에 설정할 수 있다.
(HTTP 1.1 부터 지원, 설정 시 Content-Length 헤더는 반드시 없어야 함.)
종류 : chunked, compress, deflate, gzip

 

 

 

테스트 

과제 진행 전, API에 응답값을 추가해야 할 일이 생겼었습니다. 
대략적으로 응답 객체가 5 -> 100개로 추가되는 상황이었는데요. 
데이터 크기가 한 번에 크게 증가하다 보니 어디서든 Limit 이 걸리지 않을까 우려되어 테스트를 진행하게 되었고, 

"옵션(Transfer-Encoding) 으로 인해 이슈 없음" 의 결론을 짓게 되었습니다.
테스트 과정을 말씀드리겠습니다.

 

 

nginx 설치

 

nginx, tomcat 서버 구성

1.  reverse-proxy 서버 구조를 구성하기 위해서 nginx 를 설치해줍니다.

$ brew install nginx

$ nginx -V
nginx version: nginx/1.27.3
built by clang 16.0.0 (clang-1600.0.26.4)
built with OpenSSL 3.4.0 22 Oct 2024
TLS SNI support enabled
configure arguments: --prefix=/opt/homebrew/Cellar/nginx/1.27.3 --sbin-path=/opt/homebrew/Cellar/nginx/1.27.3/bin/nginx --with-cc-opt='-I/opt/homebrew/opt/pcre2/include -I/opt/homebrew/opt/openssl@3/include' --with-ld-opt='-L/opt/homebrew/opt/pcre2/lib -L/opt/homebrew/opt/openssl@3/lib' --conf-path=/opt/homebrew/etc/nginx/nginx.conf --pid-path=/opt/homebrew/var/run/nginx.pid --lock-path=/opt/homebrew/var/run/nginx.lock --http-client-body-temp-path=/opt/homebrew/var/run/nginx/client_body_temp --http-proxy-temp-path=/opt/homebrew/var/run/nginx/proxy_temp --http-fastcgi-temp-path=/opt/homebrew/var/run/nginx/fastcgi_temp --http-uwsgi-temp-path=/opt/homebrew/var/run/nginx/uwsgi_temp --http-scgi-temp-path=/opt/homebrew/var/run/nginx/scgi_temp --http-log-path=/opt/homebrew/var/log/nginx/access.log --error-log-path=/opt/homebrew/var/log/nginx/error.log --with-compat --with-debug --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_degradation_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-http_v3_module --with-ipv6 --with-mail --with-mail_ssl_module --with-pcre --with-pcre-jit --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module

 

2. 설치가 완료되었으면, nginx.conf 파일을 수정하여 upstream 설정을 추가해줍니다.

#user  nobody;
worker_processes  1;

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid;


events {
    worker_connections  1024;
}

http {

    upstream backend {
        server 127.0.0.1:8080;
    }	


    bytes_sent   1;

    include       mime.types;
    default_type  application/octet-stream;

    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';

    #access_log  logs/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    #gzip  on;

    server {
        listen       80;
        server_name  localhost;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;

        location / {
	    proxy_pass http://backend;	
            root   html;
            index  index.html index.htm;
        }

        #error_page  404              /404.html;

        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }

        # proxy the PHP scripts to Apache listening on 127.0.0.1:80
        #
        #location ~ \.php$ {
        #    proxy_pass   http://127.0.0.1;
        #}

        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
        #
        #location ~ \.php$ {
        #    root           html;
        #    fastcgi_pass   127.0.0.1:9000;
        #    fastcgi_index  index.php;
        #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
        #    include        fastcgi_params;
        #}

        # deny access to .htaccess files, if Apache's document root
        # concurs with nginx's one
        #
        #location ~ /\.ht {
        #    deny  all;
        #}
    }


    # another virtual host using mix of IP-, name-, and port-based configuration
    #
    #server {
    #    listen       8000;
    #    listen       somename:8080;
    #    server_name  somename  alias  another.alias;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}


    # HTTPS server
    #
    #server {
    #    listen       443 ssl;
    #    server_name  localhost;

    #    ssl_certificate      cert.pem;
    #    ssl_certificate_key  cert.key;

    #    ssl_session_cache    shared:SSL:1m;
    #    ssl_session_timeout  5m;

    #    ssl_ciphers  HIGH:!aNULL:!MD5;
    #    ssl_prefer_server_ciphers  on;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}
    include servers/*;
}

 

3. 설정을 완료 했으면 nginx 재기동  $ nginx -s reload  해줍니다.

 

Spring Boot App

API 서버 역할을 할 MVC 앱을 작성합니다. (기본 port : 8080)

@RestController
public class SimpleApiController {

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public static class Response {
        List<String> stringList = new ArrayList<>();
    }

    private void addStringList(List<String> stringList) {
        stringList.add("긴문자열")
	}
    
    @GetMapping(
        value = "/test",
        produces = MediaType.APPLICATION_JSON_VALUE)
    public Response test() {
        Response response = new Response();
        for (int i = 0; i < 1000; i++) {
            addStringList(response.stringList);
        }
        return response;
    }    
}

테스트를 위해 64MB 의 크기를 갖도록 API 를 만들었습니다. 



이제 호출해봅시다. 

 

200 응답을 받았습니다. 데이터의 크기가 64MB가 되는데도 에러가 발생하지 않았습니다. 
응답이 오기까지는 약 5초정도 걸렸는데요. 왜 그런지 응답 값을 분석해보았습니다.

Connection: keep-alive
Transfer-Encoding: chunked

위 설정으로 인하여, 커넥션이 열려있는 상태로 응답을 chunked 단위로 계속 받아오는구나! 추측할 수 있었습니다!허나, 뭔가 찝찝한 기분이 들어서 좀 더 세밀한 분석을 해보기 위하여 WireShark 를 이용해봅시다.

 

WireShark 분석

  • 첫요청 을 시작으로 클라이언트에게 필요한 데이터 인코딩 방법을 응답 헤더로 전달하고있습니다.

  • 그 후 쭉 Chunk를 전달하다

  • 마지막 응답 종료 패킷을 끝으로 통신이 종료됩니다. 

왜 Chunk 사이즈는 16384, 16k 일까요?

nginx 기본 설정 client_body_buffer_size :16k  이어서 입니다.

Concept

"Making API clients easier"

Retrofit, JAXRS-2.0, WebScoekt 에 영감을 받아서 제작된 자바 클라이언트 라이브러리 입니다.
Netflix Denominator를 Http API 에 조금더 쉽게 매핑하는것을 시작으로 탄생하게 되었습니다.

Why Feign?

feign은 cxf, jersey를 사용하여 Rest, Soap 서비스를 제공 합니다.
손쉽게 http api code를 작성할 수 있고, 커스텀 인코딩/디코딩, 에러 핸들링을 지원합니다.

 

 

How To Work?

어노테이션 기반으로 동작합니다. 여러 템플릿을 제공하며 request, argument에 사용가능합니다.
텍스트 기반의 API를 만들기는 어렵지만 api작업 진행중 반복되는 코드 작성을 줄여줍니다.
유닛테스트 또한 간단화 할 수 있습니다.

 

 

Support

v10.x : java 8이상 필요, jdk6 이하 버전은 v9.x 를 사용바랍니다.

 

 

 

Use Case

Gradle

implementation 'io.github.openfeign:feign-core:13.5'
implementation 'io.github.openfeign:feign-gson:9.4.0' // decoding 위해 추가

 

Client

import feign.Param;
import feign.RequestLine;


public interface FeignTestClient {

    @RequestLine("GET /feign/{productId}")
    Product getProduct(@Param("productId") String productId);

    @RequestLine("POST /feign")
    void saveProduct(Product product);
}

 

 

Server

@RestController
public class FeignTestController {

    @GetMapping("/feign/{productId}")
    public Product getProducts(@PathVariable String productId) {
        return new Product(1L, BigDecimal.valueOf(350L));
    }

    @PostMapping("/feign")
    public void saveProduct(Product product) {
        System.out.println(product);
    }
}

 

 

Test

    public static void main(String[] args) {
        FeignTestClient feignTestClient = Feign.builder()
            .decoder(new GsonDecoder())
            .target(FeignTestClient.class, "http://127.0.0.1:8080");

        Product product = feignTestClient.getProduct("1");
        System.out.println(product);
    }

 

'백엔드 > java' 카테고리의 다른 글

Full GC가 터지는 서버를 고쳐보자!  (0) 2022.07.31

 

오늘은 24년 12월 둘째주에 업무를 진행하면서 발생했던 문제 상황과 해결한 방법을 공유하려 한다.
특정 유저가 닉네임 ( DB ) 를 업데이트 했는데, 닉네임 API 를 호출하면 이전 닉네임이 응답값으로 오는 현상이 발생했다.
히스토리 상 DB 업데이트도 성공적으로 완료되었기 때문에 문제될만한 지점이 없어보였다.

 


 

문제상황 요약

간단한 닉네임 업데이트

 

유저는 자신의 닉네임을 자유롭게 변경할수 있다!
하지만, 우리 서비스에 닉네임을 조회하는 API 가 있었고, 서비스의 급 성장으로 인해
닉네임 API 에 캐싱을 적용하게 되었었다.

 

 

 

닉네임 API 호출량 증가로 캐싱 12시간 적용!

 

문제가 되는 코드는 아래와 같았다.

  1. 트랜잭션 시작
  2. 유저를 찾는다.
  3. 닉네임을 수정하고 DB 업데이트 한다.
  4. 닉네임 캐싱 API 를 evict 처리 한다.
  5. 트랜잭션이 종료 되고 update 쿼리가 수행된다.
위 상황에서 4번이 완료되면 캐시 서버에서 해당 프로파일은 삭제된다.
이 때 5번 트랜잭션이 종료되기 전에 다시 닉네임 캐싱 API 가 호출되면, 업데이트 전 프로파일 정보로 새로 캐싱하게 된다.
최종적으로 DB와 캐시 값이 일치하지 않는 상황이 발생한다.

 


 

해결 방법

 

@Transactional 과 외부 시스템 통신 코드를 하나로 묶어서 자주 발생하는 문제중에 하나인데, 이럴 경우

Transactional 동작에 맞춰서 외부 시스템 통신코드를 실행해주는게 편리하다.

 

  1. @Transactional 종료 시, 성공적으로 commit 이 발생했다면 캐시를 evict
  2. exception 이 발생했다면 캐시 유지.

 

afterCommit 이벤트 등록

Spring 에서는 현재 상태의 Transaction 에 따라 이벤트를 등록하는 방법을 제공한다.

현재 트랜잭션 상태에 있다면, afterCommit 이벤트 리스너를 등록해서, 커밋이 완료 된 후에만 캐시를 evict 하게 할 수 있다.

annotation based 로 해결하는 방법으로는 TransactionEventListener를 이용하는 방법도 있다.

 

 

깨달은 점 : @Transactional 안에서 외부 시스템을 호출 시 주의하자.

 

 

무슨 일인가?

맡고있는 팀이 다루는 데이터가 나날히 증가하면서, 
sync를 담당하는 메시지 큐 프로세서가 Full GC를 계속해서 수행하는 이슈가 있었다. 
꽤나 오랫동안 스케일 업이나 스케일 아웃 없이 유지한 서버라서 처음에는 서버 추가로 대응할 까 했지만, 
튜닝할 수 있는 부분은 튜닝하는게 맞다고 생각하고 조사를 시작했다. 

Thread dump를 많이 떠봤지만? 글쎄,,, 

메시지 큐 프로세서 서버의 특성상 수행시간이 오래 걸리는 task들을 수행하는 경우가 많다.
Full GC 문제상황에서 계속해서 thread dump를 떠봤지만, 교착상태나 프로세스를 느리게 만드는 원인을 찾지 못했다. 
가끔씩 hang 걸린 스레드들이 있었으나, 문제 발생시마다 발견되었던 것은 아니기 때문에 원인으로 보지 않았다. 
분석 툴은 tda 를 이용했다. 

조사중에 문제는 더 심해졌으며 약 2주동안 서버를 아침 peak 시간마다 서버를 반복 배포하는 상황이 발생했다.. 

Heap dump를 떠보자! 

GC를 수행해도 메모리를 점유하고 있는 (leak) 이 있는것인가? 약 3일동안 
10초 주기로 힙덤프를 3~5씩 수행하고 비교를 시작했다. 
분석툴은 mat를 이용했다. 
mat 툴 내에서 leak 취약점은 발견되지 않았다. 또한 각 힙덤프 마다 (GC가 여러번 수행됐을 때)
계속해서 메모리를 점유하고 있는 특정 객체가 없었다.

1차 힙덤프
2차 힙덤프
3차 힙덤프

( 이 외에도 덤프 파일은 더 많았지만, 특별한 상황으로 판단할 근거가 없었다. )

 

GC가 어떻게 수행되고 있는건가?

좀 더 자세한 정보가 필요했다. Full GC 전 후 상황을 알 수 있는 로그가 없을 까? 
찾아보니 gc log를 생성하는 jvm 옵션을 걸어놨었고 해당 파일을 참조했다.
또한 실시간으로 문제가 발생할 때는 jstat으로 관찰을 시작했다. 

gc.*.log.0

2022-06-23T15:54:00.948+0900: 275799.929: [Full GC (Ergonomics) 2022-06-23T15:54:01.121+0900: 275800.103: [SoftReference, 13246 refs, 0.0021082 secs]2022-06-23T15:54:01.123+0900: 275800.105: [WeakReference, 15971 refs, 0.0021386 secs]2022-06-23T15:54:01.125+0900: 275800.107: [FinalReference, 2502 refs, 0.0002797 secs]2022-06-23T15:54:01.125+0900: 275800.107: [PhantomReference, 0 refs, 1347 refs, 0.0001439 secs]2022-06-23T15:54:01.125+0900: 275800.107: [JNI Weak Reference, 0.0013016 secs][PSYoungGen: 3156821K->74490K(3932160K)] [ParOldGen: 3145725K->3145524K(3145728K)] 6302547K->3220014K(7077888K), [Metaspace: 316669K->316669K(1359872K)], 0.7926097 secs] [Times: user=12.14 sys=0.01, real=0.79 secs] 
2022-06-23T15:54:01.741+0900: 275800.723: Total time for which application threads were stopped: 2.7901548 seconds, Stopping threads took: 0.0007279 seconds
2022-06-23T15:54:02.500+0900: 275801.482: [Full GC (Ergonomics) 2022-06-23T15:54:02.661+0900: 275801.643: [SoftReference, 15194 refs, 0.0024719 secs]2022-06-23T15:54:02.664+0900: 275801.646: [WeakReference, 17368 refs, 0.0022696 secs]2022-06-23T15:54:02.666+0900: 275801.648: [FinalReference, 14516 refs, 0.0122559 secs]2022-06-23T15:54:02.678+0900: 275801.660: [PhantomReference, 0 refs, 1346 refs, 0.0001451 secs]2022-06-23T15:54:02.678+0900: 275801.660: [JNI Weak Reference, 0.0013243 secs][PSYoungGen: 2695930K->75865K(3932160K)] [ParOldGen: 3145524K->3145721K(3145728K)] 5841454K->3221586K(7077888K), [Metaspace: 316669K->316669K(1359872K)], 1.0474940 secs] [Times: user=17.54 sys=0.02, real=1.05 secs] 
2022-06-23T15:54:03.549+0900: 275802.530: Total time for which application threads were stopped: 1.0592147 seconds, Stopping threads took: 0.0007225 seconds
2022-06-23T15:54:04.392+0900: 275803.374: [Full GC (Ergonomics) 2022-06-23T15:54:04.578+0900: 275803.559: [SoftReference, 15562 refs, 0.0025315 secs]2022-06-23T15:54:04.580+0900: 275803.562: [WeakReference, 16948 refs, 0.0023067 secs]2022-06-23T15:54:04.582+0900: 275803.564: [FinalReference, 13462 refs, 0.0109742 secs]2022-06-23T15:54:04.593+0900: 275803.575: [PhantomReference, 0 refs, 1348 refs, 0.0001486 secs]2022-06-23T15:54:04.594+0900: 275803.575: [JNI Weak Reference, 0.0013160 secs][PSYoungGen: 2621440K->75734K(3932160K)] [ParOldGen: 3145721K->3145640K(3145728K)] 5767161K->3221375K(7077888K), [Metaspace: 316669K->316669K(1359872K)], 0.5677562 secs] [Times: user=6.77 sys=0.01, real=0.56 secs] 
2022-06-23T15:54:04.961+0900: 275803.943: Total time for which application threads were stopped: 0.5793442 seconds, Stopping threads took: 0.0007261 seconds
2022-06-23T15:54:05.826+0900: 275804.808: [Full GC (Ergonomics) 2022-06-23T15:54:05.998+0900: 275804.980: [SoftReference, 15214 refs, 0.0023534 secs]2022-06-23T15:54:06.001+0900: 275804.983: [WeakReference, 17447 refs, 0.0022982 secs]2022-06-23T15:54:06.003+0900: 275804.985: [FinalReference, 12894 refs, 0.0107383 secs]2022-06-23T15:54:06.014+0900: 275804.996: [PhantomReference, 0 refs, 1347 refs, 0.0001522 secs]2022-06-23T15:54:06.014+0900: 275804.996: [JNI Weak Reference, 0.0013047 secs][PSYoungGen: 2621440K->77698K(3932160K)] [ParOldGen: 3145640K->3145499K(3145728K)] 5767080K->3223197K(7077888K), [Metaspace: 316669K->316669K(1359872K)], 0.5904243 secs] [Times: user=7.38 sys=0.00, real=0.59 secs] 
2022-06-23T15:54:06.418+0900: 275805.399: Total time for which application threads were stopped: 0.6022619 seconds, Stopping threads took: 0.0007616 seconds
2022-06-23T15:54:07.527+0900: 275806.509: [Full GC (Ergonomics) 2022-06-23T15:54:07.705+0900: 275806.687: [SoftReference, 13973 refs, 0.0021885 secs]2022-06-23T15:54:07.707+0900: 275806.689: [WeakReference, 17550 refs, 0.0022955 secs]2022-06-23T15:54:07.709+0900: 275806.691: [FinalReference, 12356 refs, 0.0101714 secs]2022-06-23T15:54:07.719+0900: 275806.701: [PhantomReference, 0 refs, 1346 refs, 0.0001466 secs]2022-06-23T15:54:07.720+0900: 275806.701: [JNI Weak Reference, 0.0012965 secs][PSYoungGen: 2621440K->75924K(3932160K)] [ParOldGen: 3145499K->3145277K(3145728K)] 5766939K->3221202K(7077888K), [Metaspace: 316669K->316669K(1359872K)], 0.5293224 secs] [Times: user=6.00 sys=0.00, real=0.53 secs] 
2022-06-23T15:54:08.057+0900: 275807.039: Total time for which application threads were stopped: 0.5409180 seconds, Stopping threads took: 0.0007210 seconds
2022-06-23T15:54:09.062+0900: 275808.043: [GC (Allocation Failure) 2022-06-23T15:54:09.116+0900: 275808.098: [SoftReference, 0 refs, 0.0000566 secs]2022-06-23T15:54:09.116+0900: 275808.098: [WeakReference, 29 refs, 0.0000224 secs]2022-06-23T15:54:09.116+0900: 275808.098: [FinalReference, 9186 refs, 0.0046865 secs]2022-06-23T15:54:09.121+0900: 275808.102: [PhantomReference, 0 refs, 0 refs, 0.0000102 secs]2022-06-23T15:54:09.121+0900: 275808.102: [JNI Weak Reference, 0.0007166 secs][PSYoungGen: 2621440K->75871K(3932160K)] 5766717K->3221516K(7077888K), 0.0664343 secs] [Times: user=1.23 sys=0.01, real=0.07 secs] 
2022-06-23T15:54:09.128+0900: 275808.110: [Full GC (Ergonomics) 2022-06-23T15:54:09.304+0900: 275808.286: [SoftReference, 14000 refs, 0.0022588 secs]2022-06-23T15:54:09.306+0900: 275808.288: [WeakReference, 17088 refs, 0.0022053 secs]2022-06-23T15:54:09.309+0900: 275808.290: [FinalReference, 2253 refs, 0.0001940 secs]2022-06-23T15:54:09.309+0900: 275808.291: [PhantomReference, 0 refs, 1348 refs, 0.0001491 secs]2022-06-23T15:54:09.309+0900: 275808.291: [JNI Weak Reference, 0.0012363 secs][PSYoungGen: 75871K->74838K(3932160K)] [ParOldGen: 3145645K->3145399K(3145728K)] 3221516K->3220238K(7077888K), [Metaspace: 316669K->316669K(1359872K)], 0.5451732 secs] [Times: user=6.80 sys=0.00, real=0.55 secs] 
2022-06-23T15:54:09.675+0900: 275808.656: Total time for which application threads were stopped: 0.6236176 seconds, Stopping threads took: 0.0007080 seconds
2022-06-23T15:54:11.208+0900: 275810.190: [Full GC (Ergonomics) 2022-06-23T15:54:11.378+0900: 275810.360: [SoftReference, 12815 refs, 0.0020839 secs]2022-06-23T15:54:11.380+0900: 275810.362: [WeakReference, 16815 refs, 0.0022654 secs]2022-06-23T15:54:11.383+0900: 275810.365: [FinalReference, 13525 refs, 0.0113922 secs]2022-06-23T15:54:11.394+0900: 275810.376: [PhantomReference, 0 refs, 1349 refs, 0.0001458 secs]2022-06-23T15:54:11.394+0900: 275810.376: [JNI Weak Reference, 0.0012956 secs][PSYoungGen: 2696278K->74025K(3932160K)] [ParOldGen: 3145399K->3145709K(3145728K)] 5841678K->3219735K(7077888K), [Metaspace: 316669K->316669K(1359872K)], 0.5401919 secs] [Times: user=6.26 sys=0.00, real=0.54 secs] 
2022-06-23T15:54:11.749+0900: 275810.731: Total time for which application threads were stopped: 0.5516511 seconds, Stopping threads took: 0.0007110 seconds
2022-06-23T15:54:12.466+0900: 275811.448: [Full GC (Ergonomics) 2022-06-23T15:54:12.655+0900: 275811.637: [SoftReference, 11622 refs, 0.0018615 secs]2022-06-23T15:54:12.657+0900: 275811.639: [WeakReference, 17258 refs, 0.0022578 secs]2022-06-23T15:54:12.660+0900: 275811.641: [FinalReference, 9433 refs, 0.0072564 secs]2022-06-23T15:54:12.667+0900: 275811.649: [PhantomReference, 0 refs, 1349 refs, 0.0001477 secs]2022-06-23T15:54:12.667+0900: 275811.649: [JNI Weak Reference, 0.0013050 secs][PSYoungGen: 2621440K->74705K(3932160K)] [ParOldGen: 3145709K->3145505K(3145728K)] 5767149K->3220210K(7077888K), [Metaspace: 316669K->316669K(1359872K)], 0.6667728 secs] [Times: user=9.21 sys=0.00, real=0.67 secs] 
2022-06-23T15:54:13.134+0900: 275812.116: Total time for which application threads were stopped: 0.6782087 seconds, Stopping threads took: 0.0007444 seconds
2022-06-23T15:54:14.515+0900: 275813.497: [Full GC (Ergonomics) 2022-06-23T15:54:14.687+0900: 275813.669: [SoftReference, 13840 refs, 0.0022529 secs]2022-06-23T15:54:14.689+0900: 275813.671: [WeakReference, 16513 refs, 0.0021787 secs]2022-06-23T15:54:14.691+0900: 275813.673: [FinalReference, 13791 refs, 0.0115372 secs]2022-06-23T15:54:14.703+0900: 275813.685: [PhantomReference, 0 refs, 1347 refs, 0.0001521 secs]2022-06-23T15:54:14.703+0900: 275813.685: [JNI Weak Reference, 0.0013446 secs][PSYoungGen: 2621440K->74322K(3932160K)] [ParOldGen: 3145505K->3145586K(3145728K)] 5766945K->3219909K(7077888K), [Metaspace: 316669K->316669K(1359872K)], 0.5750195 secs] [Times: user=7.07 sys=0.00, real=0.57 secs] 
2022-06-23T15:54:15.091+0900: 275814.072: Total time for which application threads were stopped: 0.5866793 seconds, Stopping threads took: 0.0007986 seconds
2022-06-23T15:54:16.324+0900: 275815.306: [Full GC (Ergonomics) 2022-06-23T15:54:16.491+0900: 275815.473: [SoftReference, 12165 refs, 0.0019353 secs]2022-06-23T15:54:16.493+0900: 275815.475: [WeakReference, 17343 refs, 0.0022993 secs]2022-06-23T15:54:16.495+0900: 275815.477: [FinalReference, 13299 refs, 0.0111796 secs]2022-06-23T15:54:16.506+0900: 275815.488: [PhantomReference, 0 refs, 1347 refs, 0.0001478 secs]2022-06-23T15:54:16.506+0900: 275815.488: [JNI Weak Reference, 0.0013418 secs][PSYoungGen: 2621440K->76142K(3932160K)] [ParOldGen: 3145586K->3145327K(3145728K)] 5767026K->3221470K(7077888K), [Metaspace: 316669K->316669K(1359872K)], 0.6305719 secs] [Times: user=8.26 sys=0.00, real=0.63 secs] 
2022-06-23T15:54:16.956+0900: 275815.938: Total time for which application threads were stopped: 0.6422066 seconds, Stopping threads took: 0.0007193 seconds
2022-06-23T15:54:18.088+0900: 275817.070: [GC (Allocation Failure) 2022-06-23T15:54:18.145+0900: 275817.126: [SoftReference, 0 refs, 0.0000539 secs]2022-06-23T15:54:18.145+0900: 275817.126: [WeakReference, 31 refs, 0.0000178 secs]2022-06-23T15:54:18.145+0900: 275817.126: [FinalReference, 11389 refs, 0.0057581 secs]2022-06-23T15:54:18.150+0900: 275817.132: [PhantomReference, 0 refs, 0 refs, 0.0000107 secs]2022-06-23T15:54:18.150+0900: 275817.132: [JNI Weak Reference, 0.0007090 secs][PSYoungGen: 2621440K->76871K(3932160K)] 5766767K->3222359K(7077888K), 0.0697500 secs] [Times: user=1.26 sys=0.00, real=0.07 secs] 
2022-06-23T15:54:18.158+0900: 275817.140: [Full GC (Ergonomics) 2022-06-23T15:54:18.321+0900: 275817.303: [SoftReference, 14348 refs, 0.0022840 secs]2022-06-23T15:54:18.324+0900: 275817.306: [WeakReference, 17052 refs, 0.0022095 secs]2022-06-23T15:54:18.326+0900: 275817.308: [FinalReference, 2340 refs, 0.0002111 secs]2022-06-23T15:54:18.326+0900: 275817.308: [PhantomReference, 0 refs, 1349 refs, 0.0001466 secs]2022-06-23T15:54:18.326+0900: 275817.308: [JNI Weak Reference, 0.0012911 secs][PSYoungGen: 76871K->75392K(3932160K)] [ParOldGen: 3145487K->3145670K(3145728K)] 3222359K->3221063K(7077888K), [Metaspace: 316669K->316669K(1359872K)], 0.6015073 secs] [Times: user=8.01 sys=0.00, real=0.60 secs] 
2022-06-23T15:54:18.761+0900: 275817.743: Total time for which application threads were stopped: 0.6834082 seconds, Stopping threads took: 0.0007441 seconds
2022-06-23T15:54:19.991+0900: 275818.972: [Full GC (Ergonomics) 2022-06-23T15:54:20.162+0900: 275819.144: [SoftReference, 14130 refs, 0.0022199 secs]2022-06-23T15:54:20.164+0900: 275819.146: [WeakReference, 17526 refs, 0.0023440 secs]2022-06-23T15:54:20.167+0900: 275819.149: [FinalReference, 13510 refs, 0.0107897 secs]2022-06-23T15:54:20.177+0900: 275819.159: [PhantomReference, 0 refs, 1349 refs, 0.0001465 secs]2022-06-23T15:54:20.178+0900: 275819.160: [JNI Weak Reference, 0.0013428 secs][PSYoungGen: 2696832K->0K(3932160K)] [ParOldGen: 3145670K->3145054K(3145728K)] 5842503K->3145054K(7077888K), [Metaspace: 316669K->316669K(1359872K)], 0.5521212 secs] [Times: user=6.06 sys=0.01, real=0.56 secs] 
2022-06-23T15:54:20.544+0900: 275819.525: Total time for which application threads were stopped: 0.5637847 seconds, Stopping threads took: 0.0007391 seconds
2022-06-23T15:54:21.954+0900: 275820.936: [GC (Allocation Failure) 2022-06-23T15:54:21.969+0900: 275820.951: [SoftReference, 0 refs, 0.0000471 secs]2022-06-23T15:54:21.969+0900: 275820.951: [WeakReference, 21 refs, 0.0000145 secs]2022-06-23T15:54:21.969+0900: 275820.951: [FinalReference, 11852 refs, 0.0053773 secs]2022-06-23T15:54:21.975+0900: 275820.957: [PhantomReference, 0 refs, 0 refs, 0.0000102 secs]2022-06-23T15:54:21.975+0900: 275820.957: [JNI Weak Reference, 0.0007138 secs][PSYoungGen: 2621440K->9033K(3932160K)] 5766494K->3154087K(7077888K), 0.0287361 secs] [Times: user=0.30 sys=0.01, real=0.03 secs] 
2022-06-23T15:54:21.983+0900: 275820.965: Total time for which application threads were stopped: 0.0404609 seconds, Stopping threads took: 0.0007301 seconds
2022-06-23T15:54:23.890+0900: 275822.872: [GC (Allocation Failure) 2022-06-23T15:54:23.907+0900: 275822.889: [SoftReference, 0 refs, 0.0000472 secs]2022-06-23T15:54:23.907+0900: 275822.889: [WeakReference, 31 refs, 0.0000184 secs]2022-06-23T15:54:23.907+0900: 275822.889: [FinalReference, 12855 refs, 0.0059021 secs]2022-06-23T15:54:23.913+0900: 275822.895: [PhantomReference, 0 refs, 0 refs, 0.0000104 secs]2022-06-23T15:54:23.913+0900: 275822.895: [JNI Weak Reference, 0.0007180 secs][PSYoungGen: 2630473K->10823K(3932160K)] 5775527K->3155877K(7077888K), 0.0306921 secs] [Times: user=0.34 sys=0.00, real=0.03 secs] 
2022-06-23T15:54:23.922+0900: 275822.903: Total time for which application threads were stopped: 0.0426840 seconds, Stopping threads took: 0.0007616 seconds
2022-06-23T15:54:25.789+0900: 275824.771: [GC (Allocation Failure) 2022-06-23T15:54:25.805+0900: 275824.787: [SoftReference, 0 refs, 0.0000610 secs]2022-06-23T15:54:25.805+0900: 275824.787: [WeakReference, 42 refs, 0.0000202 secs]2022-06-23T15:54:25.805+0900: 275824.787: [FinalReference, 11981 refs, 0.0053150 secs]2022-06-23T15:54:25.810+0900: 275824.792: [PhantomReference, 0 refs, 0 refs, 0.0000101 secs]2022-06-23T15:54:25.810+0900: 275824.792: [JNI Weak Reference, 0.0007284 secs][PSYoungGen: 2632263K->10025K(3932160K)] 5777317K->3155080K(7077888K), 0.0285799 secs] [Times: user=0.31 sys=0.00, real=0.03 secs] 
2022-06-23T15:54:25.819+0900: 275824.801: Total time for which application threads were stopped: 0.0403066 seconds, Stopping threads took: 0.0007221 seconds
2022-06-23T15:54:27.571+0900: 275826.552: [GC (Allocation Failure) 2022-06-23T15:54:27.586+0900: 275826.568: [SoftReference, 0 refs, 0.0000469 secs]2022-06-23T15:54:27.586+0900: 275826.568: [WeakReference, 47 refs, 0.0000220 secs]2022-06-23T15:54:27.586+0900: 275826.568: [FinalReference, 11512 refs, 0.0050805 secs]2022-06-23T15:54:27.592+0900: 275826.573: [PhantomReference, 0 refs, 0 refs, 0.0000098 secs]2022-06-23T15:54:27.592+0900: 275826.573: [JNI Weak Reference, 0.0007156 secs][PSYoungGen: 2631465K->12134K(3932160K)] 5776520K->3157189K(7077888K), 0.0287580 secs] [Times: user=0.31 sys=0.00, real=0.03 secs] 
2022-06-23T15:54:27.600+0900: 275826.582: Total time for which application threads were stopped: 0.0407009 seconds, Stopping threads took: 0.0007481 seconds
2022-06-23T15:54:29.093+0900: 275828.075: Total time for which application threads were stopped: 0.0113373 seconds, Stopping threads took: 0.0007676 seconds
2022-06-23T15:54:29.356+0900: 275828.337: [GC (Allocation Failure) 2022-06-23T15:54:29.372+0900: 275828.354: [SoftReference, 0 refs, 0.0000490 secs]2022-06-23T15:54:29.372+0900: 275828.354: [WeakReference, 25 refs, 0.0000160 secs]2022-06-23T15:54:29.372+0900: 275828.354: [FinalReference, 11428 refs, 0.0046712 secs]2022-06-23T15:54:29.377+0900: 275828.359: [PhantomReference, 0 refs, 0 refs, 0.0000102 secs]2022-06-23T15:54:29.377+0900: 275828.359: [JNI Weak Reference, 0.0007074 secs][PSYoungGen: 2633574K->11364K(3932160K)] 5778629K->3156419K(7077888K), 0.0289628 secs] [Times: user=0.33 sys=0.01, real=0.02 secs] 
2022-06-23T15:54:29.385+0900: 275828.367: Total time for which application threads were stopped: 0.0407151 seconds, Stopping threads took: 0.0007484 seconds
2022-06-23T15:54:31.560+0900: 275830.542: [GC (Allocation Failure) 2022-06-23T15:54:31.578+0900: 275830.560: [SoftReference, 0 refs, 0.0000473 secs]2022-06-23T15:54:31.578+0900: 275830.560: [WeakReference, 36 refs, 0.0000181 secs]2022-06-23T15:54:31.578+0900: 275830.560: [FinalReference, 13035 refs, 0.0055757 secs]2022-06-23T15:54:31.584+0900: 275830.566: [PhantomReference, 0 refs, 0 refs, 0.0000104 secs]2022-06-23T15:54:31.584+0900: 275830.566: [JNI Weak Reference, 0.0007132 secs][PSYoungGen: 2632804K->12432K(3932160K)] 5777859K->3157486K(7077888K), 0.0315727 secs] [Times: user=0.35 sys=0.01, real=0.03 secs] 
2022-06-23T15:54:31.593+0900: 275830.575: Total time for which application threads were stopped: 0.0439990 seconds, Stopping threads took: 0.0007568 seconds
2022-06-23T15:54:33.228+0900: 275832.210: [GC (Allocation Failure) 2022-06-23T15:54:33.246+0900: 275832.228: [SoftReference, 0 refs, 0.0000466 secs]2022-06-23T15:54:33.246+0900: 275832.228: [WeakReference, 33 refs, 0.0000191 secs]2022-06-23T15:54:33.246+0900: 275832.228: [FinalReference, 12247 refs, 0.0054947 secs]2022-06-23T15:54:33.251+0900: 275832.233: [PhantomReference, 0 refs, 0 refs, 0.0000106 secs]2022-06-23T15:54:33.251+0900: 275832.233: [JNI Weak Reference, 0.0007213 secs][PSYoungGen: 2633872K->11745K(3932160K)] 5778926K->3156800K(7077888K), 0.0306720 secs] [Times: user=0.34 sys=0.01, real=0.03 secs] 
2022-06-23T15:54:33.260+0900: 275832.242: Total time for which application threads were stopped: 0.0427162 seconds, Stopping threads took: 0.0007635 seconds
2022-06-23T15:54:34.984+0900: 275833.966: [GC (Allocation Failure) 2022-06-23T15:54:35.000+0900: 275833.982: [SoftReference, 0 refs, 0.0000471 secs]2022-06-23T15:54:35.000+0900: 275833.982: [WeakReference, 29 refs, 0.0000306 secs]2022-06-23T15:54:35.000+0900: 275833.982: [FinalReference, 11798 refs, 0.0051205 secs]2022-06-23T15:54:35.006+0900: 275833.987: [PhantomReference, 0 refs, 0 refs, 0.0000096 secs]2022-06-23T15:54:35.006+0900: 275833.987: [JNI Weak Reference, 0.0007095 secs][PSYoungGen: 2633185K->12405K(3932160K)] 5778240K->3157459K(7077888K), 0.0293101 secs] [Times: user=0.32 sys=0.00, real=0.03 secs] 
2022-06-23T15:54:35.014+0900: 275833.996: Total time for which application threads were stopped: 0.0412732 seconds, Stopping threads took: 0.0007630 seconds
2022-06-23T15:54:35.794+0900: 275834.776: Total time for which application threads were stopped: 0.0110328 seconds, Stopping threads took: 0.0006979 seconds
2022-06-23T15:54:36.727+0900: 275835.709: [GC (Allocation Failure) 2022-06-23T15:54:36.744+0900: 275835.725: [SoftReference, 0 refs, 0.0000471 secs]2022-06-23T15:54:36.744+0900: 275835.726: [WeakReference, 47 refs, 0.0000208 secs]2022-06-23T15:54:36.744+0900: 275835.726: [FinalReference, 11605 refs, 0.0047551 secs]2022-06-23T15:54:36.748+0900: 275835.730: [PhantomReference, 0 refs, 0 refs, 0.0000099 secs]2022-06-23T15:54:36.748+0900: 275835.730: [JNI Weak Reference, 0.0007143 secs][PSYoungGen: 2633845K->13402K(3932160K)] 5778899K->3158456K(7077888K), 0.0294072 secs] [Times: user=0.33 sys=0.00, real=0.03 secs] 
2022-06-23T15:54:36.757+0900: 275835.739: Total time for which application threads were stopped: 0.0414257 seconds, Stopping threads took: 0.0007846 seconds
2022-06-23T15:54:38.575+0900: 275837.557: [GC (Allocation Failure) 2022-06-23T15:54:38.593+0900: 275837.574: [SoftReference, 0 refs, 0.0000471 secs]2022-06-23T15:54:38.593+0900: 275837.574: [WeakReference, 22 refs, 0.0000154 secs]2022-06-23T15:54:38.593+0900: 275837.574: [FinalReference, 12183 refs, 0.0056325 secs]2022-06-23T15:54:38.598+0900: 275837.580: [PhantomReference, 0 refs, 0 refs, 0.0000113 secs]2022-06-23T15:54:38.598+0900: 275837.580: [JNI Weak Reference, 0.0007396 secs][PSYoungGen: 2634842K->12122K(3932160K)] 5779896K->3157177K(7077888K), 0.0309613 secs] [Times: user=0.34 sys=0.01, real=0.04 secs] 
2022-06-23T15:54:38.607+0900: 275837.589: Total time for which application threads were stopped: 0.0429210 seconds, Stopping threads took: 0.0007807 seconds
2022-06-23T15:54:39.619+0900: 275838.601: Total time for which application threads were stopped: 0.0116050 seconds, Stopping threads took: 0.0007219 seconds
2022-06-23T15:54:40.440+0900: 275839.422: [GC (Allocation Failure) 2022-06-23T15:54:40.456+0900: 275839.438: [SoftReference, 0 refs, 0.0000493 secs]2022-06-23T15:54:40.456+0900: 275839.438: [WeakReference, 26 refs, 0.0000172 secs]2022-06-23T15:54:40.456+0900: 275839.438: [FinalReference, 12527 refs, 0.0054881 secs]2022-06-23T15:54:40.462+0900: 275839.443: [PhantomReference, 0 refs, 0 refs, 0.0000102 secs]2022-06-23T15:54:40.462+0900: 275839.443: [JNI Weak Reference, 0.0007196 secs][PSYoungGen: 2633562K->11706K(3932160K)] 5778617K->3156761K(7077888K), 0.0291793 secs] [Times: user=0.32 sys=0.00, real=0.03 secs] 
2022-06-23T15:54:40.470+0900: 275839.452: Total time for which application threads were stopped: 0.0409766 seconds, Stopping threads took: 0.0007189 seconds
2022-06-23T15:54:42.248+0900: 275841.230: [GC (Allocation Failure) 2022-06-23T15:54:42.265+0900: 275841.247: [SoftReference, 0 refs, 0.0000512 secs]2022-06-23T15:54:42.265+0900: 275841.247: [WeakReference, 28 refs, 0.0000158 secs]2022-06-23T15:54:42.265+0900: 275841.247: [FinalReference, 12115 refs, 0.0045422 secs]2022-06-23T15:54:42.269+0900: 275841.251: [PhantomReference, 0 refs, 0 refs, 0.0000105 secs]2022-06-23T15:54:42.269+0900: 275841.251: [JNI Weak Reference, 0.0007131 secs][PSYoungGen: 2633146K->12509K(3932160K)] 5778201K->3157564K(7077888K), 0.0286606 secs] [Times: user=0.33 sys=0.00, real=0.03 secs] 
2022-06-23T15:54:42.278+0900: 275841.260: Total time for which application threads were stopped: 0.0405765 seconds, Stopping threads took: 0.0007569 seconds
2022-06-23T15:54:42.465+0900: 275841.447: Total time for which application threads were stopped: 0.0114296 seconds, Stopping threads took: 0.0007450 seconds
2022-06-23T15:54:44.359+0900: 275843.341: [GC (Allocation Failure) 2022-06-23T15:54:44.376+0900: 275843.358: [SoftReference, 0 refs, 0.0000445 secs]2022-06-23T15:54:44.376+0900: 275843.358: [WeakReference, 23 refs, 0.0000148 secs]2022-06-23T15:54:44.376+0900: 275843.358: [FinalReference, 12855 refs, 0.0049757 secs]2022-06-23T15:54:44.381+0900: 275843.363: [PhantomReference, 0 refs, 0 refs, 0.0000109 secs]2022-06-23T15:54:44.381+0900: 275843.363: [JNI Weak Reference, 0.0007150 secs][PSYoungGen: 2633949K->11983K(3932160K)] 5779004K->3157038K(7077888K), 0.0289794 secs] [Times: user=0.32 sys=0.00, real=0.03 secs] 
2022-06-23T15:54:44.389+0900: 275843.371: Total time for which application threads were stopped: 0.0408213 seconds, Stopping threads took: 0.0007614 seconds
2022-06-23T15:54:46.053+0900: 275845.035: [GC (Allocation Failure) 2022-06-23T15:54:46.069+0900: 275845.051: [SoftReference, 0 refs, 0.0000475 secs]2022-06-23T15:54:46.069+0900: 275845.051: [WeakReference, 20 refs, 0.0000142 secs]2022-06-23T15:54:46.069+0900: 275845.051: [FinalReference, 12427 refs, 0.0048786 secs]2022-06-23T15:54:46.074+0900: 275845.056: [PhantomReference, 0 refs, 0 refs, 0.0000102 secs]2022-06-23T15:54:46.074+0900: 275845.056: [JNI Weak Reference, 0.0007055 secs][PSYoungGen: 2633423K->11586K(3932160K)] 5778478K->3156641K(7077888K), 0.0289904 secs] [Times: user=0.32 sys=0.00, real=0.03 secs] 
2022-06-23T15:54:46.083+0900: 275845.065: Total time for which application threads were stopped: 0.0406926 seconds, Stopping threads took: 0.0007252 seconds
2022-06-23T15:54:47.007+0900: 275845.989: Total time for which application threads were stopped: 0.0111079 seconds, Stopping threads took: 0.0007446 seconds
2022-06-23T15:54:47.891+0900: 275846.873: [GC (Allocation Failure) 2022-06-23T15:54:47.909+0900: 275846.891: [SoftReference, 0 refs, 0.0000479 secs]2022-06-23T15:54:47.909+0900: 275846.891: [WeakReference, 53 refs, 0.0000231 secs]2022-06-23T15:54:47.909+0900: 275846.891: [FinalReference, 12407 refs, 0.0054633 secs]2022-06-23T15:54:47.914+0900: 275846.896: [PhantomReference, 0 refs, 0 refs, 0.0000100 secs]2022-06-23T15:54:47.914+0900: 275846.896: [JNI Weak Reference, 0.0007263 secs][PSYoungGen: 2633026K->13391K(3932160K)] 5778081K->3158446K(7077888K), 0.0310329 secs] [Times: user=0.35 sys=0.00, real=0.03 secs] 
2022-06-23T15:54:47.923+0900: 275846.905: Total time for which application threads were stopped: 0.0431955 seconds, Stopping threads took: 0.0007912 seconds
2022-06-23T15:54:49.825+0900: 275848.807: [GC (Allocation Failure) 2022-06-23T15:54:49.843+0900: 275848.825: [SoftReference, 0 refs, 0.0000542 secs]2022-06-23T15:54:49.843+0900: 275848.825: [WeakReference, 41 refs, 0.0000172 secs]2022-06-23T15:54:49.843+0900: 275848.825: [FinalReference, 12455 refs, 0.0056528 secs]2022-06-23T15:54:49.849+0900: 275848.831: [PhantomReference, 0 refs, 0 refs, 0.0000100 secs]2022-06-23T15:54:49.849+0900: 275848.831: [JNI Weak Reference, 0.0007026 secs]--[PSYoungGen: 2634831K->2634831K(3932160K)] 5779886K->5780555K(7077888K), 0.7459946 secs] [Times: user=0.92 sys=0.20, real=0.75 secs] 
2022-06-23T15:54:50.572+0900: 275849.554: [Full GC (Ergonomics) 2022-06-23T15:54:50.735+0900: 275849.717: [SoftReference, 13093 refs, 0.0021188 secs]2022-06-23T15:54:50.737+0900: 275849.719: [WeakReference, 16413 refs, 0.0021708 secs]2022-06-23T15:54:50.739+0900: 275849.721: [FinalReference, 2416 refs, 0.0007444 secs]2022-06-23T15:54:50.740+0900: 275849.722: [PhantomReference, 0 refs, 1349 refs, 0.0001461 secs]2022-06-23T15:54:50.740+0900: 275849.722: [JNI Weak Reference, 0.0013205 secs][PSYoungGen: 2634831K->0K(3932160K)] [ParOldGen: 3145723K->3144544K(3145728K)] 5780555K->3144544K(7077888K), [Metaspace: 316673K->316673K(1359872K)], 0.5460908 secs] [Times: user=6.62 sys=0.01, real=0.55 secs] 
2022-06-23T15:54:51.119+0900: 275850.101: Total time for which application threads were stopped: 1.3043852 seconds, Stopping threads took: 0.0007068 seconds
2022-06-23T15:54:51.954+0900: 275850.936: [GC (Allocation Failure) 2022-06-23T15:54:51.972+0900: 275850.954: [SoftReference, 0 refs, 0.0000527 secs]2022-06-23T15:54:51.972+0900: 275850.954: [WeakReference, 38 refs, 0.0000168 secs]2022-06-23T15:54:51.972+0900: 275850.954: [FinalReference, 11670 refs, 0.0048020 secs]2022-06-23T15:54:51.977+0900: 275850.958: [PhantomReference, 0 refs, 0 refs, 0.0000101 secs]2022-06-23T15:54:51.977+0900: 275850.958: [JNI Weak Reference, 0.0007102 secs][PSYoungGen: 2621440K->12922K(3932160K)] 5765984K->3157466K(7077888K), 0.0299901 secs] [Times: user=0.35 sys=0.00, real=0.03 secs]

자세한 로그를 확인 요약한 상황은 이랬다.

서버 스펙 : 
-Xms8g
-Xmx8g
-XX:NewSize=5g
-XX:MaxNewSize=5g
-XX:SurvivorRatio=2
-XX:+UseParallelGC
-XX:+UseParallelOldGC

1. full gc가 0.5초마다 발생함
2. full gc가 발생함에도 oldGeneration 영역의 메모리가 확보되지 않음.
3. minor gc또한 매우 빈번하게 발생하여, eden 영역의 객체들이 매우 빠르게 survivor 영역으로 넘어감. 



추론

위의 상황 및 여러 글들을 참조해서 추론을 시작했다. 

요청량 자체가 최근 급증하여 gc를 수행하는 순간에도 계속해서 힙영역이 full로 차게 되는 것 같다. 
young 영역에서 eden:survivor 비율 ,  new : old 영역의 비율을 조정해보거나, GC 알고리즘을 변경해보자. 
건드려 보려는 옵션은 다음과 같았다. 
참조 : https://d2.naver.com/helloworld/37111

1. Parallel GC

-XX:NewSize
-XX:MaxNewSize
-XX:SurvivorRatio
-XX:newRatio

ParallelGC 옵션

2. G1 GC

-XX:+UseG1GC
-XX:MaxGCPauseMillis=100

large memory 어플리케이션은 g1 gc 성능이 높다는 내용이 많았다. 


https://www.site24x7.com/learn/java/choosing-garbage-collector.html

결론

사실 플랫폼 내의 서버군들은 서버 메모리를 증량하면서 G1GC로 이미 넘어간 상황이었다.
리더 님과 팀내 의논 끝에 1번 보다는 2번으로 진행해보기로 했다. 

 S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU   YGC     YGCT    FGC    FGCT     GCT   

 0.0   20480.0  0.0   20480.0 4055040.0 2396160.0 4313088.0  2978559.6  333440.0 301559.8 42624.0 35543.8    588   27.014   0      0.000   27.014
 0.0   24576.0  0.0   24576.0 4046848.0 196608.0 4317184.0  2977576.3  333440.0 301560.8 42624.0 35543.8    589   27.053   0      0.000   27.053
 0.0   24576.0  0.0   24576.0 4046848.0 1810432.0 4317184.0  2977576.3  333440.0 301560.8 42624.0 35543.8    589   27.053   0      0.000   27.053
 0.0   24576.0  0.0   24576.0 4046848.0 3661824.0 4317184.0  2977576.3  333440.0 301560.8 42624.0 35543.8    589   27.053   0      0.000   27.053
 0.0   24576.0  0.0   24576.0 4046848.0 1560576.0 4317184.0  2978842.9  333440.0 301568.2 42624.0 35543.8    590   27.092   0      0.000   27.092
 0.0   24576.0  0.0   24576.0 4046848.0 3334144.0 4317184.0  2978842.9  333440.0 301568.2 42624.0 35543.8    590   27.092   0      0.000   27.092
 0.0   24576.0  0.0   24576.0 4046848.0 1294336.0 4317184.0  2979806.6  333440.0 301619.2 42624.0 35544.9    591   27.132   0      0.000   27.132
 0.0   24576.0  0.0   24576.0 4046848.0 3194880.0 4317184.0  2979806.6  333440.0 301619.2 42624.0 35544.9    591   27.132   0      0.000   27.132
 0.0   28672.0  0.0   28672.0 4038656.0 1146880.0 4321280.0  2979939.8  333440.0 301643.3 42624.0 35544.9    592   27.170   0      0.000   27.170
 0.0   28672.0  0.0   28672.0 4038656.0 2764800.0 4321280.0  2979939.8  333440.0 301643.3 42624.0 35544.9    592   27.170   0      0.000   27.170
 0.0   28672.0  0.0   28672.0 4038656.0 671744.0 4321280.0  2977753.6  333440.0 301647.8 42624.0 35544.9    593   27.209   0      0.000   27.209
 0.0   28672.0  0.0   28672.0 4038656.0 2260992.0 4321280.0  2977753.6  333440.0 301647.8 42624.0 35544.9    593   27.209   0      0.000   27.209
 0.0   28672.0  0.0   28672.0 4038656.0 3842048.0 4321280.0  2977753.6  333440.0 301647.8 42624.0 35544.9    594   27.209   0      0.000   27.209
 0.0   24576.0  0.0   24576.0 4046848.0 2064384.0 4317184.0  2980564.3  333440.0 301662.9 42624.0 35547.2    594   27.245   0      0.000   27.245
 0.0   28672.0  0.0   28672.0 4038656.0 208896.0 4321280.0  2977347.4  333440.0 301667.0 42624.0 35547.2    595   27.283   0      0.000   27.283
 0.0   28672.0  0.0   28672.0 4038656.0 2273280.0 4321280.0  2977347.4  333440.0 301667.0 42624.0 35547.2    595   27.283   0      0.000   27.283
 0.0   28672.0  0.0   28672.0 4038656.0 372736.0 4321280.0  2979605.4  333440.0 301668.0 42624.0 35547.2    596   27.319   0      0.000   27.319
 0.0   28672.0  0.0   28672.0 4038656.0 2260992.0 4321280.0  2979605.4  333440.0 301668.0 42624.0 35547.2    596   27.319   0      0.000   27.319
 0.0   24576.0  0.0   24576.0 4042752.0 204800.0 4321280.0  2982796.6  333440.0 301668.7 42624.0 35547.2    597   27.360   0      0.000   27.360
 0.0   24576.0  0.0   24576.0 4042752.0 1998848.0 4321280.0  2982796.6  333440.0 301668.7 42624.0 35547.2    597   27.360   0      0.000   27.360
 0.0   24576.0  0.0   24576.0 4042752.0 3846144.0 4321280.0  2982796.6  333440.0 301668.7 42624.0 35547.2    598   27.360   0      0.000   27.360
 0.0   24576.0  0.0   24576.0 4042752.0 1527808.0 4321280.0  2980284.7  333440.0 301670.6 42624.0 35547.2    598   27.396   0      0.000   27.396
 0.0   24576.0  0.0   24576.0 4042752.0 3158016.0 4321280.0  2980284.7  333440.0 301670.6 42624.0 35547.2    598   27.396   0      0.000   27.396
 0.0   24576.0  0.0   24576.0 4042752.0 1163264.0 4321280.0  2980986.3  333440.0 301693.9 42624.0 35547.2    599   27.434   0      0.000   27.434

peak 시간에도 Full GC는 발생하지 않았으며, 더불어 MQ 메시지가 밀리는 상황도 어느정도 개선되었다. (Full GC 로 인한 STW가 줄었기 때문인 듯)
이정도 단계에서 문제가 해결되어서 더 조사는 해보지 않았다. 
결론은 GC 튜닝은 각각의 특수한 상황에 따라 케이스가 매우 많기 때문에, 여러 자료를 참고 정도밖에 할 수 없었고, 맨 땅에 헤딩해야 한다는 것이다. 

GC 특성상 평균 메모리 점유량이 2.X -> 4.X로 증가했다.

 

'백엔드 > java' 카테고리의 다른 글

Open Feign  (0) 2024.12.26

개요


자바에는 직렬화 개념이 있습니다. 

직렬화(直列化) 또는 시리얼라이제이션(serialization)은 컴퓨터 과학의 데이터 스토리지 문맥에서 데이터 구조나 오브젝트 상태를 동일하거나 다른 컴퓨터 환경에 저장(이를테면 파일이나 메모리 버퍼에서, 또는 네트워크 연결 링크 간 전송)하고 나중에 재구성할 수 있는 포맷으로 변환하는 과정이다.

출처 : https://ko.wikipedia.org/wiki/%EC%A7%81%EB%A0%AC%ED%99%94

 

직렬화 - 위키백과, 우리 모두의 백과사전

 

ko.wikipedia.org

자바에서는 Serializable을 구현한 클래스를 직렬화 역직렬화할 때, 패키지 정보 가 포함되게 됩니다. 

 

문제 상황


Redis와 연동중인 프로젝트가 있다고 합시다.
이 프로젝트는 Class A를 캐시 연동하여 저장하도록 하고 있습니다.
그러던 중 리팩터링이 필요하여 Class A의 패키지를 변경하게 되었습니다. 
이렇게 되면, Cache Get 과정 중에 ClassNotFouncException이 발생할 것 입니다. 
직렬화 할 때랑, 역직렬화 할 때 패키지 정보가 달라졌으니깐요. 

리팩터링을 진행하다보면 생각보다 패키지 변경을 자주 할 수 있는데요. 
그렇다면 캐시에 한번 올라간 Object의 pacakage path는 불변해야 할까요? 


해결


spring-core 에는 Deserializer라는 인터페이스가 있습니다. 

@FunctionalInterface
public interface Deserializer<T> {

	/**
	 * Read (assemble) an object of type T from the given InputStream.
	 * <p>Note: Implementations should not close the given InputStream
	 * (or any decorators of that InputStream) but rather leave this up
	 * to the caller.
	 * @param inputStream the input stream
	 * @return the deserialized object
	 * @throws IOException in case of errors reading from the stream
	 */
	T deserialize(InputStream inputStream) throws IOException;

	/**
	 * Read (assemble) an object of type T from the given byte array.
	 * @param serialized the byte array
	 * @return the deserialized object
	 * @throws IOException in case of deserialization failure
	 * @since 5.2.7
	 */
	default T deserializeFromByteArray(byte[] serialized) throws IOException {
		return deserialize(new ByteArrayInputStream(serialized));
	}

}

inputStream을 받아 객체화 하는 인터페이스 입니다.
이 인터페이스를 구현한 클래스 DefaultDeserializer를 확인 해 봅시다.

/**
 * A default {@link Deserializer} implementation that reads an input stream
 * using Java serialization.
 *
 * @author Gary Russell
 * @author Mark Fisher
 * @author Juergen Hoeller
 * @since 3.0.5
 * @see ObjectInputStream
 */
public class DefaultDeserializer implements Deserializer<Object> {

	@Nullable
	private final ClassLoader classLoader;


	/**
	 * Create a {@code DefaultDeserializer} with default {@link ObjectInputStream}
	 * configuration, using the "latest user-defined ClassLoader".
	 */
	public DefaultDeserializer() {
		this.classLoader = null;
	}

	/**
	 * Create a {@code DefaultDeserializer} for using an {@link ObjectInputStream}
	 * with the given {@code ClassLoader}.
	 * @since 4.2.1
	 * @see ConfigurableObjectInputStream#ConfigurableObjectInputStream(InputStream, ClassLoader)
	 */
	public DefaultDeserializer(@Nullable ClassLoader classLoader) {
		this.classLoader = classLoader;
	}


	/**
	 * Read from the supplied {@code InputStream} and deserialize the contents
	 * into an object.
	 * @see ObjectInputStream#readObject()
	 */
	@Override
	@SuppressWarnings("resource")
	public Object deserialize(InputStream inputStream) throws IOException {
		ObjectInputStream objectInputStream = new ConfigurableObjectInputStream(inputStream, this.classLoader);
		try {
			return objectInputStream.readObject();
		}
		catch (ClassNotFoundException ex) {
			throw new NestedIOException("Failed to deserialize object type", ex);
		}
	}

}

생성할 떄 classLoader를 같이 받고, deserialize 과정에서 해당 classLoader로 ConfigurableObjectInputStream을 이용하는 것을 볼 수 있습니다. 
ConfigurableObjectInputStream을 확인 해 봅시다. 

public class ConfigurableObjectInputStream extends ObjectInputStream {

	@Nullable
	private final ClassLoader classLoader;

	private final boolean acceptProxyClasses;


	/**
	 * Create a new ConfigurableObjectInputStream for the given InputStream and ClassLoader.
	 * @param in the InputStream to read from
	 * @param classLoader the ClassLoader to use for loading local classes
	 * @see java.io.ObjectInputStream#ObjectInputStream(java.io.InputStream)
	 */
	public ConfigurableObjectInputStream(InputStream in, @Nullable ClassLoader classLoader) throws IOException {
		this(in, classLoader, true);
	}

	/**
	 * Create a new ConfigurableObjectInputStream for the given InputStream and ClassLoader.
	 * @param in the InputStream to read from
	 * @param classLoader the ClassLoader to use for loading local classes
	 * @param acceptProxyClasses whether to accept deserialization of proxy classes
	 * (may be deactivated as a security measure)
	 * @see java.io.ObjectInputStream#ObjectInputStream(java.io.InputStream)
	 */
	public ConfigurableObjectInputStream(
			InputStream in, @Nullable ClassLoader classLoader, boolean acceptProxyClasses) throws IOException {

		super(in);
		this.classLoader = classLoader;
		this.acceptProxyClasses = acceptProxyClasses;
	}


	@Override
	protected Class<?> resolveClass(ObjectStreamClass classDesc) throws IOException, ClassNotFoundException {
		try {
			if (this.classLoader != null) {
				// Use the specified ClassLoader to resolve local classes.
				return ClassUtils.forName(classDesc.getName(), this.classLoader);
			}
			else {
				// Use the default ClassLoader...
				return super.resolveClass(classDesc);
			}
		}
		catch (ClassNotFoundException ex) {
			return resolveFallbackIfPossible(classDesc.getName(), ex);
		}
	}

	@Override
	protected Class<?> resolveProxyClass(String[] interfaces) throws IOException, ClassNotFoundException {
		if (!this.acceptProxyClasses) {
			throw new NotSerializableException("Not allowed to accept serialized proxy classes");
		}
		if (this.classLoader != null) {
			// Use the specified ClassLoader to resolve local proxy classes.
			Class<?>[] resolvedInterfaces = new Class<?>[interfaces.length];
			for (int i = 0; i < interfaces.length; i++) {
				try {
					resolvedInterfaces[i] = ClassUtils.forName(interfaces[i], this.classLoader);
				}
				catch (ClassNotFoundException ex) {
					resolvedInterfaces[i] = resolveFallbackIfPossible(interfaces[i], ex);
				}
			}
			try {
				return ClassUtils.createCompositeInterface(resolvedInterfaces, this.classLoader);
			}
			catch (IllegalArgumentException ex) {
				throw new ClassNotFoundException(null, ex);
			}
		}
		else {
			// Use ObjectInputStream's default ClassLoader...
			try {
				return super.resolveProxyClass(interfaces);
			}
			catch (ClassNotFoundException ex) {
				Class<?>[] resolvedInterfaces = new Class<?>[interfaces.length];
				for (int i = 0; i < interfaces.length; i++) {
					resolvedInterfaces[i] = resolveFallbackIfPossible(interfaces[i], ex);
				}
				return ClassUtils.createCompositeInterface(resolvedInterfaces, getFallbackClassLoader());
			}
		}
	}


	/**
	 * Resolve the given class name against a fallback class loader.
	 * <p>The default implementation simply rethrows the original exception,
	 * since there is no fallback available.
	 * @param className the class name to resolve
	 * @param ex the original exception thrown when attempting to load the class
	 * @return the newly resolved class (never {@code null})
	 */
	protected Class<?> resolveFallbackIfPossible(String className, ClassNotFoundException ex)
			throws IOException, ClassNotFoundException{

		throw ex;
	}

	/**
	 * Return the fallback ClassLoader to use when no ClassLoader was specified
	 * and ObjectInputStream's own default class loader failed.
	 * <p>The default implementation simply returns {@code null}, indicating
	 * that no specific fallback is available.
	 */
	@Nullable
	protected ClassLoader getFallbackClassLoader() throws IOException {
		return null;
	}

}

resolveClass가 있는데, 해당 메서드는 readObject호출 할 때 호출 되며, 스트림에 저장된 클래스 패스를 이용하여 
현재 구동된 자바 프로그램에서 class를 찾습니다. 
찾지 못했을 경우, ClassNotFoundException이 발생하며, resolveFallbackIfPossible 메서드를 호출합니다. 
resolveFallbackIfPossible 메서드를 직접 구현해봅시다. 

 

public class PackageSafeValueSerializer implements Deserializer {

    private static final String ORIGIN_CLASS_PATH = "com.joohyeok.jeong.spring.cache.a";
    private static final String TARGET_CLASS_PATH = "com.joohyeok.jeong.spring.cache.b";

    private final ClassLoader classLoader;

    public PackageSafeValueSerializer(ClassLoader classLoader) {
        this.classLoader = classLoader;
    }

    @Override
    public Object deserialize(InputStream inputStream) throws IOException {
        ObjectInputStream objectInputStream = new PackageSafeObjectStream(inputStream, this.classLoader);
        try {
            return objectInputStream.readObject();
        } catch (ClassNotFoundException ex) {
            throw new NestedIOException("Failed to deserialize object type", ex);
        }
    }

    public class PackageSafeObjectStream extends ConfigurableObjectInputStream {

        private final ClassLoader classLoader;

        public PackageSafeObjectStream(InputStream in, ClassLoader classLoader) throws IOException {
            super(in, classLoader);

            this.classLoader = classLoader;
        }

        @Override
        protected Class<?> resolveFallbackIfPossible(String className, ClassNotFoundException ex)
            throws IOException, ClassNotFoundException {

            if (className.startsWith(ORIGIN_CLASS_PATH)) {
                String targetClassName = className.replace(ORIGIN_CLASS_PATH, TARGET_CLASS_PATH);

                Class<?> aClass = ClassUtils.forName(targetClassName, this.classLoader);
                if (Objects.nonNull(aClass)) {
                    return aClass;
                }
            }

            throw ex;
        }
    }
}

ORIGIN_CLASS_PATH, TARGET_CLASS_PATH 을 정의해 두었으며, ORIGIN_CLASS_PATH로 시작하면, TARGET_CLASS_PATH을 변경한 뒤 클래스를 탐색합니다. 발견하면 해당클래스를 이용하여 역직렬화를 하게 됩니다. 

 

테스트

redis 설치

테스트용 redis를 설치합니다. 
https://redis.io/docs/getting-started/

프로젝트 생성

spring boot 프로젝트를 이용, 
maven/gradle 에 spring-boot-starter-data-redis 의존성을 추가합니다. 

implementation 'org.springframework.boot:spring-boot-starter-data-redis:2.7.0'

캐시 Config 클래스 생성

@Configuration
public class CacheConfig {

    @Bean
    LettuceConnectionFactory lettuceConnectionFactory() {
        RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
        redisStandaloneConfiguration.setHostName("127.0.0.1");
        redisStandaloneConfiguration.setPort(6379);


         LettuceConnectionFactory lettuceConnectionFactory = new LettuceConnectionFactory(redisStandaloneConfiguration);
         return lettuceConnectionFactory;
    }

    @Bean
    public RedisTemplate<String, Object> redisTemplate() {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(lettuceConnectionFactory());

        JdkSerializationRedisSerializer jdkSerializationRedisSerializer = new JdkSerializationRedisSerializer(
            new SerializingConverter(),
            new DeserializingConverter(new PackageSafeValueSerializer(template.getClass().getClassLoader()))
        );

        template.setValueSerializer(jdkSerializationRedisSerializer);
        template.setKeySerializer(jdkSerializationRedisSerializer);
        return template;
    }

}

redis는 기본 주소 127.0.0.1:6379 로 세팅하였습니다.
RedisTemplate 내에 JdkSerializationRedisSerializer 를 생성하고, DeserializingConverter에 미리 정의한 PackageSafeValueSerializer를 포함시켜 줍니다.

 

테스트 코드 작성

junit을 활용합니다. 
1. com.joohyeok.jeong.spring.cache.a 객체를 생성 후 캐시에 저장합니다.

2. com.joohyeok.jeong.spring.cache.a  클래스를 삭제합니다.
3. com.joohyeok.jeong.spring.cache.a 클래스로 역직렬화 합니다. 
2에서 삭제하는 이유는 classLoader에 포함되게 하지 않기 위해서 입니다. 

@SpringBootTest
class ApplicationTests {

    private static final String key = "1";
    @Autowired
    private RedisTemplate redisTemplate;



    /**
     * TC1. com.joohyeok.jeong.spring.cache.a.TestObject 캐시 set
     */
    @Test
    void cacheTestA() {
       TestObject testObject = new TestObject();
       testObject.setName("테스트A");
        testObject.setPrice(BigDecimal.valueOf(25_000L));
        testObject.setStockQuantity(50L);
        // 1. A 패키지 cache set
        redisTemplate.opsForValue().set(key, testObject);

        // 2. A 패키지 cache get
       TestObject getObjectA = (TestObject)redisTemplate.opsForValue().get(key);

        System.out.println(String.format("getObjectA : %s", getObjectA));
    }

    /**
     * TC2. com.joohyeok.jeong.spring.cache.a.TestObject 클래스 삭제후 get
     */
    @Test
    public void cacheGetB(){
        com.joohyeok.jeong.spring.cache.b.TestObject getObjectB = (com.joohyeok.jeong.spring.cache.b.TestObject)redisTemplate.opsForValue().get(key);

        System.out.println(String.format("getObject : %s,", getObjectB));
    }

}

직렬화한 class path와 역직렬화 하는 class path가 달라졌지만, 데이터를 정상적으로 가져올 수 있습니다. 

'백엔드 > spring' 카테고리의 다른 글

ErrorHandlingDeserializer  (0) 2025.02.11
@Transactional 과 외부 시스템 연동  (0) 2024.12.23
@Transactional(readOnly=true)  (0) 2022.06.13
@InitBinder autoGrowCollectionLimit  (0) 2022.06.04

+ Recent posts