熔断限流与高并发处理,全套解决方案,包含实际案例场景及技术分析

20 篇文章 0 订阅
3 篇文章 0 订阅

1 商品详情页生成

普通秒杀商品,直接对接普通秒杀流程,如果是热点秒杀商品,则对接热点秒杀商品流程。

​ 静态页生成,我们采用freemarker来实现,因此我们要先创建模板,并且准备模板数据,然后填充模板即可。

数据模型:

​ 我们创建一个普通方法,用于根据ID查询商品详情, 并生成静态页,代码如下:

模板:

模板的静态页在资料\详情页面-静态页面\detail.html,把它打开,然后填充指定数据即可。

基础数据填充:

规格分类填充:

倒计时代码:

我们需要引入vue和axios,然后将下面倒计时代码拷贝到页面,即可实现倒计时:


<!-- 引入组件库 -->
<script src="vue.js"></script>
<script type="text/javascript" src="axios.js"></script><!-- axios交互-->
<script>
    var app = new Vue({
        el:'#app',
        data:{
            message:'',
            hours:'',
            minutes:'',
            seconds:''
        },
        methods:{
            /***
             * 时间运算
             * @param starttimes:秒杀开始时间
             * @param endtimes:秒杀结束时间
             */
            timeCalculate:function (starttimes,endtimes) {
                //获取当前时间
                let nowtimes = new Date().getTime();
                if(nowtimes>endtimes){
                    this.message='活动已结束!';
                    this.isbegin=0;
                    return;
                }
                //时间差
                let nums = 0;

                //前缀,记录倒计时描述
                let prefix ='距离结束:';

                //判断是距离开始/距离结束
                if(starttimes<=nowtimes){
                    //距离结束,运算求出nums
                    nums = endtimes-nowtimes;
                    this.isbegin=1;
                }else{
                    //距离开始
                    prefix='距离开始:';

                    //运算求出nums
                    nums = starttimes-nowtimes;
                    this.isbegin=0;
                }

                //nums定时递减
                let clock = window.setInterval(function () {
                    //时间递减
                    nums=nums-1000;

                    //消息拼接
                    prefix+app.timedown(nums);

                    //nums<0
                    if(nums<=0){
                        //结束定时任务
                        window.clearInterval(clock);
                        //刷新时间,重新调用该方法
                        app.timeCalculate(starttimes,endtimes);
                    }
                },1000);
            },

            //将毫秒转换成天时分秒
            timedown:function(num) {
                var oneSecond = 1000;
                var oneMinute=oneSecond*60;
                var oneHour=oneMinute*60
                var oneDay=oneHour*24;
                //天数
                //var days =Math.floor(num/oneDay);
                //小时
                //this.hours =Math.floor((num%oneDay)/oneHour);
                this.hours =Math.floor(num/oneHour);
                //分钟
                this.minutes=Math.floor((num%oneHour)/oneMinute);
                //秒
                this.seconds=Math.floor((num%oneMinute)/oneSecond);
                //拼接时间格式
                //var str = days+'天'+hours+'时'+minutes+'分'+seconds+'秒';
                //return str;
            },

            //计算时间
            loadTime:function () {
                //秒杀倒计时时间
                let tm1 = new Date("${sku.seckillBegin?string('yyyy-MM-dd HH:mm:ss')}").getTime();
                let tm2 = new Date("${sku.seckillEnd?string('yyyy-MM-dd HH:mm:ss')}").getTime();
                this.timeCalculate(tm1,tm2);
            },
            //下单
            addOrder:function () {
                //获取令牌
                var token = localStorage.getItem("token");
                if(token!=null && token!=''){
                    //将令牌传给后台  /lua/order/add
                    var instance = axios.create({
                    });
                    instance.defaults.headers.common['Authorization'] = 'Bearer '+token;
                    //发送请求
                    instance.post(`http://data-seckill-java.test.net/api/order/add/${sku.id}`).then(function (response) {
                        console.log(response)
                        //跳转到个人中心
                        location.href='http://data-seckill-java.test.net/#/user';
                    })
                }else{
                    //跳转登录
                    location.href='http://data-seckill-java.test.net/#/login';
                }
            }
        },
        created:function () {
            this.loadTime();
        }
    });
