1、处理高并发

  1.1高并发处理方案: 

  • 缓存 缓存的目的是提升系统访问速度和增大系统处理容量
  • 降级 降级是当服务出现问题或者影响到核心流程时,需要暂时屏蔽掉,待高峰或者问题解决后再打开
  • 限流 限流的目的是通过对并发访问/请求进行限速,或者对一个时间窗口内的请求进行限速来保护系统,一旦达到限制速率则可以拒绝服务、排队或等待、降级等处理

  1、2限流方式: mq、ratelimiter

   

2、ratelimiter是基于令牌桶算法来做的 

  guava的RateLimiter使用的是令牌桶算法,也就是以固定的频率向桶中放入令牌,例如一秒钟10枚令牌,实际业务在每次响应请求之前都从桶中获取令牌,只有取到令牌的请求才会被成功响应,获取的方式有两种:阻塞等待令牌或者取不到立即返回失败

                                                                                    

 另外简单介绍漏铜算法:

  请求以一定速度进入漏桶中,如果请求速度>处理请求速度则溢出,漏桶算法能强行处理请求速率。但如果在处理高并发时,突然来大量请求这种方案不合适  

                                                                          

 

令牌桶代码:create():每秒创建多少令牌

      tryacquire():尝试获取令牌,tryacquire():尝试获取一个令牌,如果获取不到立即返回;tryacquire(int permits, long timeout, TimeUnit unit):尝试获取permits个令牌,如果获取不到等待timeout时间

2.1 令牌桶简单代码实现

Main:

package com.citi.ark.mls.ratelimiter;

public class TestTokenLimiter {
    final static Object lock=new Object();
    public static void main(String[] args) throws InterruptedException {
        int period=500;
        int limit=2;
        int amount=2;
        TokenLimiter tokenLimiter=new TokenLimiter(limit,period,amount);
        //先产生两个令牌
        tokenLimiter.startToAddToken(lock);
        synchronized (lock){
            lock.wait();
        }
        //开始消费令牌
        for(int i=0;i<4;i++){  //开启4个线程去获取令牌
            new Thread(()->{
                while(true){
                    String name=Thread.currentThread().getName();
                    if(tokenLimiter.getToken()){
                        System.out.println(name+":拿到令牌");
                    }else{
                        System.out.println(name+":没拿到令牌");
                    }
                    try{
                        Thread.sleep(period);
                    }catch(Exception e){
                        e.printStackTrace();
                    }
                }
            }).start();
        }
    }
}
View Code

 

TokenLimiter:

package com.citi.ark.mls.ratelimiter;

import jdk.nashorn.internal.ir.Block;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

//https://blog.csdn.net/billgates_wanbin/article/details/123556273
// https://blog.csdn.net/weixin_43939924/article/details/124668935
/*
blockQueue add:   队列添加数据,当队列已经满了时会抛出异常
blockQueue offer: 队列添加数据,当队列满了时不会抛出异常,只会返回boolean值来过诉你添加成功与否
blockQueue poll:  队列移除数据,当队列没有数据不会抛出异常,指挥返回boolean值来告诉你添加成功与否
blockQueue remove:队列移除数据,当队列没有数据会抛出异常
 */
public class TokenLimiter {
    public static final String token="lp";
    private ArrayBlockingQueue<String> blockingQueue; //令牌桶
    private int limit;  //令牌桶容量
    private int period; //令牌生产间隔(ms)
    private int amount; //每次生产令牌的个数

    public TokenLimiter(int limit,int period,int amount){
        this.limit=limit;
        this.period=period;
        this.amount=amount;
        blockingQueue=new ArrayBlockingQueue<>(limit);
        init();//初始化令牌
    }
    //初始化令牌
    private void init(){
        for(int i=0;i<limit;i++){
            blockingQueue.add(token);
        }
    }
    //生产令牌
    public void startToAddToken(Object lock){
        Executors.newScheduledThreadPool(1).scheduleAtFixedRate(()->{
            synchronized (lock){ //锁对象是为了保证线程安全
                addToken();
                lock.notifyAll();
            }
        },500,this.period, TimeUnit.MILLISECONDS); //initialDelay:初始化延时;period:两次开始执行时间间隔;unit;计时单位
    }
    public void addToken(){
        for(int i=0;i<=amount;i++){
            blockingQueue.offer(token);
        }
    }
    //获取令牌
    public boolean getToken(){
        return blockingQueue.poll()!=null;
    }
}
View Code

 

2.2 guava的RateLimiter:

package com.leolztang.sb.aop.ratelimiter;

import org.springframework.stereotype.Service;

import com.google.common.util.concurrent.RateLimiter;

@Service
public class AccessLimitService {
    RateLimiter rateLimiter=RateLimiter.create(100.0);
    //尝试获取令牌
    public boolean tryAcquire() {
        return rateLimiter.tryAcquire();
    }
}

测试类: 

package com.leolztang.sb.aop.ratelimiter;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/limit")
public class AccessLimiterController {
    private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    ExecutorService fixedThreadPool = Executors.newFixedThreadPool(120);
    @Autowired
    private AccessLimitService accessLimitService;
    
    @RequestMapping(value="/ratelimit",method = RequestMethod.GET)
    @ResponseBody
    public void tryForVisit() {
        for(int i=0;i<200;i++) {
            fixedThreadPool.execute(new Runnable() {
                
                @Override
                public void run() {
                    String str=visitAccess();
                    System.out.println("output:"+str);
                }
            });
        }
    }
    
    
    
    public String visitAccess() {
        if(accessLimitService.tryAcquire()) {
            try {
                Thread.sleep(2000);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
            return "aceess success [" + sdf.format(new Date()) + "]";
        }else {
            return "aceess limit [" + sdf.format(new Date()) + "]";
        }
    }
}

pom文件引入guava依赖:

<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>18.0</version>
</dependency>

 
posted on 2019-07-17 15:12  colorfulworld  阅读(3271)  评论(0编辑  收藏  举报