Micro Service/mSVC개발

[SC14] Spring Cloud LoadBalancer 란 ?

Happy@Cloud 2021. 2. 22. 19:38

1. Spring Cloud LoadBalancer(SCL) 이해

1.1 WHY ?

Spring Cloud LoadBalancer(이하 SCL)는 Spring Cloud Ribbon과 동일한 Client-side Load Balancer입니다. 

Spring Cloud Ribbon편에서 얘기했듯이 Client side Load balancer가 필요한 이유는

부하분산을 적절하게 하여 서비스의 가용성을 최대화하기 위합입니다. 

또한, 2018년 12월 이후 Ribbon은 EOS되었기 때문에 Spring Cloud LoadBalancer를 사용하는것이 좋습니다. 

 

1.2 HOW ?

1) Netflix Ribbon vs Spling Cloud LoadBalancer 

- 지원 HttpClient

Ribbon은 Blocking방식의 HttpClient인 RestTemplate만 지원합니다. 

반면, SCL은 RestTemplate뿐 아니라, Non-blocking방식을 지원하는 Spring WebClient도 지원합니다. 

Spring WebClient를 이해하지 않고 이후 실습을 진행하기 어려우니, 먼저 Spring WebClient편을 학습하고 진행하십시오. 

 

- Load Balancing 정책

Ribbon은 RoundRobin, AvailabilityFilteringRule, WeightedResponseTimeRule의 3가지 정책을 지원합니다.

SCL은 RoundRobin과 Random정책만 지원합니다.  

 

2) 아키텍처

SCL은 Spring Cloud Common프로젝트의 일부이며 매뉴얼은 아래에 있습니다. 

docs.spring.io/spring-cloud-commons/docs/current/reference/html/#spring-cloud-loadbalancer

SCL의 아키텍처는 Ribbon과 동일합니다. 

아래와 같이 Eureka와 같은 Service Registry와 연동하여 대상 서버의 리스트를 얻고, 

L/B정책에 따라 연결할 대상 어플리케이션을 결정합니다. 

SCL이 결정한 서버의 정보를 HttpClient(예: RestTemplate, WebClient)에 전달하여 대상 어플리케이션을 호출합니다.   

출처: https://www.nginx.com/blog/service-discovery-in-a-microservices-architecture/

 

좀 더 SCL의 아키텍처를 자세히 그리면 아래와 같습니다. 

- Client의 요청은 EventLoop에 Job으로 등록됩니다. EventLoop는 WebClient의 Non-blocking처리를 위한 중계자입니다. 

- WebClient의 filter로 주입된 ReactorLoadBalancerExchangeFilterFunction은 Service Instance목록을 얻기 위해 

ServiceInstanceListSupplier으로 생성된 Bean이 있는지 먼저 체크합니다.

없으면, Service Registry 서버에서 Service Instance 목록을 구합니다.  

구해진 service instance목록에서 L/B정책(Default는 RoundRobin)에 따라 연결할 Instance를 결정합니다. 

결정된 instance의 주소를 WebClient에 전달합니다.

- WebClient는 SCL이 제공한 instance의 주소로 호출합니다. 

- 응답이 오면 Event Loop는 그 결과를 Client에 리턴합니다. 

 

 

중요) spring.cloud.loadbalancer.ribbon.enabled=false로 하여야 SCL이 로딩됩니다. 

 

 

2. Spring Cloud LoadBalancer 실습

요청자앱 webclient와 제공자앱 webserver를 만들어 실습하겠습니다. 

2.1 WebServer 개발

1) 프로젝트 생성

vscode에서 java project를 처음 만드시는분은 Spring Cloud Gateway편의 '2. SCG서버 개발 > 1) 프로젝트 생성'부분을 참고하십시오,. 

아래와 같이 프로젝트를 생성합니다. 

- SpringBoot 버전: 2.3.9

- Group ID: com.springcloud

- Artifact ID: webserver

- Packaging Type: Jar

- Java Version: 1.8이상 

- Dependency: Spring Web

- 맨 마지막 폴더 선택은 프로젝트를 생성할 상위 디렉토리로 지정하십시오. 

2) pom.xml 수정

Description을 수정하고, springcloud dependency, dependencyManagement, finalName을 추가합니다. 

<?xml version="1.0" encoding="UTF-8"?>
<project ...>
	...
	<description>Demo project for Spring Cloud Loadbalancer</description>
	<properties>
		<java.version>1.8</java.version>
		<spring-cloud.version>Hoxton.SR9</spring-cloud.version>
	</properties>
	<dependencies>
		...
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-actuator</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-config</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-bus-amqp</artifactId>
		</dependency>
	</dependencies>

	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>${spring-cloud.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

	<build>
		<finalName>webserver</finalName>
		...
	</build>

</project>

 

3) Main class 수정

