Uma implementação da fila de atraso assíncrona

Autor: JD Retail Zhang Luyao

1. Cenários de aplicação

Atualmente, existem muitas funções no sistema que requerem processamento atrasado: cancelamento de tempo limite de pagamento, tempo limite de fila, envio atrasado de SMS, WeChat e outros lembretes, atualização de token, expiração de cartão de membro, etc. Por meio do processamento atrasado, os recursos do sistema são bastante economizados e não há necessidade de consultar o banco de dados para tarefas de processamento.

Atualmente, a maioria das funções é concluída por tarefas de temporização. Existem dois tipos de tarefas de temporização: quartz e xxljob. O tempo de pesquisa é curto e executado uma vez por segundo, o que coloca uma certa pressão no banco de dados e tem um intervalo de 1 segundo erro. O tempo de polling é longo, por exemplo, uma vez a cada 30 minutos, um dado é inserido às 03h01, e a expiração é normalmente executada às 3h31, mas quando o polling é realizado às 3h30, os dados de 3:00-3:30 é escaneado, mas a varredura é menor que 3: Os dados de 31 só podem ser escaneados às 4:00, o que equivale a um atraso de 29 minutos!

2. Pesquisa sobre métodos de processamento de atraso

1.DelayQueue

1. Método de implementação:

A fila de bloqueio de atraso fornecida pelo jvm classifica as tarefas com diferentes tempos de atraso por meio da fila de prioridade, bloqueia por meio da condição e adquire as tarefas atrasadas durante o período de suspensão.

Quando uma nova tarefa é adicionada, ele julgará se a nova tarefa é a primeira tarefa a ser executada. Se for, a fila de espera será liberada para evitar que os elementos recém-adicionados precisem ser executados e não possam ser normalmente obtidos pela execução fio.

2. Problemas existentes:

1. Operação autônoma, depois que o sistema está inativo, não é possível realizar novas tentativas efetivas

2. Falha ao executar registro e backup

3. Nenhum mecanismo de repetição

4. Quando o sistema reiniciar, a tarefa será apagada!

5. Não é permitido o consumo de fragmentos

3. Vantagens: implementação simples, bloqueio quando não há tarefa, economia de recursos e tempo de execução preciso

2. Fila de atraso mq

Método de implementação: confie no mq e obtenha a função de consumo atrasado definindo o tempo de consumo atrasado. Assim como o rabbitMq e o jmq, você pode definir o tempo de consumo atrasado. RabbitMq é implementado definindo o tempo de expiração da mensagem e colocando-a na fila de mensagens mortas para consumo.

Problemas existentes:

1. A configuração de tempo não é flexível. Cada fila tem um tempo de expiração fixo. Sempre que uma fila de atraso é criada, uma nova fila de mensagens precisa ser criada

Vantagens: Contando com o jmq, ele pode monitorar, consumir registros e tentar novamente de forma eficaz, tem a capacidade de consumir várias máquinas ao mesmo tempo e não tem medo de tempo de inatividade

3. Tarefas agendadas

Pesquisando dados qualificados por meio de tarefas agendadas

deficiência:

1. É necessário ler o banco de dados comercial, o que coloca uma certa pressão no banco de dados.

2. Há um atraso

3. Quando a quantidade de dados digitalizados é muito grande, ela ocupa muitos recursos do sistema.

4. A fragmentação não pode ser consumida

vantagem:

1. Após o consumo falhar, você pode continuar a consumir na próxima vez e ter a capacidade de tentar novamente.

2. Poder de compra estável

4.redis

As tarefas são armazenadas em redis e a fila zset de redis é usada para classificar de acordo com a pontuação. O programa obtém continuamente o consumo de dados da fila por meio do thread para realizar a fila de atraso

vantagem:

1. Consultar redis é mais rápido que o banco de dados, e o comprimento da fila definido é muito grande, e a consulta será realizada de acordo com a estrutura da tabela de salto, que é altamente eficiente

2. Redis pode ser classificado de acordo com o carimbo de data/hora, você só precisa consultar a tarefa da pontuação no carimbo de data/hora atual

3. Sem medo de reiniciar a máquina

4. Consumo distribuído

deficiência:

