Introduction
We continue to build on the code from previous blogs and add Ribbon components to provide client load balancing. Load balancing is an important part of achieving high concurrency, high performance, scalable services. It can distribute requests to different servers in a cluster to relieve the burden on each server. Client load balancing is run in client programs, such as our web project, and then randomly select a server to send requests by obtaining the IP address list of the cluster. Compared with server load balancing, it does not require consuming server resources.
Basic environment
Git: Project source code
Update configuration
This time we need to start two product service programs locally to verify load balancing, so we need to provide different ports for the second program. The configuration of Spring Cloud Configuration Service Center will override the local system environment variables by default. We need to set the port of the product service through the system environment variables, so we need to modify the configuration file of the product service in the configuration center git repository.
server: port: 8081spring: cloud: config: allow-override: true override-system-properties: false
The default value of allow-override is true. I wrote it out to explain it. It means that the configuration items of the remote configuration center are allowed to overwrite the local configuration, not that the local configuration is allowed to overwrite the remote configuration. Of course we can set it to false, but to provide more precise coverage rules, the default value is preserved here.
We have added override-system-properties=false, that is, although the configuration file of the remote configuration center can overwrite the local configuration, do not overwrite the local system variables. After the modification is completed, submit it to the git repository.
In addition, add some logs to the ProductController of the productService project to verify whether load balancing is effective:
package cn.zxuqian.controllers;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;@RestControllerpublic class ProductController { private static Logger log = LoggerFactory.getLogger(ProductController.class); @RequestMapping("/products") public String productList() { log.info("Access to /products endpoint"); return "Coat, jacket, sweater, T-shirt"; }}Configure Ribbon for web
First add Ribbon's dependencies in pom.xml:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-ribbon</artifactId></dependency>
Then modify the Application class and add the following code:
@EnableCircuitBreaker@EnableDiscoveryClient@RibbonClient(name = "product-service")@SpringBootApplicationpublic class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } @Bean @LoadBalanced public RestTemplate rest(RestTemplateBuilder builder) { return builder.build(); }} Here we use the @RibbonClient(name = "product-service") annotation to mark this project as a Ribbon load balancing client. It needs to select one of the product service clusters to access the required services. The name attribute here corresponds to the spring.application.name attribute configured in the productService project.
The @LoadBalanced annotation indicates that RestTemplate will be configured to automatically use Ribbon's LoadBalancerClient to select the service's uri and send the request.
We add the following code to the ProductService class:
@Servicepublic class ProductService { private final RestTemplate restTemplate; @Autowired private DiscoveryClient discoveryClient; public ProductService(RestTemplate restTemplate) { this.restTemplate = restTemplate; } @HystrixCommand(fallbackMethod = "backupProductList") public String productList() { List<ServiceInstance> instances = this.discoveryClient.getInstances("product-service"); if(instances != null && instances.size() > 0) { return this.restTemplate.getForObject(instances.get(0).getUri() + "/products", String.class); } return ""; } public String backupProductList() { return "Jack, Sweater"; } public String productListLoadBalanced() { return this.restTemplate.getForObject("http://product-service/products", String.class); }}A new productListLoadBalanced method has been added here, which is accessed the same service as the previous productList method, but it is load balancing with the Ribbon Client. Here, the host of uri has become the name of the service to be accessed, which is consistent with the name attribute configured in @RibbonClient. Finally, add the following code in our ProductController:
@RestControllerpublic class ProductController { @Autowired private ProductService productService; @RequestMapping("/products") public String productList() { return productService.productList(); } @RequestMapping("/productslb") public String productListLoadBalanced() { return productService.productListLoadBalanced(); }}To create a method that specifically handles /productslb requests, and call productServie to provide load balancing.
At this point our code is completed. The code seems simple, but in fact all configurations use default values. Ribbon provides programming and configuration methods to configure Ribbon Client. Let me give you a brief introduction. When you go deeper into Ribbon, let’s take a look at how to modify its configuration. Ribbon provides the following configuration (the interface is on the left and the default implementation is on the right):
Because our project uses Eureka, some configuration items are different from the default implementation. For example, Eureka uses DiscoveryEnabledNIWSServerList instead of ribbonServerList to get a list of services registered on Eureka. There is a simple Configuration class below, from Spring official website:
public class SayHelloConfiguration { @Autowired IClientConfig ribbonClientConfig; @Bean public IPing ribbonPing(IClientConfig config) { return new PingUrl(); } @Bean public IRule ribbonRule(IClientConfig config) { return new AvailabilityFilteringRule(); }}Ribbon will not send Ping to check the server's health status by default, and the default is normal. Then IRune is implemented as ZoneAvoidanceRule by default to avoid the zone with many problems with AWS EC2. This is not available in the local test environment. It is then replaced with AvailabilityFilteringRule. This can enable the circuit breaker function that comes with Ribbon to filter servers that are not working properly.
test
First, start our configserver configuration center service, then start the registration Eureka registration and discovery service, and then start two productServices. The first one is to use the spring-boot:run plug-in to start, and the second one is to provide it with a new port, which can be started with the following command:
$ SERVER_PORT=8082 mvn spring-boot:run
Finally, start our web client project, visit http://localhost:8080/productslb, and then refresh a few times. You will see that two command line windows running productService will randomly appear:
Access to /products endpoint
The above is all the content of this article. I hope it will be helpful to everyone's learning and I hope everyone will support Wulin.com more.