Spring Cloud를 활용한 MSA 설치 및 구성

김경민·2022년 7월 24일
1

Cloud Native, DevOps

목록 보기
8/10

spring cloud

spring cloud vs kubernetes

-> jdk-11.0.15.1_windows-x64_bin.exe

-> spring-tool-suite-4-4.15.1.RELEASE-e4.24.0-win32.win32.x86_64.self-extracting.jar

-> apache-maven-3.8.6-bin.zip

jdk 11 설치

maven 압축해제

JAVA_HOME C:\Program Files\Java\jdk-11.0.15.1

Path %JAVA_HOME%\bin

MAVEN_HOME C:\apache-maven-3.8.6

Path %MAVEN_HOME%\bin

명령창 테스트

java -version

javac -version

mvn

start.spring.io

캐시경로
C:\Users\HPE.m2\repository

https://projectlombok.org/download
java -jar lombok.jar을 STS에 설치

http://localhost:8080/v1/organization/optimaGrowth/license/2313202

eureka server

spring.application.name=MyServer
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
server.port=8761

spring.application.name=MyClient1
server.port=8081
eureka.instance.prefer-ip-address=true
eureka.client.service-url.defaultZone=http://localhost:8761/eureka
eureka.client.fetch-registry=true
eureka.client.register-with-eureka=true

package com.test.myclient1;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@EnableEurekaClient
@RestController
public class MyClient1Application {

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

 @RequestMapping(value="/")
  public String hello() {
  return "Eureka Client Application";
 }

}

spring.application.name=MyClient2
server.port=8082
eureka.instance.prefer-ip-address=true
eureka.client.service-url.defaultZone=http://localhost:8761/eureka
eureka.client.fetch-registry=true
eureka.client.register-with-eureka=true

spring.application.name=MyClient3
server.port=8083
eureka.instance.prefer-ip-address=true
eureka.client.service-url.defaultZone=http://localhost:8761/eureka
eureka.client.fetch-registry=true
eureka.client.register-with-eureka=true

package com.test.myclient3;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.test.myclient3.MyClient3Application;

@SpringBootApplication
@EnableEurekaClient
@RestController
public class MyClient3Application {
 public static void main(String[] args) {
 SpringApplication.run(MyClient3Application.class, args);
}

@RequestMapping(value="/")
public String hello() {
 return "Eureka Client Application #3";
}

}

마이크로서비스 기반 아키텍처(MSA) VS 서비스 지향 아키텍처(SOA)

  • Microservices3Monitoring 도구
    • Hystrix 대시보드
    • 유레카(Eureka) 관리자 대시보드
    • 스프링 부트 관리자 대시보드

