springcloud introduction case

brief introduction

Spring Cloud is one of the projects under spring. The official website address is: http://projects.spring.io/spring-cloud/
Spring is best at integration, taking the best framework in the world and integrating it into its own projects.
Spring Cloud is the same. It integrates some popular technologies and realizes functions such as configuration management, service discovery, intelligent routing, load balancing, fuse, control bus, cluster status and so on.
The main components involved include:
Netflix

  • Eureka: Registration Center
  • Zuul: service gateway
  • Ribbon: load balancing
  • Feign: service call
  • Hystrix: fuse
  • wait

edition

Correspondence between spring clone and Spring Boot versions

Release Train Boot Version
Hoxton 2.2.x
Greenwich 2.1.x
Finchley 2.0.x
Edgware 1.5.x
Dalston 1.5.x

Create parent project

In microservices, you need to create multiple projects at the same time. First, you need to create a parent project. Subsequent projects take this project as the parent and use Maven's aggregation and inheritance. Uniformly manage the version and configuration of subprojects

<!--Aggregate parent project-->
<packaging>pom</packaging>

<parent> 
	<groupId>org.springframework.boot</groupId> 
	<artifactId>spring-boot-starter-parent</artifactId> 
	<version>2.1.5.RELEASE</version> 
	<packaging>pom</packaging>
	<relativePath/> 
</parent>

<!--The version information is specified here-->
<properties> 
	<java.version>1.8</java.version> 
	<spring-cloud.version>Greenwich.SR1</spring-cloud.version> 
	<mapper.starter.version>2.1.5</mapper.starter.version> 
	<mysql.version>5.1.46</mysql.version> 
</properties>

<dependencyManagement> 
	<dependencies>
		<!-- springCloud --> 
		<dependency> 
			<groupId>org.springframework.cloud</groupId> 
			<artifactId>spring-cloud-dependencies</artifactId> 
			<version>${spring-cloud.version}</version> 
			<type>pom</type> 
			<!--there import Be sure to add it, or the subproject will report an error-->
			<scope>import</scope> 
		</dependency>
		<!-- currency Mapper starter --> 
		<dependency> 
			<groupId>tk.mybatis</groupId> 
			<artifactId>mapper-spring-boot-starter</artifactId> 
			<version>${mapper.starter.version}</version> 
		</dependency>
		<!-- mysql drive --> 
		<dependency> 
			<groupId>mysql</groupId> 
			<artifactId>mysql-connector-java</artifactId> 
			<version>${mysql.version}</version> 
		</dependency>
	</dependencies>
</dependencyManagement>

<!--lombok Simplify code-->
<dependencies> 
	<dependency> 
		<groupId>org.projectlombok</groupId> 
		<artifactId>lombok</artifactId> 
	</dependency> 
</dependencies>

<build> 
	<plugins> 
		<plugin> 
			<groupId>org.springframework.boot</groupId> 
			<artifactId>spring-boot-maven-plugin</artifactId> 
		</plugin> 
	</plugins> 
</build>

Subproject

The version numbers are uniformly managed by the parent project. The child project does not need to add version numbers, but simply introduce dependencies

In micro services, we need both service producers and service consumers

REST style Web Services

@RestController// Declare that it is a rest style controller
@RequestMapping("/user")
@RefreshScope// Profile change auto refresh attribute
public class UserController {
	// Inject service layer
    @Autowired
    private UserService userService;

    @GetMapping("/{id}")
    public User queryById(@PathVariable Long id){
        return userService.queryById(id);
    }
}

Initiator of microservice

@SpringBootApplication

@This is a springboot application, and the marked class is a main program, springapplication run(App.class, args); Incoming class app Class must be a class marked by @ SpringBootApplication. For example:

@SpringBootApplication 
public class ConsumerApplication { 

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

@SpringBootApplication is a composite annotation, which is equivalent to the combination of the following annotations:
1. The @ spotbootconfiguration (or @ Configuration) Configuration class, corresponding to the Configuration file, is essentially a @ Componet, but it is more meaningful. See the meaning of the name
2. @EnableAutoConfiguration: turn on automatic configuration and scan all annotations of the package where the main configuration class is located and all descendant packages below it
3. @ComponentScan: configure packages to be scanned

@SpringBootConfiguration

The function of this annotation is the same as that of @ Configuration, which is used to declare that the current class is a Configuration class. IOC container managed beans can be generated through the @ bean annotation Define beans in ConsumerApplication and inject them into UserController
ConsumerApplication:

@SpringBootApplication
public class ConsumerApplication {

