Technology sharing

SpringBoot Redisson utitur ad Redis et actu usus missionibus agunt

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

Praefatio

existSpringBoot utitur RedisTemplate et StringRedisTemplate ad agunt RedisRedisTemplate introducimus et quomodo SpringBoot operatur Redis per RedisTemplate et StringRedisTemplate.
RedisTemplate的好处就是基于SpringBoot自动装配的原理,使得整合redis时比较简单。

Cum ergo SrpingBoot Redis per RedisTemplate operari possit, cur Redisson iterum apparet?Rddisson Sinica documenta
Reddissin也是一个redis客户端,其在提供了redis基本操作的同时,还具备其他客户端一些不具备的高精功能,例如:分布式锁+看门狗、分布式限流、远程调用等等。Reddissin的缺点是api抽象,学习成本高。

I. Overview

Incipiens a verna tabernus 2.x versione, ver-boot-data redis utitur clientela Lactuca ad operandum data per defaltam.

1.1 Lactuca

SpringBoot2之后,默认就采用了lettuce。
Redis client provectus est, iacuit communicationis eventus agitatae secundum synchronisationi filo-tutae compage, asynchrono et reactivo usu, uvas, vigilias, pipelines et encoders sustinens.
Lactuca API tuta est et unum lactucam connexionem operari potest ad varias operationes perficiendas.

1.2 Reddisson

Stratum communicationis agitatae innixa in compage netty, modus asynchronus est, API salva est et unum nexum Redisson operari potest ad varias operationes perficiendas.
Data fabricae Javae distributae et scalabiles adducit, operationes chordas non sustinet et lineamenta Redis non sustinet ut genus, transactiones, pipelines, et partitiones.
Multas operas operationes distributas praebet, sicut seras distribuit, collectiones distribuit, et queues per Redis moras sustentare potest.

Summatim: Prioritize utens Lactuca, quae provectas lineas distribuit sicut seras distribuit et collectiones distribuit.

2. Ver-Botrum integrat Redisson

2.1 clientelas Introducendis

<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson-spring-boot-starter</artifactId>
    <version>3.13.6</version>
</dependency>
  • 1
  • 2
  • 3
  • 4
  • 5

Notice: Post hanc dependentiam introducto, nihil opus est iterum introducerespring-boot-starter-data-redis,Quod?redisson-spring-boot-starter Intromissus interne et excludit Redis' Luttuce et Jedis clientes. Configuratio igitur Luttuce et Jedis in applicatione.yaml effectum non habebit.
Insert imaginem descriptionis hic

Cum Redisson in incepto utendo, RedissonClient plerumque utimur pro notitiarum operationibus. Sed aliqui amici RedissonClient incommodum ad operandum invenire possunt, vel potius uti RedisTemplate ad operationes RedisTemplate.refer adSpringBoot utitur RedisTemplate et StringRedisTemplate ad agunt Redis

Inventum est post consilium Redisson inductum, nexus officinas in fundo RedisTemplate adhibitas etiam Redisson fuisse.
Insert imaginem descriptionis hic

2.2 file configurationis

Configurationis redis informationes in application.yaml addere.

spring:
  data:
    redis:
      mode: master
      # 地址
      host: 30.46.34.190
      # 端口,默认为6379
      port: 6379
      # 密码,没有不填
      password: ''
      # 几号库
      database: 1
      sentinel:
        master: master
        nodes: 30.46.34.190
      cluster:
        nodes: 30.46.34.190
      lettuce:
        pool:
          # 连接池的最大数据库连接数
          max-active: 200
          # 连接池最大阻塞等待时间(使用负值表示没有限制)
          max-wait: -1ms
          # 连接池中的最大空闲连接
          max-idle: 50
          # 连接池中的最小空闲连接
          min-idle: 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

2.3 configurationis genus

@Configuration
@EnableConfigurationProperties({RedisProperties.class})
public class RedissonConfig {

    private static final String REDIS_PROTOCOL_PREFIX = "redis://";

    @Value("${spring.data.redis.mode}")
    private String redisMode;

    private final RedisProperties redisProperties;

    public RedissonConfig(RedisProperties redisProperties) {
        this.redisProperties = redisProperties;
    }

