目录
1、负载均衡
负载均衡简单的说就是将用户的请求平摊的分配到多个服务上,从而达到系统的高可用(HA)。常见的负载均衡有软件Nginx,LVS,硬件 F5等。相应的在中间件,例如:dubbo和SpringCloud中均给我们提供了负载均衡,SpringCloud的负载均衡算法可以自定义。常见的两种负载均衡方式如下。
- 集中式LB
即在服务的消费方和提供方之间使用独立的LB设施(可以是硬件,如F5, 也可以是软件,如nginx), 由该设施负责把访问请求通过某种策略转发至服务的提供方。
- 进程内LB
将LB逻辑集成到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选择出一个合适的服务器。Ribbon就属于进程内LB,它只是一个类库,集成于消费方进程,消费方通过它来获取到服务提供方的地址。
Ribbon本地负载均衡和Nginx服务器端负载均衡的区别?
一、Ribbon本地负载均衡,在调用微服务接口时候,会在注册中心上获取信息服务列表之后缓存到jvm本地,从而在本地实现RPC远程服务调用技术。
二、Nginx是在服务器端实现LB,客户端所有的请求都会交给Nginx,然后由Nginx实现转发请求。即LB由服务器端实现。
2、Ribbon简介
Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡的工具。
简单的说,Ribbon是Netflix发布的开源项目,主要功能是提供客户端的软件负载均衡算法,将Netflix的中间层服务连接在一起。Ribbon客户端组件提供一系列完善的配置项如连接超时,重试等。在配置文件中列出Load Balancer(简称LB)后面所有的机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随机连接等)去连接这些机器。我们也很容易使用Ribbon实现自定义的负载均衡算法。
Ribbon架构(1)
Ribbon在工作时分成两步
第一步先选择 EurekaServer ,它优先选择在同一个区域内负载较少的server.
第二步再根据用户指定的策略,在从server取到的服务注册列表中选择一个地址。
其中Ribbon提供了多种策略:比如轮询、随机和根据响应时间加权。
总结:Ribbon其实就是一个软负载均衡的客户端组件,他可以和其他所需请求的客户端结合使用,和eureka结合只是其中的一个实例。
3、 IRULE的7中默认算法
IRule:根据特定算法中从服务器列表中选取一个要访问的服务,Ribbon默认的算法为轮询算法;
Ribbon中的7种负载均衡算法:
- RoundRobinRule:轮询;
- RandomRule:随机;
- AvailabilityFilteringRule:会先过滤掉由于多次访问故障而处于断路器状态的服务,还有并发的连接数量超过阈值的服务,然后对剩余的服务列表按照轮询策略进行访问;
- WeightedResponseTimeRule:根据平均响应时间计算所有服务的权重,响应时间越快的服务权重越大被选中的概率越大。刚启动时如果统计信息不足,则使用RoundRobinRule(轮询)策略,等统计信息足够,会切换到WeightedResponseTimeRule;
- RetryRule:先按照RoundRobinRule(轮询)策略获取服务,如果获取服务失败则在指定时间内进行重试,获取可用的服务;
- BestAvailableRule:会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务;
- ZoneAvoidanceRule:复合判断Server所在区域的性能和Server的可用性选择服务器;
4 、项目实践
4.1、准备工作
继续用上一节的工程(SpringCloud-Project), 启动eureka-server,端口为8761; 启动eureka-client 两次,端口分别为8001 、8002。SpringBoot设置本地项目多端口启动
4.2、创建一个Ribbon的服务
1、在maven主工程springcloud-Project(SpringCloud(1)---服务的注册与发现 Eureka(Greenwich版本))下,新建module:service-ribbon,pom文件导入依赖。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>SpringCloud-Project</artifactId>
<groupId>com.boyue</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>service-feign</artifactId>
<!--Feign默认集成了Ribbon,并和Eureka结合,默认实现了负载均衡的效果-->
<dependencies>
<!--Web的起步依赖spring-boot-starter-web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--Eureka客户端的起步依赖spring-cloud-starter-netflix-eureka-client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>2.1.0.RELEASE</version>
</dependency>
<!--Feign的起步依赖spring-cloud-starter-openfeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
</dependencies>
</project>
代码提示:
springcloud更新换代比较快;
spring-cloud-starter-eureka-server是1.5以前的版本依赖;
spring-cloud-starter-netflix-eureka-server是最新版本的依赖(推荐)。
2、启动类上添加注解:@SpringBootApplication,@EnableDiscoveryClient,使用@LoadBalanced并配置负载均衡的配置信息类。
@SpringBootApplication
@EnableDiscoveryClient
public class ServiceRibbon {
public static void main(String[] args) {
SpringApplication.run(ServiceRibbon.class,args);
}
/**
* 向程序的ioc注入一个bean: restTemplate;
* 并通过@LoadBalanced注解表明这个restRemplate开启负载均衡的功能
* 基于Ribbon
* 此方法需要@Configuration注解,在@SpringBootApplication中已经包含
*/
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
代码提示:
@EnableDiscoveryClient和@EnableEurekaClient的区别
简单区分下,他们在功能上是一致的:写在启动类的上,开启服务注册发现功能。
不同的是,当注册中心不一样时,像:eureka、consul、zookeeper,使用时也有了区别。
EnableDiscoveryClient注解在common包中,通过项目的classpath来决定使用哪种实现,而EnableEurekaClient注解在netflix包中,只会使用eureka这种实现方式;
所以,使用EnableDiscoverClient,对任何注册中心都适用。而EnableEurekaClient是为eureka服务的。
3、 配置文件application.yaml,其中需要将该服务注册Eureka注册中心:
server:
port: 8001
#我们可以指定微服务的名称后续在调用的时候,
# 只需要使用该名称就可以进行服务的访问
spring:
application:
name: SERVICE-RIBBON
#eureka是集群时候,只需要逗号,分隔即可
eureka:
instance:
hostname: localhost
# 修改Eureka的默认描述信息
# 我们让实例url指向 主机ip+端口号
instance-id: http://${eureka.instance.hostname}:${server.port}
client:
service-url:
defaultZone: http://localhost:8761/eureka/,http://localhost:7001/eureka/,http://localhost:7002/eureka/
4、写一个测试类UserService,通过之前注入ioc容器的restTemplate来消费EUREKA-CLIENT服务的“/info”接口,在这里我们直接用的程序名替代了具体的url地址,在ribbon中它会根据服务名来选择具体的服务实例,根据服务实例在请求的时候会用具体的url替换掉服务名,代码如下:
@Service
public class UserServiceImpl implements UserService {
@Autowired
private RestTemplate restTemplate;
@Override
public RespBean restGetInfo() {
// 通过之前注入ioc容器的restTemplate来消费eureka-client服务的“/info”接口,
// 在这里我们直接用的程序名替代了具体的url地址,
// 在ribbon中它会根据服务名来选择具体的服务实例,
// 根据服务实例在请求的时候会用具体的url替换掉服务名
return restTemplate.getForObject("http://EUREKA-CLIENT/eurekaClient/user/eurekaClientInfo",RespBean.class);
// return restTemplate.postForObject("http://EUREKA-CLIENT/eurekaClient/user/eurekaClientInfo","POST",RespBean.class);
}
}
5、写一个controller,在controller中用调用HelloService 的restGetInfo()方法,代码如下:
@RestController
@RequestMapping("/rest")
public class RestServiceController {
@Autowired
private UserService userService;
@GetMapping("/restGetInfo")
public RespBean restGetInfo(){
return userService.restGetInfo();
}
}
6、EUREKA-CLIENT服务中eurekaClientInfo服务类:
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private DiscoveryClient client;
@Value("${server.port}")
private String port;
@Value("${spring.application.name}")
private String applicationName;
@GetMapping("/eurekaClientInfo")
public RespBean eurekaClientInfo(){
String info="被调用服务的名称:"+applicationName+",服务的端口:"+port;
return RespBean.ok("服务信息如下:",info);
}
}
7、我们可以在idea中,一个服务(EUREKA-CLIENT)按照多个端口同时启动。springboot设置本地项目多端口启动。在浏览器上多次访问http://localhost:8001/rest/restGetInfo,浏览器交替显示:
这说明当我们通过调用restTemplate.getForObject("http://EUREKA-CLIENT/eurekaClient/user/eurekaClientInfo",RespBean.class)方法时,已经做了负载均衡,访问了不同的端口的服务实例。
扫码关注微信公众号: