高并发

2020-07-05

基准测试

环境

ThinkPad E14 
CPU: AMD 4500U
内存: 16G
JDK: 1.8
测试工具:Jmeter
负载:1000线程  ramp-up 5秒 1000循环次数

执行语句

jmeter -n -t 测试计划.jmx -l results.log -e -o out

eg.1

@RequestMapping("buy/get")
public String buy_get() {
    Jedis jedis = jedisPool.getResource();
    String res = jedis.get("jedis:test-99");
    jedis.close();
    return res;
}

TPS: 13926.99 13899.51 18116.93

eg.2

@RequestMapping("buy/get2")
public String buy_get2() {
    return RedisUtil.get("jedis:test-99");
}

TPS: 14203.74 18032.31 14112.73

eg.3

@RequestMapping("buy/get3")
public String buy_get3() {
    ValueOperations operations = redisTemplate.opsForValue();
    return (String) operations.get("RedisTemplate:test-99");
}

TPS: 11842.17 11517.69 11489.50

eg.4

@RequestMapping("buy/get4")
public String buy_get4() {
    return RedisUtil2.strings().get("jedis:test-99");
}

TPS: 17779.36 12976.56 17614.63

eg.5

@RequestMapping("buy/get5")
public R buy_get5() {
    return R("malu99");
}

TPS: 18562.52 21023.86 20304.16

eg.6

@RequestMapping("buy/get6")
public String buy_get6() {
    return "malu99";
}

TPS: 21628.64 23030.86 22103.36

加入负载因子(限流计数器)

eg.1

加入负载因子对性能几乎没损耗,不过该方案线程不安全

private static int l = 0;
private static long my = 0;

@RequestMapping("buy/factor1")
public String buy_factor1() {
    l++;
    if ((l % 10) == 0) {
        l = 0;
        my++;
        return "malu"+my;
    } else {
        return "error";
    }
}

TPS: 22849.83 22617.27 21693.86

返回结果:

第一次 第二次 第三次
malu99654 malu99671 malu99517

eg.2

接下来写入service来做更多的测试,首先测没有负载因子的情况:

buyController

@Autowired
buyService buyService;

@RequestMapping("buy/factor2")
public R buy_factor2() {
    return buyService.factor2();
}

buyService

public interface buyService {
    R factor2();
}

buyServiceImpl

@Service
public class buyServiceImpl implements buyService {
    @Override
    public R factor2() {
        return R();
    }
}

TPS: 19955.70 19022.98 20781.38

eg.3

使用私有静态属性,在多线程情况下不安全:

private static int l = 0;
private static long my = 0;

@Override
public R factor3(){
    l++;
    if ((l % 10) == 0) {
        l = 0;
        my++;
        return R("malu"+my);
    } else {
        return R("error");
    }
}

TPS: 22611.13 20362.87 19967.25

返回结果:

第一次 第二次 第三次
malu99815 malu99885 malu99852

eg.4

为了线程安全,首先想到的是同步方法

/**
 * 同步方法
 *
 * @return
 */
@Override
synchronized public R factor4() {
    l++;
    if ((l % 10) == 0) {
        l = 0;
        my++;
        return R("malu" + my);
    } else {
        return R("error");
    }
}

TPS: 18390.13 19664.91 18653.93

返回结果(线程安全):

第一次 第二次 第三次
malu100001 malu100001 malu100001

eg.5

同步代码段一样可以

/**
 * 同步代码段
 *
 * @return
 */
@Override
public R factor5() {
    synchronized (buyServiceImpl.class) {
        l++;
        if ((l % 10) == 0) {
            l = 0;
            my++;
            return R("malu" + my);
        } else {
            return R("error");
        }
    }
}

TPS: 19155.99 20210.59 20727.11

返回结果(线程安全):

第一次 第二次 第三次
malu100001 malu100001 malu100001

eg.6

让我们试试原子类AtomicLong

//记录实际累加的数量
private static AtomicLong successNum = new AtomicLong(0);

/**
 * 原子类 AtomicLong
 *
 * @return
 */
@Override
public R factor6() {
    if ((successNum.incrementAndGet() % 10) == 0) {
        my++;
        return R("malu" + my);
    } else {
        return R("error");
    }
}