</script>

倒计时时间显示:

效果如下:

2 热点商品状态通知

2.1 后端实现

​ 抢单先经过nginx,nignx会创建热点商品抢单排队队列,order此时会监听消息,监听消息后,会进行下单,下单完成或者失败,都会向message发送消息,此时客户端也就是商品详情页和message建立了长连接,正好可以获取消息。

2.2 前端对接

​ 前端采用vue+websocket实现,当抢单的商品为热点商品的时候,需要建立websocket链接,一旦监听到消息表明抢单有结果了,可以根据结果选择跳转到订单列表页,或者输出抢单结果。


//下单
addOrder:function () {
    //获取令牌
    localStorage.setItem("token","eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4MzRlZmE3Mi0zODRmLTQ0YzMtYjE0Ny02ZTRiMGNjNWE4NDkiLCJpYXQiOjE1OTA2MzY3NDgsImlzcyI6IjgzNGVmYTcyLTM4NGYtNDRjMy1iMTQ3LTZlNGIwY2M1YTg0OSIsInN1YiI6IjgzNGVmYTcyLTM4NGYtNDRjMy1iMTQ3LTZlNGIwY2M1YTg0OSIsInBob25lIjoiMTM2NzAwODEzNzYiLCJuYW1lIjoi5rKI5Z2k5p6XIiwidXNlcm5hbWUiOiJpdGhlaW1hIiwiZXhwIjoxNTkxNDM2NzQ4fQ.IvOURAz7gpNqz22sIffGkDLvQglFx2-vQWI_wq-FgVY")
    var token = localStorage.getItem("token");
    if(token!=null && token!=''){
        //将令牌传给后台  /lua/order/add
        var instance = axios.create({
        });
        instance.defaults.headers.common['Authorization'] = 'Bearer '+token;
        //发送请求
        instance.post(`http://data-seckill-java.test.net/lua/order/add?id=${sku.id}`).then(function (response) {
            console.log(response)
            //跳转到个人中心
            //location.href='http://data-seckill-java.test.net/#/user';
            if(response.data.code==202){
                //如果在排队,就建立链接
                app.initWebSocket(response.data.username);
            }
        })
    }else{
        //跳转登录
        //location.href='http://data-seckill-java.test.net/#/login';
        console.log("未登录!")
    }
},
//初始化创建websocket链接
initWebSocket(uname){
    //实现化WebSocket对象,指定要连接的服务器地址与端口  建立连接
    this.socket = new WebSocket("ws://localhost:28082/ws/"+uname);
    //打开事件
    this.socket.onopen = function() {
        console.log("Socket 已打开");
    };
    //接收消息
    this.socket.onmessage=this.loadMessage;
},
//后端消息接收
loadMessage(e){
    console.log(e.data)
}

3 Sentinel限流

​ 随着微服务的流行,服务和服务之间的稳定性变得越来越重要。 Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。

3.1 Sentinel介绍

Sentinel 具有以下特征:


1.丰富的应用场景: Sentinel 承接了阿里巴巴近 10 年的双十一大促销流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、实时熔断下游不可用应用等。

2.完备的实时监控: Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。

3.广泛的开源生态: Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。

4.完善的 SPI 扩展点: Sentinel 提供简单易用、完善的 SPI 扩展点。您可以通过实现扩展点,快速的定制逻辑。例如定制规则管理、适配数据源等。

​ Sentinel 目前已经针对 Servlet、Dubbo、Spring Boot/Spring Cloud、gRPC 等进行了适配,用户只需引入相应依赖并进行简单配置即可非常方便地享受 Sentinel 的高可用流量防护能力。Sentinel 还为 Service Mesh 提供了集群流量防护的能力。未来 Sentinel 还会对更多常用框架进行适配。

Sentinel 分为两个部分:

  • 核心库(Java 客户端)不依赖任何框架/库,能够运行于所有 Java 运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持。
  • 控制台(Dashboard)基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等应用容器。

Sentinel 的关注点在于:

  • 多样化的流量控制
  • 熔断降级
  • 系统负载保护
  • 实时监控和控制台

Sentinel和Hystrix对比:

