1. What is Ribbon
At present, the mainstream load schemes are divided into the following two types:
- Centralized load balancing uses independent agents between consumers and service providers, including hardware (such as F5) and software (such as Nginx).
- The client does load balancing according to its own request, and the Ribbon belongs to the client to do load balancing.
Spring Cloud Ribbon is a set of client-side load balancing tools based on Netflix Ribbon. The ribbon client component provides a series of perfect configurations, such as timeout, Retry, etc. Get all machine instances provided by the service through the Load Balancer, and the ribbon will automatically call these services based on certain rules (polling, random). Ribbon can also implement our own load balancing algorithm.
1.1 load balancing of client
For example, in the ribbon in spring cloud, the client will have a server address list. Before sending a request, select a server through the load balancing algorithm and then access it. This is client load balancing; That is, the load balancing algorithm is allocated on the client.
1.2 load balancing at the server
For example, Nginx performs load balancing through Nginx, first sends a request, and then selects one of multiple servers for access through the load balancing algorithm; That is, the load balancing algorithm is allocated on the server side.
1.3 common load balancing algorithms
- Random, which is executed by randomly selecting services. Generally, this method is less used;
- Rotation training, the default implementation mode of load balancing, and queuing processing after requests come;
- Weighted rotation training, through the classification of server performance, assigns higher weights to servers with high configuration and low load, so as to balance the pressure of each server;
- The address hash is used to schedule the server through the modular mapping of the hash value of the address requested by the client. ip hash
- The minimum number of links. Even if the request is balanced, the pressure may not be balanced. The minimum number of connections method is to allocate the request to the server with the lowest current pressure according to the server's conditions, such as request backlog and other parameters. Minimum active number
1.4 Ribbon module
name | explain |
---|---|
ribbon-loadbalancer | Load balancing module can be used independently or together with other modules. |
Ribbon | The built-in load balancing algorithms are implemented in it. |
ribbon-eureka | The module based on Eureka package can integrate Eureka quickly and conveniently. |
ribbon-transport | Support multiple protocols based on Netty, such as HTTP, Tcp, Udp, etc. |
ribbon-httpclient | Based on the REST client encapsulated by Apache HttpClient, it integrates the load balancing module and can be directly used in the project to call the interface. |
ribbon-example | Ribbon uses code examples that can get twice the result with half the effort. |
ribbon-core | Some core and universal codes, some configurations of client APIs and definitions of other APIs. |
1.5 Ribbon usage
Write a client to call the interface
public class RibbonDemo { public static void main(String[] args) { // Service list List<Server> serverList = Lists.newArrayList( new Server("localhost", 8020), new Server("localhost", 8021)); // Building load instances ILoadBalancer loadBalancer = LoadBalancerBuilder.newBuilder() .buildFixedServerListLoadBalancer(serverList); // Call 5 times to test the effect for (int i = 0; i < 5; i++) { String result = LoadBalancerCommand.<String>builder() .withLoadBalancer(loadBalancer).build() .submit(new ServerOperation<String>() { @Override public Observable<String> call(Server server) { String addr = "http://" + server.getHost() + ":" + server.getPort() + "/order/findOrderByUserId/1"; System.out.println(" Call address:" + addr); URL url = null; try { url = new URL(addr); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.connect(); InputStream in = conn.getInputStream(); byte[] data = new byte[in.available()]; in.read(data); return Observable.just(new String(data)); } catch (Exception e) { e.printStackTrace(); } return null; } }).toBlocking().first(); System.out.println(" Call result:" + result); } } }
The above example mainly demonstrates how the Ribbon performs load operations and calls the bottom layer HttpURLConnection of the interface.
2. Spring Cloud rapid integration Ribbon
2.1. Import dependency
<!--add to ribbon Dependence of--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
Nacos discovery relies on ribbon, so you don't need to introduce ribbon dependency
2.2. Add @ LoadBalanced annotation
@Configuration public class RestConfig { @Bean @LoadBalanced public RestTemplate restTemplate() { return new RestTemplate(); }
2.3. Modifying controller
@Autowired private RestTemplate restTemplate; @RequestMapping(value = "/findOrderByUserId/{id}") public R findOrderByUserId(@PathVariable("id") Integer id) { // RestTemplate call //String url = "http://localhost:8020/order/findOrderByUserId/"+id; //Simulation ribbon implementation //String url = getUri("mall-order")+"/order/findOrderByUserId/"+id; // Add @ LoadBalanced String url = "http://mall-order/order/findOrderByUserId/"+id; R result = restTemplate.getForObject(url,R.class); return result; }
3. Ribbon kernel principle
3.1 Ribbon principle
3.1.1 simulation ribbon implementation
@Autowired private RestTemplate restTemplate; @RequestMapping(value = "/findOrderByUserId/{id}") public R findOrderByUserId(@PathVariable("id") Integer id) { // RestTemplate call //String url = "http://localhost:8020/order/findOrderByUserId/"+id; //Simulation ribbon implementation String url = getUri("mall-order")+"/order/findOrderByUserId/"+id; // Add @ LoadBalanced //String url = "http://mall-order/order/findOrderByUserId/"+id; R result = restTemplate.getForObject(url,R.class); return result; } @Autowired private DiscoveryClient discoveryClient; public String getUri(String serviceName) { List<ServiceInstance> serviceInstances = discoveryClient.getInstances(serviceName); if (serviceInstances == null || serviceInstances.isEmpty()) { return null; } int serviceSize = serviceInstances.size(); //polling int indexServer = incrementAndGetModulo(serviceSize); return serviceInstances.get(indexServer).getUri().toString(); } private AtomicInteger nextIndex = new AtomicInteger(0); private int incrementAndGetModulo(int modulo) { for (;;) { int current = nextIndex.get(); int next = (current + 1) % modulo; if (nextIndex.compareAndSet(current, next) && current < modulo){ return current; } } }
3.1.2 @LoadBalanced annotation principle
Reference source code: loadbalancenautoconfiguration
@LoadBalanced uses @ Qualifier as the filter condition injected by restTemplates to filter out restTemplates with load balancing ID.
The restTemplate annotated by @ LoadBalanced will be customized, and the loadbalancerinceptor interceptor interceptor will be added.
3.1.3 Ribbon related interfaces
Reference: org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration
-
IClientConfig: the client configuration of Ribbon, which is implemented by DefaultClientConfigImpl by default.
-
IRule: Ribbon's load balancing policy is implemented by ZoneAvoidanceRule by default. This policy can select the best region instance for access in a multi region environment.
-
IPing: the instance checking policy of Ribbon is implemented by DummyPing by default. This checking policy is a special implementation. In fact, it does not check whether instances are available, but always returns true. By default, it is considered that all service instances are available.
-
ServerList: the maintenance mechanism of service instance list, which is implemented by ConfigurationBasedServerList by default.
-
ServerListFilter: service instance list filtering mechanism. By default, ZonePreferenceServerListFilter is adopted. This policy can give priority to filtering out service instances in the same region as the requestor.
-
ILoadBalancer: the load balancer is implemented by ZoneAwareLoadBalancer by default. It has the ability of region awareness.
3.2 Ribbon load balancing strategy
- Random rule: randomly select a Server.
- RetryRule: for the on-board retry mechanism of the selected load balancing policy, if the server selection is unsuccessful within a configured time period, it has been trying to select an available server by using the subRule method.
- Roundrobin rule: polling selection, polling index, and selecting the Server corresponding to the index.
- Availability filtering rule: filter out the back-end servers marked as circuit tripped that have failed to connect all the time, and filter out those high concurrency back-end servers, or use an availability predicate to include the logic of filtering servers. In fact, it is to check the running status of each server recorded in status.
- Best available rule: select a Server with the smallest concurrent request, and inspect the servers one by one. If the Server is tripped, skip it.
- WeightedResponseTimeRule: weighted according to the response time. The longer the response time, the smaller the weight, and the lower the possibility of being selected.
- ZoneAvoidanceRule: the default load balancing policy is to judge the performance of the region where the Server is located and the availability of the Server, and select Server. In the environment without region, it is similar to random rule
- NacosRule: call in the same cluster first
3.2.1 modify the default load balancing policy
**Global configuration: * * when calling other microservices, the specified load balancing algorithm is used
@Configuration public class RibbonConfig { /** * Global configuration * Specify load balancing policy */ @Bean public IRule() { // Specify to use the load balancing policy provided by Nacos (preferentially call instances of the same cluster based on random weight) return new NacosRule(); }
Local configuration: when calling the service provided by the specified microservice, the corresponding load balancing algorithm is used
Modify application.yml
# The name of the micro service being called mall-order: ribbon: # Specify to use the load balancing policy provided by Nacos (preferentially call instances of the same cluster based on random & weight) NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule
3.2.2 user defined load balancing strategy
The load policy can be customized by implementing the IRule interface. The main selection service logic is in the choose method.
1) Implement load balancing strategy based on Nacos weight
@Slf4j public class NacosRandomWithWeightRule extends AbstractLoadBalancerRule { @Autowired private NacosDiscoveryProperties nacosDiscoveryProperties; @Override public Server choose(Object key) { DynamicServerListLoadBalancer loadBalancer = (DynamicServerListLoadBalancer) getLoadBalancer(); String serviceName = loadBalancer.getName(); NamingService namingService = nacosDiscoveryProperties.namingServiceInstance(); try { //nacos weight based algorithm Instance instance = namingService.selectOneHealthyInstance(serviceName); return new NacosServer(instance); } catch (NacosException e) { log.error("Exception getting service instance:{}", e.getMessage()); e.printStackTrace(); } return null; } @Override public void initWithNiwsConfig(IClientConfig clientConfig) { }
2) Configure custom policies
2.1) local configuration:
Modify application.yml
# The name of the micro service being called mall-order: ribbon: # Custom load balancing policy (based on random & weight) NFLoadBalancerRuleClassName: com.tuling.mall.ribbondemo.rule.NacosRandomWithWeightRule
2.2) global configuration
@Bean public IRule ribbonRule() { return new NacosRandomWithWeightRule(); }
3) Local configuration mode 2
You can use @ RibbonClient to specify microservices and their load balancing policies.
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class,DruidDataSourceAutoConfigure.class}) //@RibbonClient(name = "mall-order",configuration = RibbonConfig.class) //Configuring multiple ribbonconfigs cannot be scanned by @ CompentScan of @ SpringbootApplication, otherwise it is the effect of global configuration @RibbonClients(value = { // Define the configuration class outside the package scanned by the SpringBoot main program @RibbonClient(name = "mall-order",configuration = RibbonConfig.class), @RibbonClient(name = "mall-account",configuration = RibbonConfig.class) }) public class MallUserRibbonDemoApplication { public static void main(String[] args) { SpringApplication.run(MallUserRibbonDemoApplication.class, args); } }
**Note: there are pits here** It cannot be written in the @ CompentScan scan of the @ SpringbootApplication annotation, otherwise the custom configuration class will be shared by all RibbonClients. This is not recommended. yml is recommended
[the external chain picture transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-xNnqkfmy-1634186801538)(assets/13571.png)]
3.3 starvation loading
When calling a service, if the network condition is bad, the first call will timeout.
The Ribbon is lazy by default, which means that the client will be created only when the call is initiated.
Enable hungry loading to solve the problem of slow calling for the first time
ribbon: eager-load: # Enable ribbon starvation loading enabled: true # Configure mall user to load using ribbon, and multiple are separated by commas clients: mall-order
Source code corresponding property configuration class: RibbonEagerLoadProperties
Test: