前言

  • Spring Cloud Ribbon是为客户端提供负载均衡功能的服务,简单来说,就是从注册中心Eureka、Consul获取可用服务实例列表,然后将请求根据某种策略发到这些实例上面执行

简单示例

1、集成ribbon
1
2
3
4
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
2、消费方
  • 定义消费方调用方法,通过@LoadBalanced注解开启客户端负债均衡

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    @Configuration
    @RestController
    public class DemoController {
    /**
    * 主要用来调用REST服务,本身并不具备调用分布式服务的能力,但通过@LoadBalanced注解开启客户端负债均衡
    * @return
    */
    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate() {
    return new RestTemplate();
    }

    @RequestMapping(value = "/router", method = RequestMethod.GET,
    produces = MediaType.APPLICATION_JSON_VALUE)
    public String router() {
    RestTemplate restTpl = getRestTemplate();
    // 根据应用名称调用服务
    String json = restTpl.getForObject("http://eureka-provider/person/1", String.class);
    return json;
    }
    }
  • 消费方配置文件

1
2
3
4
5
6
7
8
9
10
11
server:
port: 9050
spring:
application:
name: eureka-consumer
eureka:
instance:
hostname: localhost
client:
service-url:
defaultZone: http://localhost:9010/eureka/,http://localhost:9020/eureka/
3、服务方
  • 服务提供方法, 这里就是简单模拟了一下根据personId获取人员信息,并返回对应服务的端口

    1
    2
    3
    4
    5
    6
    7
    @RequestMapping(value = "/person/{personId}", method = RequestMethod.GET,
    produces = MediaType.APPLICATION_JSON_VALUE)
    public Person findPerson(@PathVariable("personId") Integer personId, HttpServletRequest request) {
    Person person = new Person(personId, "songsy", 18);
    person.setName(person.getName() + "端口:" + IpConfigurationUtils.getPort());
    return person;
    }
  • 配置文件

1
2
3
4
5
6
7
8
9
spring:
application:
name: eureka-provider
eureka:
instance:
hostname: localhost
client:
service-url:
defaultZone: http://localhost:9010/eureka/,http://localhost:9020/eureka/
3.1 服务方1 以9011端口启动
1
2
3
public static void main(String[] args) {
new SpringApplicationBuilder(Slave1ProviderApplication.class).properties("server.port=9011").run(args);
}
3.2 服务方2 以9023端口启动
1
2
3
public static void main(String[] args) {
new SpringApplicationBuilder(Slave1ProviderApplication.class).properties("server.port=9023").run(args);
}
4、测试结果
  • 连续访问 http://localhost:9050/router 会得到不同的结果, 可以看到已经实现了负载均衡
1
2
3
{"id":1,"name":"songsy端口:9023","age":18}

{"id":1,"name":"songsy端口:9011","age":18}

@LoadBalanced注解解析

  • 上一章节Ribbon实现客户端负债均衡是通过@LoadBalanced注解来开启的,下面是@LoadBalanced 注解,查看注释可以看到该注解具体发挥作用的是LoadBalancerClient
1
2
3
4
5
6
7
8
9
10
11
12
/**
* 使用 LoadBalancerClient 该类来配置
* Annotation to mark a RestTemplate bean to be configured to use a LoadBalancerClient
* @author Spencer Gibb
*/
@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Qualifier
public @interface LoadBalanced {
}
  • 查看LoadBalancerClient.java类可以看到主要有三种方法,这三种方法就是Ribbon的具体要做的事情
    • choose() 根据传入的服务实例名serviceId,从负债均衡中挑选一个对应服务的实例
    • reconstructURI()http://myservice/path/to/service构建一个真实的host:port形式的url
    • execute() 根据挑选出来服务实例执行请求
  • ServiceInstanceChooser.java
1
2
3
4
5
6
7
8
9
10
public interface ServiceInstanceChooser {

/**
* 根据传入的服务实例名serviceId,从负债均衡中挑选一个对应服务的实例
* Choose a ServiceInstance from the LoadBalancer for the specified service
* @param serviceId the service id to look up the LoadBalancer
* @return a ServiceInstance that matches the serviceId
*/
ServiceInstance choose(String serviceId);
}
  • LoadBalancerClient.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/**
* Represents a client side load balancer
* @author Spencer Gibb
*/
public interface LoadBalancerClient extends ServiceInstanceChooser {

/**
* 根据挑选出来服务实例执行请求
* execute request using a ServiceInstance from the LoadBalancer for the specified
* service
* @param serviceId the service id to look up the LoadBalancer
* @param request allows implementations to execute pre and post actions such as
* incrementing metrics
* @return the result of the LoadBalancerRequest callback on the selected
* ServiceInstance
*/
<T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException;

/**
* 根据服务实例执行请求
* execute request using a ServiceInstance from the LoadBalancer for the specified
* service
* @param serviceId the service id to look up the LoadBalancer
* @param serviceInstance the service to execute the request to
* @param request allows implementations to execute pre and post actions such as
* incrementing metrics
* @return the result of the LoadBalancerRequest callback on the selected
* ServiceInstance
*/
<T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException;

/**
* 将 http://myservice/path/to/service 构建一个真实的host:port形式的url
* Create a proper URI with a real host and port for systems to utilize.
* Some systems use a URI with the logical serivce name as the host,
* such as http://myservice/path/to/service. This will replace the
* service name with the host:port from the ServiceInstance.
* @param instance
* @param original a URI with the host as a logical service name
* @return a reconstructed URI
*/
URI reconstructURI(ServiceInstance instance, URI original);
}

其他

  • 它内部提供了一个叫做ILoadBalance的接口代表负载均衡器的操作,比如有添加服务器操作、选择服务器操作、获取所有的服务器列表、获取可用的服务器列表等等。
  • 连接服务实例超时怎么办,连接服务实例后读取内容超时怎么处理
  • 断路器是怎么处理的

总结

  • 负载均衡器的目的主要是根据负责均衡策略选取合适的服务端实例

参考

https://blog.csdn.net/zhxdick/article/details/79449146