SentinelHystrix
隔离策略信号量隔离(并发线程数限流)线程池隔离/信号量隔离
熔断降级策略基于响应时间、异常比率、异常数基于异常比率
实时指标实现滑动窗口(LeapArray)滑动窗口(基于 RxJava)
规则配置支持多种数据源支持多种数据源
扩展性多个扩展点插件的形式
基于注解的支持支持支持
调用链路信息支持同步调用不支持
限流基于 QPS / 并发数,支持基于调用关系的限流有限支持
流量整形支持慢启动、匀速器模式不支持
系统负载保护支持不支持
控制台开箱即用,可配置规则、查看秒级监控、机器发现等较为简单
常见框架的适配Servlet、Spring Cloud、Dubbo、gRPC 等Servlet、Spring Cloud Netflix

学习参考地址:Sentinel · alibaba/spring-cloud-alibaba Wiki · GitHub

3.2 Sentinel控制台安装

​ Sentinel 提供一个轻量级的开源控制台,它提供机器发现以及健康情况管理、监控(单机和集群)、规则管理和推送的功能。https://github.com/alibaba/Sentinel/wiki/%E6%8E%A7%E5%88%B6%E5%8F%B0

Sentinel 控制台包含如下功能:

  • 查看机器列表以及健康情况:收集 Sentinel 客户端发送的心跳包,用于判断机器是否在线。
  • 监控 (单机和集群聚合):通过 Sentinel 客户端暴露的监控 API,定期拉取并且聚合应用监控信息,最终可以实现秒级的实时监控。
  • 规则管理和推送:统一管理推送规则。
  • 鉴权:生产环境中鉴权非常重要。这里每个开发者需要根据自己的实际情况进行定制。

1)jar包运行方式安装

Sentinel控制台下载地址:Releases · alibaba/Sentinel · GitHub

jar包下载后,直接启动即可,启动命令如下:

java -Dserver.port=8858 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.7.2.jar

控制台访问:http://localhost:8858 效果如下:

登录的账号密码都是sentinel。

2)Docker安装


docker pull bladex/sentinel-dashboard

docker run --name sentinel -d -p 8858:8858 -d bladex/sentinel-dashboard

访问http://192.168.211.137:8858/ 登录后,效果如下:

3.3 Sentinel案例

​ 我们基于SpringCloud工程集成Sentinel,实现限流操作。为了节省时间,我们可以直接把写好的半成品案例导入到IDEA中来,将资料\sentinel中的sentinel工程导入进来,分别有一个生产者一个消费者和一个微服务网关。

生产者:provider

消费者:consumer

微服务网关:gateway

3.3.1 基于Feign的服务降级

​ 因为我们项目中服务之间调用使用的是feign,因此这里只讲解基于feign的服务降级实现。

1)引入依赖包:

在消费者中引入sentinel的包。


<!-- spring cloud alibaba sentinel 依赖 -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

2)Sentinel配置

​ Sentinel 适配了 Feign 组件。如果想使用,除了引入 spring-cloud-starter-alibaba-sentinel 的依赖外还需要 开启Sentinel对feign的支持,并且配置控制台信息,我们可以在bootstrap.yml中配置,配置如下:


spring:
  cloud:
    #sentinel
    sentinel:
      transport:
        port: 8719
        dashboard: 192.168.211.137:8858

#Sentinel对feign的支持
feign:
  sentinel:
    enabled: true

3)配置服务降级方法

​ 服务降级的方法就是原来springcloud中的服务降级配置,这里的配置完全是feign的配置,所以不详细讲解了。

​ 创建服务降级处理工厂对象com.test.feign.fallback.GoodsFeignFallback,代码如下:


@Component
public class GoodsFeignFallback implements FallbackFactory<GoodsFeign> {

    @Override
    public GoodsFeign create(Throwable throwable) {
        return new GoodsFeign() {
            @Override
            public String one(Integer max) {
                return "服务降级";
            }
        };
    }
}

​ 在feign上添加fallbackFactory指向服务降级的工厂类对象,代码如下:


@FeignClient(value = "goods",fallbackFactory = GoodsFeignFallback.class)
public interface GoodsFeign {

    /***
     * 获取一件商品
     */
    @GetMapping(value = "/goods/one/{max}")
    String one(@PathVariable(value = "max")Integer max);
}
3.3.2 Sentinel控制台使用