@SpringBootApplication
@EnableDiscoveryClient
public class WebserverApplication {

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

}


4) bootstrap.yaml 작성

application.properties의 파일명을 bootstrap.yaml로 변경하고, 아래 내용을 추가합니다. 

server:
  port: ${service_port:5011}

spring:
  application:
    name: webserver
  profiles:
    active: ${profile:local}
    include: common 
  cloud: 
    config:
      uri: ${config_servers:http://localhost:9001}
      searchPaths: ${spring.application.name}
      default-label: main

 

5) WebserverController 작성

 아래와 같이 '/webclient/{param}' API를 개발합니다. 

@RestController
public class WebserverController {
    private final Logger log = LoggerFactory.getLogger(getClass());
	@Value("${server.port}")
	int port;

    @GetMapping("/webclient/{param}")
	public String testWebClient(
		@PathVariable String param, 
		@RequestHeader HttpHeaders headers,
		@CookieValue(name = "httpclient-type", required=false, defaultValue="undefined") String httpClientType) {
		
		log.info(">>>> Cookie 'httpclient-type={}'", httpClientType);

		headers.forEach((key, value) -> {
			log.info(String.format(">>>>> Header '%s' => %s", key, value));
		});
		
		log.info("### Received: /webclient/" + param);
		
		String msg = "("+headers.get("host")+":"+port+")"+param + " => Working successfully !!!";
		log.info("### Sent: " + msg);
		return msg;
	} 
}

 

6) Local Test

Local에서 빌드 & 실행한 후, curl로 테스트 합니다. 

Terminal에서 프로젝트 디렉토리로 이동한 후 수행합니다. 

./mvnw clean package -DskipTests && java -jar target/webserver.jar
curl -i http://localhost:5011/webclient/Hello
HTTP/1.1 200
Content-Type: text/plain;charset=UTF-8
Content-Length: 39
Date: Wed, 24 Feb 2021 16:29:48 GMT

(5011)Hello => Working successfully !!!

 

7) git repository생성 및 push

아래 예를 참조하여 수행하십시오.

~/Documents/springboot/sc-hklee/webserver
❯ git init
/Users/happycloudpak/Documents/springboot/sc-hklee/webserver/.git/ 안의 빈 깃 저장소를 다시 초기화했습니다

~/Documents/springboot/sc-hklee/webserver master*
❯ git checkout -b main
새로 만든 'main' 브랜치로 전환합니다

~/Documents/springboot/sc-hklee/webserver main*
❯ git remote add origin https://happycloudpak@github.com/sc-hklee/webserver.git

~/Documents/springboot/sc-hklee/webserver main*
❯ git add . && git commit -m "first" && git push -u origin main

 

8) configmng에 config파일 생성

webserver디렉토리를 만들고 아래 파일들을 추가하십시오.

webserver-cicd-common.properties

# Container Image info
image_registry=harbor.io
image_project=sc-hklee
image_repository=webserver
image_tag=0.0.1

# resources
req_cpu=64m
req_mem=64Mi
limit_cpu=1024m
limit_mem=1024Mi

 

webserver-cicd-dev.properties

# namespace, sa
namespace=hklee
serviceaccount=sa-hklee

# Service info
service_target_port=5011
service_port=5011
service_host=webserver.169.56.84.41.nip.io
service_replicas=2

image_pull_policy=Always

 

webserver-cicd-prod.properties

# namespace, sa
namespace=hklee
serviceaccount=sa-hklee

# Service info
service_target_port=5011
service_port=5011
service_host=webserver.169.56.84.41.nip.io
service_replicas=3

image_pull_policy=Always

 

webserver-common.yaml

내용없음

 

webserver-secret-common.properties

mq_pw=guest

 

configmng를 repository로 push합니다. 

 

9) 빌드 & 배포

NFS서버로 로그인한 후, hklee계정으로 바꾸고, run-cicd를 이용하여 배포합니다. 

[root@nfs ~]# su - hklee

[hklee@nfs ~]$ cd work
[hklee@nfs work]$ git clone https://github.com/sc-hklee/webserver.git

[hklee@nfs work]$ cd webserver
[hklee@nfs scg]$ run-cicd hklee passw0rd . dev . java config

 

http://{ingress host}/webclient/hello로 웹브라우저에서 정상적으로 오픈되는지 확인합니다. 

