目前主流的LB方案可分成两类:
一种是集中式LB, 即在服务的消费方和提供方之间使用独立的LB设施(可以是硬件,如F5, 也可以是软件,如nginx), 由该设施负责把访问请求通过某种策略转发至服务的提供方;
另一种是进程内LB,将LB逻辑集成到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选择出一个合适的服务器。Ribbon就属于后者,它只是一个类库,集成于消费方进程,消费方通过它来获取到服务提供方的地址。
使用负载均衡带来的好处很明显:
- 当集群里的1台或者多台服务器down的时候,剩余的没有down的服务器可以保证服务的继续使用
- 使用了更多的机器保证了机器的良性使用,不会由于某一高峰时刻导致系统cpu急剧上升
软负载均衡的实现方式有两种,分别是服务端的负载均衡和客户端的负载均衡
服务端负载均衡:当浏览器向后台发出请求的时候,会首先向反向代理服务器发送请求,反向代理服务器会根据客户端部署的ip:port映射表以及负载均衡策略,来决定向哪台服务器发送请求,一般会使用到nginx反向代理技术。
客户端负载均衡:当浏览器向后台发出请求的时候,客户端会向服务注册器(例如:Eureka Server),拉取注册到服务器的可用服务信息,然后根据负载均衡策略,直接命中哪台服务器发送请求。这整个过程都是在客户端完成的,并不需要反向代理服务器的参与。
负载均衡有好几种实现策略,常见的有:
- 随机 (Random)
- 轮询 (RoundRobin)
- 一致性哈希 (ConsistentHash)
- 哈希 (Hash)
-
加权(Weighted)
- ConsumerApplication
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
package com.baoguoding.consumer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
@EnableEurekaClient
@RibbonClient(name = "compute-service", configuration = TestConfiguration.class)
@ComponentScan(excludeFilters = {@ComponentScan.Filter(type= FilterType.ANNOTATION,value=ExcludeFromComponentScan.class)})
public class ConsumerApplication {
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
}
- ExcludeFromComponentScan
1
2
3
4
package com.baoguoding.consumer;
public @interface ExcludeFromComponentScan {
}
- RestTemplateController
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
package com.baoguoding.consumer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.concurrent.atomic.AtomicInteger;
@RestController
public class RestTemplateController {
@Autowired
private RestTemplate restTemplate;
private AtomicInteger sn = new AtomicInteger(0);
@RequestMapping(value = "/test", method = RequestMethod.GET)
public void test(@RequestParam Integer a, @RequestParam Integer b) {// 将原来的ip:port的形式,改成注册到Eureka Server上的应用名即可
System.out.println("==============================");
String result = restTemplate.getForObject("http://compute-service/add?a="+a +"&b="+b + "&sn="+sn.incrementAndGet(), String.class);
System.out.println("返回结果:" + result);
}
}
- TestConfiguration
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.baoguoding.consumer;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RoundRobinRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@ExcludeFromComponentScan
public class TestConfiguration {
@Bean
public IRule ribbonRule() {
//return new RandomRule(); //设置负载均衡的规则为随机
return new RoundRobinRule(); //默认的轮询策略
}
}
- application.properties
1
2
3
spring.application.name=consumer-service
server.port=2230
eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/