​ 我们接着启动生产者和消费者,并访问我们的控制台:http://192.168.211.137:8858/#/dashboard/home,效果如下:

指定方法流量实时监控:

选择 实时监控,再输入被访问的方法名字,就会出现指定方法访问的流量报表,如下图:

簇点链路

这块主要是单个节点中所有资源以及实时的调用数据,如下图:

3.3.2.1 流量控制

流控:

​ 在上图中,针对每个资源都有3个操作按钮,其中流控主要用于做流量控制操作,其原理是监控应用流量的 QPS 或并发线程数等指标,当达到指定的阈值时对流量进行控制,以避免被瞬时的流量高峰冲垮,从而保障应用的高可用性。

讲解:


resource:资源名,即限流规则的作用对象
count: 限流阈值
grade: 限流阈值类型(QPS 或并发线程数)
limitApp: 流控针对的调用来源,若为 default 则不区分调用来源
strategy: 调用关系限流策略
controlBehavior: 流量控制效果(直接拒绝、Warm Up、匀速排队)

流控效果:


快速失败:
	当QPS超过任何规则的阈值后,新的请求就会立即拒绝,拒绝方式为抛出FlowException . 这种方式适用于对系统处理能力确切已知的情况下,比如通过压测确定了系统的准确水位时。
	
Warm Up:
	当系统长期处理低水平的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过"冷启动",让通过的流量缓慢增加,在一定时间内逐渐增加到阈值的上限,给系统一个预热的时间,避免冷系统被压垮。
	
排队等待:
	匀速排队严格控制请求通过的时间间隔,也即是让请求以均匀的速度通过,对应的是漏桶算法。
3.3.2.2 降级规则

RT:

异常比例:

异常数量:

3.3.2.3 热点规则

​ 热点即经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高的 Top K 数据,并对其访问进行限制。

参数说明:

3.3.2.4 黑白名单

参数说明:

| resource | 资源名,即限流规则的作用对象 | - |
| limitApp | 对应的黑名单/白名单,不同 origin 用 , 分隔,如 appA,appB | default,代表不区分调用来源 |
| strategy | 限制模式,AUTHORITY_WHITE 为白名单模式,AUTHORITY_BLACK 为黑名单模式,默认为白名单模式 | AUTHORITY_WHITE |

3.4 微服务网关Sentinel限流

​ 在秒杀项目中,我们需要集成Sentinel进行限流操作,有些商品目前属于非热点商品,但也有可能存在部分商品会在中途突然增加抢购热度成为热点,成为热点有可能会导致瞬间的流量膨胀,这时候我们可以采用Sentinel做限流操作,以实现对后台微服务的保护。后台秒杀我们在微服务网关进行限流操作。

1)引入依赖

微服务网关集成Sentinel限流,需要引入如下依赖包:


<!--Sentinel-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>

<!-- spring cloud alibaba sentinel 依赖 -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

2)配置控制台信息

在bootstrap.yml中配置endpoint以及控制台信息,配置如下:


spring:
  cloud:
    #sentinel
    sentinel:
      transport:
        port: 8719
        dashboard: 192.168.211.137:8858
        
#endpoint
management:
  endpoints:
    web:
      exposure:
        include: '*'

现在所有的集成就成功了,我们启动微服务网关,看看Sentinel控制台也会显示微服务网关的节点信息,并且可以在控制台配置限流策略。

4 nginx限流

一般情况下,首页的并发量是比较大的,即使 有了多级缓存,当用户不停的刷新页面的时候,也是没有必要的,另外如果有恶意的请求 大量达到,也会对系统造成影响。

而限流就是保护措施之一。

4.1 生活中限流对比

  • 水坝泄洪,通过闸口限制洪水流量(控制流量速度)。
  • 办理银行业务:所有人先领号,各窗口叫号处理。每个窗口处理速度根据客户具体业务而定,所有人排队等待叫号即可。若快下班时,告知客户明日再来(拒绝流量)
  • 火车站排队买票安检,通过排队 的方式依次放入。(缓存带处理任务)

4.2 nginx的限流

nginx提供两种限流的方式:

  • 一是控制速率
  • 二是控制并发连接数
4.2.1 控制速率

