Beanstalk功能介绍及使用方法

一:介绍

Beanstalk,一个高性能、轻量级的分布式内存队列系统,最初设计的目的是想通过后台异步执行耗时的任务来降低高容量Web应用系统的页面访问延迟,支持过有9.5 million用户的Facebook Causes应用。
githubBeanstalkd · GitHub
官网:About – beanstalkd

二:功能特性

2.1 优先级

任务job可以设置优先级,0 代表最高优先级,默认优先级为1024。

2.2 延迟 delay

指定时间后开始执行

2.3 持续执行,直至成功

可以对一个消息多次消费,只要不进行delete,消息可以一直在队列中

2.4 持久化

可以通过binlog将job及其状态记录到文件里面,在Beanstalkd下次启动时可以通过读取binlog来恢复之前的job及状态。

2.5 超时控制

为了防止某个consumer长时间占用任务但不能处理的情况,Beanstalkd为reserve操作设置了timeout时间,如果该consumer不能在指定时间内完成且删除该job,job将被迁移回READY状态,供其他consumer执行。

2.6 分布式容错

因为它是类Memcached设计,beanstalkd各个server之间并不知道彼此的存在,都是通过client来实现分布式以及根据tube名称去特定server获取job。

三:使用场景

  • 用作延时队列:比如可以用于如果用户30分钟内不操作,任务关闭。
  • 用作定时任务:比如可以用于专门的后台任务。
  • 用作异步操作:这是所有消息队列都最常用的,先将任务仍进去,顺序执行。
  • 用作循环队列:用release命令可以循环执行任务,比如可以做负载均衡任务分发。
  • 用作兜底机制:比如一个请求有失败的概率,可以用Beanstalk不断重试,设定超时时间,时间内尝试到成功为止。

其实我们最重要的使用场景是把它作为延迟队列类使用,比如:

  1. 下订单后多长时间没有付款,要取消订单,并退库存
  2. 用户注册成功后,发送一封邮件
  3. 定期检查退款状态的订单是否退款成功

四:Beanstalkd设计基本概念

4.1 核心概念

  • job:一个需要异步处理的任务,是Beanstalkd中的基本单元,需要放在一个tube中
  • tube:一个有名的队列,用来存储统一类型的job,是producer和consumer操作的对象。tube可以称为管道
  • producer:Job 的生产者,通过 put 命令来将一个 job 放到一个 tube 中
  • consumer:Job的消费者,通过 reserve/release/bury/delete 命令来获取 job 或改变 job 的状态

4.2 job的生命周期

生产者生成任务,并根据业务需求将任务放到不同的管道中。比如与注册有关的任务放到注册管道中,和订单有关的任务放到订单管道中。

任务进入管道到离开管道一共有5个状态 :
(ready,delayed,reserved,buried,delete)

  • READY - 需要立即处理的任务,当延时 (DELAYED) 任务到期后会自动成为当前任务;
  • DELAYED - 延迟执行的任务, 当消费者处理任务后, 可以用将消息再次放回 DELAYED 队列延迟执行;
  • RESERVED - 已经被消费者获取, 正在执行的任务。Beanstalkd 负责检查任务是否在 TTR(time-to-run) 内完成;
  • BURIED - 保留的任务: 任务不会被执行,也不会消失,除非有人把它 "踢" 回队列;
  • DELETED - 消息被彻底删除。Beanstalkd 不再维持这些消息。

状态流程

1.生产任务:

      当producer生产者put一个job到tube时,这个job就处于 ready 状态,等待consumer来消费处理;
      producer生产者也可以选择延迟put一个job,这时job就先达到 delayed 状态(比如设置一个5秒延迟的job,那么5秒之后,这个job才会变成 ready 状态,才可以被consumer消费)

2. 消费任务:

     consumer获取了当前 ready 的job后,该job就会迁移到 reserved 状态,这样其他的consumer就不能在操作该job

3. 消费完任务后:

      当consumer消费完该job后,可以选择delete, release 或者 bury 3种操作。

  • delete操作:job从系统消亡,之后不能在获取;
  • release操作:可以重新把该job状态迁移回 ready (也可以延迟状态 delayed 操作),使其他的consumer可以继 续获取和执行该job;
  • bury操作: 把job置为 buried 状态,及是把该job休眠,等到需要的时候,还可以将休眠的 job 重新置为 ready  状态, 也可以delete掉 buried 状态的job。

