728x90
반응형
728x90
반응형
반응형

환경: windows11, 아래 설치 진행

2024.02.10 - [서버 세팅 & tool/docker] - [windows] docker; 컨테이너 가상화

 

[windows] docker; 컨테이너 가상화

virtualization 물리적인 컴퓨터 리소스를 다른 시스템이나 애플리케이션에서 사용할 수 있도록 제공 플랫폼 가상화 리소스 가상화 하이퍼바이저(hypervisor) Virtual machine manager(VMM) 다수의 운영체제를

bangpurin.tistory.com

도커 기본 명령어

docker run -d -p 3306:3306 -e MYSQL_ALLOW_EMPTY_PASSWORD=true --name mysql mysql:5.7
//도커로 mysql:5.7을 백그라운드로 실행하는데 
//포트 포워딩으로 호스트의 3306이랑 도커의 3306이랑 연결
//mysql5.7실행 시 필요한 설정을 -e옵션으로 주고
//이름을 mysql로 설정(아니면 랜덤)

실행 확인

docker ps -a

백그라운드라 로그가 안뜨는데, 지나간 로그를 보려면

docker logs 이름/컨테이너id

잘 뜬건 확인했고 terminal로 실행하려면

 docker exec -it mysql /bin/bash

그러면 터미널로 붙어서 쓰는것과 동일한 효과

여기서  mysql -uroot -p -h127.0.0.1 써서 접속해서 디비 사용하면 됨

삭제 시 중지하고 삭제 해야 

 

서비스를 jar이미지 만들어보기

0. 사용하고자 하는 이미지가 있으면 docker hub에서 확인

1. 아래 경로에 Dockerfile 작성

1-1. Dockerfile 내용물

FROM openjdk:17-ea-slim-buster
VOLUME /tmp
COPY target/user-service-0.0.1-SNAPSHOT.jar user-service.jar
ENTRYPOINT ["java", "-jar", "user-service.jar"]

2. 빌드

1. jar 최신화; Dockerfile 과 동일한 이름으로 jar 생성되었는지 확인 

 mvn clean compile package -DskipTests=true

2. 이미지 올릴 나의 docker hub 계정 확인

3. docker 이미지 만들라는 명령어 실행

맨 마지막에 . 찍어서 현재 폴더임을 나타냄 

 docker build --tag haileyjhbang/user-service:1.0 .

4. repository에 push

docker push haileyjhbang/user-service:1.0

1.0 태그 안주면 latest 를 찾기 때문에 에러가 남 

5. repository 확인 

 

만든 이미지 설치

1. 기존 이미지 삭제(확인 용)

docker rmi 4c828476b26a

2. 이미지 id로 삭제

 docker pull haileyjhbang/user-service:1.0

3. 실행

docker run haileyjhbang/user-service:1.0

728x90
반응형
반응형

환경: windows11

 

virtualization

  • 물리적인 컴퓨터 리소스를 다른 시스템이나 애플리케이션에서 사용할 수 있도록 제공
  • 플랫폼 가상화
  • 리소스 가상화

하이퍼바이저(hypervisor)

  • Virtual machine manager(VMM)
  • 다수의 운영체제를 동시에 실행하기 위한 논리적 플랫폼
  • type1(native / bare metal): 하드웨어에 직접 하이퍼바이저를 설치해서 가상화 운영
  • type2(hosted): 하드웨어 위에 os가 있고 그 위세 하이퍼바이저를 설치해서 가상화 운영
    • 보통 사용 방식

OS virtualization; os 가상화

  • host os 위에 guest os 전체를 가상화
  • VMware, VirtualBox
  • 자유도가 높으나 시스템에 부하가 많고 느려
  • 여러 vm을 띄우면 중복적인 리소스가 반복적으로 사용하게 될 수도 있음 

container virtualization; 컨테이너 가상화

  • host os가 가진 리소스를 적게 사용하며 필요한 프로세스 실행
  • 최소한의 라이브러리와 도구만 포함
  • container의 생성 속도 빠름
  • 중복 리소스 제거 가능. 도커 엔진이 가진 리소스 사용

컨테이너 이미지

  • 컨테이너 실행에 필요한 설정 값 모두
  • 이미지 안에 의존성을 이미 다 가지고 있기 때문에 별도 설치할게 없음 
  • 이미지를 가지고 실체화 한 것이 컨테이너 
  • 이미지 저장소: registry
    • public registry: docker hub
    • private registry 운영 가능 
  • 도커 호스트: 이미지를 실행할 수 있는 곳; 레지스트리에서 다운로드한 이미지를 실행 
    • run: create + start

dockerfile

  • 도커 이미지를 생성하기 위한 스크립트 파일 
  • 자체 문법 DSL(domain specific language) 언어 사용하여 이미지를 생성하기 위한 과정을 기술 
  • docker desktop -> docker container 사용

docker desktop download

docker cmd를 사용하기 위해서는 docker desktop을 실행하여 docker daemon을 실행하여야 함

run 해보고 이상없는지 확인하기 위해 아무 cmd 열어서 아래 명령어 입력

docker info

현재 docker가 가지고 있는 image 확인

docker image ls

현재 docker실행중인 container

docker container ls

명령어

  • create
  • start 실행
  • run = create + start 이미지 없으면 다운로드까지
  • tag: version같은 것; 혹은 용도별 마킹/없으면 자동으로 lastest
  • --name 이름 안 넣으면 랜덤 하게 됨; 중복 안됨, --rm 컨테이너 stop 하고 나서, -it iternative terminal,  --link....
  • 호스트=pc 

 

https://hub.docker.com/

 

Docker Hub Container Image Library | App Containerization

Increase your reach and adoption on Docker Hub With a Docker Verified Publisher subscription, you'll increase trust, boost discoverability, get exclusive data insights, and much more.

hub.docker.com

도커 이미지 관리; 다운로드 가능

docker pull ubuntu:16.04 //down
docker images | grep 16.04 //검색
docker run ubuntu:16.04 //실행 but 바로 종료
docker ps // 도커 컨테이너 실행중인것 확인
docker container ls -a //전체 히스토리 확인
docker container rm 컨테이너ID //컨테이너 삭제
728x90
반응형
반응형

환경: windows11, springboot2.7.6, java17

 

prometheus(저장 서버)

  • metrics를 수집하고 모니터링 및 알람에 사용되는 오픈소스 애플리케이션
  • 시간순으로 데이터가 남음(time series database; TSDB)
  • pull 방식의 구조와 다양한 metric exporter 제
  • 시계열 DB에 metrics 저장 -> 조회가능(query)

grafana(시각화)

  • 데이터 시각화, 모니터링 및 분석을 위한 오픈소스 애플리케이션
  • 시계열 데이터를 시각화하기 위한 대시보드 제공

 

prometheus 다운로드

https://prometheus.io/download/

 

os에 맞는 파일을 다운로드하고 압축을 푼다.

1. 폴더 안의 prometheus.yml 수정하여 수집하고자 하는 서비스를 등록 

8000번은 gateway

2. prometheus 실행 

9090포트로 실행됨

http://localhost:9090/graph

 

grafana 다운로드

https://grafana.com/grafana/download?platform=windows

 

os에 맞는 파일을 다운로드하고 압축을 푼다.

bin 폴더 안에 grafana-server.exe파일 실행(공식 문서에서 실행 가이드 확인)

http://localhost:3000/ (admin / admin으로 로그인)

 

연동

grafana 로그인 후

save & test 클릭

이미 다른사람들이 만들어 놓은 dashboard 불러오기

 

아래 사이트 방문하여 대시보드 다운로드

https://grafana.com/grafana/dashboards/?pg=docs-grafana-latest-dashboards

id를 복사하여 아래 창의 id 넣는 부분에 붙여 넣기 하면 import 가능 

아까 등록한 prometheus 선택하고 import

아래는 대표적인 대시보드

https://grafana.com/grafana/dashboards/3662-prometheus-2-0-overview/

https://grafana.com/grafana/dashboards/4701-jvm-micrometer/

https://grafana.com/grafana/dashboards/11506-spring-cloud-gateway/

 

import 후 metrics 설정을 다시 해야 제대로 된 결과가 나온다. 각 그래프를 edit 하여 지표를 최신화한다.