控制速率的方式之一就是采用漏桶算法。

(1)漏桶算法实现控制速率限流

漏桶(Leaky Bucket)算法思路很简单,水(请求)先进入到漏桶里,漏桶以一定的速度出水(接口有响应速率),当水流入速度过大会直接溢出(访问频率超过接口响应速率),然后就拒绝请求,可以看出漏桶算法能强行限制数据的传输速率.示意图如下:

(2)nginx的配置

配置示意图如下:

修改/usr/local/openresty/nginx/conf/nginx.conf:


#限流设置   binary_remote_addr:根据请求IP进行限流  contentRateLimit:缓存空间名称 10m:缓存空间
#rate=2r/s:每秒钟允许有2个请求被处理
limit_req_zone $binary_remote_addr zone=contentRateLimit:10m rate=2r/s;


#静态页
location /web/items/ {
     #限流
     limit_req zone=contentRateLimit;
     content_by_lua_file /usr/local/openresty/nginx/lua/items-access.lua;
}

配置说明:


binary_remote_addr 是一种key,表示基于 remote_addr(客户端IP) 来做限流,binary_ 的目的是压缩内存占用量。
zone:定义共享内存区来存储访问信息, contentRateLimit:10m 表示一个大小为10M,名字为contentRateLimit的内存区域。1M能存储16000 IP地址的访问信息,10M可以存储16W IP地址访问信息。
rate 用于设置最大访问速率,rate=10r/s 表示每秒最多处理10个请求。Nginx 实际上以毫秒为粒度来跟踪请求信息,因此 10r/s 实际上是限制:每100毫秒处理一个请求。这意味着,自上一个请求处理完后,若后续100毫秒内又有请求到达,将拒绝处理该请求.我们这里设置成2 方便测试。

测试:

重新加载配置文件


cd /usr/local/openresty/nginx/sbin

./nginx -s reload

访问页面:http://192.168.211.137/web/items/S1235433012716498944.html ,连续刷新会直接报错。

(3)处理突发流量

上面例子限制 2r/s,如果有时正常流量突然增大,超出的请求将被拒绝,无法处理突发流量,可以结合 burst 参数使用来解决该问题。

例如,如下配置表示:

上图代码如下:


server {
    listen       80;
    server_name  data-changgou-java.test.net;
    location /update_content {
        content_by_lua_file /root/lua/update_content.lua;
    }
    location /read_content {
        limit_req zone=contentRateLimit burst=4;
        content_by_lua_file /root/lua/read_content.lua;
    }
}

burst 译为突发、爆发,表示在超过设定的处理速率后能额外处理的请求数,当 rate=10r/s 时,将1s拆成10份,即每100ms可处理1个请求。

此处,**burst=4 **,若同时有4个请求到达,Nginx 会处理第一个请求,剩余3个请求将放入队列,然后每隔500ms从队列中获取一个请求进行处理。若请求数大于4,将拒绝处理多余的请求,直接返回503.

不过,单独使用 burst 参数并不实用。假设 burst=50 ,rate依然为10r/s,排队中的50个请求虽然每100ms会处理一个,但第50个请求却需要等待 50 * 100ms即 5s,这么长的处理时间自然难以接受。

因此,burst 往往结合 nodelay 一起使用。

例如:如下配置:

如上表示:

平均每秒允许不超过2个请求,突发不超过4个请求,并且处理突发4个请求的时候,没有延迟,等到完成之后,按照正常的速率处理。

如上两种配置结合就达到了速率稳定,但突然流量也能正常处理的效果。

4.2.2 控制并发量

ngx_http_limit_conn_module 提供了限制连接数的能力。主要是利用limit_conn_zone和limit_conn两个指令。

利用连接数限制 某一个用户的ip连接的数量来控制流量。

注意:并非所有连接都被计算在内 只有当服务器正在处理请求并且已经读取了整个请求头时,才会计算有效连接。此处忽略测试。

配置语法:


Syntax:	limit_conn zone number;
Default: —;
Context: http, server, location;

(1)配置限制固定连接数

如下,配置如下:

上图配置如下:


#根据IP地址来限制,存储内存大小10M
limit_conn_zone $binary_remote_addr zone=addr:1m;


