Microservice Architecture Foundation (II)
To be continuedContinue the previous basic project for expansion
Load balancing and service invocation
-
Ribbon
Ribbon local load balancing client & nginx server load balancing difference
Nginx is server load balancing. All client requests will be handed over to nginx, and then nginx will forward the requests. That is, load balancing is realized by the server. It belongs to in-process load balancing.
Ribbon local load balancing, when calling the micro service interface, will obtain the registration information list in the registry and cache it to the JVM local, so as to realize the RPC remote service call technology locally. It belongs to centralized load balancing.
As long as the dependency is added, the Ribbon can be automatically integrated, which is why the previous project can use load balancing:
<!-- Service registration --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
You can find:
The difference between getforebject() & getforentity()
The return object of getForObject()/postForObject() is the object converted from the data in the response body, which can be basically understood as Json
The return object of getForEntity()/postForEntity() is the ResponseEntity object, which contains some important information in the response body, such as response header, response status code, response body, etc.
Simple test:
Add the following test code to the control layer of port 80
@GetMapping("/test/book/id/{id}") public Object findBookById_1(@PathVariable("id") String id){ ResponseEntity responseEntity = restTemplate.getForEntity(URL1+"/book/id/"+id,String.class); if(responseEntity.getStatusCode().is2xxSuccessful()){ // Judge status code information // Success code // return responseEntity.getBody(); Return response body data return responseEntity; }else{ return responseEntity.getStatusCode(); } }
Ribbon load balancing strategy
- Polling (roundrobin rule)
- Random rule
- First obtain the service according to the roundrobin rule policy. If the service acquisition fails, retry within the specified time to obtain the available service (RetryRule)
- For the extension of roundrobin rule, the faster the response speed, the greater the weight of the instance selection, and the easier it is to be selected (WeightResponseTimeRule)
- First filter out the services in the circuit breaker tripping state due to multiple faults, and then select a service with the least concurrency (BestAvailableRelu)
- First filter out the failed instances, and then select the instance with less concurrency (availability filtering rule)
- The default rule is to composite judge the new capabilities in the region where the server is located and the availability of the server, and select the server (zoneavoidance rule)
In order to prevent conflicts with the @ Component annotation (because @ springbootapplication contains this annotation), you should at least use the policy above the parent directory of the directory where the annotation is located
First, create a policy class:
Policy class:
package cn.rule; import com.netflix.loadbalancer.IRule; import com.netflix.loadbalancer.RandomRule; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class MySelfRule { @Bean // Inject IoC management public IRule myRule(){ return new RandomRule(); // Select random strategy } }
Modify the main startup class (add comments, fill in the name of load balancing target application and rule class settings):
package cn.wu; import cn.rule.MySelfRule; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; import org.springframework.cloud.netflix.ribbon.RibbonClient; @SpringBootApplication @EnableEurekaClient @RibbonClient(name = "micro-service-1",configuration = MySelfRule.class) public class MainApplication { public static void main(String[] args) { SpringApplication.run(MainApplication.class,args); } }
Done, final test
-
OpenFeign
What is OpenFeign?
OpenFeign is a declarative Web service client, which makes it easier to write the We service client. With OpenFeign, you just need to create an interface and use annotations. Using OpenFeign in the Spring Cloud can use HTTP requests to access remote services, just like calling local methods. Developers are completely unaware that they are calling remote methods, let alone accessing HTTP requests.
What can OpenFeign do?
OpenFeign is designed to make it easier to write Java Http clients
When Ribbon+RestjTemplate is used earlier, the encapsulation of http requests by RestTemplate forms a set of template calling methods. However, in the actual development, because there may be more than one invocation of service dependencies, and often an interface will be invoked multiple times, the invocation of these dependent services is usually packaged for some client classes encapsulated by each microservice. Therefore, OpenFeign further encapsulates on this basis to help us define and implement the definition of dependent service interfaces. Under the implementation of OpenFeign, we only need to create an interface and configure it by annotation to complete the interface binding to the service provider, which simplifies the opening of automatically encapsulating the service call client when using the Spring Cloud Ribbon. At the same time, OpenFeign integrates Ribbon, which can define the service binding interface through OpenFeign, and implement service invocation gracefully and simply in a declarative way.
New service consumer module (main-service-8081):
Dependent configuration:
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>Project</artifactId> <groupId>cn.wu</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>main-service-8081</artifactId> <properties> <java.version>1.8</java.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <spring-boot.version>2.3.7.RELEASE</spring-boot.version> <spring-cloud.version>Hoxton.SR9</spring-cloud.version> </properties> <dependencies> <!-- Entity class --> <dependency> <groupId>cn.wu</groupId> <artifactId>api-commons</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <!-- Service registration --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <!-- openfeign rely on --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </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> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>${spring-boot.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.1</version> <configuration> <source>1.8</source> <target>1.8</target> <encoding>UTF-8</encoding> </configuration> </plugin> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>2.3.7.RELEASE</version> <configuration> <mainClass>cn.wu.MainApplication8081</mainClass> </configuration> <executions> <execution> <id>repackage</id> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </project>
application.yml:
server: port: 8081 eureka: client: register-with-eureka: true # Register yourself fetch-registry: true # To obtain the registration information, you need to retrieve the service service-url: # For the service port address of the cluster version, you can directly fill in all the server addresses defaultZone: http://eureka7001.cn:7001/eureka,http://eureka7002.cn:7002/eureka spring: application: name: main-service-8081
Modify the main startup class:
package cn.wu; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.openfeign.EnableFeignClients; @SpringBootApplication @EnableFeignClients // Open Feign public class MainApplication8081 { public static void main(String[] args) { SpringApplication.run(MainApplication8081.class,args); } }
Create a new Fegin service interface:
package cn.wu.service.feign; import cn.wu.entity.Book; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; // Pit: the @ Component annotation has been included here, and the Bean is named as the default name // value represents the name of the micro service application @FeignClient(value = "micro-service-1") // Using Feign public interface BookService { @PostMapping("/book/add") boolean addBook(Book book); @GetMapping("/book/id/{id}") String findBookById(@PathVariable("id") String id); }
New control layer:
package cn.wu.controller; import cn.wu.entity.Book; import cn.wu.service.fegin.BookService; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; @RestController public class BookController { @Resource // injection private BookService bookService; @GetMapping("/book/id/{id}") public String findBookById(@PathVariable("id") String id){ return bookService.findBookById(id); } @PostMapping("/book/add") public boolean addBook(Book book){ return bookService.addBook(book); } }
Operation results:
Timeout control
Scenario Description: if the service provider's processing time is greater than the maximum waiting time of the service consumer, a 500 timeout error will appear
Therefore, it is necessary to improve the waiting time of service consumers
Simulation timeout
Add the following codes to the two service provider control layers:
// Test timeout control @GetMapping("/test/timeout") public String testTimeOut(){ try { Thread.sleep(4000); // Time required to simulate business } catch (InterruptedException e) { e.printStackTrace(); } return "test Timeout"; }
Add the following code to the service consumer interface:
// Test timeout control @GetMapping("/test/timeout") String testTimeOut();
The service consumer adds the following code in the control layer:
@GetMapping("/test/timeout") public String testTimeOut(){ return bookService.testTimeOut(); }
By default, the result is timeout:
Add the application.yml configuration file, and finally restart:
feign: client: config: default: ConnectionTimeOut: 5000 # Maximum waiting time to connect to the network ReadTimeOut: 5000 # The maximum time available to read resources after a connection is established
Visit again and wait for a while. At this time, the result is:
Log printing
In short, it is to monitor and output the call of Fegin interface
Log level
NONE: by default, no logs are displayed
BASIC: only record the request method, URL, response status code and execution time
HEADERS: in addition to the information defined in BASIC, there are also header information of request and response
FULL: in addition to the information defined in HEAERS, there are also the body and metadata of the request and response
Add configuration class:
package cn.wu.config; import feign.Logger; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class FeignConfig { @Bean // Injection IoC container management Logger.Level feignLoggerLevel(){ return Logger.Level.FULL; // The return log level is detailed log } }
application.yml add log configuration:
logging: level: cn.wu.service.feign.BookService: debug
After accessing the URL, the console outputs details:
2021-11-06 16:08:39.536 INFO 18720 --- [erListUpdater-0] c.netflix.config.ChainedDynamicProperty : Flipping property: micro-service-1.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647 2021-11-06 16:08:42.641 DEBUG 18720 --- [nio-8081-exec-1] cn.wu.service.feign.BookService : [BookService#testTimeOut] <--- HTTP/1.1 200 (4324ms) 2021-11-06 16:08:42.641 DEBUG 18720 --- [nio-8081-exec-1] cn.wu.service.feign.BookService : [BookService#testTimeOut] connection: keep-alive 2021-11-06 16:08:42.641 DEBUG 18720 --- [nio-8081-exec-1] cn.wu.service.feign.BookService : [BookService#testTimeOut] content-length: 13 2021-11-06 16:08:42.641 DEBUG 18720 --- [nio-8081-exec-1] cn.wu.service.feign.BookService : [BookService#testTimeOut] content-type: text/plain;charset=UTF-8 2021-11-06 16:08:42.641 DEBUG 18720 --- [nio-8081-exec-1] cn.wu.service.feign.BookService : [BookService#testTimeOut] date: Sat, 06 Nov 2021 08:08:42 GMT 2021-11-06 16:08:42.641 DEBUG 18720 --- [nio-8081-exec-1] cn.wu.service.feign.BookService : [BookService#testTimeOut] keep-alive: timeout=60 2021-11-06 16:08:42.641 DEBUG 18720 --- [nio-8081-exec-1] cn.wu.service.feign.BookService : [BookService#testTimeOut] 2021-11-06 16:08:42.643 DEBUG 18720 --- [nio-8081-exec-1] cn.wu.service.feign.BookService : [BookService#testTimeOut] test Timeout 2021-11-06 16:08:42.643 DEBUG 18720 --- [nio-8081-exec-1] cn.wu.service.feign.BookService : [BookService#testTimeOut] <--- END HTTP (13-byte body)