    /**
     * 逻辑参考 RedissonAutoConfiguration#redisson()
     */
    @Bean(destroyMethod = "shutdown")
    public RedissonClient redisson(@Autowired(required = false) List<RedissonAutoConfigurationCustomizer> redissonAutoConfigurationCustomizers) throws IOException {
        Config config = new Config();
        config.setCheckLockSyncedSlaves(false);

        int max = redisProperties.getLettuce().getPool().getMaxActive();
        int min = redisProperties.getLettuce().getPool().getMinIdle();

        switch (redisMode) {
            case "master": {
                SingleServerConfig singleConfig = config.useSingleServer()
                        .setAddress(REDIS_PROTOCOL_PREFIX + redisProperties.getHost() + ":" + redisProperties.getPort())
                        .setDatabase(redisProperties.getDatabase())
                        .setPassword(redisProperties.getPassword());

                if (redisProperties.getConnectTimeout() != null) {
                    singleConfig.setConnectTimeout((int) redisProperties.getConnectTimeout().toMillis());
                }

                singleConfig.setConnectionPoolSize(max);
                singleConfig.setConnectionMinimumIdleSize(min);
            }
            break;
            case "sentinel": {
                String[] nodes = convert(redisProperties.getSentinel().getNodes());

                SentinelServersConfig sentinelConfig = config.useSentinelServers()
                        .setMasterName(redisProperties.getSentinel().getMaster())
                        .addSentinelAddress(nodes)
                        .setDatabase(redisProperties.getDatabase())
                        .setPassword(redisProperties.getPassword());

                if (redisProperties.getConnectTimeout() != null) {
                    sentinelConfig.setConnectTimeout((int) redisProperties.getConnectTimeout().toMillis());
                }

                sentinelConfig.setMasterConnectionPoolSize(max);
                sentinelConfig.setMasterConnectionMinimumIdleSize(min);
                sentinelConfig.setSlaveConnectionPoolSize(max);
                sentinelConfig.setSlaveConnectionMinimumIdleSize(min);
            }
            break;
            case "cluster": {
                String[] nodes = convert(redisProperties.getCluster().getNodes());

                ClusterServersConfig clusterConfig = config.useClusterServers()
                        .addNodeAddress(nodes)
                        .setPassword(redisProperties.getPassword());

                if (redisProperties.getConnectTimeout() != null) {
                    clusterConfig.setConnectTimeout((int) redisProperties.getConnectTimeout().toMillis());
                }

                clusterConfig.setMasterConnectionMinimumIdleSize(min);
                clusterConfig.setMasterConnectionPoolSize(max);
                clusterConfig.setSlaveConnectionMinimumIdleSize(min);
                clusterConfig.setSlaveConnectionPoolSize(max);
            }
            break;
            default:
                throw new IllegalArgumentException("无效的redis mode配置");
        }

        if (redissonAutoConfigurationCustomizers != null) {
            for (RedissonAutoConfigurationCustomizer customizer : redissonAutoConfigurationCustomizers) {
                customizer.customize(config);
            }
        }

        return Redisson.create(config);

    }