#测试
location /user/ {
     limit_conn addr 2;
     proxy_pass http://172.16.0.235:18083;
}

表示:


limit_conn_zone $binary_remote_addr zone=addr:10m;  表示限制根据用户的IP地址来显示,设置存储地址为的内存大小10M

limit_conn addr 2;   表示 同一个地址只允许连接2次。

测试:

此时开3个线程,测试的时候会发生异常,开2个就不会有异常

(2)限制每个客户端IP与服务器的连接数,同时限制与虚拟服务器的连接总数

如下配置:

代码如下图:


limit_conn_zone $binary_remote_addr zone=perip:10m;
limit_conn_zone $server_name zone=perserver:10m; 

#测试
location /user/ {
     #limit_conn addr 2;
     limit_conn perip 10;#单个客户端ip与服务器的连接数.
     limit_conn perserver 100; #限制与服务器的总连接数
     proxy_pass http://172.16.0.235:18083;
}

4.3 秒杀Nginx限流

​ 秒杀抢单,需要在抢单的入口进行限流,限流根据每个IP和总的并发量进行配置,配置如下:

5 Lvs+Nginx集群配置

5.1 Lvs介绍

​ LVS(Linux Virtual Server)即Linux虚拟服务器,是由章文嵩博士主导的开源负载均衡项目,目前LVS已经被集成到Linux内核模块中。该项目在Linux内核中实现了基于IP的数据请求负载均衡调度方案,其体系结构如图1所示,终端互联网用户从外部访问公司的外部负载均衡服务器,终端用户的Web请求会发送给LVS调度器,调度器根据自己预设的算法决定将该请求发送给后端的某台Web服务器,比如,轮询算法可以将外部的请求平均分发给后端的所有服务器,终端用户访问LVS调度器虽然会被转发到后端真实的服务器,但如果真实服务器连接的是相同的存储,提供的服务也是相同的服务,最终用户不管是访问哪台真实服务器,得到的服务内容都是一样的,整个集群对用户而言都是透明的。

工作中采用Lvs集群模式:

5.2 Lvs工作解析模式介绍

NAT模式:即网络地址转换

访问步骤:


1.用户通过互联网DNS服务器解析到公司负载均衡设备上面的外网地址,相对于真实服务器而言,LVS外网IP又称VIP(Virtual IP Address),用户通过访问VIP,即可连接后端的真实服务器(Real Server),而这一切对用户而言都是透明的,用户以为自己访问的就是真实服务器,但他并不知道自己访问的VIP仅仅是一个调度器,也不清楚后端的真实服务器到底在哪里、有多少真实服务器。

2.用户将请求发送至124.126.147.168,此时LVS将根据预设的算法选择后端的一台真实服务器(192.168.0.1~192.168.0.3),将数据请求包转发给真实服务器,并且在转发之前LVS会修改数据包中的目标地址以及目标端口,目标地址与目标端口将被修改为选出的真实服务器IP地址以及相应的端口。

3.真实的服务器将响应数据包返回给LVS调度器,调度器在得到响应的数据包后会将源地址和源端口修改为VIP及调度器相应的端口,修改完成后,由调度器将响应数据包发送回终端用户,另外,由于LVS调度器有一个连接Hash表,该表中会记录连接请求及转发信息,当同一个连接的下一个数据包发送给调度器时,从该Hash表中可以直接找到之前的连接记录,并根据记录信息选出相同的真实服务器及端口信息。

TUN负载均衡模式(IP隧道):

​ 在LVS(NAT)模式的集群环境中,由于所有的数据请求及响应的数据包都需要经过LVS调度器转发,如果后端服务器的数量大于10台,则调度器就会成为整个集群环境的瓶颈。我们知道,数据请求包往往远小于响应数据包的大小。因为响应数据包中包含有客户需要的具体数据,所以LVS(TUN)的思路就是将请求与响应数据分离,让调度器仅处理数据请求,而让真实服务器响应数据包直接返回给客户端。VS/TUN工作模式拓扑结构如图3所示。其中,IP隧道(IP tunning)是一种数据包封装技术,它可以将原始数据包封装并添加新的包头(内容包括新的源地址及端口、目标地址及端口),从而实现将一个目标为调度器的VIP地址的数据包封装,通过隧道转发给后端的真实服务器(Real Server),通过将客户端发往调度器的原始数据包封装,并在其基础上添加新的数据包头(修改目标地址为调度器选择出来的真实服务器的IP地址及对应端口),LVS(TUN)模式要求真实服务器可以直接与外部网络连接,真实服务器在收到请求数据包后直接给客户端主机响应数据。