	public static void  main(String[]args){
		SpringApplication.run(ConsumerApplication.class,args);
	}
	
	@Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}

UserController:

@RestController
public class UserController {
	@Autowired
    private RestTemplate restTemplate;
    
    @GetMapping("/{id}")
    public User queryById(@PathVariable Long id){
		String url = "http://localhost:9091/user/" + id;
		return restTemplate.getForObject(url, User.class);
    }
}

@EnableAutoConfiguration

@EnableAutoConfiguration is the core annotation for spring boot to realize automatic configuration. Through this annotation, the bean s required for spring application are injected into the container. @ EnableAutoConfiguration source code injects an implementation class of ImportSelector, AutoConfigurationImportSelector, through @ Import

@Import({AutoConfigurationImportSelector.class})

AutoConfigurationImportSelector. This ImportSelector can finally dynamically load the required bean s according to our configuration
The source code of the dynamic loading implementation method of AutoConfigurationImportSelector is as follows:

// annotationMetadata is the annotation where @ Import is located. It is specified here as EnableAutoConfiguration
public String[] selectImports(AnnotationMetadata annotationMetadata) {
	if (!this.isEnabled(annotationMetadata)) {
		return NO_IMPORTS;
	} else {
		try {
			//Load the metadata information of XXConfiguration (including the bean conditions for some classes to be generated). Continue to follow up this method call, and you will find that the loaded metadata is spring autoconfigure metadata of META-INF in spring boot autoconfigure jar package properties
			AutoConfigurationMetadata autoConfigurationMetadata 
				= AutoConfigurationMetadataLoader
						.loadMetadata(this.beanClassLoader);
			 //Get the attribute set in the annotation. The exclude and excluden ame attribute values set in SpringBootApplication are actually the two attribute values set in EnableAutoConfiguration
			AnnotationAttributes attributes 
				= this.getAttributes(annotationMetadata);
			//From the spring boot autoconfigure jar package, meta-inf / spring Factories loads the name of the configuration class. Open this file and find that it contains all the configuration classes provided by the spring boot framework
			List<String> configurations 
				= this.getCandidateConfigurations(
						annotationMetadata, 
						attributes);
			//Remove duplicates
			configurations = this.removeDuplicates(configurations);
			
			configurations = this.sort(
					configurations, 
					autoConfigurationMetadata);
			//Get the class that you don't need to generate bean s for your own configuration
			Set<String> exclusions = this.getExclusions(
					annotationMetadata, 
					attributes);
			//Verify whether the exclude d classes are all classes in the springboot automation configuration. If so, throw an exception
			this.checkExcludedClasses(configurations, exclusions);
			//Delete the exclude d class
			configurations.removeAll(exclusions);
			//Filter and brush the classes that meet OnClassCondition
			configurations = this.filter(
					configurations, 
					autoConfigurationMetadata);

			this.fireAutoConfigurationImportEvents(
					configurations, 
					exclusions);
			//Returns the class path of the bean to be injected
			return StringUtils.toStringArray(configurations);
		} catch (IOException var6) {
			throw new IllegalStateException(var6);
		}
	}
}

@ComponentScan

As everyone who has used the spring framework knows, there are four annotations in spring:

  • @Service,
  • @Repository,
  • @Component,
  • @Controller
    Used to define a bean@ The ComponentScan annotation is used to automatically scan the classes identified by these annotations and finally generate beans in the ioc container. The automatic scanning range can be dynamically determined by setting the @ ComponentScan basePackages, includeFilters and excludeFilters properties. The types are not scanned. By default: it scans all types, and the scanning range is the class of the configured class package and sub package where the @ ComponentScan annotation is located,

RestTemplate template tool class

Spring provides a RestTemplate template tool class, encapsulates the HTTP based client, and realizes the serialization and deserialization of objects and json, which is very convenient. RestTemplate does not limit the client type of HTTP, but abstracts it. At present, three commonly used types are supported:

  • HTTPClient
  • OkHTTP
  • JDK native URLConnection (default)
    ConsumerApplication:
@SpringBootApplication 
public class ConsumerApplication {

	public static void main(String[] args) { 
		SpringApplication.run(ConsumerApplication.class, args); 
	}
	
	@Bean 
	public RestTemplate restTemplate(){ 
		return new RestTemplate(); 
	} 
}

UserController:

@RestController
public class UserController {
	@Autowired
    private RestTemplate restTemplate;
    