eureka서버에 등록되었는지 확인합니다. 

 

 

2.2 webclient 개발

아래와 같이 프로젝트를 생성합니다.

- SpringBoot 버전: 2.3.9

- Group ID: com.springcloud

- Artifact ID: webclient

- Packaging Type: Jar

- Java Version: 1.8이상 

- Dependency: Spring Reactive Web

- 맨 마지막 폴더 선택은 프로젝트를 생성할 상위 디렉토리로 지정하십시오. 

2) pom.xml 수정

Description을 수정하고, springcloud dependency, dependencyManagement, finalName을 추가합니다. 

SCL을 사용하기 위해선 반드시 spring-cloud-starter-loadbalancer가 추가되어야 합니다. 

<?xml version="1.0" encoding="UTF-8"?>
<project ...>
	...
	<description>Demo project for Spring Cloud Loadbalancer(Client)</description>
	<properties>
		<java.version>1.8</java.version>
		<spring-cloud.version>Hoxton.SR9</spring-cloud.version>
	</properties>
	<dependencies>
		...
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-loadbalancer</artifactId> 			
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-actuator</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-config</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-bus-amqp</artifactId>
		</dependency>
		
		<!-- swagger 3 -->
		<dependency>
			<groupId>io.springfox</groupId>
			<artifactId>springfox-boot-starter</artifactId>
			<version>3.0.0</version>
		</dependency>

	</dependencies>

	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>${spring-cloud.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>
	
	<build>
		<finalName>webclient</finalName>
		...
	</build>

</project>

 

3) bootstrap.yaml 작성

기존 application.properties의 이름을 bootstrap.yaml로 바꾼 후 수행하십시오. 

server:
  port: ${service_port:5001}

spring:
  application:
    name: webclient
  profiles:
    active: ${profile:local}
    include: common 
  cloud: 
    config:
      uri: ${config_servers:http://localhost:9001}
      searchPaths: ${spring.application.name}
      default-label: main

 

4) Main Class 수정

@EnableDiscoveryClient를 추가합니다. 

@SpringBootApplication
@EnableDiscoveryClient
public class WebclientApplication {

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

}

 

5) SwaggerConfig.java 개발

@Configuration
public class SwaggerConfig {
	@Bean
	public Docket api() {
		ApiInfo apiInfo = new ApiInfoBuilder()
				.title("Spring Cloud Loadbalancer")
				.description("SCL Test")
				.version("1.0")
				.build();
		
		return new Docket(DocumentationType.SWAGGER_2)
				.apiInfo(apiInfo)
				.select()
				.apis(RequestHandlerSelectors.basePackage("com.springcloud.webclient"))
				.paths(PathSelectors.any())
				.build();		
	}
}

 

6) API 개발

Controller class를 아래와 같이 작성합니다. 

WebClient를 만들고, filter로 SCL에서 제공하는 ReactorLoadBalancerExchangeFilterFunction을 지정하였습니다. 

baseUrl은 'http://{service ID}'로 지정하셔야 합니다. 

package com.springcloud.webclient;

import org.springframework.cloud.client.loadbalancer.reactive.ReactorLoadBalancerExchangeFilterFunction;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.reactive.function.client.WebClient;

import reactor.core.publisher.Mono;

@RestController
public class Controller {
    private final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(getClass());
    private final ReactorLoadBalancerExchangeFilterFunction lbFunction;

    Controller(ReactorLoadBalancerExchangeFilterFunction lbFunction) {
        this.lbFunction = lbFunction;
    }

    @GetMapping("/testscl1")    
    public Mono<String> testSCL1() {
        WebClient client = WebClient.builder()
            .filter(this.lbFunction)
            .baseUrl("http://webserver")
            .build();

        return client.get()
            .uri("/webclient/test SCL1")
            .retrieve()
            .bodyToMono(String.class);
    }    
}

 

7) git repository 작성 및 푸시


8) configmng의 config파일 생성

webclient디렉토리를 만들고, 아래 config파일들을 작성하십시오. 

webclient-cicd-common.properties

# Container Image info
image_registry=harbor.io
image_project=sc-hklee
image_repository=webclient
image_tag=0.0.1

# resources
req_cpu=64m
req_mem=64Mi
limit_cpu=1024m
limit_mem=1024Mi

 

webclient-cicd-dev.properties

# namespace, sa
namespace=hklee
serviceaccount=sa-hklee

# Service info
service_target_port=5001
service_port=5001
service_host=webclient.169.56.84.41.nip.io
service_replicas=1

image_pull_policy=Always

 

webclient-cicd-prod.properties

# namespace, sa
namespace=hklee
serviceaccount=sa-hklee

# Service info
service_target_port=5001
service_port=5001
service_host=webclient.169.56.84.41.nip.io
service_replicas=2

image_pull_policy=Always

 

webclient-common.yaml

# disable ribbon. use ReactorLoadBalancerExchangeFilterFunction
spring.cloud:
  loadbalancer.ribbon: false 

 

webclient-secret-common.properties

mq_pw=guest

 

저장 후, repository에 푸시하십시오.

 

9) Local 테스트

Local에서 먼저 config와 eureka앱을 실행하십시오. 

좌측 하단의 Spring Boot Dashboard에서 디버그모드로 실행하십시오. 

 

Terminal을 열고, webserver와 webclient를 빌드 & 실행하십시오.  webserver는 실행만 하면 됩니다.

~/Documents/springboot/sc-hklee/webserver main
❯ java -jar target/webserver.jar
~/Documents/springboot/sc-hklee/webclient main
❯ ./mvnw clean package -DskipTests && java -jar target/webclient.jar

 

브라우저를 열고, webclient의 swagger page를 오픈하십시오. 

정상적으로 SCL이 동작하는지 테스트 합니다. 

 

10) 빌드 & 배포

NFS서버로 로그인한 후, hklee계정으로 바꾸고, run-cicd를 이용하여 배포합니다. 

[root@nfs ~]# su - hklee

[hklee@nfs ~]$ cd work
[hklee@nfs work]$ git clone https://github.com/sc-hklee/webclient.git

[hklee@nfs work]$ cd webclient
[hklee@nfs scg]$ run-cicd hklee passw0rd . dev . java config

 

11) 테스트

http://{ingress host}/swagger-ui/를 열어 테스트합니다. 

EUREKA서버가 여러대인 경우, 각 서버에 모두 서비스 목록이 동기화될때까지 조금 기다려야 합니다. 

그 전까지는 에러가 발생합니다. 

 

3. Spring Cloud LoadBalancer 활용

1) ServiceInstanceListSupplier 사용

EUREKA와 같은 Service Registry없이 Service목록을 프로그램적으로 SCL에 제공할 수 있습니다. 

아래와 같이 ServiceInstanceListSupplier를 Spring Bean으로 생성하시면 됩니다. 

필터 ReactorLoadBalancerExchangeFilterFunction는 ServiceInstanceListSupplier로 등록된 Bean이 있으면

그 서비스 목록을 우선적으로 사용합니다. 

CustomLoadBalancerConfig.java파일을 만드십시오. 

주소를 k8s pod의 host와 포트로 지정합니다.  

CustomServiceInstanceListSupplier의 첫번째 인자는 Service ID입니다.

어떤값으로 하던 상관 없습니다. 일반적으로는 대상 application name과 동일하게 합니다.  

package com.springcloud.webclient;

import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class CustomLoadBalancerConfig {
    static String webServerUris = "webserver-0.webserver:5011,webserver-1.webserver:5011";

    public static class WebServiceConfig { 
        @Bean
        public ServiceInstanceListSupplier customServiceInstanceListSupplier() {
            return new CustomServiceInstanceListSupplier("myserver", webServerUris);
        }
    }
}

 

CustomServiceInstanceListSupplier.java

package com.springcloud.webclient;

import java.util.ArrayList;
import java.util.List;

import org.springframework.cloud.client.DefaultServiceInstance;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;

import reactor.core.publisher.Flux;

public class CustomServiceInstanceListSupplier implements ServiceInstanceListSupplier {

    private final String serviceId;
    private final String webServerUris;

    CustomServiceInstanceListSupplier(String serviceId, String webServerUris) {
        this.serviceId = serviceId;
        this.webServerUris = webServerUris;
    }


    @Override
    public String getServiceId() {
        return serviceId;
    }

    @Override
    public Flux<List<ServiceInstance>> get() {
        List lists = new ArrayList();
        String[] uris = webServerUris.split(",");
        for(String uri:uris) {
            String[] tmp = uri.split(":");
            lists.add(new DefaultServiceInstance(serviceId + lists.size(), serviceId, tmp[0], Integer.parseInt(tmp[1]), false));
        }

        return Flux.just(lists);        
    }
}

 

git push 후, 다시 빌드 & 배포하고, swagger page에서 테스트 하십시오. 

이번에는 아래와 같이 EUREKA의 주소가 아닌, ServiceInstanceListSupplier가 제공한 주소를 이용하여 Load Balancing합니다. 

 

 