DR负载均衡模式(推荐:直接路由模式):

​ 在LVS(TUN)模式下,由于需要在LVS调度器与真实服务器之间创建隧道连接,这同样会增加服务器的负担。与LVS(TUN)类似,DR模式也叫直接路由模式,其体系结构如图4所示,该模式中LVS依然仅承担数据的入站请求以及根据算法选出合理的真实服务器,最终由后端真实服务器负责将响应数据包发送返回给客户端。与隧道模式不同的是,直接路由模式(DR模式)要求调度器与后端服务器必须在同一个局域网内,VIP地址需要在调度器与后端所有的服务器间共享,因为最终的真实服务器给客户端回应数据包时需要设置源IP为VIP地址,目标IP为客户端IP,这样客户端访问的是调度器的VIP地址,回应的源地址也依然是该VIP地址(真实服务器上的VIP),客户端是感觉不到后端服务器存在的。由于多台计算机都设置了同样一个VIP地址,所以在直接路由模式中要求调度器的VIP地址是对外可见的,客户端需要将请求数据包发送到调度器主机,而所有的真实服务器的VIP地址必须配置在Non-ARP的网络设备上,也就是该网络设备并不会向外广播自己的MAC及对应的IP地址,真实服务器的VIP对外界是不可见的,但真实服务器却可以接受目标地址VIP的网络请求,并在回应数据包时将源地址设置为该VIP地址。调度器根据算法在选出真实服务器后,在不修改数据报文的情况下,将数据帧的MAC地址修改为选出的真实服务器的MAC地址,通过交换机将该数据帧发给真实服务器。整个过程中,真实服务器的VIP不需要对外界可见。

5.3 Lvs-DR配置

​ 综合上面分析,我们可以得出结论,DR模式性能效率比较高,安全性很高,因此一般公司都推荐使用DR模式。我们这里也配置DR模式实现Lvs+Nginx集群。

3台机器:


192.168.211.136
192.168.211.137
192.168.211.138

VIP:

192.168.211.130
5.3.1 Vip配置

关闭网络配置管理器(每台机器都要做)


systemctl stop NetworkManager
systemctl disable NetworkManager

配置虚拟IP(VIP)

/etc/sysconfig/network-scripts创建文件ifcfg-ens33:1,内容如下:


BOOTPROTO=static
DEVICE=ens33:1
ONBOOT=yes
IPADDR=192.168.211.130
NETMASK=255.255.255.0

重启网络服务:

service network restart

我们可以看到在原来的网卡上面添加了一个虚拟IP 130:

同时需要对192.168.211.137192.168.211.138构建虚拟机IP,但只是用于返回数据,而不能被用户访问到,这时候需要操作ifcfg-lo

IPADDR=127.0.0.1,这里127.0.0.1属于本地回环地址,不属于任何一个有类别地址类。它代表设备的本地虚拟接口,所以默认被看作是永远不会宕掉的接口。

NETMASK=255.255.255.255

192.168.211.137
ifcfg-lo拷贝一份ifcfg-lo:1,并修改ifcfg-lo:1配置,内容如下:

刷新lo:

ifup lo

查看IP可以发现lo下多了130ip:

192.168.211.138:

操作同上。

5.3.2 LVS集群管理工具安装

ipvsadm用于对lvs集群进行管理,需要手动安装。

安装命令:

yum install ipvsadm

版本查看:

ipvsadm -Ln

效果如下:

5.3.3 ARP配置(地址解析协议)

arp_ignore和arp_announce参数都和ARP协议相关,主要用于控制系统返回arp响应和发送arp请求时的动作。这两个参数很重要,特别是在LVS的DR场景下,它们的配置直接影响到DR转发是否正常。

arp-ignore:arp_ignore参数的作用是控制系统在收到外部的arp请求时,是否要返回arp响应(0~8,2-8用的很少)


0,只要本机配置了IP,就能响应请求。
1,请求的目标地址到达对应的网络接口,才能响应对应请求

arp-announce:ARP通告行为:


0,本机上任何网络接口都向外通告(也就是任何请求都对用户进行响应),所有网卡都能接收通告
1,尽可能避免本网卡与不匹配的目标进行通告
2,只在本网卡进行通告(推荐)

配置文件:/etc/sysctl.conf,将如下文件拷贝进去:


net.ipv4.conf.all.arp_ignore = 1
net.ipv4.conf.default.arp_ignore = 1
net.ipv4.conf.lo.arp_ignore = 1
net.ipv4.conf.all.arp_announce = 2
net.ipv4.conf.default.arp_announce = 2
net.ipv4.conf.lo.arp_announce = 2

刷新配置:

sysctl -p

添加路由:

route add -host 192.168.211.130 dev lo:1

添加了一个host地址,目的是用于接收数据报文,接收到了数据报文后会交给lo:1处理。(防止关机失效,需要将上述命令添加到/etc/rc.local中)

添加完host后,可以查看一下:route -n,效果如下:

此时如果无法识别route,需要安装相关工具yum install net-tools

上述配置我们同样要在192.168.211.138中配置。

5.3.4 集群配置

ipvsadm命令讲解:


ipvsadm -A:用于创建集群
ipvsadm -E:用于修改集群
ipvsadm -D:用于删除集群
ipvsadm -C:用于清除集群数据
ipvsadm -R:用于重置集群配置规则
ipvsadm -S:用于保存修改的集群规则
ipvsadm -a:用于添加一个rs节点
ipvsadm -e:用于修改一个rs节点
ipvsadm -d:用于删除一个rs节点

添加集群TCP服务地址:(外部请求由该配置指定的VIP处理)

ipvsadm -A -t 192.168.211.130:80 -s rr

参数说明:


-A:添加集群配置
-t:TCP请求地址(VIP)
-s:负载均衡算法

负载均衡算法:

算法说明
rr轮询算法,它将请求依次分配给不同的rs节点,也就是RS节点中均摊分配。这种算法简单,但只适合于RS节点处理性能差不多的情况
wrr加权轮训调度,它将依据不同RS的权值分配任务。权值较高的RS将优先获得任务,并且分配到的连接数将比权值低的RS更多。相同权值的RS得到相同数目的连接数。
Wlc加权最小连接数调度,假设各台RS的全职依次为Wi,当前tcp连接数依次为Ti,依次去Ti/Wi为最小的RS作为下一个分配的RS
Dh目的地址哈希调度(destination hashing)以目的地址为关键字查找一个静态hash表来获得需要的RS
SH源地址哈希调度(source hashing)以源地址为关键字查找一个静态hash表来获得需要的RS
Lc最小连接数调度(least-connection),IPVS表存储了所有活动的连接。LB会比较将连接请求发送到当前连接最少的RS.
Lblc基于地址的最小连接数调度(locality-based least-connection):将来自同一个目的地址的请求分配给同一台RS,此时这台服务器是尚未满负荷的。否则就将这个请求分配给连接数最小的RS,并以它作为下一次分配的首先考虑。

配置rs(2个)节点:


ipvsadm -a -t 192.168.211.130:80 -r 192.168.211.137:80 -g
ipvsadm -a -t 192.168.211.130:80 -r 192.168.211.138:80 -g

参数说明:


-a:给集群添加一个节点
-t:指定VIP地址
-r:指定real server地址
-g:表示LVS的模式为dr模式

添加了节点后,我们通过ipvsadm -Ln查看,可以看到多了2个节点。

此时访问VIP:192.168.211.130的时候,发现一直是138 rs,原因是因为lvs这里有一个用户请求持久化操作,会将用户请求的数据持久化,下次请求的时候,会从持久化数据中取出来,如果有持久化数据,就按持久化数据中进行访问,没有则轮询,因此我们需要配置持久化时间。

此时我们来查看集群列表,会多了持久化时间:

ipvsadm -E -t 192.168.211.130:80 -s rr -p 5

设置tcp

ipvsadm --set 2 2 2

效果如下:

访问192.168.211.130,在输入ipvsadm -Lnc,效果如下:

  • 23
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

纵然间

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值