Abstract: the original address: https://zijiancode.cn/archive… Welcome to reprint, reprint, please keep the abstract, thank you!

The samples in the article will use the services in the Nacos article, and the reader can either view the article in conjunction with the Gitee code directly

gitee: https://gitee.com/lzj960515/m…

What is the Ribbon

The Ribbon is a client-side IPC library developed by Netflix that provides the following features

  • Load balancing
  • Fault tolerance
  • Support multiple protocols (HTTP, TCP, UDP)
  • Caching and batching

Making address:
https://github.com/Netflix/ri…

What is client-side load balancing?

In inter-process communication, the service provider (server) has multiple instances, and the service consumer (client) independently selects an instance to initiate the call through the load balancing algorithm, which is the load balancing of the client.

The counterpart to this is server-side load balancing, commonly seen in Nginx

Introduction to Ribbon

1. Introduce dependencies related to the Ribbon

<dependencies> <dependency> <groupId>com.netflix.ribbon</groupId> <artifactId>ribbon</artifactId> < version > 2.3.0 < / version > < / dependency > < the dependency > < groupId > com.net flix. Ribbon < / groupId > <artifactId>ribbon-core</artifactId> </dependency> <dependency> <groupId>com.netflix.ribbon</groupId> <artifactId>ribbon-loadbalancer</artifactId> </dependency> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> </dependency> <dependency> <groupId>io.reactivex</groupId> <artifactId>rxjava</artifactId> </dependency> <dependency> <groupId>io.reactivex</groupId> <artifactId>rxnetty</artifactId> <version>0.4.9</version> </dependency> <dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> </dependency> <! -- used to send a request, --> <dependency> < grouppid >cn.hutool</ grouppid > <artifactId>hutool-http</artifactId> <version>5.6.3</version>  </dependency> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> The < version > 1.2 < / version > < / dependency > < / dependencies >

2. Write code