2) WebClient를 Spring Bean으로 미리 생성하여 사용하기

Spring WebClient는 매번 생성하면 시간이 소요되기 때문에, 미리 Spring Bean으로 생성해 놓고 사용합니다. 

아래와 같이 Controller와 Config class를 만드십시오. 

WebClient에 대한 설명은 Spring WebClient 쉽게 이해하기를 참조하십시오. 

 

WebClientConfig.java

package com.springcloud.webclient;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.web.reactive.function.client.ClientRequest;
import org.springframework.web.reactive.function.client.ExchangeFilterFunction;
import org.springframework.web.reactive.function.client.ExchangeStrategies;
import org.springframework.web.reactive.function.client.WebClient;

import io.netty.channel.ChannelOption;
import io.netty.handler.timeout.ReadTimeoutHandler;
import io.netty.handler.timeout.WriteTimeoutHandler;
import reactor.core.publisher.Mono;
import reactor.netty.http.client.HttpClient;

@Configuration
public class WebClientConfig {
    private final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(getClass());
    
    @Bean
    public WebClient webClient() {
        HttpClient httpClient = HttpClient.create()
            .tcpConfiguration(
                client -> client.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 3000) //miliseconds
                    .doOnConnected(
                        conn -> conn.addHandlerLast(new ReadTimeoutHandler(5))  //sec
                            .addHandlerLast(new WriteTimeoutHandler(60)) //sec
                    )
            );

        //Memory 조정: 2M (default 256KB)
        ExchangeStrategies exchangeStrategies = ExchangeStrategies.builder()
            .codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(2*1024*1024)) 
            .build();

        return WebClient.builder()
            .clientConnector(new ReactorClientHttpConnector(httpClient))
            .filter(
                (req, next) -> next.exchange(
                    ClientRequest.from(req).header("from", "webclient").build()
                )
            )
            .filter(
                ExchangeFilterFunction.ofRequestProcessor(
                    clientRequest -> {
                        log.info(">>>>>>>>>> REQUEST <<<<<<<<<<");
                        log.info("Request: {} {}", clientRequest.method(), clientRequest.url());
                        clientRequest.headers().forEach(
                            (name, values) -> values.forEach(value -> log.info("{} : {}", name, value))
                        );
                        return Mono.just(clientRequest);
                    }
                )
            )
            .filter(
                ExchangeFilterFunction.ofResponseProcessor(
                    clientResponse -> {
                        log.info(">>>>>>>>>> RESPONSE <<<<<<<<<<");
                        clientResponse.headers().asHttpHeaders().forEach((name, values) -> values.forEach(value -> log.info("{} : {}", name, value)));
                        return Mono.just(clientResponse);
                    }
                )
            )
            .exchangeStrategies(exchangeStrategies)            
            .defaultHeader("user-agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.87 Safari/537.3")
            .defaultCookie("httpclient-type", "webclient")
            .build();
    }
}

 

WebClientController.java

package com.springcloud.webclient;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.loadbalancer.reactive.ReactorLoadBalancerExchangeFilterFunction;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.reactive.function.client.WebClient;

import reactor.core.publisher.Mono;

@RestController
public class WebClientController {
    private final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(getClass());
    private final ReactorLoadBalancerExchangeFilterFunction lbFunction;

    WebClientController(ReactorLoadBalancerExchangeFilterFunction lbFunction) {
        this.lbFunction = lbFunction;
    }

    @Autowired
    private WebClient webClient;

    @GetMapping("/testscl2")
    public Mono<String> testSCL2() {
        return webClient
                .mutate() 
                .filter(lbFunction)
                .baseUrl("http://webserver")
                .build()
                .get()
                .uri("/webclient/test SCL2")
                .retrieve()
                .bodyToMono(String.class);
    }    
}

 

git push 후, 다시 빌드 & 배포하고, swagger page에서 테스트 하십시오. 

ServiceInstanceListSupplier가 있기 때문에, 거기서 제공한 주소를 이용하여 Load Balancing하는것을 볼 수 있습니다.  

EUREKA와 연동하고 싶으면, 아래와 같이 @Bean을 주석 처리하여 생성되지 않게 하시면 됩니다. 

@Configuration
public class CustomLoadBalancerConfig {
    static String webServerUris = "webserver-0.webserver:5011,webserver-1.webserver:5011";

    public static class WebServiceConfig { 
        //@Bean
        public ServiceInstanceListSupplier customServiceInstanceListSupplier() {
            return new CustomServiceInstanceListSupplier("myserver", webServerUris);
        }
    }
}