Article directory
1. Microservice calls Ribbon
brief introduction
We have learned about eureka service registration and discovery before, but we haven't learned about service invocation combined with eureka cluster.
In this case, we need to use the Ribbon, combined with eureka, to realize the service call;
Ribbon is a load balancer published by Netflix, which helps to control the behavior of HTTP and TCP clients. After the service provider address is configured for the ribbon, the ribbon can automatically help service consumers to request based on some load balancing algorithm. Ribbon provides us with many load balancing algorithms by default, such as polling, random, etc. Of course, we can also implement a custom load balancing algorithm for the ribbon.
In Spring Cloud, when the Ribbon is used with Eureka, the Ribbon can automatically obtain the service provider address list from Eureka Server, and request one of the service provider instances based on the load balancing algorithm. It shows the architecture of Ribbon when used with Eureka.
Preliminary application
Ribbon is the client side load balancing, so it must integrate the consumer side, that is, the consumer side
Modify microservice-student-consumer-80
pom.xml adds ribbon related dependency
<!--ribbon Dependent dependence--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-ribbon</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency>
Add in application.yml
server: port: 80 context-path: / eureka: client: service-url: defaultZone: http://eureka2001.wr.com:2001/eureka/,http://eureka2002.wr.com:2002/eureka/,http://eureka2003.wr.com:2003/eureka/ register-with-eureka: false
The ribbon calls the service provider in combination with eureka;
Spring cloudconfig needs to add a load balancing configuration @ LoadBalanced
Because it is integrated with eureka, start the class studentconsumerapplication and annotate @ EnableEurekaClient
Add configuration to application.yml of service provider microservice-student-provider-1001, and specify the following application name:
application: name: microservice-student
To modify the PRE_HOST of StudentConsumerController, change it to the specified microservice application name;
Our microservice application name is microservice student
Therefore, in the controller on the service caller's side, the pre ﹣ host can be changed to http://microservice-study;
Microservice-study is the application name of Eureka registry
After the above configuration is completed, you can test it
Start three eureka first, then the service provider, then the service consumer;
If the following result appears, the configuration is successful
2. Ribbon load balancing
According to microservice-student-provider-1001, build a microservice-student-provider subproject, and then kill the subproject microservice-student-provider-1001;
copy all packages in microservice-student-provider-1001 to microservice-student-provider
Then related pom
<?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>com.wr</groupId> <artifactId>T237microservice</artifactId> <version>1.0-SNAPSHOT</version> </parent> <artifactId>microservice-student-provider</artifactId> <properties> <java.version>1.8</java.version> </properties> <dependencies> <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> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> </dependency> <dependency> <groupId>com.wr</groupId> <artifactId>microservice-common</artifactId> <version>1.0-SNAPSHOT</version> <scope>compile</scope> </dependency> <!--Add registry Eureka Related configuration--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <!-- actuator Monitoring introduction --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
application.yml
--- server: port: 1001 context-path: / spring: datasource: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/xm?useUnicode=true&characterEncoding=utf8 username: root password: 1234 jpa: hibernate: ddl-auto: update show-sql: true application: name: microservice-student profiles: provider-1001 eureka: instance: hostname: localhost appname: microservice-student instance-id: microservice-student:1001 prefer-ip-address: true client: service-url: defaultZone: http://eureka2001.wr.com:2001/eureka/,http://eureka2002.wr.com:2002/eureka/,http://eureka2003.wr.com:2003/eureka/ info: groupId: com.wr.testSpringcloud artifactId: microservice-student-provider-1001 version: 1.0-SNAPSHOT userName: http://wr.com phone: 123456 --- server: port: 1002 context-path: / spring: datasource: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/xm?useUnicode=true&characterEncoding=utf8 username: root password: 1234 jpa: hibernate: ddl-auto: update show-sql: true application: name: microservice-student profiles: provider-1002 eureka: instance: hostname: localhost appname: microservice-student instance-id: microservice-student:1002 prefer-ip-address: true client: service-url: defaultZone: http://eureka2001.wr.com:2001/eureka/,http://eureka2002.wr.com:2002/eureka/,http://eureka2003.wr.com:2003/eureka/ info: groupId: com.wr.testSpringcloud artifactId: microservice-student-provider-1002 version: 1.0-SNAPSHOT userName: http://wr.com phone: 123456 --- server: port: 1003 context-path: / spring: datasource: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/xm?useUnicode=true&characterEncoding=utf8 username: root password: 1234 jpa: hibernate: ddl-auto: update show-sql: true application: name: microservice-student profiles: provider-1003 eureka: instance: hostname: localhost appname: microservice-student instance-id: microservice-student:1003 prefer-ip-address: true client: service-url: defaultZone: http://eureka2001.wr.com:2001/eureka/,http://eureka2002.wr.com:2002/eureka/,http://eureka2003.wr.com:2003/eureka/ info: groupId: com.wr.testSpringcloud artifactId: microservice-student-provider-1003 version: 1.0-SNAPSHOT userName: http://wr.com phone: 123456
Startup class
package com.wr.microservicestudentprovider; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.domain.EntityScan; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; @EntityScan("com.wr.*.*") @EnableEurekaClient @SpringBootApplication public class MicroserviceStudentProviderApplication { public static void main(String[] args) { SpringApplication.run(MicroserviceStudentProviderApplication.class, args); } }
StudentProviderController.java
@Value("${server.port}") private String port; @RequestMapping("/ribbon") public String ribbon(){ return "Job number ["+port+"]Serving you"; }
StudentConsumerController.java
@RequestMapping("/ribbon") public String ribbon(){ return restTemplate.getForObject(SERVER_IP_PORT + "/student/ribbon", String.class); }
Last configuration, start (start the registry, then the service producer, and finally the service consumer)
The above is just a preliminary example. It has not really used ribbon load balancing yet. Now we need to build three service provider clusters
Final test results
Eureka Registration Center: http://eureka2001.wr.com:2001/
Service producer:
Service consumer: http://localhost/student/list
http://localhost/student/ribbon
If a service producer, provider1001aapplication, is disconnected at this time, and the 1001 producer may be connected when executing http://localhost/student/ribbon, it will be down
This is also true when executing http://localhost/student/list
This default polling strategy can't meet the actual needs. For example, there are three service providers who suddenly hang up one. In this case, the default polling always has a 1 / 3 probability of access failure. So let's take a look at the strategies that ribbon provides by default??
We use custom polling algorithm here
Service consumer SpringCloudConfig configuration class
Specify the IRule implementation;
3. Feign introduction and Application
brief introduction
A brief introduction to the declarative service call Feign;
Feign is a declarative Web Service client, which makes it easier to write a Web Service client. We just need to use feign to create an interface and configure it with annotations. It has pluggable annotation support, including feign annotation and JAX-RS annotation. Feign also supports pluggable encoders and decoders. Spring Cloud adds support for Spring MVC annotation to feign, and integrates Ribbon and Eureka to provide load balanced HTTP client implementation.
This passage seems official. Let's talk about the actual use. The previous Ribbon calls the service provider through restTemplate. The disadvantage is that the same request needs to be written multiple times, which is inconvenient for unified maintenance. When Feign comes, it's necessary to unify the request to make a service as FeignClient, and then other calls to the Controller need to be used Connect the injection service and call the service method directly; meanwhile, Feign integrates the Ribbon and Eureka, so if you want to configure the load balance, you can configure the Ribbon directly, without any other special place; of course, Fiegn also integrates the service fault-tolerant protection, breaker Hystrix, later on.
application
1. Build a service in the common project (the actual project must be multiple services) as Feign client, use Feign client to call the server provider, of course, you can configure load balancing; the purpose of Feign client definition is to facilitate calls to other projects;
Modify microservice common
To introduce Feign dependency into pom.xml:
<!--Introduce Feign rely on--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-feign</artifactId> </dependency>
We defined FeignClient and specified the service name microservice-study to be called
After the common project is modified, under maven clean and then install;
Create a new StudentClientService interface;
package com.wr.microservicecommon.service; import com.wr.microservicecommon.entity.Student; import org.springframework.cloud.netflix.feign.FeignClient; 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.RequestMapping; import java.util.List; /** * Student Feign Interface client * @author Administrator * */ @FeignClient(value="MICROSERVICE-STUDENT") public interface StudentClientService { /** * Query student information according to id * @param id * @return */ @GetMapping(value="/student/get/{id}") public Student get(@PathVariable("id") Integer id); /** * Query student information * @return */ @GetMapping(value="/student/list") public List<Student> list(); /** * Add or modify student information * @param student * @return */ @PostMapping(value="/student/save") public boolean save(Student student); /** * Delete student information according to id * @return */ @GetMapping(value="/student/delete/{id}") public boolean delete(@PathVariable("id") Integer id); @RequestMapping("/student/ribbon") public String ribbon(); }
Create a Feign consumer project;
Refer to microservice-student-consumer-80 to build a microservice-student-consumer-feign-80
Copy the code, including pom.xml
Pom dependence
<?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>com.wr</groupId> <artifactId>T237microservice</artifactId> <version>1.0-SNAPSHOT</version> </parent> <artifactId>microservice-student-consumer-feign-80</artifactId> <properties> <java.version>1.8</java.version> </properties> <dependencies> <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> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </dependency> <!-- Effective immediately after modification, hot deployment --> <dependency> <groupId>org.springframework</groupId> <artifactId>springloaded</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> <!--ribbon Dependent dependence--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-ribbon</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <!--Introduce Feign rely on--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-feign</artifactId> </dependency> <dependency> <groupId>com.wr</groupId> <artifactId>microservice-common</artifactId> <version>1.0-SNAPSHOT</version> <scope>compile</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
SpringCloudConfig.java
package com.wr.microservicestudentconsumerfeign80.config; import com.netflix.loadbalancer.IRule; import com.netflix.loadbalancer.RetryRule; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate; @Configuration public class SpringCloudConfig { @LoadBalanced // Introducing ribbon load balancing @Bean public RestTemplate getRestTemplate() { return new RestTemplate(); } /** * Custom call rules (the service provider will not call after dropping the line to solve the polling problem) * @return */ @Bean public IRule myRule(){ return new RetryRule(); // return new RandomRule(); } }
yml file
server: port: 80 context-path: / eureka: client: service-url: defaultZone: http://eureka2001.javaxl.com:2001/eureka/,http://eureka2002.javaxl.com:2002/eureka/,http://eureka2003.javaxl.com:2003/eureka/ register-with-eureka: false
Modify startup class
package com.wr.microservicestudentconsumerfeign80; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; import org.springframework.cloud.netflix.feign.EnableFeignClients; @EnableEurekaClient @EnableFeignClients(value = "com.wr.*.*") @SpringBootApplication(exclude={DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class}) public class MicroserviceStudentConsumerFeign80Application { public static void main(String[] args) { SpringApplication.run(MicroserviceStudentConsumerFeign80Application.class, args); } }
StudentConsumerController.java
package com.wr.microservicestudentconsumerfeign80.controller; import com.wr.microservicecommon.entity.Student; import com.wr.microservicecommon.service.StudentClientService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import org.springframework.web.client.RestTemplate; import java.util.List; @RestController @RequestMapping("/student") public class StudentConsumerController { @Autowired private StudentClientService studentClientService; @Autowired private RestTemplate restTemplate; @PostMapping(value = "/save") private boolean save(Student student) { return studentClientService.save(student); } @GetMapping(value = "/list") public List<Student> list() { return studentClientService.list(); } @GetMapping(value = "/get/{id}") public Student get(@PathVariable("id") Integer id) { return studentClientService.get(id); } @GetMapping(value = "/delete/{id}") public boolean delete(@PathVariable("id") Integer id) { try { studentClientService.delete(id); return true; } catch (Exception e) { return false; } } @RequestMapping("/ribbon") public String ribbon(){ return studentClientService.ribbon(); } }
Because with Fiegn, the restTemplate is removed, and the service is injected instead, and the service method is called to realize the service call;
Finally, test the load balance;
The result is consistent with the figure above, except that when one of the services goes down, the other two can be called directly and continue to use.