Redis调用方式

部署运行你感兴趣的模型镜像

一、普通同步方式

最简单和基础的调用方式,

@Test
public void test1Normal() {
    Jedis jedis = new Jedis("localhost");
    long start = System.currentTimeMillis();
    for (int i = 0; i < 100000; i++) {
        String result = jedis.set("n" + i, "n" + i);
    }
    long end = System.currentTimeMillis();
    System.out.println("Simple SET: " + ((end - start)/1000.0) + " seconds");
    jedis.disconnect();
}

很简单吧,每次set之后都可以返回结果,标记是否成功。

二、事务方式(Transactions)

redis的事务很简单,他主要目的是保障,一个client发起的事务中的命令可以连续的执行,而中间不会插入其他client的命令。

看下面例子:

@Test
public void test2Trans() {
    Jedis jedis = new Jedis("localhost");
    long start = System.currentTimeMillis();
    Transaction tx = jedis.multi();
    for (int i = 0; i < 100000; i++) {
        tx.set("t" + i, "t" + i);
    }
    List<Object> results = tx.exec();
    long end = System.currentTimeMillis();
    System.out.println("Transaction SET: " + ((end - start)/1000.0) + " seconds");
    jedis.disconnect();
}

我们调用jedis.watch(…)方法来监控key,如果调用后key值发生变化,则整个事务会执行失败。另外,事务中某个操作失败,并不会回滚其他操作。这一点需要注意。还有,我们可以使用discard()方法来取消事务。

三、管道(Pipelining)

有时,我们需要采用异步方式,一次发送多个指令,不同步等待其返回结果。这样可以取得非常好的执行效率。这就是管道,调用方法如下:

@Test
public void test3Pipelined() {
    Jedis jedis = new Jedis("localhost");
    Pipeline pipeline = jedis.pipelined();
    long start = System.currentTimeMillis();
    for (int i = 0; i < 100000; i++) {
        pipeline.set("p" + i, "p" + i);
    }
    List<Object> results = pipeline.syncAndReturnAll();
    long end = System.currentTimeMillis();
    System.out.println("Pipelined SET: " + ((end - start)/1000.0) + " seconds");
    jedis.disconnect();
}

四、管道中调用事务

就Jedis提供的方法而言,是可以做到在管道中使用事务,其代码如下:

@Test
public void test4combPipelineTrans() {
    jedis = new Jedis("localhost"); 
    long start = System.currentTimeMillis();
    Pipeline pipeline = jedis.pipelined();
    pipeline.multi();
    for (int i = 0; i < 100000; i++) {
        pipeline.set("" + i, "" + i);
    }
    pipeline.exec();
    List<Object> results = pipeline.syncAndReturnAll();
    long end = System.currentTimeMillis();
    System.out.println("Pipelined transaction: " + ((end - start)/1000.0) + " seconds");
    jedis.disconnect();
}

但是经测试(见本文后续部分),发现其效率和单独使用事务差不多,甚至还略微差点。

五、分布式直连同步调用

@Test
public void test5shardNormal() {
    List<JedisShardInfo> shards = Arrays.asList(
            new JedisShardInfo("localhost",6379),
            new JedisShardInfo("localhost",6380));

    ShardedJedis sharding = new ShardedJedis(shards);

    long start = System.currentTimeMillis();
    for (int i = 0; i < 100000; i++) {
        String result = sharding.set("sn" + i, "n" + i);
    }
    long end = System.currentTimeMillis();
    System.out.println("Simple@Sharing SET: " + ((end - start)/1000.0) + " seconds");

    sharding.disconnect();
}

这个是分布式直接连接,并且是同步调用,每步执行都返回执行结果。类似地,还有异步管道调用。

六、分布式直连异步调用

@Test
public void test6shardpipelined() {
    List<JedisShardInfo> shards = Arrays.asList(
            new JedisShardInfo("localhost",6379),
            new JedisShardInfo("localhost",6380));

    ShardedJedis sharding = new ShardedJedis(shards);

    ShardedJedisPipeline pipeline = sharding.pipelined();
    long start = System.currentTimeMillis();
    for (int i = 0; i < 100000; i++) {
        pipeline.set("sp" + i, "p" + i);
    }
    List<Object> results = pipeline.syncAndReturnAll();
    long end = System.currentTimeMillis();
    System.out.println("Pipelined@Sharing SET: " + ((end - start)/1000.0) + " seconds");

    sharding.disconnect();
}

七、分布式连接池同步调用

如果,你的分布式调用代码是运行在线程中,那么上面两个直连调用方式就不合适了,因为直连方式是非线程安全的,这个时候,你就必须选择连接池调用。