server:
port: 9000
spring:
cloud:
gateway:
routes:
- id: helloModule
uri: http://localhost:8086/
predicates:
- Path=/hello/**
- id: goodbyeModule
uri: http://localhost:8087/
predicates:
- Path=/goodbye/**
http://localhost:9000/hello
-> http://localhost:8086/hello

http://localhost:9000/goodbye
-> http://localhost:8087/goodbye

Gateway Server http://localhost:9000/hello or goodbye

Service #1 http://localhost:8086
Service #1 http://localhost:8087
ApiGateway 9001 - gateway, eurekaclient
Loadbalancer 9000 - loadbalancer, eurekaclient
EurekaServer 8090 - eurekaserver
Service01 8081 - eurekaclient, spring mvc
Service02 8082 - eurekaclient, spring mvc

package com.test.model;

public class Member {
private String id;
private String name;
}

package com.test.model;

 public class Member {
  private String id;
  public String getId() {
  return id;
 }
 public void setId(String id) {
  this.id = id;
 }
 public String getName() {
  return name;
 }
 public void setName(String name) {
  this.name = name;
 }
 private String name;
}


package com.test.controllers;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.test.model.Member;

@RestController
public class MemberController {
 @RequestMapping(value="/member", method=RequestMethod.GET)
 public Member getInfo() {
  Member m = new Member();
  m.setId("hong");
  m.setName("홍길동");
  return m;
 }
}
package com.test.controllers;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.test.model.Member;

@RestController
public class MemberController {
	@RequestMapping(value="/member", method=RequestMethod.GET)
	public Member getInfo() {
		Member m = new Member();
		m.setId("hong");
		m.setName("홍길동");
		return m;
	}
}

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.7.2</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.test</groupId>
	<artifactId>Service01</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>Service01</name>
	<description>Demo project for Spring Boot</description>
	<properties>
		<java.version>11</java.version>
		<spring-cloud.version>2021.0.3</spring-cloud.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-actuator</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</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.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</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>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

spring.application.name=EurekaServer
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
server.port=8090

package com.test;

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

@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {

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

}




spring.application.name=Service01
server.port=8081
eureka.client.service-url.defaultZone=http://localhost:8090/eureka



spring.application.name=EurekaServer
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
server.port=8090
eureka.client.service-url.defaultZone=http://localhost:8090/eureka/
http://localhost:8080/member1
http://localhost:8080/member2
http://localhost:8090
http://localhost:8081/member
http://localhost:8082/member2






package com.test.controllers;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.stereotype.Controller;

@Controller
public class ConsumerControllerClient {
	@Autowired
	private LoadBalancerClient loadBalancer;
	public void getHello() {
		ServiceInstance serviceInstance = loadBalancer.choose("SERVICE");
		System.out.println(serviceInstance.getUri());
		String baseUrl = serviceInstance.getUri().toString();
		baseUrl = baseUrl = "/member";
		// http://localhost:9000/member
		// -> http://localhost:8081/member -> SERVICE01
		// -> http://loaclhost:8082/member -> SERVICE02
	}
}



package com.test.controllers;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.stereotype.Controller;
import org.springframework.web.client.RestTemplate;

@Controller
public class ConsumerControllerClient {
	@Autowired
	private LoadBalancerClient loadBalancer;
	public void getHello() {
		ServiceInstance serviceInstance = loadBalancer.choose("SERVICE");
		System.out.println(serviceInstance.getUri());
		String baseUrl = serviceInstance.getUri().toString();
		baseUrl = baseUrl = "/member";
		// http://localhost:9000/member
		// -> http://localhost:8081/member -> SERVICE01
		// -> http://loaclhost:8082/member -> SERVICE02
		RestTemplate restTemplate = new RestTemplate();
	}
}
package com.test.controllers;

import java.io.IOException;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.client.RestTemplate;

@Controller
public class ConsumerControllerClient {
	@Autowired
	private LoadBalancerClient loadBalancer;
	public void getMember() {
		ServiceInstance serviceInstance = loadBalancer.choose("SERVICE");
		System.out.println(serviceInstance.getUri());
		String baseUrl = serviceInstance.getUri().toString();
		baseUrl = baseUrl = "/member";
		// http://localhost:9000/member
		// -> http://localhost:8081/member -> SERVICE01
		// -> http://loaclhost:8082/member -> SERVICE02
		RestTemplate restTemplate = new RestTemplate(); 

		ResponseEntity<String> response=null;
		try{
			response=restTemplate.exchange(baseUrl,  HttpMethod.GET, getHeaders(),String.class);
		} catch (Exception ex) {
			System.out.println(ex);
		}
		System.out.println(response.getBody());
	}

	private static HttpEntity<?> getHeaders() throws IOException {
		HttpHeaders headers = new HttpHeaders();
		headers.set("Accept", MediaType.APPLICATION_JSON_VALUE);
		return new HttpEntity<>(headers);
	}
}
package com.test;

import java.io.IOException;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.ApplicationContext;
import org.springframework.web.client.RestClientException;

import com.test.controllers.ConsumerControllerClient;

@SpringBootApplication
public class LoadbalancerApplication {

	public static void main(String[] args) throws RestClientException, IOException {

		ApplicationContext ctx = SpringApplication.run(LoadbalancerApplication.class, args);
		
		ConsumerControllerClient consumerControllerClient=ctx.getBean(ConsumerControllerClient.class);
		System.out.println(consumerControllerClient);
		for(int inx = 0; inx <= 10; inx++) {
			consumerControllerClient.getMember();
		}
	}

}

Service
/member




spring.application.name=LoadBalancer1
server.port=9000
eureka.client.serviceUrl.defaultZone=http://localhost:8090/eureka
package com.test.controllers;

import java.io.IOException;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.client.RestTemplate;

@Controller
public class ConsumerControllerClient {
	@Autowired
	private LoadBalancerClient loadBalancer;
	public void getMember() {
		ServiceInstance serviceInstance = loadBalancer.choose("SERVICE");
		System.out.println(serviceInstance.getUri());
		String baseUrl = serviceInstance.getUri().toString();
		baseUrl = baseUrl = "/member";
		// http://localhost:9000/member
		// -> http://localhost:8081/member -> SERVICE01
		// -> http://loaclhost:8082/member -> SERVICE02
		RestTemplate restTemplate = new RestTemplate(); 

		ResponseEntity<String> response=null;
		try{
			response=restTemplate.exchange(baseUrl,  HttpMethod.GET, getHeaders(),String.class);
		} catch (Exception ex) {
			System.out.println(ex);
		}
		System.out.println(response.getBody());
	}

	private static HttpEntity<?> getHeaders() throws IOException {
		HttpHeaders headers = new HttpHeaders();
		headers.set("Accept", MediaType.APPLICATION_JSON_VALUE);
		return new HttpEntity<>(headers);
	}
}
package com.test;

import java.io.IOException;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.ApplicationContext;
import org.springframework.web.client.RestClientException;

import com.test.controllers.ConsumerControllerClient;

@SpringBootApplication
public class LoadbalancerApplication {

	public static void main(String[] args) throws RestClientException, IOException {

		ApplicationContext ctx = SpringApplication.run(LoadbalancerApplication.class, args);
		
		ConsumerControllerClient consumerControllerClient=ctx.getBean(ConsumerControllerClient.class);
		System.out.println(consumerControllerClient);
		for(int inx = 0; inx <= 10; inx++) {
			consumerControllerClient.getMember();
		}
	}

}


//EurekaServer 미사용 코드 쿠현
package com.test;

import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.client.DefaultServiceInstance;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Flux;

import java.util.Arrays;
import java.util.List;

@SpringBootApplication
public class LoadBalancerClientApplication {

	public static void main(String[] args) {
//		SpringApplication.run(LoadBalancerClientApplication.class, args);
		
		ConfigurableApplicationContext ctx = new SpringApplicationBuilder(LoadBalancerClientApplication.class)
                .web(WebApplicationType.NONE)
                .run(args);

        WebClient loadBalancedClient = ctx.getBean(WebClient.Builder.class).build();

        for(int i = 1; i <= 10; i++) {
            String response =
                loadBalancedClient.get().uri("http://example-service/hello")
                    .retrieve().toEntity(String.class)
                    .block().getBody();
            System.out.println(response);
        }
    }
}
@Configuration
class DemoServerInstanceConfiguration {
    @Bean
    ServiceInstanceListSupplier serviceInstanceListSupplier() {
        return new DemoInstanceSupplier("example-service");
    }
}

@Configuration
@LoadBalancerClient(name = "example-service", configuration = DemoServerInstanceConfiguration.class)
class WebClientConfig {
    @LoadBalanced
    @Bean
    WebClient.Builder webClientBuilder() {
        return WebClient.builder();
    }
}

class DemoInstanceSupplier implements ServiceInstanceListSupplier {
    private final String serviceId;

    public DemoInstanceSupplier(String serviceId) {
        this.serviceId = serviceId;
    }

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

    @Override
    public Flux<List<ServiceInstance>> get() {
        return Flux.just(Arrays
            .asList(new DefaultServiceInstance(serviceId + "1", serviceId, "localhost", 8082, false),
                    new DefaultServiceInstance(serviceId + "2", serviceId, "localhost", 8083, false)));
    }
}

EurekaServer and Ribbon



spring.application.name=Service04
server.port=8084
package com.test.controllers;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.test.model.Member;

@RestController
public class MemberController {
	@RequestMapping(value="/goodbye", method=RequestMethod.GET)
	public Member getInfo() {
		Member m = new Member();
		m.setId("lee");
		m.setName("이순신");
		return m;
	}
}



server:
  port: 9000

spring:
  cloud:
    gateway:
      routes:
      - id: helloModule
        uri: http://localhost:8083/
        predicates:
        - Path=/hello/**
      - id: goodbye
        uri: http://localhost:8084/
        predicates:
        - Path=/goodbye/** 


spring cloud config server github

spring:
  cloud:
    config:
      server:
        git:
          uri: https://github.com/spring-cloud-samples/config-repo

spring-cloud-config

server:
 port: 8888
spring:
 cloud:
  config:
   server:
    git:
     uri: https://github.com/formin/MySpringClud.git


server:
 port: 8089
spring:
 application:
  name: MyConfigClient
 cloud:
  config:
   name: myconfig
   profile: test
 config:
  import:
   configserver:http://localhost:8888

package com.test;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@RestController
public class MyConfigClient1Application {

	public static void main(String[] args) {
		SpringApplication.run(MyConfigClient1Application.class, args);
	}
	
	@Value("${message.text:default}")
	private String messageValue;
	
	@RequestMapping(value="/")
	public String getMessageValue() {
		return messageValue;
	}
}

java -jar jenkins.war

Microservice Patterns: With examples in Java

Practical Microservices: Event Sourcing and CQRS with Event-Driven Architectures

Microservices with Spring Boot and Spring Cloud: Build resilient and scalable microservices using Spring Cloud, Istio, and Kubernetes, 2nd Edition

스프링 부트 및 스프링 클라우드가 있는 핸드온 마이크로서비스: 스프링 클라우드, 이스티오, 쿠버네티스를 사용하여 Java 마이크로서비스 구축 및 배포

microservices RESULTS Sponsored Dependency Injection Principles, Practices, and Patterns by Mark Seemann and Steven van Deursen

microservices RESULTS Sponsored Dependency Injection Principles, Practices, and Patterns by Mark Seemann and Steven van Deursen

Cloud Native Java: 스프링 부츠, 스프링 클라우드 및 클라우드 파운드리가 있는 탄력적인 시스템 설계

0개의 댓글