也就是说:当消费者处理完任务后,任务的状态可能是delete(删除,处理成功),可能是buried(预留,意味着先把任务放一边,等待条件成熟还要用),可能是ready,也可能是delayed,需要根据具体业务场景自己进行判断定义

注意点:

1、延迟时间是消费者去获取消息修改消息状态时生效的,比如分别put一个10秒延迟的消息和1秒延迟的消息,如果马上消费那么就是一秒后拿到1秒的消息,十秒后拿到10秒的消息,但是如果是等了10秒以后才去消费,那么两条消息的顺序不变,因为两条消息都已经是ready状态。


示意图:

五:Java简单示例

5.1 pom依赖

<dependency>
    <groupId>com.dinstone</groupId>
    <artifactId>beanstalkc</artifactId>
    <version>2.2.0</version>
</dependency>

5.2 生产者及消费者

        Configuration config = new Configuration();
        //地址
        config.setServiceHost("127.0.0.1");
        //端口
        config.setServicePort(3097);
        BeanstalkClientFactory factory = new BeanstalkClientFactory(config);
        //创建生产者
        JobProducer producer = factory.createJobProducer("beanstalkd-demo1");
        //创建消费者,tube支持多个
        JobConsumer consumer = factory.createJobConsumer("beanstalkd-demo1", "beanstalkd-demo2");
        //发布消息
        String msg = "测试发布消息";
        /**
         * 四个入参分别是
         *  优先级:越小越优先
         *  延迟时间:过了该时间,消息状态被置为ready状态,可以消费
         *  消费者处理超时时间:如果消费者不能在这个时间内消费完且删除掉,该消息会重新置为ready状态,可以消费
         *  消息内容
         */
        //返回的是jobId,消费者可根据id修改消息状态
        long jobId = producer.putJob(100, 10, 10, msg.getBytes());
        //接收消息
        //入参为阻塞时间,5秒内取不到消息则job为空
        Job job = consumer.reserveJob(5);
        if(job != null) {
            long id = job.getId(); //jobId
            byte[] data = job.getData();  //消息内容
            //下面针对消息进行处理逻辑
            
            //处理过直接删除消息
            consumer.deleteJob(id);
            //也可以重新把该消息置为delay状态
            //入参点进去看名词便知
//            consumer.releaseJob(id, 100, 10);
        }

六、springboot集成(和上面使用一个pom依赖)

6.1 bean生成

import com.dinstone.beanstalkc.BeanstalkClientFactory;
import com.dinstone.beanstalkc.JobConsumer;
import com.dinstone.beanstalkc.JobProducer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 描述:
 *
 * @author qiu
 * @date 2023/4/13 14:18
 */
@Configuration
public class BeanStalkConfig {
    private final Logger LOGGER = LoggerFactory.getLogger(BeanStalkConfig.class);

    @Autowired
    private BeanStalkProperties properties;

    /**
     * 创建一个BeanstalkClientFactory
     */
    @Bean
    public BeanstalkClientFactory getFactory() {
        com.dinstone.beanstalkc.Configuration config = new com.dinstone.beanstalkc.Configuration();
        config.setServiceHost(properties.getHost());
        config.setServicePort(properties.getPort());
        BeanstalkClientFactory factory = new BeanstalkClientFactory(config);
        return factory;
    }

    /**
     * 创建一个生产者
     */
    @Bean
    public JobProducer jobProducer() {
        return getFactory().createJobProducer(BeanStalkInfo.TUBE_NAME);
    }

    /**
     * 创建一个消费者
     */
    @Bean
    public JobConsumer jobConsumer() {
        return getFactory().createJobConsumer(BeanStalkInfo.TUBE_NAME);
    }

}

 6.2 配置属性信息

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

/**
 * 描述:
 *
 * @author qiu
 * @date 2023/4/13 14:18
 */
@Component
public class BeanStalkProperties {

    @Value("${spring.beanstalk.host}")
    private String host;

    @Value("${spring.beanstalk.port}")
    private Integer port;

    public String getHost() {
        return host;
    }

    public void setHost(String host) {
        this.host = host;
    }

    public Integer getPort() {
        return port;
    }

    public void setPort(Integer port) {
        this.port = port;
    }
}

6.3 使用

        直接注入JobConsumer和JobProducer使用即可,至于怎么调度根据自己业务场景自行处理。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值