1. Limitado pelo desempenho do redis, 10 W simultâneos

2. Vários comandos não podem garantir a atomicidade. Usar scripts lua exigirá que todos os dados estejam em um fragmento redis.

5. Roda do tempo

A execução de tarefas atrasadas por meio da roda do tempo também é baseada na operação autônoma do jvm. Por exemplo, kafka e netty implementam rodas do tempo, e o watchdog de redisson também é realizado por meio da roda do tempo do netty.

Desvantagens: Não é adequado para o uso de serviços distribuídos e as tarefas serão perdidas após o tempo de inatividade.

3. Alcance metas

Compatível com os componentes de eventos assíncronos atualmente em uso e fornece componentes de atraso de alto desempenho, repetíveis, registrados, de monitoramento de alarme e mais confiáveis.

•Confiabilidade da transmissão da mensagem: Depois que uma mensagem entra na fila de espera, é garantido que ela seja consumida pelo menos uma vez.

•Cliente suporta rico: suporta vários idiomas.

• Alta disponibilidade: oferece suporte à implantação de várias instâncias. Depois que uma instância é suspensa, há uma instância de backup que continua a fornecer serviços.

•Tempo real: um determinado erro de tempo é permitido.

•Suporte para exclusão de mensagens: os usuários corporativos podem excluir mensagens especificadas a qualquer momento.

• Consulta de consumo de suporte

• Suporta repetição manual

• Aumentar o monitoramento da execução do evento assíncrono atual

4. Projeto de arquitetura

5. Atrasar a implementação do componente

1. Princípio de implementação

No momento, optamos por usar o jimdb para implementar a função de atraso por meio do zset e armazenar o id da tarefa e o tempo de execução correspondente como pontuação na fila zset. Por padrão, eles serão classificados por pontuação e, a cada vez, pegamos o id da tarefa de 0 pontos no tempo atual,

Ao enviar uma tarefa atrasada, um id único será gerado de acordo com o timestamp + ip da máquina + queueName + sequência, e o corpo da mensagem será construído, criptografado e colocado na fila zset.

Ao mover a thread, a tarefa que atingiu o tempo de execução é movida para a fila de liberação, esperando que o consumidor a obtenha.

A parte de monitoramento integra ump

Os registros de consumo são concluídos por meio de backup redis + persistência do banco de dados.

O método implementado pelo cache é apenas um tipo de implementação, cujo método de implementação pode ser controlado por meio de parâmetros e pode ser expandido livremente por meio do spi.

2. Estrutura da mensagem

Cada Trabalho deve conter os seguintes atributos:

• Tópico: tipo de trabalho, ou seja, QueueName

•Id: o identificador único do Trabalho. Usado para recuperar e excluir as informações do Trabalho especificadas.

•Atraso: O trabalho precisa atrasar o tempo. Unidade: segundos. (O servidor irá convertê-lo em tempo absoluto)

•Body: O conteúdo do Job, que é armazenado no formato json para que os consumidores façam processamentos de negócios específicos.

•traceId: o traceId do encadeamento de envio, após o pfinder subseqüente suportar a configuração traceId, ele pode compartilhar o mesmo traceid com o encadeamento de envio, o que é conveniente para rastreamento de log

A estrutura específica é mostrada na figura abaixo:

TTR é projetado para garantir a confiabilidade da transmissão de mensagens.

3. Fluxo de dados e fluxograma

Publique e consuma com base no método redis-disruptor, que pode ser usado como uma mensagem. Os consumidores usam a fila sem bloqueio do disruptor de evento assíncrono original para consumo e não há bloqueio entre aplicativos diferentes e filas diferentes

1. Apoie o aplicativo para apenas publicar, não consumir e alcançar a função de fila de mensagens.

2: Suporte a agrupamento.Para o problema de chaves grandes, se houver muitos eventos, você pode definir o número de filas de atraso e baldes de fila de tarefas para reduzir o problema de bloqueio de redis causado por chaves grandes.

3: Através da configuração do ducc, o desempenho é expandido. Atualmente, apenas o consumo está ativado e o consumo está desativado.

4: Suporta configuração de tempo limite para evitar que threads de consumidores sejam executados por muito tempo