    @GetMapping("/{id}")
    public User queryById(@PathVariable Long id){
		String url = "http://localhost:9091/user/" + id;
		return restTemplate.getForObject(url, User.class);
    }
}

Eureka registry

reflection

User service provides services externally and needs to expose its own address. The consumer (caller) needs to record the address of the service provider. If the address changes in the future, it needs to be updated in time. This doesn't seem to matter when there are few services, but in today's increasingly complex Internet environment, a project will certainly split into more than a dozen or even dozens of micro services. At this time, if you still manage the address artificially, it will not only be difficult to develop, but also very troublesome to test, release and go online in the future, which runs counter to the idea of DevOps.

Architecture diagram

Eureka: it's a service registry (which can be a cluster) that exposes its address to the outside world
Provider: register your information (address and services) with Eureka after startup. It can be a Spring Boot application or any other technology implementation, as long as you provide REST style services.
Consumers: subscribe to the service from Eureka. Eureka will send the address list of all providers of the corresponding service to consumers and update it regularly
Heartbeat (renewal): the provider periodically refreshes its status to Eureka through HTTP

build

Eureka is a service registration center, which only does service registration; They do not provide services or consume services. You can build Web projects using Eureka or Spring Boot.

eureka registry

Server

Add dependency
<!-- Eureka Server -->
<dependency> 
	<groupId>org.springframework.cloud</groupId> 
	<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> 
</dependency> 
Startup class
@EnableEurekaServer //Eureka service when declaring the current application 
@SpringBootApplication 
public class EurekaServerApplication { 
	public static void main(String[] args) {
		SpringApplication.run(EurekaServerApplication.class, args); 
	} 
}
configuration file
# Specify the port number
server.port=10086

# Name the service
spring.application.name=eureka-sevver

# eureka service address, if it is a cluster; Other cluster eureka addresses need to be specified
eureka.client.service-url.defaultZone=http://127.0.0.1:10086/eureka
# Don't register yourself
eureka.client.register-with-eureka=false
# No pull service
eureka.client.fetch-registry=false

Client registration

Add Eureka client dependency on the consumer demo of the service consumption project; You can use the tool class DiscoveryClient to obtain the corresponding service address list according to the service name.

Add dependency
<!-- Eureka client --> 
<dependency> 
	<groupId>org.springframework.cloud</groupId> 
	<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> 
</dependency>
Startup class
@SpringBootApplication 
@EnableDiscoveryClient // Open Eureka client 
public class UserConsumerDemoApplication { 

	public static void main(String[] args) { 
		SpringApplication.run(UserConsumerDemoApplication.class, args); 
	}
	
	@Bean 
	public RestTemplate restTemplate() { 
		return new RestTemplate(); 	
	}
	
}
configuration file
# Name the service
spring.application.name=consumer-demo

# eureka service address
eureka.client.service-url.defaultZone=http://127.0.0.1:10086/eureka
Modify code
@RestController
public class UserController {
	@Autowired
    private RestTemplate restTemplate;
    
    @GetMapping("/{id}")
    public User queryById(@PathVariable Long id){
		// String url = "http://localhost:9091/user/" + id;
		
		// Pull all services called user service registered in eureka registry
		List<ServiceInstance> serviceInstances = 
			discoveryClient.getInstances("user-service"); 
		// We only registered one, so we only need to get the index of subscript 0
		ServiceInstance serviceInstance = serviceInstances.get(0); 
		// Concatenate strings to form a url address
		String url = "http://" + 
				serviceInstance.getHost() + 
				":" + 
				serviceInstance.getPort() + 
				"/user/" + 
				id;
		
		return restTemplate.getForObject(url, User.class);
	}
}

Highly available Eureka server

nstances("user-service"); 
		// We only registered one, so we only need to get the index of subscript 0
		ServiceInstance serviceInstance = serviceInstances.get(0); 
		// Concatenate strings to form a url address
		String url = "http://" + 
				serviceInstance.getHost() + 
				":" + 
				serviceInstance.getPort() + 
				"/user/" + 
				id;
		
		return restTemplate.getForObject(url, User.class);
	}
}

Highly available Eureka server

Multiple Eureka servers will also register as services. When the service provider registers with a node in the Eureka Server cluster, the node will synchronize the service information to each node in the cluster, so as to realize data synchronization. Therefore, no matter the client accesses any node in the Eureka Server cluster, it can obtain the complete service list information.

Tags: Spring Spring Boot

Posted by conor.higgins on Thu, 05 May 2022 12:58:14 +0300