Public class RibbonDemo {public static void main(String[] args) throws Exception { The second is the number of attempts to call other instances again. 1 is the number of attempts to call another instance again after the failure, or the failure will return the failure. final RetryHandler retryHandler = new DefaultLoadBalancerRetryHandler(0, 1, true); List<Server> serverList = lists.neWarRayList (new Server("127.0.0.1", 8083), new Server("127.0.0.1", 8084)); // create a load balancer The default load balancing algorithm for polling ILoadBalancer loadBalancer. = LoadBalancerBuilder newBuilder () buildFixedServerListLoadBalancer (serverList);  for (int i = 0; i < 6; i++) { String result = LoadBalancerCommand.<String>builder() .withLoadBalancer(loadBalancer) .withRetryHandler(retryHandler) .build() .submit(server -> { String url = "http://" + server.getHost() + ":" + server.getPort() + "/integral/remain"; return Observable.just(HttpUtil.get(url)); }).toBlocking().first(); System.out.println(result); }}}

I’m using it here
Nacos articlePoints service, partners can start a service at random.

3. The test

For two instances, the ports are 8083 and 8084 respectively. The test results are:

Your current points are: 31 Service Port: 8084 Your current points are: 78 Service Port: 8083 Your current points are: 37 Service Port: 8084 Your current points are: 31 Service Port: 8083 Your current points are: 59 Service Port: 8084 Your current points are: 54 Service port: 8083

Stop 8083 and test again

Your current points are: 66 Service Port: 8084 Your current points are: 32 Service Port: 8084 Your current points are: 52 Service Port: 8084 Your current points are: 87 Service Port: 8084 Your current points are: 66 Service Port: 8084 Your current points are: 46 Service port: 8084

You can find that 6 times are successful, and all tuned to port 8084 service

Change the RetryHandler to the following configuration and then test

RetryHandler retryHandler = new DefaultLoadBalancerRetryHandler(0, 0, true);
Your current integral is: 59 service port: 8084 Exception in the thread. "main" cn hutool. Core. IO. IORuntimeException: ConnectException: Connection refused (Connection refused)

The first time the service on port 8084 was called, and the second time the service on port 8083 was called, an exception was thrown

Spring Cloud integrates Ribbon

After understanding the basic use of the Ribbon, the next step is to learn how to integrate it into the micro-service system. Netflix also provides a spring-cloud-starter-Netflix-Ribbon for us to quickly integrate into the Spring Cloud.

Making address: https://github.com/spring-clo…

Note: Select the Tag for 2.x. The Ribbon has been removed for the branches of 3.x

The basic use

1. Introduce a dependency in the my-order service

<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>

In fact, when we introduced a dependency on NaCos, NaCos already introduced the dependency, so we’re just going to make fun of it

2. Configuration RestTemplate

@LoadBalanced
@Bean
public RestTemplate restTemplate(){
  return new RestTemplate();
}

3. Initiate the call

restTemplate.getForObject("http://my-goods/goods/get", String.class);

That’s it? Well, that’s it

RestTemplate Interceptor

After you see the basic use, you must think it’s amazing, so you add a @LoadBalanced annotation, and you’re done?

Let’s talk a little bit about how he works.

In the RESTTemplate, an interceptor mechanism is implemented, for example

public class RestTemplateDemo { public static void main(String[] args) { RestTemplate restTemplate = new RestTemplate();  List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>(); interceptors.add(new RestTemplateInterceptor()); restTemplate.setInterceptors(interceptors); System. Out. Println (restTemplate. GetForObject (" http://127.0.0.1:8083/integral/remain ", String class)); } static class RestTemplateInterceptor implements ClientHttpRequestInterceptor { @Override public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { URI uri = request.getURI(); System.out.println(uri.getRawPath()); return execution.execute(request, body); }}}

The custom interceptor RestTemplateInterceptor is added before the call is made, and when called, it will enter the intercept method

The @LoadBalancerInterceptor annotation adds a LoadBalancerInterceptor to the RestTemplate when the service starts

Test effect:

12:43:33. 585. [the main] the DEBUG org. Springframework. Web. Client. The RestTemplate - HTTP GET http://127.0.0.1:8083/integral/remain 12:43:33. 603. [the main] the DEBUG org. Springframework. Web. Client. The RestTemplate - Accept = [text/plain, application/json, application/*+json, * / *] / integral/remain 12:43:33) 773 [main] the DEBUG org. Springframework. Web. Client. The RestTemplate - 200 OK Response 12:43:33. 775. [the main] the DEBUG org. Springframework. Web. Client. RestTemplate - Reading to [Java. Lang. String] as "text/plain; Charset = utf-8 "Your current credit is: 83 Service port: 8083

Now let’s simulate the Ribbon replacing the server with a real IP address

public class RestTemplateDemo { public static void main(String[] args) { RestTemplate restTemplate = new RestTemplate();  List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>(); interceptors.add(new RestTemplateInterceptor()); restTemplate.setInterceptors(interceptors); System.out.println(restTemplate.getForObject("http://my-goods/goods/get", String.class)); } static class RestTemplateInterceptor implements ClientHttpRequestInterceptor { @SneakyThrows @Override public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { return execution.execute(new MyRequestWrapper(request), body); }} static class MyRequestWrapper extends HttpRequestWrapper {/** ** private final Map<String; String> serverMap = maps.newhashMap ("my-goods", "127.0.1:8081 "); public MyRequestWrapper(HttpRequest request) { super(request); } @SneakyThrows @Override public URI getURI() { URI uri = super.getRequest().getURI(); String server = uri.getHost(); String host = ServerMap.get (server); Return new URI(uri.getScheme() + "://" + host + uri.getRawPath()); }}}

A custom MyRequestWrapper is created and the getUri method is overwritten to perform URI substitution, mimicking the Ribbon’s process of replacing the service name with the real IP.

The logic of getting service information from a ServerMap can be further encapsulated as fetching multiple service information, using a load-balancing policy to fetch one of them.

Load balancing strategy

A number of load-balancing strategies are implemented in the Ribbon, all of which are implemented in an iRule interface

RetryRule: A retry policy containing an internal subRule. Use subRule(polling by default) to select a service. If the service is not available, keep retrying for a configured period of time, directly find the available service or timeout.

RoundrobinRule: Polling policy.

WeightedResponseTimeRule: weighted according to the response time, the longer the response time, the smaller the weight, the lower the possibility of being chosen.

ZoneAVOIDanceRule: The default load-balancing strategy, which gets a list of services based on their region and availability, and then polls. No regions are equivalent to polling.

AvailabilityFilteringRule: using polling choose services, services for state judgment of choice, the filter has been connection service, until you find the normal service available.

BestavaliableRule: Select the service with the least concurrent requests.

RandomRule: Random strategy.

NacosRule: The policy provided by Nacos is the same as the cluster-first policy.

use

Use @Bean annotations

@Configuration public class RuleConfiguration { @Bean public IRule rule(){ return new NacosRule(); }}

The above methods will take effect globally

If you want to set a different load policy for each service, you can configure it as follows:

Configuration mode

my-goods:
    ribbon:
      NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule

Note that this method must have a service name and is used separately
ribbon.NFLoadBalancerRuleClassNameIt’s not going to work

Custom load balancing policies

In addition to using the load-balancing policies that come with the framework, we can implement our own policies, such as same-version-first calls

@Slf4j public class VersionRule extends AbstractLoadBalancerRule { @Autowired private NacosDiscoveryProperties nacosDiscoveryProperties; @Autowired private NacosServiceManager nacosServiceManager; @Override public Server choose(Object key) {try {Map<String, String> metadata = this.nacosDiscoveryProperties.getMetadata(); String version = metadata.get("version"); / / to obtain a list of call service String group = this. NacosDiscoveryProperties. GetGroup (); DynamicServerListLoadBalancer loadBalancer = (DynamicServerListLoadBalancer) getLoadBalancer(); String name = loadBalancer.getName(); NamingService namingService = nacosServiceManager .getNamingService(nacosDiscoveryProperties.getNacosProperties()); List<Instance> instances = namingService.selectInstances(name, group, true); if (CollectionUtils.isEmpty(instances)) { log.warn("no instance in service {}", name); return null; } List<Instance> instancesToChoose = instances; List<Instance> sameclusterInstances = instances.stream() if (StringUtils.isNotBlank(version)) {List<Instance> sameclusterInstances = instances.stream(); .filter(instance -> Objects.equals(version, instance.getMetadata().get("version"))) .collect(Collectors.toList()); if (! CollectionUtils.isEmpty(sameClusterInstances)) { instancesToChoose = sameClusterInstances; } else {log.warn("A cross-cluster call occurs, name = {}, version = {}, instance = {}", name, version, instances); } } Instance instance = ExtendBalancer.getHostByRandomWeight2(instancesToChoose); return new NacosServer(instance); } catch (Exception e) { log.warn("NacosRule error", e); return null; } } @Override public void initWithNiwsConfig(IClientConfig iClientConfig) { } }

Add version configuration:

spring:
  application:
    name: my-order
  main:
    allow-bean-definition-overriding: true
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.1.11:8850
        namespace: public
        username: nacos
        password: nacos
        metadata:
          version: 1.0

Metadata is a Map structure with customizable key and value pairs

Service fault tolerance

When a request for a service fails due to network jitter or other problems, and we wish to retry or call a different service, we can add the following configuration

Ribbon: # with service retries MaxAutoRetries: 1 # try again to call the number of other instances MaxAutoRetriesNextServer: 1 # so operating a retry, default retry only get request OkToRetryOnAllOperations: true # service connection timeout ConnectTimeout: 1000 # service response timeout ReadTimeout: RestClient: enabled: true otherwise the above configuration is invalid

Complete configuration

The grab-load: enabled: true clients: -my-goods The grab-load: enabled: true clients: -my-goods maxAutoretries: 1 # try again to call the number of other instances MaxAutoRetriesNextServer: 1 # so operating a retry, default retry only get request OkToRetryOnAllOperations: ReadTimeout: 2000 RestClient: Enabled: true my-goods: Ribbon: NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule

For configuration information about the Ribbon, see the class CommonClientConfigKey

summary

This article introduces the Ribbon, what the Ribbon is, how to use it, and how to integrate it into the Spring Cloud. It also introduces how to customize the load balancing strategy.

After reading it, you must have some harvest ~ Want to know more exciting content, welcome to pay attention to the public number: programmer A Jian, A Jian in the public number to welcome your arrival ~