@Test
public void test7shardSimplePool() {
    List<JedisShardInfo> shards = Arrays.asList(
            new JedisShardInfo("localhost",6379),
            new JedisShardInfo("localhost",6380));

    ShardedJedisPool pool = new ShardedJedisPool(new JedisPoolConfig(), shards);

    ShardedJedis one = pool.getResource();

    long start = System.currentTimeMillis();
    for (int i = 0; i < 100000; i++) {
        String result = one.set("spn" + i, "n" + i);
    }
    long end = System.currentTimeMillis();
    pool.returnResource(one);
    System.out.println("Simple@Pool SET: " + ((end - start)/1000.0) + " seconds");

    pool.destroy();
}

上面是同步方式,当然还有异步方式。

八、分布式连接池异步调用

@Test
public void test8shardPipelinedPool() {
    List<JedisShardInfo> shards = Arrays.asList(
            new JedisShardInfo("localhost",6379),
            new JedisShardInfo("localhost",6380));

    ShardedJedisPool pool = new ShardedJedisPool(new JedisPoolConfig(), shards);

    ShardedJedis one = pool.getResource();

    ShardedJedisPipeline pipeline = one.pipelined();

    long start = System.currentTimeMillis();
    for (int i = 0; i < 100000; i++) {
        pipeline.set("sppn" + i, "n" + i);
    }
    List<Object> results = pipeline.syncAndReturnAll();
    long end = System.currentTimeMillis();
    pool.returnResource(one);
    System.out.println("Pipelined@Pool SET: " + ((end - start)/1000.0) + " seconds");
    pool.destroy();
}

九、需要注意的地方

  1. 事务和管道都是异步模式。在事务和管道中不能同步查询结果。比如下面两个调用,都是不允许的:

     Transaction tx = jedis.multi();
     for (int i = 0; i < 100000; i++) {
         tx.set("t" + i, "t" + i);
     }
     System.out.println(tx.get("t1000").get());  //不允许
    
     List<Object> results = tx.exec();
    
     …
     …
    
     Pipeline pipeline = jedis.pipelined();
     long start = System.currentTimeMillis();
     for (int i = 0; i < 100000; i++) {
         pipeline.set("p" + i, "p" + i);
     }
     System.out.println(pipeline.get("p1000").get()); //不允许
    
     List<Object> results = pipeline.syncAndReturnAll();
  2. 事务和管道都是异步的,个人感觉,在管道中再进行事务调用,没有必要,不如直接进行事务模式。

  3. 分布式中,连接池的性能比直连的性能略好(见后续测试部分)。

  4. 分布式调用中不支持事务。

    因为事务是在服务器端实现,而在分布式中,每批次的调用对象都可能访问不同的机器,所以,没法进行事务。

十、测试

运行上面的代码,进行测试,其结果如下:

Simple SET: 5.227 seconds

Transaction SET: 0.5 seconds
Pipelined SET: 0.353 seconds
Pipelined transaction: 0.509 seconds

Simple@Sharing SET: 5.289 seconds
Pipelined@Sharing SET: 0.348 seconds

Simple@Pool SET: 5.039 seconds
Pipelined@Pool SET: 0.401 seconds

另外,经测试分布式中用到的机器越多,调用会越慢。上面是2片,下面是5片:

Simple@Sharing SET: 5.494 seconds
Pipelined@Sharing SET: 0.51 seconds
Simple@Pool SET: 5.223 seconds
Pipelined@Pool SET: 0.518 seconds

下面是10片:

Simple@Sharing SET: 5.9 seconds
Pipelined@Sharing SET: 0.794 seconds
Simple@Pool SET: 5.624 seconds
Pipelined@Pool SET: 0.762 seconds

下面是100片:

Simple@Sharing SET: 14.055 seconds
Pipelined@Sharing SET: 8.185 seconds
Simple@Pool SET: 13.29 seconds
Pipelined@Pool SET: 7.767 seconds

分布式中,连接池方式调用不但线程安全外,根据上面的测试数据,也可以看出连接池比直连的效率更好。

十一、完整的测试代码

package com.example.nosqlclient;

import java.util.Arrays;
import java.util.List;

import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.JedisShardInfo;
import redis.clients.jedis.Pipeline;
import redis.clients.jedis.ShardedJedis;
import redis.clients.jedis.ShardedJedisPipeline;
import redis.clients.jedis.ShardedJedisPool;
import redis.clients.jedis.Transaction;

import org.junit.FixMethodOrder;
import org.junit.runners.MethodSorters;

@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class TestJedis {

    private static Jedis jedis;
    private static ShardedJedis sharding;
    private static ShardedJedisPool pool;

    @BeforeClass
    public static void setUpBeforeClass() throws Exception {
        List<JedisShardInfo> shards = Arrays.asList(
                new JedisShardInfo("localhost",6379),
                new JedisShardInfo("localhost",6379)); //使用相同的ip:port,仅作测试


        jedis = new Jedis("localhost"); 
        sharding = new ShardedJedis(shards);

        pool = new ShardedJedisPool(new JedisPoolConfig(), shards);
    }

    @AfterClass
    public static void tearDownAfterClass() throws Exception {
        jedis.disconnect();
        sharding.disconnect();
        pool.destroy();
    }

    @Test
    public void test1Normal() {
        long start = System.currentTimeMillis();
        for (int i = 0; i < 100000; i++) {
            String result = jedis.set("n" + i, "n" + i);
        }
        long end = System.currentTimeMillis();
        System.out.println("Simple SET: " + ((end - start)/1000.0) + " seconds");
    }

    @Test
    public void test2Trans() {
        long start = System.currentTimeMillis();
        Transaction tx = jedis.multi();
        for (int i = 0; i < 100000; i++) {
            tx.set("t" + i, "t" + i);
        }
        //System.out.println(tx.get("t1000").get());

        List<Object> results = tx.exec();
        long end = System.currentTimeMillis();
        System.out.println("Transaction SET: " + ((end - start)/1000.0) + " seconds");
    }

    @Test
    public void test3Pipelined() {
        Pipeline pipeline = jedis.pipelined();
        long start = System.currentTimeMillis();
        for (int i = 0; i < 100000; i++) {
            pipeline.set("p" + i, "p" + i);
        }
        //System.out.println(pipeline.get("p1000").get());
        List<Object> results = pipeline.syncAndReturnAll();
        long end = System.currentTimeMillis();
        System.out.println("Pipelined SET: " + ((end - start)/1000.0) + " seconds");
    }

    @Test
    public void test4combPipelineTrans() {
        long start = System.currentTimeMillis();
        Pipeline pipeline = jedis.pipelined();
        pipeline.multi();
        for (int i = 0; i < 100000; i++) {
            pipeline.set("" + i, "" + i);
        }
        pipeline.exec();
        List<Object> results = pipeline.syncAndReturnAll();
        long end = System.currentTimeMillis();
        System.out.println("Pipelined transaction: " + ((end - start)/1000.0) + " seconds");
    }

    @Test
    public void test5shardNormal() {
        long start = System.currentTimeMillis();
        for (int i = 0; i < 100000; i++) {
            String result = sharding.set("sn" + i, "n" + i);
        }
        long end = System.currentTimeMillis();
        System.out.println("Simple@Sharing SET: " + ((end - start)/1000.0) + " seconds");
    }

    @Test
    public void test6shardpipelined() {
        ShardedJedisPipeline pipeline = sharding.pipelined();
        long start = System.currentTimeMillis();
        for (int i = 0; i < 100000; i++) {
            pipeline.set("sp" + i, "p" + i);
        }
        List<Object> results = pipeline.syncAndReturnAll();
        long end = System.currentTimeMillis();
        System.out.println("Pipelined@Sharing SET: " + ((end - start)/1000.0) + " seconds");
    }

    @Test
    public void test7shardSimplePool() {
        ShardedJedis one = pool.getResource();

        long start = System.currentTimeMillis();
        for (int i = 0; i < 100000; i++) {
            String result = one.set("spn" + i, "n" + i);
        }
        long end = System.currentTimeMillis();
        pool.returnResource(one);
        System.out.println("Simple@Pool SET: " + ((end - start)/1000.0) + " seconds");
    }

    @Test
    public void test8shardPipelinedPool() {
        ShardedJedis one = pool.getResource();

        ShardedJedisPipeline pipeline = one.pipelined();

        long start = System.currentTimeMillis();
        for (int i = 0; i < 100000; i++) {
            pipeline.set("sppn" + i, "n" + i);
        }
        List<Object> results = pipeline.syncAndReturnAll();
        long end = System.currentTimeMillis();
        pool.returnResource(one);
        System.out.println("Pipelined@Pool SET: " + ((end - start)/1000.0) + " seconds");
    }
}

 

您可能感兴趣的与本文相关的镜像

Stable-Diffusion-3.5

Stable-Diffusion-3.5

图片生成
Stable-Diffusion

Stable Diffusion 3.5 (SD 3.5) 是由 Stability AI 推出的新一代文本到图像生成模型,相比 3.0 版本,它提升了图像质量、运行速度和硬件效率