    private String[] convert(List<String> nodesObject) {
        List<String> nodes = new ArrayList<String>(nodesObject.size());
        for (String node : nodesObject) {
            if (!node.startsWith(REDIS_PROTOCOL_PREFIX)) {
                nodes.add(REDIS_PROTOCOL_PREFIX + node);
            } else {
                nodes.add(node);
            }
        }
        return nodes.toArray(new String[0]);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103

2.4 Quomodo utor

@Component
public class RedissonService {
	@Resource
    protected RedissonClient redissonClient;

	public void redissonExists(String key){
		RBucket<String> rBucketValue = redissonClient.getBucket(key, StringCodec.INSTANCE);
		if (rBucketValue.isExists()){
            String value = rBucketValue.get();
            // doSomething
        } else {
           // doElseSomething
        }
	}

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

2.5 De ​​missionibus

2.5.1 lock

Discipuli cum aliqua experientia cogitant de redis, cum usum comarum distributum commemorant.

Finis essentialis comis distributus est foveam in Redis occupare (solum positum est, principium carrotum occupare foveas). vos have ut cedere aut iterum conare postea.

Communiter modi pro comis distribuuntur

1. utere setNx imperium
Explicatio huius praecepti est (si non exstat). foveam). exempli gratia:

# set 锁名 值
setnx distribution-lock  locked

// dosoming

del  distribution-lock
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

Sed quaestio est cum hoc mandato.
Forsitan nonnulli amici cum cogitatione cogitaverunt nos aliud spatium huius clavis statuere posse. exempli gratia:

setnx distribution-lock  locked

expire distribution-lock  10

// dosoming

del  distribution-batch
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

Hoc etiam facto, sunt adhuc problemata cum logica. Quia setnx et exspiratio sunt duo mandata, si redis servo suspendit inter setnx et exspiratura, exspirare non erit supplicium, et expiratio temporis occasus deficiet, et seram faciet. adhuc occidetur.

Radix causa est quia duo mandata setnx et exspiratio non sunt mandata atomica.

Et redis res non possunt solvere problema setnx et exspirare, quia exspirare dependet ex executione setnx. Res si aliud iudicari non possunt, ratio setnx+ exspirare ad comas distributas deducendi optimalis solutio non est.

2. utere setNx ex imperio
Problema setNx+exspirationis supra memoratum est. Ad solvendam hanc quaestionem, Redis ministri extensum parametri statuti praecepti in versione 2.8 induxerunt, ut mandatum setnx et exspirare simul exsecutioni mandarentur. exempli gratia:

# set 锁名 值 ex 过期时间(单位:秒) nx
set distribution-lock locked ex 5 nx

// doSomthing

del distribution-lock
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

Logice loquendo, setNx Ex solutione iam optimal est et seram distributam fieri non faciet deadlock.

Sed problemata adhuc oriuntur in progressu nostro.
Cum hoc seram in principio constituimus tempus expiratum, quid si tempus exsecutionis nostrae negotii logica excedat tempus statutum? Locus erit ubi unum stamen non perfecit executionem et alterum stamen crinem distributum teneat.
Si igitur ex compositione setNx uteris, debes curare ut tempus cincinnorum tuum maius sit quam negotium executionis temporis post seram.

3. Usus lua script + vigilia canis latae extensio mechanism
Multum solutionis huius online invenire possum, ideo hic singula non ingrediar.

Instrumenta Redisson comis distributa

SetNx et setNx Ex mandatis superius allatis sunt mandata indigena a Redis servo provideta, et plus minusve problemata sunt. Ad solvendum problema logicum negotium logicum majus esse quam Nx Ex mandato lock timeout, Redisson internum praebet internum internum. Vigil invigilat ut seram monitorem instituatur. Munus eius est ad validitatem periodi pessuli continue extendendam antequam instantia Redisson clauditur. Ex defectu, speculator reclusus lock timeout est 30 seconds (hoc est, renovatio 30 secundarum).

// 加锁以后10秒钟自动解锁
// 无需调用unlock方法手动解锁
lock.lock(10, TimeUnit.SECONDS);

// 尝试加锁,最多等待100秒,上锁以后10秒自动解锁
boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS);
if (res) {
   try {
     ...
   } finally {
       lock.unlock();
   }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
@Resource
RedissonClient redissonClient;

@GetMapping("/testDistributionLock")
public BaseResponse<String> testRedission(){
    RLock lock = redissonClient.getLock("redis:distributionLock");
    try {
        boolean locked = lock.tryLock(10, 3, TimeUnit.SECONDS);
        if(locked){
            log.info("获取锁成功");
            Thread.sleep(100);
            return ResultUtils.success("ok" );
        }else{
            log.error("获取锁失败");
            return ResultUtils.error(ErrorCode.SYSTEM_ERROR);
        }
    } catch (InterruptedException e) {
        throw new BusinessException(ErrorCode.SYSTEM_ERROR,"出异常了");
    } finally {
        lock.unlock();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

2.5.2 Current limitandi

Adversus nos problema de necessitate ad modum currentis interfaces seu logicae negotii sub magno concursu.

RateLimiter dicitur signum situlae currentis limitationis. Hoc genus limitationis currentis est primum signum situlae definire, specificare quot signa intra certum temporis spatium generantur, et certum numerum signa ex signo situlae obtinent singulis diebus accessed. . Si obtines Si bene, ponitur pro valido accessu.