5장 마이크로서비스 도구 다루기

sua·2022년 9월 3일
0
post-thumbnail

UI를 추출하고 게임화 서비스와 연결하기

곱셈 마이크로서비스의 정적 콘텐츠를 추출하는 이유

  1. 상호 의존성 -> 게임화 API를 수정하면 곱셈 서비스도 수정하고 다시 배포해야 하는 문제
  2. 유연하게 확장 불가능

[솔루션]


정적 콘텐츠 옮기기

  1. 웹서버인 제티 다운로드 (https://www.eclipse.org/jetty/download.php)
  2. 루트 폴더에 ui 폴더 만들기
  3. ui 폴더 안에 start.d 폴더 만들고 deploy.ini와 http.ini 넣기
  4. ui 폴더 안에 webapps 폴더 안에 ui 폴더를 만들고 webapps 폴더 안에 만들었던 정적 콘텐츠 넣기
  5. http.ini 파일을 텍스트 에디터로 열고 jetty.http.port를 9090으로 변경
# jetty.http.port=9090
  1. UI 폴더에서 제티 서버 실행
java -jar C:\Users\sua\Desktop\jetty-distribution-9.4.48.v20220622/start.jar
  1. http://localhost:9090/ui/index.html 로 접속

UI와 게임화 서비스 연결

  1. gamification-client.js 만들기
function updateLeaderBoard() {
  $.ajax({
    url: "http://localhost:8081/leaders"
  }).then(function (data) {
    $('#leaderboard-body').empty();
    data.forEach(function (row) {
      $('#leaderboard-body').append('<tr><td>' + row.userId + '</td>' +
        '<td>' + row.totalScore + '</td>');
    });
  });
}

function updateStats(userId) {
  $.ajax({
    url: "http://localhost:8081/stats?userId=" + userId,
    success: function (data) {
      $('#stats-div').show();
      $('#stats-user-id').empty().append(userId);
      $('#stats-score').empty().append(data.score);
      $('#stats-badges').empty().append(data.badges.join());
    },
    error: function (data) {
      $('#stats-div').show();
      $('#stats-user-id').empty().append(userId);
      $('#stats-score').empty().append(0);
      $('#stats-badges').empty();
    }
  });
}

$(document).ready(function () {

  updateLeaderBoard();

  $("#refresh-leaderboard").click(function (event) {
    updateLeaderBoard();
  });

});

기존 서비스 수정

  1. CORS 문제 발생
  2. 백엔드 서비스에서 CORS를 활성화 하기
  3. WebConfiguration 생성
package microservices.book.gamification.configuration;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

@Configuration
@EnableWebMvc
public class WebConfiguration extends WebMvcConfigurerAdapter {

  /**
   * CORS(Cross-Origin Resource Sharing) 설정
   * 자세한 정보 : http://docs.spring.io/spring/docs/current/spring-framework-reference/html/cors.html
   *
   * @param registry
   */
  @Override
  public void addCorsMappings(final CorsRegistry registry) {
    registry.addMapping("/**");
  }

}
package microservices.book.multiplication.configuration;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

@Configuration
@EnableWebMvc
public class WebConfiguration extends WebMvcConfigurerAdapter {

  /**
   * CORS(Cross-Origin Resource Sharing) 설정
   * 자세한 정보 : http://docs.spring.io/spring/docs/current/spring-framework-reference/html/cors.html
   *
   * @param registry
   */
  @Override
  public void addCorsMappings(final CorsRegistry registry) {
    registry.addMapping("/**");
  }

}

(거의) 노력 없이 새로고 더 나은 UI 만들기

  1. index.html 부트스트랩 추가하기
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Multiplication v1</title>
  <link href="css/bootstrap.min.css" rel="stylesheet">
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
  <script src="multiplication-client.js"></script>
  <script src="gamification-client.js"></script>
  <meta name="viewport" content="width=device-width, initial-scale=1">
</head>

<body>
<div class="container">
  <div class="row">
    <div class="col-md-12">
      <h1 class="text-center">안녕하세요, 소셜 곱셈입니다!</h1>
    </div>
  </div>
  <div class="row">
    <div class="col-md-6">
      <h3 class="text-center">오늘의 문제:</h3>
      <h1 class="text-center">
        <span class="multiplication-a"></span> x <span class="multiplication-b"></span>
      </h1>
      <p>
      <form id="attempt-form">
        <div class="form-group">
          <label for="result-attempt">답은?</label>
          <input type="text" name="result-attempt" id="result-attempt" class="form-control">
        </div>
        <div class="form-group">
          <label for="user-alias">닉네임:</label>
          <input type="text" name="user-alias" id="user-alias" class="form-control">
        </div>
        <input type="submit" value="확인" class="btn btn-default">
      </form>
      </p>
      <div class="result-message"></div>

      <div id="stats-div" style="display: none;">
        <h2>통계</h2>
        <table id="stats" class="table">
          <tbody>
          <tr>
            <td class="info">사용자 ID:</td>
            <td id="stats-user-id"></td>
          </tr>
          <tr>
            <td class="info">점수:</td>
            <td id="stats-score"></td>
          </tr>
          <tr>
            <td class="info">배지:</td>
            <td id="stats-badges"></td>
          </tr>
          </tbody>
        </table>
      </div>

    </div>
    <div class="col-md-6">
      <h3>리더보드</h3>
      <table id="leaderboard" class="table">
        <tr>
          <th>사용자 ID</th>
          <th>점수</th>
        </tr>
        <tbody id="leaderboard-body"></tbody>
      </table>
      <div class="text-right">
        <button id="refresh-leaderboard" class="btn btn-default" type="submit">새로고침</button>
      </div>

      <div id="results-div" style="display: none;">
        <h2>최근 답안</h2>
        <table id="results" class="table">
          <tr>
            <th>답안 ID</th>
            <th>곱셈</th>
            <th>입력한 값</th>
            <th>정답?</th>
          </tr>
          <tbody id="results-body"></tbody>
        </table>
      </div>
    </div>
  </div>
</div>
<script src="js/bootstrap.min.js"></script>
</body>
</html>

  1. multiplication-client.js 수정
function updateMultiplication() {
  $.ajax({
    url: "http://localhost:8080/multiplications/random"
  }).then(function (data) {
    // 폼 비우기
    $("#attempt-form").find("input[name='result-attempt']").val("");
    $("#attempt-form").find("input[name='user-alias']").val("");
    // 무작위 문제를 API로 가져와서 추가하기
    $('.multiplication-a').empty().append(data.factorA);
    $('.multiplication-b').empty().append(data.factorB);
  });
}

function updateResults(alias) {
  var userId = -1;
  $.ajax({
    async: false,
    url: "http://localhost:8080/results?alias=" + alias,
    success: function (data) {
      $('#results-div').show();
      $('#results-body').empty();
      data.forEach(function (row) {
        $('#results-body').append('<tr><td>' + row.id + '</td>' +
          '<td>' + row.multiplication.factorA + ' x ' + row.multiplication.factorB + '</td>' +
          '<td>' + row.resultAttempt + '</td>' +
          '<td>' + (row.correct === true ? 'YES' : 'NO') + '</td></tr>');
      });
      userId = data[0].user.id;
    }
  });
  return userId;
}

$(document).ready(function () {

  updateMultiplication();

  $("#attempt-form").submit(function (event) {

    // 폼 기본 제출 막기
    event.preventDefault();

    // 페이지에서 값 가져오기
    var a = $('.multiplication-a').text();
    var b = $('.multiplication-b').text();
    var $form = $(this),
      attempt = $form.find("input[name='result-attempt']").val(),
      userAlias = $form.find("input[name='user-alias']").val();

    // API 에 맞게 데이터를 조합하기
    var data = {user: {alias: userAlias}, multiplication: {factorA: a, factorB: b}, resultAttempt: attempt};

    // POST 로 데이터 보내기
    $.ajax({
      url: 'http://localhost:8080/results',
      type: 'POST',
      data: JSON.stringify(data),
      contentType: "application/json; charset=utf-8",
      dataType: "json",
      async: false,
      success: function (result) {
        if (result.correct) {
          $('.result-message').empty()
            .append("<p class='bg-success text-center'>정답입니다! 축하드려요!</p>");
        } else {
          $('.result-message').empty()
            .append("<p class='bg-danger text-center'>오답입니다! 그래도 포기하지 마세요!</p>");
        }
      }
    });

    updateMultiplication();

    setTimeout(function () {
      var userId = updateResults(userAlias);
      updateStats(userId);
      updateLeaderBoard();
    }, 300);
  });
});

서비스 디스커버리와 로드 밸런싱

서비스 디스커버리

  1. 서비스 레지스트리 : 모든 서비스 인스턴스와 인스턴스의 이름을 추적
  2. 레지스터 에이전트 : 모든 서비스가 서로를 찾을 수 있도록 설정을 제공하는 에이전트
  3. 서비스 디스커버리 클라이언트 : 서비스 레지스트리에서 별칭으로 서비스를 조회

로드 밸런싱

게임화 마이크로서비스가 클라이언트로써 http://multiplication/ 에 접속하려고 하면 유레카는 두 URL을 모두 반환하고 소비자가 어떤 인스턴스를 호출할지 결정


폴리글랏 시스템, 유레카, 리본

[자바가 아닌 언어로 게임화 마이크로서비스를 작성한 가상의 상황]

  • 사이드카 마이크로서비스(5678 포트)는 프록시처럼 동작. 애플리케이션이 엔드포인트를 노출해 해당 서비스가 발견되고 상태를 체크
  • 유레카로 자바가 아닌 애플리케이션의 인스턴스를 등록하고, 레지스트리 클라이언트로 다른 마이크로서비스의 가용한 인스턴스를 얻음
  • 애플리케이션은 사이드카의 API에 접근해 다른 서비스의 인스턴스를 찾음

API 게이트웨이와 라우팅

API 게이트웨이 패턴

소비자가 마이크로서비스의 설계를 알고 있다는 문제
-> 내부 구조를 드러내지 않는 REST API를 만들어야 함

[API 게이트웨이 패턴 적용한 솔루션]

-> 이 패턴은 기존의 일체형을 마이크로서비스 아키텍처로 단계별로 분할하고 진화시키는 완벽한 방법
-> 장점 : 서비스 디스커버리나 로드 밸런싱을 필요로 하지 않음


함께 동작하는 주울, 유레카, 리본

[솔루션]

  • 요청을 라우팅할 때 API 게이트웨이 마이크로서비스만 사용 -> 게임화와 곱셈 마이크로서비스는 서로를 알지 못하게 유지

코드 작성

API 게이트웨이와 주울 구현

  1. 새로운 스프링 부트 애플리케이션 만들기

  1. 의존성 추가
implementation("org.springframework.cloud:spring-cloud-starter-netflix-zuul:2.1.2.RELEASE")

  1. 메인 클래스에 @EnableZuulProxy 애너테이션 추가
package microservices.book.gateway;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;

@EnableZuulProxy
@SpringBootApplication
public class GatewayApplication {

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

}
  1. application.yml 작성
server:
  port: 8000

zuul:
  prefix: /api
  routes:
    multiplications:
      path: /multiplications/**
      url: http://localhost:8080/multiplications
    results:
      path: /results/**
      url: http://localhost:8080/results
    leaders:
      path: /leaders/**
      url: http://localhost:8081/leaders
    stats:
      path: /stats/**
      url: http://localhost:8081/stats

endpoints:
  trace:
    sensitive: false

ribbon:
  eureka:
    enabled: false

[주울의 매핑 결과]


  1. gamification-client.js/multiplication-client.js 서버 URL 수정

var SERVER_URL = "http://localhost:8000/api";

  1. application.properties 서버 URL 수정
# REST 클라이언트 설정
multiplicationHost=http://localhost:8000/api

[시스템 실행 단계]
1. RabbitMQ 서버를 실행
2. 게이트웨이 마이크로서비스를 실행
3. 곱셈 마이크로서비스를 실행
4. 게임화 마이크로서비스를 실행
5. ui 루트 폴더에서 제티 웹 서버를 실행

[시스템 현재 상태]


서비스 디스커버리 구현

  1. 서비스 레지스트리 만들기

  1. 애플리케이션 클래스에 @EnableEurekaServer 애너테이션 추가
plugins {
	id 'org.springframework.boot' version '2.7.3'
	id 'io.spring.dependency-management' version '1.0.13.RELEASE'
	id 'java'
}

group = 'microservices.book'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

repositories {
	mavenCentral()
}

ext {
	set('springCloudVersion', "2020.0.1")
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
	implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-server'
}

dependencyManagement {
	imports {
		mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
	}
}


tasks.named('test') {
	useJUnitPlatform()
}
package microservices.book.serviceregistry;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@EnableEurekaServer
@SpringBootApplication
public class ServiceRegistryApplication {

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

}
  1. application.properties 설정
server.port=8761

  1. build.gradle 설정
implementation 'org.springframework.boot:spring-boot-starter-actuator'

  1. GameficationApplication 수정
package microservices.book.gamification;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@EnableEurekaClient
@SpringBootApplication
public class GamificationApplication {

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

}

  1. application.properties 설정 (gamificatin)
# 서비스 디스커버리 설정
eureka.client.service-url.default-zone=http://localhost:8761/eureka/

  1. bootstrap.properties 추가 (gamification)
spring.application.name=gamification

  1. bootstrap.properties 추가 (service-registry)
spring.application.name=service-registry

  1. application.yml 수정 (gateway)
server:
  port: 8000

zuul:
  ignoredServices: '*'
  prefix: /api
  routes:
    multiplications:
      path: /multiplications/**
      serviceId: multiplication
      strip-prefix: false
    results:
      path: /results/**
      serviceId: multiplication
      strip-prefix: false
    leaders:
      path: /leaders/**
      serviceId: gamification
      strip-prefix: false
    stats:
      path: /stats/**
      serviceId: gamification
      strip-prefix: false

endpoints:
  routes:
    sensitive: false
  trace:
    sensitive: false

eureka:
  client:
    service-url:
      default-zone: http://localhost:8761/eureka/

데이터베이스와 무상태 서비스

애플리케이션이 너무 복잡해지는 것을 피하려면 항상 상태를 저장하지 않는 마이크로서비스를 설계하는 것이 좋음

  1. 곱셈과 게임화 서비스의 JDBC URL 수정
spring.datasource.url=jdbc:h2:mem:testdb;MODE=MYSQL;AUTO_SERVER=TRUE;

이벤트 중심 아키텍처와 로드 밸런싱

RabbitMQ는 클러스터에서도 동작함
[여러 인스턴스로 동작하는 시스템]


리본으로 로드 밸런싱하기

  1. MultiplicationController 로그 추가
package microservices.book.multiplication.controller;

import lombok.extern.slf4j.Slf4j;
import microservices.book.multiplication.domain.Multiplication;
import microservices.book.multiplication.service.MultiplicationService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 곱셈 애플리케이션의 REST API 를 구현한 클래스
 */
@Slf4j
@RestController
@RequestMapping("/multiplications")
final class MultiplicationController {

  private final MultiplicationService multiplicationService;

  private final int serverPort;

  @Autowired
  public MultiplicationController(final MultiplicationService multiplicationService, @Value("${server.port}") int serverPort) {
    this.multiplicationService = multiplicationService;
    this.serverPort = serverPort;
  }

  @GetMapping("/random")
  Multiplication getRandomMultiplication() {
    log.info("무작위 곱셈을 생성한 서버 @ {}", serverPort);
    return multiplicationService.createRandomMultiplication();
  }

}

  1. MultiplicatiionResultAttemptController 로그 추가
package microservices.book.multiplication.controller;

import lombok.extern.slf4j.Slf4j;
import microservices.book.multiplication.domain.MultiplicationResultAttempt;
import microservices.book.multiplication.service.MultiplicationService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.Optional;

/**
 * 사용자가 POST 로 답안을 전송하도록 REST API 를 제공하는 클래스
 */
@Slf4j
@RestController
@RequestMapping("/results")
final class MultiplicationResultAttemptController {

    private final MultiplicationService multiplicationService;

    private final int serverPort;

    @Autowired
    MultiplicationResultAttemptController(
            final MultiplicationService multiplicationService,
            @Value("${server.port}") int serverPort) {
        this.multiplicationService = multiplicationService;
        this.serverPort = serverPort;
    }

    @PostMapping
    ResponseEntity<MultiplicationResultAttempt> postResult(@RequestBody MultiplicationResultAttempt multiplicationResultAttempt) {
        boolean isCorrect = multiplicationService.checkAttempt(multiplicationResultAttempt);
        MultiplicationResultAttempt attemptCopy = new MultiplicationResultAttempt(
                multiplicationResultAttempt.getUser(),
                multiplicationResultAttempt.getMultiplication(),
                multiplicationResultAttempt.getResultAttempt(),
                isCorrect
        );
        return ResponseEntity.ok(attemptCopy);
    }

    @GetMapping
    ResponseEntity<List<MultiplicationResultAttempt>> getStatistics(@RequestParam("alias") String alias) {
        return ResponseEntity.ok(
                multiplicationService.getStatsForUser(alias)
        );
    }

    @GetMapping("/{resultId}")
    ResponseEntity<Optional<MultiplicationResultAttempt>> getResultById(final @PathVariable("resultId") Long resultId) {
        log.info("조회 결과 {} 조회한 서버 @ {}", resultId, serverPort);
        return ResponseEntity.ok(
                multiplicationService.getResultById(resultId)
        );
    }

}

로드밸런싱 전략을 미세 조정하기

리본은 서비스에 핑을 보내고 결과에 따라 로드 밸런싱하는 기능이 있음
-> 스프링 빈 두 개를 설정해야 함 (기본 상태 체크 메커니즘을 변경하는 IPing과 기본 로드 밸런싱 전략을 수정하는 IRule)
-> 메인 클래스인 GatewayApplication이 애너테이션으로 설정 클래스를 가리키게 함

  1. RibbonConfiguration 추가 (gateway)
package microservices.book.gateway.configuration;

import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.*;
import org.springframework.context.annotation.Bean;

public class RibbonConfiguration {

  @Bean
  public IPing ribbonPing(final IClientConfig config) {
    return new PingUrl(false, "/health");
  }

  @Bean
  public IRule ribbonRule(final IClientConfig config) {
    return new AvailabilityFilteringRule();
  }

}
  1. GatewayApplication 수정 (gateway)
package microservices.book.gateway;

import microservices.book.gateway.configuration.RibbonConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.ribbon.RibbonClients;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;


@EnableZuulProxy
@EnableEurekaClient
@RibbonClients(defaultConfiguration = RibbonConfiguration.class)
@SpringBootApplication
public class GatewayApplication {

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

[서비스 디스커버리와 로드 밸런싱을 준비한 논리적인 관점]


서킷 브레이커와 REST 클라이언트

하이트릭스와 주울

주울이 요청을 리다이렉트하지 못하면 특정 서비스에 대한 폴백이 있는지 확인 -> 폴백이 있으면 기본 응답을 구성해서 반환

  1. HystrixFallbackConfiguration 생성 (gateway)
package microservices.book.gateway.configuration;

import org.springframework.cloud.netflix.zuul.filters.route.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpResponse;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;

@Configuration
public class HystrixFallbackConfiguration {

    @Bean
    public FallbackProvider zuulFallbackProvider() {
        return new FallbackProvider() {

            @Override
            public String getRoute() {
                // 헷갈릴 수 있는데 serviceId 프로퍼티이지 경로가 아님
                return "multiplication";
            }

            @Override
            public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
                return new ClientHttpResponse() {
                    @Override
                    public HttpStatus getStatusCode() throws IOException {
                        return HttpStatus.OK;
                    }

                    @Override
                    public int getRawStatusCode() throws IOException {
                        return HttpStatus.OK.value();
                    }

                    @Override
                    public String getStatusText() throws IOException {
                        return HttpStatus.OK.toString();
                    }

                    @Override
                    public void close() {
                    }

                    @Override
                    public InputStream getBody() throws IOException {
                        return new ByteArrayInputStream("{\"factorA\":\"죄송합니다, 서비스가 중단되었습니다!\",\"factorB\":\"?\",\"id\":null}".getBytes());
                    }

                    @Override
                    public HttpHeaders getHeaders() {
                        HttpHeaders headers = new HttpHeaders();
                        headers.setContentType(MediaType.APPLICATION_JSON);
                        headers.setAccessControlAllowCredentials(true);
                        headers.setAccessControlAllowOrigin("*");
                        return headers;
                    }
                };
            }
            
        };
    }
}

REST 클라이언트의 하이스트릭스

  1. 의존성 추가
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-hystrix'

  1. MultiplicationAttemptClientImpl에 하이스트릭스 추가
package microservices.book.gamification.client;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import microservices.book.gamification.client.dto.MultiplicationResultAttempt;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

/**
 * Multiplication 마이크로서비스와 REST 로 연결하기 위한
 * MultiplicationResultAttemptClient 인터페이스의 구현체
 */
@Component
class MultiplicationResultAttemptClientImpl implements MultiplicationResultAttemptClient {

    private final RestTemplate restTemplate;
    private final String multiplicationHost;

    @Autowired
    public MultiplicationResultAttemptClientImpl(final RestTemplate restTemplate,
                                                 @Value("${multiplicationHost}") final String multiplicationHost) {
        this.restTemplate = restTemplate;
        this.multiplicationHost = multiplicationHost;
    }

    @HystrixCommand(fallbackMethod = "defaultResult")
    @Override
    public MultiplicationResultAttempt retrieveMultiplicationResultAttemptbyId(final Long multiplicationResultAttemptId) {
        return restTemplate.getForObject(
                multiplicationHost + "/results/" + multiplicationResultAttemptId,
                MultiplicationResultAttempt.class);
    }

    private MultiplicationResultAttempt defaultResult(final Long multiplicationResultAttemptId) {
        return new MultiplicationResultAttempt("fakeAlias",
                10, 10, 100, true);
    }

    @Override
    public MultiplicationResultAttempt retrieveMultiplicationResultAttemptById(Long multiplicationId) {
        return null;
    }
}

  1. 메인 클래스에 @EnableCircuitBreakder 애너테이션 추가
package microservices.book.gamification;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@EnableEurekaClient
@EnableCircuitBreaker
@SpringBootApplication
public class GamificationApplication {

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

}
profile
가보자고

0개의 댓글