Abstract: the original address: zijiancode. Cn/archives/ri… Welcome to reprint, please keep the abstract when reprint, thank you!
The sample in this article uses the services in the Nacos article, which can be viewed either directly from the article or in conjunction with gitee code
Gitee: gitee.com/lzj960515/m…
What is the Ribbon
Ribbon is a client-side IPC(interprocess communication) library developed by Netflix that provides the following features
- Load balancing
- Fault tolerance
- Supports multiple protocols (HTTP, TCP, and UDP)
- Caching and batching
Github address: github.com/Netflix/rib…
What is client load balancing?
In inter-process communication, the service provider (server) has multiple instances, and the service consumer (client) autonomously selects an instance to initiate invocation through the load balancing algorithm, which is the load balancing of the client.
The counterpart is load balancing on the server side, as is common with Nginx
Introduction to Ribbon
1. Introduce Ribbon dependencies
<dependencies>
<dependency>
<groupId>com.netflix.ribbon</groupId>
<artifactId>ribbon</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>com.netflix.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 requests, you can also use JDK -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-http</artifactId>
<version>5.6.3</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
Copy the code
2. Write code
public class RibbonDemo {
public static void main(String[] args) throws Exception {
The first parameter is the number of times the same instance was called. The second parameter is the number of times other instances were tried again.
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 is 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";
returnObservable.just(HttpUtil.get(url)); }).toBlocking().first(); System.out.println(result); }}}Copy the code
Here, I used the credits service of Nacos. Friends can choose a service by themselves.
3. The test
Create two instances, port 8083 and port 8084, test result:
Your current credits are: 31 Service port: 8084 Your current credits are: 78 Service port: 8083 Your current credits are: 37 Service port: 8084 Your current credits are: 31 Service port: 8083 Your current credits are: 59 Service port: 8084 54 Service port: 8083Copy the code
Stop the 8083 and test again
Your current credits are: 66 Service port: 8084 Your current credits are: 32 Service port: 8084 Your current credits are: 52 Service port: 8084 Your current credits are: 87 Service port: 8084 Your current credits are: 66 Service port: 8084 46 Service port: 8084Copy the code
It can be found that all six times are successful, and all the services are switched to port 8084
Test by changing the RetryHandler to the following configuration
RetryHandler retryHandler = new DefaultLoadBalancerRetryHandler(0, 0, true);
Copy the code
Your current integral is: 59 service port: 8084 Exception in the thread. "main" cn hutool. Core. IO. IORuntimeException: ConnectException: Connection refused (Connection refused)Copy the code
The service on port 8084 was called the first time, and an exception was thrown the second time
Spring Cloud integrates Ribbon
After understanding the basic usage of the Ribbon, the next step is to learn how to integrate it into the microservice system. Netflix also provides a Spring-cloud-starter-Netflix-Ribbon for quick integration into spring Cloud.
Github address: github.com/spring-clou…
Note: Select the 2.x tag. The 3.x branch has been removed from the Ribbon
The basic use
1. Introduce dependencies in the my-Order service
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
Copy the code
Nacos dependency has already been introduced when Nacos dependency is introduced
2. Configuration RestTemplate
@LoadBalanced
@Bean
public RestTemplate restTemplate(a){
return new RestTemplate();
}
Copy the code
3. Initiate the call
restTemplate.getForObject("http://my-goods/goods/get", String.class);
Copy the code
That’s gone? Well, that’s gone
RestTemplate Interceptor
After viewing the basic usage, you must be surprised to see that you have added a @loadBalanced annotation.
Let’s talk a little bit about how it works.
In RestTemplate, the 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());
returnexecution.execute(request, body); }}}Copy the code
A custom RestTemplateInterceptor is added before the call, which will enter the Intercept method when called
The @loadBalanced annotation adds an interceptor, LoadBalancerInterceptor, to the RestTemplate at service startup
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 score is: 83 Service port: 8083Copy the code
Now let’s simulate the Ribbon by 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(newMyRequestWrapper(request), body); }}static class MyRequestWrapper extends HttpRequestWrapper {
/** * Emulated registry storage service information */
private final Map<String, String> serverMap = Maps.newHashMap("my-goods"."127.0.0.1:8081");
public MyRequestWrapper(HttpRequest request) {
super(request);
}
@SneakyThrows
@Override
public URI getURI(a) {
URI uri = super.getRequest().getURI();
String server = uri.getHost();
// Simulate fetching the real IP from the registry
String host = serverMap.get(server);
/ / replace URI
return new URI(uri.getScheme() + ": / /"+ host + uri.getRawPath()); }}}Copy the code
We define MyRequestWrapper and override the getURI method to replace the URI. This mimicked the Ribbon’s process of replacing service names with real IP addresses.
The logic to get service information from the serverMap can be further encapsulated to fetch multiple service information, using a load balancing policy to fetch one of them.
Load Balancing Policy
There are many load balancing policies implemented in the Ribbon, all of which are implemented in an IRule interface
RetryRule: A retry policy that contains an internal subRule. It uses subRule(polling by default) to select services. If a service is not available, the system tries again within the configured time to find available services or times out.
RoundRobinRule: indicates a 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 policy that gets a list of services based on their area and availability and then polls them. No region is 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: Selects the service with the least concurrent requests.
RandomRule: a RandomRule strategy.
NacosRule: policy provided by Nacos, same cluster first policy.
use
Use @bean annotations
@Configuration
public class RuleConfiguration {
@Bean
public IRule rule(a){
return newNacosRule(); }}Copy the code
The above methods take effect globally
If you want to set a different load policy for each service, do as follows:
Configuration mode
my-goods:
ribbon:
NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule
Copy the code
Note that this way must with a service name, used alone ribbon. NFLoadBalancerRuleClassName will not take effect
User-defined load balancing policies
In addition to using the built-in load balancing policies in the framework, we can also implement our own policies, such as same-version-first call
@Slf4j
public class VersionRule extends AbstractLoadBalancerRule {
@Autowired
private NacosDiscoveryProperties nacosDiscoveryProperties;
@Autowired
private NacosServiceManager nacosServiceManager;
@Override
public Server choose(Object key) {
try {
// Get metadata information
Map<String, String> metadata = this.nacosDiscoveryProperties.getMetadata();
// Retrieve the version information
String version = metadata.get("version");
// Get the list of invoked services
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;
if (StringUtils.isNotBlank(version)) {
// Select services of the same 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) {}}Copy the code
Add version configuration:
spring:
application:
name: my-order
main:
allow-bean-definition-overriding: true
cloud:
nacos:
discovery:
server-addr: 192.1681.11.: 8850
namespace: public
username: nacos
password: nacos
metadata:
version: 1.0
Copy the code
Metadata is a map structure with customizable key-value pairs
Service fault tolerance
Sometimes the request fails due to network jitter or other problems. In this case, you can try again or invoke another service. You can add the following configuration
ribbon:
Number of retries for the same service
MaxAutoRetries: 1
# Number of attempts to call other instances again
MaxAutoRetriesNextServer: 1
By default, only GET requests are retried
OkToRetryOnAllOperations: true
Service connection timeout
ConnectTimeout: 1000
Service response timeout
ReadTimeout: 2000
Otherwise, the configuration is invalid
restclient:
enabled: true
Copy the code
Complete configuration
ribbon:
# hungry load, create client at startup
eager-load:
enabled: true
clients:
- my-goods
Number of retries for the same service
MaxAutoRetries: 1
# Number of attempts to call other instances again
MaxAutoRetriesNextServer: 1
By default, only GET requests are retried
OkToRetryOnAllOperations: false
Service connection timeout
ConnectTimeout: 1000
Service response timeout
ReadTimeout: 2000
restclient:
enabled: true
my-goods:
ribbon:
NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule
Copy the code
For configuration information about the Ribbon, see the CommonClientConfigKey class
summary
This article introduces the Ribbon, what it is, how to use it, how to integrate it into Spring Cloud, and how to customize load balancing policies.
If you want to know more exciting content, welcome to pay attention to the public account: programmer AH Jian, AH Jian in the public account welcome your arrival ~