TPS: 20164.14 19359.96 19009.24

返回结果(线程安全):

第一次 第二次 第三次
malu100001 malu100001 malu100001

eg.7

来看JDK8里的LongAdder

private static LongAdder longAdder = new LongAdder();

/**
 * LongAdder
 *
 * @return
 */
@Override
public R factor7() {
    longAdder.increment();
    if ((longAdder.sum() % 10) == 0) {
        my++;
        return R("malu" + my);
    } else {
        return R("error");
    }
}

TPS: 18867.57 18704.17 20490.54

返回结果(没有锁,这样使用线程不安全):

第一次 第二次 第三次
malu99766 malu99868 malu99664

zookeeper分布式锁

互斥锁(排他锁)

ZkConfig.java

/**
 * 类说明: zookeeper配置
 *
 * @author : Malu
 * @Since : 2020-07-30 15:15
 **/
@Configuration
public class ZkConfig {
    @Bean(initMethod = "start")
    public CuratorFramework curatorFramework() {
        return CuratorFrameworkFactory.newClient(
                "192.168.3.27:2181",
                5000,
                5000,
                new RetryNTimes(5, 5000));
    }
}

ZkController.java

@Autowired
private CuratorFramework curatorFramework;

/**
 * zookeeper互斥锁(排他锁),高并发测试
 *
 * @return R
 */
@GetMapping("/mutex2")
public R getMutex2() throws Exception {
    // 获取一个分布式互斥锁
    InterProcessMutex lock = new InterProcessMutex(curatorFramework, "/mutex_lock2");
    try {
        // 开启两个进程测试,会发现:如果一个分布式互斥锁获取了锁,那么直到锁释放为止数据都不会被侵扰
        lock.acquire();
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        // 在finally中释放,防止死锁
        lock.release();
    }
    return R();
}

环境:

CPU: i5 4590 @ 3.30 Ghz
内存: 32G
JDK: 1.8
测试工具:Jmeter
负载:200线程  ramp-up 5秒 10000循环次数

Windows10 本机zookeeper

TPS: 755.19

redisson分布式锁

pom.xml

<!-- https://mvnrepository.com/artifact/org.redisson/redisson-spring-boot-starter -->
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson-spring-boot-starter</artifactId>
    <version>3.13.3</version>
</dependency>

application.yml

spring:
  redis:
    host: 192.168.1.47
    port: 6381
    database: 15
    timeout: 10000

注意:Redis密码为空时不要加password参数,spring-boot自动装配会报错。

redissonController.java

@Autowired
private RedissonClient redissonClient;

@RequestMapping("lock2")
public R lock2() {
    RLock lock = redissonClient.getLock("redisson_lock2");
    lock.lock();
    lock.unlock();
    return R();
}

环境:

CPU: i5 4590 @ 3.30 Ghz
内存: 32G
JDK: 1.8
测试工具:Jmeter
负载:200线程  ramp-up 5秒 10000循环次数

独立Linux Redis

TPS: 1594.7 1594.08

可重入锁(tryLock)

/**
 * RLock分布式可重入锁,tryLock
 *
 * @return
 */
@RequestMapping("lock5")
public R lock5() throws InterruptedException {
    RLock lock = redissonClient.getLock("redisson_lock5");
    boolean getLockOK = lock.tryLock();
    if (getLockOK) {
        try {
            Thread.sleep(10 * 1000);
        } catch (InterruptedException ie) {
            ie.printStackTrace();
        } finally {
            lock.unlock();
        }
        return R("获取到锁");
    } else {
        return R("没有获取到锁");
    }
}

环境:

CPU: i5 4590 @ 3.30 Ghz
内存: 32G
JDK: 1.8
测试工具:Jmeter
负载:200线程  ramp-up 5秒 10000循环次数

本机Windows Redis

TPS: 1913.48

环境:

CPU: i5 4590 @ 3.30 Ghz
内存: 32G
JDK: 1.8
测试工具:Jmeter
负载:200线程  ramp-up 5秒 10000循环次数

独立Linux Redis

TPS: 5751.29 5553.32 5686.2

参考

使用Redis搭建电商秒杀系统 https://tech.antfin.com/docs/2/63920

Redis高并发秒杀测试 https://github.com/14251104246/redis-demo