什么是负载均衡
当一台服务器的单位时间内的访问量越大时,服务器压力就越大,大到超过自身承受能力时,服务器就会崩溃。为了避免服务器崩溃,让用户有更好的体验,我们通过负载均衡的方式来分担服务器压力。
我们可以建立很多很多服务器,组成一个服务器集群,当用户访问网站时,先访问一个中间服务器,在让这个中间服务器在服务器集群中选择一个压力较小的服务器,然后将该访问请求引入该服务器。如此以来,用户的每次访问,都会保证服务器集群中的每个服务器压力趋于平衡,分担了服务器压力,避免了服务器崩溃的情况。
Ribbon
Spring Cloud Ribbons是基于Netflix Ribbon实现的一套客户端负载均衡的工具。
Spring Cloud Ribbon虽然只是一个工具类框架,它不像服务注册中心、配置中心、API网关那样需要独立部署,但是它几乎存在于每一个Spring Cloud构建的微服务和基础设施中。因为微服务间的调用,API网关的请求转发等内容,实际上都是通过Ribbon来实现的,包括后续我们将要介绍的Feign,它也是基于Ribbon实现的工具。所以,对Spring Cloud Ribbon的理解和使用,对于我们使用Spring Cloud来构建微服务非常重要。
SpringCloud使用Ribbon
1.修改客户端的pom.xml文件
<?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>springclouddemo</artifactId> <groupId>com.aomeibox</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>customer-order</artifactId> <name>customer-order</name> <url>http://www.example.com</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.7</maven.compiler.source> <maven.compiler.target>1.7</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> <version>1.4.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <!-- Ribbon 相关--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> <version>1.1.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-ribbon</artifactId> </dependency> </dependencies> </project>
2.修改客户端的MyConfig类
package com.aomeibox.config; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate; /** * Created by jl on 2018/12/27. */ @Configuration public class MyConfig { @Bean @LoadBalanced //负载均衡工具 public RestTemplate restTemplate(){ return new RestTemplate(); } }
3.修改客户端的controller,通过在Eureka中注册的ApplicaitonName进行访问
package com.aomeibox.con; import com.aomeibox.pojo.User; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; /** * Created by leo on 2018/12/24. */ @RestController public class OrderController { @Autowired private RestTemplate template;//spring提供的一个用于访问rest接口的模板对象 private static final String url = "http://PROVIDER-USER"; // @Autowired // private EurekaClient eurekaClient; @GetMapping("/order/{id}") public User getUser(@PathVariable Integer id){ // InstanceInfo eureka = eurekaClient.getNextServerFromEureka("PROVIDER-USER", false); //访问提供者获取数据 // User user = template.getForObject(eureka.getHomePageUrl()+"/user/"+ id, User.class);//通过访问rest 获取到json数据,然后转换成User对象 User user = template.getForObject(url+"/user/"+ id, User.class); return user; } }
4.运行项目
Ribbon负载均衡策略
使用负载均衡带来的好处很明显:
- 当集群里的1台或者多台服务器down的时候,剩余的没有down的服务器可以保证服务的继续使用
- 使用了更多的机器保证了机器的良性使用,不会由于某一高峰时刻导致系统cpu急剧上升
负载均衡有好几种实现策略,常见的有:
- 随机 (RandomRuler)
- 轮询 (RoundRobinRuler) (默认)
- 一致性哈希 (ConsistentHashRuler)
- 哈希 (HashRuler)
- 加权(WeightedRuler)
RoundRobinRule轮询策略表示每次都取下一个服务器,比如一共有5台服务器,第1次取第1台,第2次取第2台,第3次取第3台,以此类推。
WeightedResponseTimeRule继承了RoundRobinRule,开始的时候还没有权重列表,采用父类的轮询方式,有一个默认每30秒更新一次权重列表的定时任务,该定时任务会根据实例的响应时间来更新权重列表,choose方法做的事情就是,用一个(0,1)的随机double数乘以最大的权重得到randomWeight,然后遍历权重列表,找出第一个比randomWeight大的实例下标,然后返回该实例。
BestAvailableRule策略用来选取最少并发量请求的服务器。
如果需要改变轮训的策略,需要修改自己的配置类。
package com.aomeibox.config; import com.netflix.loadbalancer.IRule; import com.netflix.loadbalancer.RandomRule; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate; /** * Created by leo on 2018/12/27. */ @Configuration public class MyConfig {//@Configuration + 本类 = applicationContext.xml文件 @Bean @LoadBalanced //负载均衡工具 public RestTemplate restTemplate(){ return new RestTemplate(); } @Bean public IRule myRule(){ return new RandomRule();//随机策略 } }
自定义负载均衡策略
1.主启动类新增注解。注意,自己编写的Rule类,不能和主启动类放在同一个包下
package com.aomeibox; import com.aomei.MySelfRule; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; import org.springframework.cloud.netflix.ribbon.RibbonClient; /** * * 消费者 */ @SpringBootApplication @EnableEurekaClient @RibbonClient(name = "PROVIDER-USER",configuration = MySelfRule.class)//MySelfRule.class不能在本类的包或者子包下 public class CustomerOrder { public static void main( String[] args ) { SpringApplication.run(CustomerOrder.class); } }
2.新增MyRule类。
package com.aomei; import com.netflix.loadbalancer.BestAvailableRule; import com.netflix.loadbalancer.IRule; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * Created by leo on 2018/12/29. */ @Configuration public class MySelfRule { @Bean public IRule myRule(){ return new BestAvailableRule(); } }
Feign负载均衡
上述的Ribbon功能很强大,可以自定义算法。
Feign 是一个声明web服务客户端,这便得编写web服务客户端更容易,使用Feign 创建一个接口并对它进行注解,它具有可插拔的注解支持包括Feign注解与JAX-RS注解,Feign还支持可插拔的编码器与解码器,Spring Cloud 增加了对 Spring MVC的注解,Spring Web 默认使用了HttpMessageConverters, Spring Cloud 集成 Ribbon 和 Eureka 提供的负载均衡的HTTP客户端 Feign。
上述大部分,我们通常是以微服务的ApplicaitonName进行访问的。但是在开发编程过程中,我们通常是面向接口编程。因此出现了Feign。Feign可以使用接口+注解,调用其他项目的接口。
@FeignClient("provider-user") public interface UserClient { @RequestMapping(method = RequestMethod.GET, value = "/getuser") public User getuserinfo(); @RequestMapping(method = RequestMethod.GET, value = "/getuser") public String getuserinfostr(); @RequestMapping(method = RequestMethod.GET, value = "/info") public String info(); }
@SpringBootApplication @EnableDiscoveryClient @EnableFeignClients public class FeignApplication { public static void main(String[] args) { SpringApplication.run(FeignApplication.class, args); } }
@RestController public class UserController { @Autowired UserClient userClient; @RequestMapping(value = "/getuserinfo", method = RequestMethod.GET) public User getuserinfo() { return userClient.getuserinfo(); } @RequestMapping(value = "/getuserinfostr", method = RequestMethod.GET) public String getuserinfostr() { return userClient.getuserinfostr(); } @RequestMapping(value = "/info", method = RequestMethod.GET) public String info() { return userClient.info(); } }