sum 부분에는 prometheus 에서 검색가능한 지표를 넣어야 하고

job 부분에는 아까 prometheus.yml 에 넣은 job name을 써야 한다.

비슷한 지표를 찾기위해 지구본으로 검색해야한다.. 매 버전?마다 다른 것 같다.

여기에 없는 것도 되긴 함.. 따로 찾아봐야 할 듯.

프로메테우스 서버 설정 참고

 

되는 거 하나.. 이거 설정하고 관련 서비스 api 몇 번 쏘고 새로고침하니까 나온다..

spring_cloud_gateway_requests_seconds_count{outcome="SUCCESSFUL", routeId=~"user-service", job=~"apigateway-service"}

728x90
반응형
반응형

환경: springboot2.7.6, java17

 

micrometer

  • jvm 기반의 애플리캐이션 metrics 제공
  • springboot2 +
  • premetheus 등 다양한 모니터링 시스템 지원
  • (구) turbine server -> hystrix client

 

timer

  • 짧은 지연 시간, 이벤트의 사용 빈도 측정
  • 시계열로 이벤트의 시간, 호출 빈도 등 제공
  • @Timed 제공

 

서비스 연동

1. dependency 추가

<!-- micrometer -->
<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-registry-prometheus</artifactId>
</dependency>

<!-- actuator -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator </artifactId>
</dependency>

2. application.yml actuator 정보 추가

management:
  endpoints:
    web:
      exposure:
        include: info, metrics, prometheus #추가

3. @Timed 어노테이션으로 metrics 마킹

    @GetMapping("/welcome")
    @Timed(value = "users.welcome", longTask = true)
    public String welcome(){
//        return environment.getProperty("greeting.message");
        return greeting.getMessage();
    }

4. 추가한 metrics는 사용을 하면 /actuator/metrics 에 표기됨

4. 그리고 실제 호출정보는 /actuator/premetheus 에 남음

728x90
반응형
반응형

환경: springboot2.7.6, spring cloud2021.0.8, java17

 

zipkin

  • 분산 환경의 데이터 수집, 추적 시스템(오픈소스, 트위터 시작, google drapper에서 발전)
  • 분산 환경에서의 시스템 병목 현상 파악
  • collector, query service, database, webui 로 구성
  • span : 하나의 요청에 사용되는 작업 단위; 64bit unique ID(in msa component)
  • trace: 트리 구조로 이뤄진 span set; 하나의 요청에 같은 trace ID 발급(in total flow)
  • spring cloud sleuth; zipkin 서버와 연동, trace/span id를 로그에 추가 가능 

 

다운로드

 

로컬 터미널로 실행을 하고 http://127.0.0.1:9411/zipkin/ 들어가면 웹화면이 나온다.

 

서비스에서 사용하기(양쪽 모두에 세팅)

1. dependency 추가

<!-- zipkin -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-sleuth-zipkin</artifactId>
</dependency>

2. application.yml 추가

spring:
  zipkin:
    base-url: http://localhost:9411
    enabled: true
  sleuth:
    sampler:
      probability: 1.0

3. 재시작하고 연동되어있는 api를 날려보면

첫번째 서버

2024-02-08 15:54:27.101  INFO [user-service,89bf1666da761e42,89bf1666da761e42] 27456 --- [o-auto-1-exec-5] c.e.userservice.service.UserServiceImpl  : before call orders msa
2024-02-08 15:54:27.210 DEBUG [user-service,89bf1666da761e42,cc4d64c75759fa06] 27456 --- [pool-4-thread-1] c.e.u.client.OrderServiceClient          : [OrderServiceClient#getOrders] ---> GET http://order-service/order-service/fcb59ec4-b2d6-4fd2-aefd-c1004542c801/orders HTTP/1.1
2024-02-08 15:54:27.211 DEBUG [user-service,89bf1666da761e42,cc4d64c75759fa06] 27456 --- [pool-4-thread-1] c.e.u.client.OrderServiceClient          : [OrderServiceClient#getOrders] ---> END HTTP (0-byte body)
2024-02-08 15:54:28.095 DEBUG [user-service,89bf1666da761e42,cc4d64c75759fa06] 27456 --- [pool-4-thread-1] c.e.u.client.OrderServiceClient          : [OrderServiceClient#getOrders] <--- HTTP/1.1 200 (883ms)
2024-02-08 15:54:28.095 DEBUG [user-service,89bf1666da761e42,cc4d64c75759fa06] 27456 --- [pool-4-thread-1] c.e.u.client.OrderServiceClient          : [OrderServiceClient#getOrders] connection: keep-alive
2024-02-08 15:54:28.095 DEBUG [user-service,89bf1666da761e42,cc4d64c75759fa06] 27456 --- [pool-4-thread-1] c.e.u.client.OrderServiceClient          : [OrderServiceClient#getOrders] content-type: application/json
2024-02-08 15:54:28.096 DEBUG [user-service,89bf1666da761e42,cc4d64c75759fa06] 27456 --- [pool-4-thread-1] c.e.u.client.OrderServiceClient          : [OrderServiceClient#getOrders] date: Thu, 08 Feb 2024 06:54:28 GMT
2024-02-08 15:54:28.096 DEBUG [user-service,89bf1666da761e42,cc4d64c75759fa06] 27456 --- [pool-4-thread-1] c.e.u.client.OrderServiceClient          : [OrderServiceClient#getOrders] keep-alive: timeout=60
2024-02-08 15:54:28.096 DEBUG [user-service,89bf1666da761e42,cc4d64c75759fa06] 27456 --- [pool-4-thread-1] c.e.u.client.OrderServiceClient          : [OrderServiceClient#getOrders] transfer-encoding: chunked
2024-02-08 15:54:28.096 DEBUG [user-service,89bf1666da761e42,cc4d64c75759fa06] 27456 --- [pool-4-thread-1] c.e.u.client.OrderServiceClient          : [OrderServiceClient#getOrders] 
2024-02-08 15:54:28.097 DEBUG [user-service,89bf1666da761e42,cc4d64c75759fa06] 27456 --- [pool-4-thread-1] c.e.u.client.OrderServiceClient          : [OrderServiceClient#getOrders] [{"productId":"Catalog3","qty":1,"unitPrice":2000,"totalPrice":2000,"createdAt":"2024-02-08T15:51:49","orderId":"17af8280-b028-42d6-b590-594578c75d4c"},{"productId":"Catalog3","qty":2,"unitPrice":2000,"totalPrice":4000,"createdAt":"2024-02-08T15:52:28","orderId":"765e3a28-c6ae-4e48-a4a3-dd2cc73d3caa"}]
2024-02-08 15:54:28.097 DEBUG [user-service,89bf1666da761e42,cc4d64c75759fa06] 27456 --- [pool-4-thread-1] c.e.u.client.OrderServiceClient          : [OrderServiceClient#getOrders] <--- END HTTP (303-byte body)
2024-02-08 15:54:28.138  INFO [user-service,89bf1666da761e42,89bf1666da761e42] 27456 --- [o-auto-1-exec-5] c.e.userservice.service.UserServiceImpl  : after call orders msa

user-service,89bf1666da761e42,89bf1666da761e42

user-service,89bf1666da761e42,cc4d64c75759fa06

  • traceId: 89bf1666da761e42
  • spanId: cc4d64c75759fa06
  • 처음엔 tId랑 같은걸로 시작했다가 새로운 커낵션이 맺어지면 새로운 spanId를 딴다.

 

두번째 서버

2024-02-08 15:54:27.567  INFO [order-service,89bf1666da761e42,730648c3fe5dad8a] 4968 --- [o-auto-1-exec-4] c.e.o.controller.OrderController         : before get orders
Hibernate: select order0_.id as id1_0_, order0_.created_at as created_2_0_, order0_.order_id as order_id3_0_, order0_.product_id as product_4_0_, order0_.qty as qty5_0_, order0_.total_price as total_pr6_0_, order0_.unit_price as unit_pri7_0_, order0_.user_id as user_id8_0_ from orders order0_ where order0_.user_id=?
2024-02-08 15:54:28.069  INFO [order-service,89bf1666da761e42,730648c3fe5dad8a] 4968 --- [o-auto-1-exec-4] c.e.o.controller.OrderController         : after call orders msa

order-service,89bf1666da761e42,730648c3fe5dad8a

  • traceId: 89bf1666da761e42
  • spanId: 730648c3fe5dad8a

4. 해당 값을 가지로 웹으로 들어가서 검색

http://localhost:9411/zipkin/traces/89bf1666da761e42

msa component, 시간 등등 해당 요청으로 연결된 모든 정보를 알 수 있다.

어떤 서비스에서 에러가 발생하면 아래처럼 에러를 표시해준다.

728x90
반응형
반응형

환경: springboot2.7.6, java11

 

circuit breaker

  • 장애가 발생하는 서비스에 반복적인 호출이 되지 못하게 차단
  • 특정 서비스가 정상적으로 동작하지 않을 경우 다른 기능으로 대체수행하여 장애를 회피함
  • open이 되었을 때 우회수행
  • spring cloud netflix hystrix(19년 deprecated)
  • resilience 4j ; 경량/자바 8 이상 지원

 

1. pom.xml 에 의존성 추가

<!-- resilience4j -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
</dependency>

2. open feign 을 통해 다른 msa component로 통신하던 부분을 circuit breaker로 변경

우선 기본적으로 제공하는 circuitBreakerFactory을 사용해본다.

//클래스 상단에 주입
    private final CircuitBreakerFactory circuitBreakerFactory;
    
    ...

//함수 안
		//open-feign with error decoder
        //orders = orderServiceClient.getOrders(userId);

        CircuitBreaker circuitBreaker =circuitBreakerFactory.create("circuitbreaker");
        orders = circuitBreaker.run(() -> orderServiceClient.getOrders(userId), throwable -> new ArrayList<>());

3. 이렇게 해두고 해당 msa component를 down 시킨상태에서 api 요청.

로그에는 접속 불가가 뜨지만 결과는 빈 array 반환됨

4. 기본 세팅말고 설정을 상세하게 바꾸고 싶다면 아래처럼 custom하여 빈으로 등록하면 됨

각 설정 의미는 공식 문서 참고

@Configuration
public class Resilience4Config {

    @Bean
    public Customizer<Resilience4JCircuitBreakerFactory> globalCustomConfig(){
        TimeLimiterConfig timeLimiterConfig = TimeLimiterConfig.custom().timeoutDuration(Duration.ofSeconds(4)).build();
        CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom()
                .failureRateThreshold(4)
                .waitDurationInOpenState(Duration.ofMillis(1000))
                .slidingWindowType(CircuitBreakerConfig.SlidingWindowType.COUNT_BASED) //기본값임
                .slidingWindowSize(2)
                .build();
        return factory -> factory.configureDefault(id -> new Resilience4JConfigBuilder(id)
                                                            .timeLimiterConfig(timeLimiterConfig)
                                                            .circuitBreakerConfig(circuitBreakerConfig)
                                                            .build());
    }
}
728x90
반응형
반응형

이전 작업:

2024.02.04 - [서버 세팅 & tool/kafka] - [windows] kakfa connect 연동

2024.02.08 - [개발/kafka] - [spring-kafka] producer, consumer 기초

환경: springboot 2.7.6, spring-kafka, java11

목표: 인스턴스 별로 하나씩 있던 디비를 공용 maria 디비로 전환, kafka connect 이용하여 source 쐈을 때 sink로 받아서 db에 저장

 

1. maria db 접속, 필요한 테이블 생성

2. pom.xml, application.yml 수정

<dependency>
    <groupId>org.mariadb.jdbc</groupId>
    <artifactId>mariadb-java-client</artifactId>
    <version>3.1.4</version>
</dependency>

3. kafka producer 추가

  • orders 이라는 sink connect로 전송
  • db에 데이터를 저장하는 것이라 kafka가 원하는 db 포맷으로 만들어야 함
@Service
@Slf4j
@RequiredArgsConstructor
public class OrderProducer {
    private final KafkaTemplate kafkaTemplate;

    private final List<Field> fields = Arrays.asList(new Field("string", true, "order_id"),
            new Field("string", true, "user_id"),
            new Field("string", true, "product_id"),
            new Field("int32", true, "qty"),
            new Field("int32", true, "unit_price"),
            new Field("int32", true, "total_price"));

    private final  Schema schema = Schema.builder().type("struct").fields(fields).optional(false).name("orders").build();

    public KafkaOrderDto send(String topic, OrderDto orderDto){

        Payload payload = Payload.builder().orderId(orderDto.getOrderId()).userId(orderDto.getUserId()).productId(orderDto.getProductId()).qty(orderDto.getQty()).unitPrice(orderDto.getUnitPrice()).totalPrice(orderDto.getTotalPrice()).build();
        KafkaOrderDto kafkaOrderDto = new KafkaOrderDto(schema, payload);

        ObjectMapper mapper = new ObjectMapper();
        String jsonString = "";
        try{
            jsonString = mapper.writeValueAsString(kafkaOrderDto);
        }catch (JsonProcessingException e){
            e.printStackTrace();
        }
        kafkaTemplate.send(topic, jsonString);
        log.info("kafka producer sent: {}" , kafkaOrderDto);
        return kafkaOrderDto;
    }
}

 

호출부, 여기서 앞부분이 sink topic이름

orderProducer.send("orders", orderDto);

4. kafka sink connector 추가

마리아 디비랑 연결되는 토픽이 orders 라고 정의하고 등록하는 것임

잘 생성되었나 확인

 

5. 이렇게 되면 해당 인스턴스가 여러가 떠 있어도 어떤 인스턴스가 디비 수정 건을 받았건 상관없이 모두 마리아 디비로 들어가서 단일로 관리가 가능

 

728x90
반응형

'개발 > kafka' 카테고리의 다른 글

[spring-kafka] producer, consumer 기초  (0) 2024.02.08
반응형

환경: springboot2.7.6, java11, h2 연결 

 

producer 정보 보내는 쪽

1. pom.xml 추가

<!-- kafka -->
<dependency>
    <groupId>org.springframework.kafka</groupId>
    <artifactId>spring-kafka</artifactId>
</dependency>

 

2. producer kafka 연결 설정

@EnableKafka
@Configuration
public class KafkaProducerConfig {

    @Bean
    public ProducerFactory<String, String> producerFactory(){
        Map<String, Object> properties = new HashMap<>();

        properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "127.0.0.1:9092");
        properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
        properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);

        return new DefaultKafkaProducerFactory<>(properties);
    }

    @Bean
    public KafkaTemplate<String, String> kafkaTemplate(){
        return new KafkaTemplate<>(producerFactory());
    }
}

 

3. 카프카를 사용하여 전송하려는 메세지 함수 설정

@Service
@Slf4j
@RequiredArgsConstructor
public class KafkaProducer {
    private final KafkaTemplate<String, String> kafkaTemplate;

    //tojson
    public OrderDto send(String kafkaTopic, OrderDto orderDto){
        ObjectMapper mapper = new ObjectMapper();
        String jsonString = "";
        try{
            jsonString = mapper.writeValueAsString(orderDto);
        }catch (JsonProcessingException e){
            e.printStackTrace();
        }
        kafkaTemplate.send(kafkaTopic, jsonString);
        log.info("kafka producer sent: {}" , orderDto);
        return orderDto;
    }
}

 

 

consumer 정보 받는 

1. 동일

2. consumer kafka 연결 설정

@EnableKafka
@Configuration
public class KafkaConsumerConfig {

    @Bean
    public ConsumerFactory<String, String> consumerFactory(){
        Map<String, Object> properties = new HashMap<>();

        properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "127.0.0.1:9092");
        properties.put(ConsumerConfig.GROUP_ID_CONFIG, "consumerGroupId");//consumer grouping
        properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
        properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);

        return new DefaultKafkaConsumerFactory<>(properties);
    }

    @Bean
    public ConcurrentKafkaListenerContainerFactory<String, String> kafkaListenerContainerFactory(){
        ConcurrentKafkaListenerContainerFactory<String, String> kafkaListenerContainerFactory = new ConcurrentKafkaListenerContainerFactory<>();
        kafkaListenerContainerFactory.setConsumerFactory(consumerFactory());
        return kafkaListenerContainerFactory;
    }
}

 

3. 받는 listener 설정

@Service
@Slf4j
@RequiredArgsConstructor
public class KafkaConsumer {
    private final CatalogRepository catalogRepository;

    @KafkaListener(topics = "example-catalog-topic") //데이터가 전달되면 가져와서 실행
    public void updateQty(String kafkaMessage){
        log.info("kafka message: {}", kafkaMessage);
        Map<String, Object> map = new HashMap<>();
        ObjectMapper mapper = new ObjectMapper();
        try {
            map = mapper.readValue(kafkaMessage, new TypeReference<Map<String, Object>>() {});
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }

        Catalog catalog = catalogRepository.findByProductId((String)map.get("productId"));
        if(catalog != null){
            catalog.setStock(catalog.getStock() - (Integer)map.get("qty"));
            catalogRepository.save(catalog);
        }
    }
}
728x90
반응형
반응형

환경: springboot2.7.6(hibernate core 5.6.14), spring-jpa, java 11, mysql5.7

 

spring jpa를 이용하여 아래 쿼리를 질의하려고 할 때

select (@rank := @rank +1) as ranking,
hutw.* 
from user_stat_weekly hutw, (select @rank := 0) as ranking
where hutw.start_date ='2024-02-05'
order by hutw.prize_total desc

 

@를 사용한 사용자 변수는 native query를 사용해야 한다.

 

허나 그냥 사용하면 아래와 같은 에러가 발생한다.

Caused by: org.hibernate.QueryException: Space is not allowed after parameter prefix ':'

:= @rank 이 부분에서 발생하는 것인데, 하이버네이트 버전에 따라 역슬래시를 하나나 두 개 넣어야 한다고 한다.

내가 사용하는 버전에서는 아래처럼 두 개 넣었더니 성공한다.

SELECT (@rank \\:= @rank +1) ...

 

잘 지나가나 싶어서 실행해 보면 아래 에러가 발생하는데, 해당 에러는 native query 결과를 object에 세팅할 때 class를 매핑해서 나는 것으로 projection interface로 바꾸면 해결된다.

No converter found capable of converting from type [org.springframework.data.jpa.repository.query.AbstractJpaQuery$TupleConverter$TupleBackedMap] to type
728x90
반응형
반응형

환경: mysql 5.7

 

mysql 8버전부터 rank over 함수가 들어오면서 드디어! 편한 함수의 세상이 열리긴 한다만..

아직 5.7을 사용하고 있기에 그 이하 버전에 등수를 구하는 방법을 알아야 한다.

서버에 부하가 가지 않고 제일 좋은 방법은 집계할 때 등수를 같이 쌓아주어 그냥 select 해가면 되는 것이지만..

서버는 그닥 user-friendly 하지 않다. 그리고 기획자는 서버에 없는 것만 요청한다..

 

1. count 사용 서브쿼리

select * from (
	select 
    (select count(*) +1 from table for_rank where for_rank.start_date ='2023-02-06' and for_rank.prize_total > this.prize_total) as r,
	this.* 
    from table this 
	where this.start_date ='2023-02-06'
	order by prize_total  desc
) as with_rank
where id = 'tbot0275'

2. @사용자 정의 변수 사용

select * from (
	select (@rank := @rank +1) as r,
	this.* 
	from table this, (select @rank := 0) as ranking
	where this.start_date ='2023-02-06'
	order by prize_total desc
)as weekly_with_rank
where id = 'tbot0275'

 

허나 두 쿼리의 결과가 다를 수 있다. 왜냐면 동점자를 다루는 방식이 다르기 때문.

위 이미지에서처럼 1번의 경우 동점자를 같은 등수로 치게 되고 2번의 경우는 어쨌건 한 줄 서기라 조회에 따라 랜덤 하게 등수가 나올 수 있다.

 

728x90
반응형

'개발 > sql' 카테고리의 다른 글

DB isolation level  (0) 2024.05.22
[mysql] merge into..?  (0) 2024.05.17
[DB] 분산환경에서 데이터 저장소 선택과 활용  (0) 2023.07.24
[형상관리] flyway vs liquibase  (0) 2022.07.08
[mysql] jsonpath  (0) 2022.05.27

+ Recent posts