Gargalo: A velocidade de consumo é lenta e a velocidade de produção é muito rápida, o que fará com que a fila do buffer de anel fique cheia. Quando o aplicativo atual é um produtor e um consumidor, o produtor irá dormir e o desempenho depende da velocidade de consumo A máquina pode ser expandida horizontalmente para melhorar diretamente o desempenho. Monitore o comprimento da fila de redis. Se ela continuar crescendo, considere adicionar consumidores para melhorar diretamente o desempenho.

Possível situação: Como uma aplicação compartilha um disruptor e possui 64 threads consumidoras, se o consumo de um determinado evento for muito lento, todas as 64 threads estão consumindo este evento, o que fará com que outros eventos sejam consumidos por nenhuma thread consumidora, e o produtor thread também irá consumir está bloqueada, fazendo com que o consumo de todos os eventos seja bloqueado.

Para observar se existe tal gargalo de desempenho mais tarde, você pode dar a cada fila um pool de encadeamentos do consumidor.

6. exemplo de demonstração

Adicionar arquivo de configuração

Determine se deseja habilitar jd.event.enable:true

<dependency> <groupId>com.jd.car</groupId>
 <artifactId>senna-event</artifactId>
 <version>1.0-SNAPSHOT</version> </dependency>

configuração

jd:
senna:
event:
enable: true
queue:
retryEventQueue:
bucketNum: 1
handleBean: retryHandle

Código de consumo:

package com.jd.car.senna.admin.event;

import com.jd.car.senna.event.EventHandler;
import com.jd.car.senna.event.annotation.SennaEvent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

/**
* @author zhangluyao
* @description
* @create 2022-02-21-9:54 下午
*/
@Slf4j
@Component("retryHandle")
public class RetryQueueEvent extends EventHandler {

@Override
protected void onHandle(String key, String eventType) {
log.info("Handler开始消费:{}", key);
}

@Override
protected void onDelayHandle(String key, String eventType) {
log.info("delayHandler开始消费:{}", key);
}
}

Formulário de anotação:

package com.jd.car.senna.admin.event;

import com.jd.car.senna.event.EventHandler;
import com.jd.car.senna.event.annotation.SennaEvent;
import lombok.extern.slf4j.Slf4j;

/**
* @author zhangluyao
* @description
* @create 2022-02-21-9:54 下午
*/
@Slf4j
@SennaEvent(queueName = "testQueue", bucketNum = 5,delayBucketNum = 5,delayEnable = true)
public class TestQueueEvent extends EventHandler {

@Override
protected void onHandle(String key, String eventType) {
log.info("Handler开始消费:{}", key);
}

@Override
protected void onDelayHandle(String key, String eventType) {
log.info("delayHandler开始消费:{}", key);
}
}

enviar código


package com.jd.car.senna.admin.controller;

import com.jd.car.senna.event.queue.IEventQueue;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.concurrent.CompletableFuture;


/**
* @author zly
*/
@RestController
@Slf4j
public class DemoController {

@Lazy
@Resource(name = "testQueue")
private IEventQueue eventQueue;

@ResponseBody
@GetMapping("/api/v1/demo")
public String demo() {
log.info("发送无延迟消息");
eventQueue.push("no delay 5000 millseconds message 3");
return "ok";
}

@ResponseBody
@GetMapping("/api/v1/demo1")
public String demo1() {
log.info("发送延迟5秒消息");
eventQueue.push(" delay 5000 millseconds message,name",1000*5L);
return "ok";
}

@ResponseBody
@GetMapping("/api/v1/demo2")
public String demo2() {
log.info("发送延迟到2022-04-02 00:00:00执行的消息");
eventQueue.push(" delay message,name to 2022-04-02 00:00:00", new Date(1648828800000));
return "ok";
} 

}

Consulte o design de Youzan: https://tech.youzan.com/queuing_delay/

7. Aplicação atual:

1. Yunxiu cancelará automaticamente após 24 horas na fila da loja

2. A Meituan solicita a atualização do token regularmente.

3. O cartão de garantia será gerado em 24 horas

5. Adiamento da geração do extrato

6. Envio de SMS atrasado

{{o.name}}
{{m.name}}

Guess you like

Origin my.oschina.net/u/4090830/blog/8586952