<think>我们之前已经优化了RedisDelayQueue类,现在需要给出调用该类的示例代码。 调用方式主要包括:实例化RedisDelayQueue、添加延迟任务、启动消费线程等步骤。 假设我们是在Spring Boot环境中使用,因此需要考虑Spring的依赖注入。 我们将创建一个配置类来配置RedisDelayQueue,并在服务类中使用它。 步骤: 1. 配置RedisTemplate(如果尚未配置) 2. 创建一个配置类,实例化RedisDelayQueue 3. 在服务类中注入RedisDelayQueue,并调用其方法添加任务 4. 在应用启动时启动延迟队列的监听 注意:在Spring中,我们通常使用@PostConstruct来启动监听,但要注意多实例问题(如果是集群部署,每个实例都会启动监听,可能导致重复消费)。因此,我们使用Redis的原子操作已经避免了重复消费(Lua脚本确保任务只被一个消费者处理),所以每个实例都可以启动监听线程。 以下是一个示例调用方式: </think>### RedisDelayQueue 调用方式详解 以下是优化后的 `RedisDelayQueue` 的完整调用方式和集成示例,适用于 Spring Boot 环境: ```java import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.Scheduled; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; @SpringBootApplication @EnableScheduling public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } // 配置 RedisTemplate @Bean public RedisTemplate<String, String> redisTemplate(LettuceConnectionFactory connectionFactory) { RedisTemplate<String, String> template = new RedisTemplate<>(); template.setConnectionFactory(connectionFactory); template.setKeySerializer(new StringRedisSerializer()); template.setValueSerializer(new GenericJackson2JsonRedisSerializer()); return template; } // 创建 RedisDelayQueue 实例 @Bean public RedisDelayQueue<MsgInfoPojo> delayQueue( RedisTemplate<String, String> redisTemplate, ImApi imApi ) { return new RedisDelayQueue<>( redisTemplate, "delayQueue-ws-im-msg", imApi, 5 // 消费线程数 ); } } @Service @Slf4j public class MessageService { private final RedisDelayQueue<MsgInfoPojo> delayQueue; private final ImApi imApi; public MessageService(RedisDelayQueue<MsgInfoPojo> delayQueue, ImApi imApi) { this.delayQueue = delayQueue; this.imApi = imApi; } // 初始化时启动队列消费 @PostConstruct public void init() { log.info("Starting delay queue consumer"); delayQueue.start(); } // 应用关闭时停止队列 @PreDestroy public void destroy() { log.info("Stopping delay queue consumer"); delayQueue.stop(); } /** * 添加延迟消息 * @param messages 消息内容 * @param delaySeconds 延迟秒数 * @param traceId 追踪ID(可选) */ public void addDelayedMessage(List<MsgInfoPojo> messages, long delaySeconds, String traceId) { boolean success = delayQueue.setDelayTasks( messages, delaySeconds * 1000, // 转换为毫秒 traceId ); if (success) { log.info("Added delayed message, traceId={}, delay={}s", traceId, delaySeconds); } else { log.error("Failed to add delayed message, traceId={}", traceId); // 可选:添加失败处理逻辑 } } // 示例:创建定时发送的消息 @Scheduled(fixedDelay = 60000) // 每分钟执行一次 public void scheduleReminderMessages() { List<MsgInfoPojo> reminderMessages = createReminderMessages(); addDelayedMessage(reminderMessages, 300, "reminder-" + System.currentTimeMillis()); } private List<MsgInfoPojo> createReminderMessages() { // 创建提醒消息逻辑 return Collections.emptyList(); } } @RestController @RequestMapping("/messages") public class MessageController { private final MessageService messageService; public MessageController(MessageService messageService) { this.messageService = messageService; } @PostMapping("/delay") public ResponseEntity<String> sendDelayedMessage( @RequestBody List<MsgInfoPojo> messages, @RequestParam long delaySeconds, @RequestParam(required = false) String traceId ) { String effectiveTraceId = traceId == null ? UUID.randomUUID().toString() : traceId; messageService.addDelayedMessage(messages, delaySeconds, effectiveTraceId); return ResponseEntity.ok() .header("X-Trace-Id", effectiveTraceId) .body("Delayed message scheduled"); } } ``` ### 调用方式详解 #### 1. 基础调用方法 ```java // 添加延时任务(5秒后执行) List<MsgInfoPojo> messages = Arrays.asList(msg1, msg2); delayQueue.setDelayTasks(messages, 5000); // 添加带追踪ID的任务 delayQueue.setDelayTasks(messages, 10000, "order-12345"); ``` #### 2. 生产环境推荐调用模式 ```java @Service public class OrderService { private final RedisDelayQueue<MsgInfoPojo> delayQueue; // 添加订单支付提醒 public void schedulePaymentReminder(String orderId) { MsgInfoPojo reminderMsg = createPaymentReminder(orderId); delayQueue.setDelayTasks( Collections.singletonList(reminderMsg), 3600 * 1000, // 1小时后提醒 "payment-reminder-" + orderId ); } } ``` #### 3. 错误处理与重试 ```java public void reliableAddDelayedMessage(List<MsgInfoPojo> messages, long delayMillis) { int maxRetries = 3; for (int i = 0; i < maxRetries; i++) { if (delayQueue.setDelayTasks(messages, delayMillis)) { return; // 成功则退出 } try { Thread.sleep(100 * (i + 1)); // 指数退避等待 } catch (InterruptedException e) { Thread.currentThread().interrupt(); break; } } log.error("Failed to add delayed message after {} retries", maxRetries); // 可选:降级处理或发送警报 } ``` #### 4. 批量添加任务优化 ```java public void batchAddTasks(List<DelayTaskData> tasks) { // 使用Redis管道批量添加 redisTemplate.executePipelined((RedisCallback<Object>) connection -> { for (DelayTaskData task : tasks) { String json = JSON.toJSONString(task); connection.zAdd( delayQueueName.getBytes(), System.currentTimeMillis() + task.getDelay(), json.getBytes() ); } return null; }); } ``` ### 集成注意事项 1. **线程配置**: ```java // 在资源充足的服务器上 new RedisDelayQueue(redisTemplate, "queue-name", imApi, 10); // 在资源受限环境 new RedisDelayQueue(redisTemplate, "queue-name", imApi, 2); ``` 2. **集群环境部署**: ```yaml # application.yml spring: redis: cluster: nodes: - redis-node1:6379 - redis-node2:6379 - redis-node3:6379 lettuce: pool: max-active: 20 max-idle: 10 ``` 3. **监控集成示例**: ```java @Bean public MeterRegistryCustomizer<MeterRegistry> metricsCommonTags() { return registry -> registry.config().commonTags( "application", "message-service", "queue", "delayQueue-ws-im-msg" ); } // 在RedisDelayQueue中添加 private Counter successCounter; public RedisDelayQueue(..., MeterRegistry registry) { successCounter = registry.counter("delay_queue.success"); } private void handleTask(String payload) { try { // ...处理逻辑 successCounter.increment(); } // ... } ``` ### 调用场景示例 #### 场景1:订单超时取消 ```java public void createOrder(Order order) { // 保存订单到数据库 orderRepository.save(order); // 设置30分钟超时取消 MsgInfoPojo cancelMsg = createCancelMessage(order.getId()); delayQueue.setDelayTasks( Collections.singletonList(cancelMsg), 30 * 60 * 1000, // 30分钟 "order-cancel-" + order.getId() ); } // 订单支付成功后取消超时任务 public void onPaymentSuccess(String orderId) { // 实际应用中通过任务ID删除 redisTemplate.opsForZSet().removeIf( delayQueueName, item -> item.contains("order-cancel-" + orderId) ); } ``` #### 场景2:重试失败消息 ```java public void handleFailedMessage(MessageProcessingException ex) { if (ex.isRecoverable()) { // 5分钟后重试 delayQueue.setDelayTasks( ex.getOriginalMessages(), 5 * 60 * 1000, "retry-" + ex.getTraceId() ); } else { // 加入死信队列 deadLetterService.add(ex.getOriginalMessages()); } } ``` ### 性能调优建议 1. **Lua脚本优化**: ```lua -- KEYS[1]: 队列名 -- ARGV[1]: 当前时间戳 -- ARGV[2]: 批量大小 local tasks = redis.call('ZRANGEBYSCORE', KEYS[1], '-inf', ARGV[1], 'LIMIT', 0, ARGV[2]) if #tasks > 0 then redis.call('ZREM', KEYS[1], unpack(tasks)) return tasks end return nil ``` 2. **动态线程调整**: ```java public void adjustThreadCount(int newCount) { if (newCount > 0 && newCount <= MAX_THREADS) { executor.setCorePoolSize(newCount); executor.setMaximumPoolSize(newCount); } } // 根据负载自动调整 @Scheduled(fixedRate = 60000) public void autoScaleThreads() { long pendingCount = redisTemplate.opsForZSet().count( delayQueueName, 0, System.currentTimeMillis() + 60000 ); int requiredThreads = Math.min( MAX_THREADS, (int) Math.ceil(pendingCount / 1000.0) ); delayQueue.adjustThreadCount(requiredThreads); } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值