目录
一、Ribbon负载均衡
1.1 什么是负载均衡
通俗的讲,负载均衡就是将负载(工作任务,访问请求)进行分摊到多个操作单元(服务器,组件)上进行执行。
1.2 自定义实现负载均衡
1.2.1 创建两个服务提供者provider
1.2.2 配置文件application.yml设置不同的端口号
#ribbon_provider_1
server:
port: 9090
spring:
cloud:
nacos:
discovery:
server-addr: 192.168.57.132 #nacos服务的地址
application:
name: ribbon-provider #向注册中心注册的名字
#ribbon_provider_2
server:
port: 9091
spring:
cloud:
nacos:
discovery:
server-addr: 192.168.57.132 #nacos服务的地址
application:
name: ribbon-provider #向注册中心注册的名字
1.2.3 创建服务消费者consumer
1.2.4 配置文件application.yml
server:
port: 80
spring:
cloud:
nacos:
discovery:
server-addr: 192.168.57.132 #nacos服务的地址
application:
name: ribbon-consumer #向注册中心注册的名字
1.2.5 controller
@RestController
@RequestMapping(value = "/consumer")
public class ConsumerController {
//发送Rest请求的工具类
@Autowired
private RestTemplate restTemplate;
//发现服务的工具类
@Autowired
private DiscoveryClient discoveryClient;
private int index;
@RequestMapping("/getService/{id}")
public User getServic(@PathVariable Integer id){
//获取所有服务
List<String> serviceList = discoveryClient.getServices();
//随机方式获得服务
//int currentIndex = new Random().nextInt(serviceList.size());
//轮询方式获得服务
index=index + 1;
int currentIndex = (index) % serviceList.size();
ServiceInstance serviceInstance = discoveryClient.getInstances("ribbon-provider").get(currentIndex);
String url = "http://"+serviceInstance.getHost()+":"+serviceInstance.getPort()+"/provider/getUserById/"+id;
return restTemplate.getForObject(url, User.class);
}
}
//RestTemplate工具类
@Configuration
public class ConfigBean {
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
1.2.6 测试: 分别使用轮询和随机策略调用服务提供者
1.3 Ribbon介绍
1.3.1 什么是Ribbon
Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡的工具。
我们不需要去引入ribbon的依赖,因为在nacos里面已经集成了ribbon的依赖
Ribbon默认提供 很多种负载均衡算法,例如轮询、随机 等等。
1.3.2 负载均衡策略
负载均衡接口:com.netflix.loadbalancer.IRule
1.3.2 随机策略
Ribbon的默认内置了7种负载均衡策略:
1、RoundRobinRule
轮询策略,Rabbon默认采用的策略,若经过一轮轮询没有找到可用的provider(提供者),其最多轮询10轮,若最终
没有找到,则返回NULL。
举例:当前有3个提供者A,B,C,先挨个轮询1遍,A,B,C都不访问(1轮),在A,B,C访问一遍(2轮次),一共试10轮
如果还不能访问,则返回NULL。
2、RandomRule
随机策略,从所有可用的provider(提供者)中选择一个。
3、RetryRule
重试策略,先按照RoundRobinRule策略获取provider(策略者)能获取到直接返回,若获取失败,则在指定的时限内重试,
默认的时限为500毫秒。【RoundRobinRule轮询策略,默认是10轮,而RetryRule我给你500毫秒,你可以一直重试,直到找到为止】
4、BestAvailableRule
最可用策略。选择并发量最小的provider(提供者), 即连接的消费者数量最少的provider 。
5、AvailabilityFilteringRule
可用过滤算法。该算法规则是:过滤掉处于熔断状态的provider与已经超过连接极限的provider,对剩余provider采用轮询策略。
6、ZoneAvoidanceRule
zone回避策略。根据provider所在zone及provider的可用性,对provider进行选择。
7、WeightedResponseTimeRule
“权重响应时间”策略。根据每个provider的平均响应时间计算其权重,响应时间越快权重越大,被选中的机率就越高。
在刚启动时采用轮询策略。后面就会根据权重进行选择了。
1.4 基于ribbon实现负载均衡
1.4.1 ConfigBean
@Configuration
public class ConfigBean {
@Bean
/**
* 添加了@LoadBalanced注解之后,Ribbon会给restTemplate请求添加一个拦截器,在拦截器中获取
* 注册中心的服务列表,并使用Ribbon内置的负载均衡算法从服务列表里选中一个服务,通过获取到的服务信息 * (ip,port)替换 serviceId 实现负载请求。
*/
@LoadBalanced //开启负载均衡
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
//随机策略
@Bean
public IRule iRule() {
return new RandomRule();
}
}
1.4.2 Controller
@RestController
@RequestMapping(value = "/consumer")
public class ConsumerController {
//发送Rest请求的工具类
@Autowired
private RestTemplate restTemplate;
@RequestMapping("/get/{id}")
public User getUser(@PathVariable Integer id){
//ribbon-provider为服务要访问的名字即可,Ribbon会把服务名替换成ip和端口号
String url = "http://ribbon-provider/provider/getUserById/"+id;
return restTemplate.getForObject(url,User.class);
}
}
1.4.3 测试
分别使用轮询和随机策略调用服务提供者
1.5 Ribbon的问题
①拼接url和参数比较麻烦
②默认不能剔除死亡的服务(如果其中的一台服务宕机了,浏览器会访问失败)
二、声明式服务调用Feign
2.1 背景
当我们通过RestTemplate调用其它服务的API时,所需要的参数须在请求的URL中进行拼接,如果参数少的话或许我们还可以忍受,一旦有多个参数的话,这时拼接请求字符串就会效率低下,并且显得好傻。
那么有没有更好的解决方案呢?答案是确定的有,Netflix已经为我们提供了一个框架:Feign。
2.2 Feign概述
Feign是Spring Cloud提供的声明式、模板化的HTTP客户端, 它使得调用远程服务就像调用本地服务一样简单,只需要创建一个接口并添加一个注解即可。
Spring Cloud集成Feign并对其进行了增强,使Feign支持了Spring MVC注解;Feign默认集成了Ribbon,所以Fegin默认就实现了负载均衡的效果。
2.3 Feign入门程序
2.3.1 创建服务提供者
application.yml
server:
port: 9090
spring:
cloud:
nacos:
discovery:
server-addr: 192.168.57.132:8848 #nacos服务的地址
application:
name: feign-provider #向注册中心注册的名字
创建controller和service
//controller
@RestController
@RequestMapping("/provider")
public class ProviderController {
@Autowired
private UserService userService;
@RequestMapping("/getUserById")
public User getUserById(@RequestParam("id") Integer id){
return userService.getUserById(id);
}
}
//servic接口
public interface UserService {
User getUserById(Integer id);
}
//UserService 实现类
@Service
public class UserServiceImpl implements UserService {
@Override
public User getUserById(Integer id) {
return new User(id,"彭于晏-1",23);
}
}
2.3.2 创建feign接口
pom.xml依赖
<dependencies>
<!--Spring Cloud OpenFeign Starter -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>com.bjpowernode</groupId>
<artifactId>springcloud_common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
创建接口
@FeignClient(value = "feign-provider")//value属性写服务名字
@RequestMapping("/provider")//对应提供者的requestmapping
public interface UserFeign {
@RequestMapping("/getUserById")
User getUserById(@RequestParam("id") Integer id);
}
2.3.3 创建消费者
<dependencies>
<!--nacos客户端-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--引入feign接口-->
<dependency>
<groupId>com.bjpowernode</groupId>
<artifactId>feign_interface</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
在启动器上加入@EnableFeignClients注解扫描feign接口
在controller调用feign接口
测试
2.4 feign原理概述
1、将feign接口的代理类扫描到Spring容器中:
@EnableFeignClients开启feign注解扫描:FeignClientsRegistrar.registerFeignClients()扫描被 @FeignClient标识的接口生成代理类,
并把接口和代理类交给Spring的容器管理。
2、为接口的方法创建RequestTemplate
当consumer调用feign代理类时,代理类会调用SynchronousMethodHandler.invoke()创建RequestTemplate(url,参数)
3、发出请求
代理类会通过RequestTemplate创建Request,然后client(URLConnetct、HttpClient、OkHttp)使用Request发送请求
2.5 feign接口传参的三种方式
1、路径拼接?传参,在参数前加入@RequestParam("id")注解:
2、restful传参,在参数前加入@PathVariable("id")注解:
3、pojo,在参数前加入@RequestBody注解:
2.6 OpenFeign性能优化
1、日志增强
OpenFeign
虽然提供了日志增强功能,但是默认是不显示任何日志的,不过开发者在调试阶段可以自己配置日志的级别。
OpenFeign
的日志级别如下:
- NONE:默认的,不显示任何日志;
- BASIC:仅记录请求方法、URL、响应状态码及执行时间;
- HEADERS:除了BASIC中定义的信息之外,还有请求和响应的头信息;
- FULL:除了HEADERS中定义的信息之外,还有请求和响应的正文及元数据。
1.1、配置文件application.yml 文件中设置日志的开启:
#com.bjpowernode.feign包下接口的日志级别
logging:
level:
com.bjpowernode.feign: debug
#OpenFeign 的日志级别
feign:
client:
config:
default:#此处写服务名字或default默认的
loggerLevel: full
控制台打印的日志效果
1.2、配置类中配置 OpenFeign 日志级别
自定义一个配置类,在其中设置日志级别,这样是全局的配置
@Configuration
public class FeignConfiguration {
@Bean
Logger.Level feignLoggerLevel(){
return Logger.Level.FULL;
}
}
局部配置,在客户端接口指定此配置
configuration = FeignConfiguration.class
@FeignClient(value = "feign-provider", fallbackFactory = RemoteUserFallbackFactory.class, configuration = FeignConfiguration.class)
public interface UserFeign{
@RequestMapping("/getUserById")
User getUserById(@RequestParam("id") Integer id);
}
2、http连接池
两台服务器建立HTTP连接的过程涉及到多个数据包的交换,很消耗时间。采用HTTP连接池可以节约大量的时间提示吞吐量。
Feign的HTTP客户端支持3种框架:HttpURLConnection、HttpClient、OkHttp。
观察源码可以发现,Feign 默认是采用java.net.HttpURLConnection 的,每次请求都会建立、关闭连接。
2.1、在feign接口工程添加依赖
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
2.2、 配置文件中开启
feign:
# 开启 httpclient
httpclient:
enabled: true
2.3、如何验证是否开启了呢
在feign.SynchronousMethodHandler#executeAndDecode()
这个方法中可以清楚的看出调用哪个 client。
没开启之前是这样的:
开启之后:
3、gzip压缩
介绍
gzip 是一种数据格式,采用用 deflate 算法压缩 data;gzip 是一种流行的文件 压缩算法,应用十分广泛,尤其是在 Linux 平台。
能力
当 Gzip 压缩到一个纯文本文件时,效果是非常明显的,大约可以减少 70% 以上的文件大小。
作用:
网络数据经过压缩后实际上降低了网络传输的字节数,可以加快网页加载的速度,改善用户体验
在消费者配置文件中开启gzip压缩
server:
compression:
enabled: true #开启gzip压缩
4、feign超时处理
在访问网页的时候,经常会出现网络不好导致访问超时。如果后台没有设置网络超时处理,则超时一秒并且没有设置日志输出,后台就会抛出这样的异常:
在consumer的配置文件中可以这样设置:
1、方式一:全局的处理
ribbon:
ConnectTimeout: 5000 #请求连接的超时时间
ReadTimeout: 5000 #请求处理的超时时间
2、方式二:
feign:
client:
config:
feign-provider:#这里可以写服务名,或者default默认的,全局的处理
ConnectTimeout: 5000 #请求连接的超时时间
ReadTimeout: 5000 #请求处理的超时时间
通过以上设置,就可以进行超时处理了!!!