最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

springboot使用Redis隊(duì)列實(shí)戰(zhàn)

 更新時(shí)間:2024年07月04日 11:46:12   作者:book多得  
本文主要介紹了springboot使用Redis隊(duì)列實(shí)戰(zhàn),包含四種實(shí)現(xiàn)方式,基于List的 LPUSH+BRPOP的實(shí)現(xiàn), 基于Sorted-Set的實(shí)現(xiàn),PUB/SUB訂閱/發(fā)布模式和基于Stream類型的實(shí)現(xiàn),感興趣的可以了解一下

前言

MQ應(yīng)用有很多,比如ActiveMQ,RabbitMQ,Kafka等,但是也可以基于redis來實(shí)現(xiàn),可以降低系統(tǒng)的維護(hù)成本和實(shí)現(xiàn)復(fù)雜度,本篇介紹redis中實(shí)現(xiàn)消息隊(duì)列的幾種方案,并通過springboot實(shí)戰(zhàn)使其更易懂。

1. 基于List的 LPUSH+BRPOP 的實(shí)現(xiàn)

2. 基于Sorted-Set的實(shí)現(xiàn)

3. PUB/SUB,訂閱/發(fā)布模式

4. 基于Stream類型的實(shí)現(xiàn)

1. 基于List的 LPUSH+BRPOP 的實(shí)現(xiàn)

描述

使用rpush和lpush操作入隊(duì)列,lpop和rpop操作出隊(duì)列。

List支持多個(gè)生產(chǎn)者和消費(fèi)者并發(fā)進(jìn)出消息,每個(gè)消費(fèi)者拿到都是不同的列表元素。

優(yōu)點(diǎn)

一旦數(shù)據(jù)到來則立刻醒過來,消息延遲幾乎為零。

缺點(diǎn)

  • 不能重復(fù)消費(fèi),一旦消費(fèi)就會(huì)被刪除
  • 不能做廣播模式 , 不支持分組消費(fèi)
  • lpop和rpop會(huì)一直空輪訓(xùn),消耗資源 ,但可以 引入阻塞讀blpop和brpop 同時(shí)也有新的問題 如果線程一直阻塞在那里,Redis客戶端的連接就成了閑置連接,閑置過久,服務(wù)器一般會(huì)主動(dòng)斷開連接,減少閑置資源占用,這個(gè)時(shí)候blpop和brpop或拋出異常

實(shí)戰(zhàn)

代碼

@Slf4j
@Service
public class ListRedisQueue {

    //隊(duì)列名
    public static final String KEY = "listQueue";

    @Resource
    private RedisTemplate redisTemplate;

    public void produce(String message) {
        redisTemplate.opsForList().rightPush(KEY, message);
    }

    public void consume() {
        while (true) {
            String msg = (String) redisTemplate.opsForList().leftPop(KEY);
            log.info("瘋狂獲取消息:" + msg);
        }
    }

    public void blockingConsume() {
        while (true) {
            List<Object> obj = redisTemplate.executePipelined(new RedisCallback<Object>() {

                @Override
                public Object doInRedis(RedisConnection connection) throws DataAccessException {
                    //隊(duì)列沒有元素會(huì)阻塞操作,直到隊(duì)列獲取新的元素或超時(shí),5表示如果沒元素就每五秒去拿一次消息
                    return connection.bRPop(5, KEY.getBytes());
                }
            }, new StringRedisSerializer());
            for (Object str : obj) {
                log.info("blockingConsume獲取消息 : {}", str);
            }
        }
    }

}

測(cè)試

lPop/rPop消費(fèi)數(shù)據(jù)

    @Autowired
    private ListRedisQueue listRedisQueue;

    @Test
    public void produce() {
        for (int i = 0; i < 5; i++) {
            listRedisQueue.produce("第"+i + "個(gè)數(shù)據(jù)");
        }
    }

    @Test
    public void consume() {
        produce();
        logger.info("生產(chǎn)消息完畢");
        listRedisQueue.consume();
    }

輸出

blpop / brpop 消費(fèi)數(shù)據(jù)

    @Test
    public void blockingConsume() {
        produce();
        logger.info("生產(chǎn)消息完畢");
        listRedisQueue.blockingConsume();
    }

輸出

2. 基于Sorted-Set的實(shí)現(xiàn)延時(shí)隊(duì)列

描述

其實(shí)zset就是sorted set。為了避免sorted set簡(jiǎn)寫sset導(dǎo)致命令沖突,所以改為zset。同理例如class-->clazz

sorted set從字面意思上,很容易就可以理解,是個(gè)有序且不可重復(fù)的數(shù)據(jù)集合。類似set和hash的混合體,但是相比于set,zset內(nèi)部由score進(jìn)行排序.

優(yōu)點(diǎn)

可以自定義消息ID,在消息ID有意義時(shí),比較重要。

缺點(diǎn)

缺點(diǎn)也明顯,不允許重復(fù)消息(因?yàn)槭羌希?,同時(shí)消息ID確定有錯(cuò)誤會(huì)導(dǎo)致消息的順序出錯(cuò)。

實(shí)戰(zhàn)

代碼

@Slf4j
@Service
public class SortedSetRedisQueue {
    //隊(duì)列名
    public static final String KEY = "sortedSet_queue";
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    public void produce(String msg, Double score) {
        // 創(chuàng)建Sorted Set實(shí)例
        ZSetOperations zSetOperations = redisTemplate.opsForZSet();
        // 添加數(shù)據(jù)
        zSetOperations.add(KEY, msg, score);
    }

    public void consumer() throws InterruptedException {
        // 創(chuàng)建SortedSet實(shí)例
        ZSetOperations zSetOperations = redisTemplate.opsForZSet();
        while (true) {
            // 拿取數(shù)據(jù) (rangeByScore返回有序集合中指定分?jǐn)?shù)區(qū)間的成員列表。有序集成員按分?jǐn)?shù)值遞增(從小到大)次序排列)
            Set<String> order = zSetOperations.rangeByScore(KEY, 0, System.currentTimeMillis(), 0, 1);
            if (ObjectUtils.isEmpty(order)) {
                log.info("當(dāng)前沒有數(shù)據(jù) 當(dāng)前線程睡眠3秒");
                TimeUnit.SECONDS.sleep(3);
                // 跳過本次循環(huán) 重新循環(huán)拿取數(shù)據(jù)
                continue;
            }
            // 利用迭代器拿取Set中的數(shù)據(jù)
            String massage = order.iterator().next();
            // 過河拆遷,拿到就刪除消息
            if (zSetOperations.remove(KEY, massage) > 0) {
                //做些業(yè)務(wù)處理
                log.info("我拿到的消息:" + massage);
            }
        }

    }
}

測(cè)試

    @Autowired
    private SortedSetRedisQueue sortedSetRedisQueue;
    @Test
    public void sortedSetProduce() throws InterruptedException {
        for (int i = 0; i < 5; i++) {
            TimeUnit.SECONDS.sleep(1);
            // 生成分?jǐn)?shù)
            double score = System.currentTimeMillis();
            sortedSetRedisQueue.produce("第"+i + "個(gè)數(shù)據(jù)",score);
        }
    }
    @Test
    public void sortedSetConsumer() throws InterruptedException {
        sortedSetProduce();
        logger.info("生產(chǎn)消息完畢");
        sortedSetRedisQueue.consumer();
    }
}

輸出

3.PUB/SUB,訂閱/發(fā)布模式

描述

SUBSCRIBE,用于訂閱信道

PUBLISH,向信道發(fā)送消息

UNSUBSCRIBE,取消訂閱

此模式允許生產(chǎn)者只生產(chǎn)一次消息,由中間件負(fù)責(zé)將消息復(fù)制到多個(gè)消息隊(duì)列,每個(gè)消息隊(duì)列由對(duì)應(yīng)的消費(fèi)組消費(fèi)。

優(yōu)點(diǎn)

  • 一個(gè)消息可以發(fā)布到多個(gè)消費(fèi)者
  • 消費(fèi)者可以同時(shí)訂閱多個(gè)信道,因此可以接收多種消息(處理時(shí)先根據(jù)信道判斷)
  • 消息即時(shí)發(fā)送,消費(fèi)者會(huì)自動(dòng)接收到信道發(fā)布的消息

缺點(diǎn)

  • 消息發(fā)布時(shí),如果客戶端不在線,則消息丟失
  • 消費(fèi)者處理消息時(shí)出現(xiàn)了大量消息積壓,則可能會(huì)斷開通道,導(dǎo)致消息丟失
  • 消費(fèi)者接收消息的時(shí)間不一定是一致的,可能會(huì)有差異(業(yè)務(wù)處理需要判重)

實(shí)戰(zhàn)

監(jiān)聽器

@Slf4j
@Component
public class RedisMessageListenerListener implements MessageListener {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    /**
     * 消息處理
     *
     * @param message
     * @param pattern
     */
    @Override
    public void onMessage(Message message, byte[] pattern) {
        String channel = new String(pattern);
        log.info("onMessage --> 消息通道是:{}", channel);

        RedisSerializer<?> valueSerializer = redisTemplate.getValueSerializer();
        Object deserialize = valueSerializer.deserialize(message.getBody());
        log.info("反序列化的結(jié)果:{}", deserialize);
        if (deserialize == null) return;
        String md5DigestAsHex = DigestUtils.md5DigestAsHex(deserialize.toString().getBytes(StandardCharsets.UTF_8));
        log.info("計(jì)算得到的key: {}", md5DigestAsHex);
        Boolean result = redisTemplate.opsForValue().setIfAbsent(md5DigestAsHex, "1", 20, TimeUnit.SECONDS);
        if (Boolean.TRUE.equals(result)) {
            // redis消息進(jìn)行處理
            log.info("接收的結(jié)果:{}", deserialize.toString());
        } else {
            log.info("其他服務(wù)處理中");
        }
    }
}

實(shí)現(xiàn)MessageListener 接口,就可以通過onMessage()方法接收到消息了,該方法有兩個(gè)參數(shù):

  • 參數(shù) message 的 getBody() 方法以二進(jìn)制形式獲取消息體, getChannel() 以二進(jìn)制形式獲取消息通道
  • 參數(shù) pattern 二進(jìn)制形式的消息通道(實(shí)際和 message.getChannel() 返回值相同)

綁定監(jiān)聽器

@Configuration
public class RedisMessageListenerConfig {
    @Bean
    public RedisMessageListenerContainer getRedisMessageListenerContainer(RedisConnectionFactory redisConnectionFactory,
                                                                          RedisMessageListenerListener redisMessageListenerListener) {
        RedisMessageListenerContainer redisMessageListenerContainer = new RedisMessageListenerContainer();
        redisMessageListenerContainer.setConnectionFactory(redisConnectionFactory);
        redisMessageListenerContainer.addMessageListener(redisMessageListenerListener, new ChannelTopic(PubSubRedisQueue.KEY));
        return redisMessageListenerContainer;
    }
}

RedisMessageListenerContainer 是為Redis消息偵聽器 MessageListener 提供異步行為的容器。處理偵聽、轉(zhuǎn)換和消息分派的低級(jí)別詳細(xì)信息。

本文使用的是主題訂閱:ChannelTopic,你也可以使用模式匹配:PatternTopic,從而匹配多個(gè)信道。

生產(chǎn)者

@Service
public class PubSubRedisQueue {
    //隊(duì)列名
    public static final String KEY = "pub_sub_queue";

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    public void produce(String message) {
        redisTemplate.convertAndSend(KEY, message);
    }
}  

測(cè)試

@Slf4j
@RestController
@RequestMapping(value = "/queue")
public class RedisMQController {
    @Autowired
    private PubSubRedisQueue pubSubRedisQueue;

    @RequestMapping(value = "/pubsub/produce", method = RequestMethod.GET)
    public void pubsubProduce(@RequestParam(name = "msg") String msg) {
        pubSubRedisQueue.produce(msg);
    }

隨便找個(gè)瀏覽器請(qǐng)求生產(chǎn)者接口:

所以每插入一條消息,監(jiān)聽者則立即進(jìn)去消費(fèi)

4. 基于Stream類型的實(shí)現(xiàn)(Redis Version5.0)

描述

Stream為redis 5.0后新增的數(shù)據(jù)結(jié)構(gòu)。支持多播的可持久化消息隊(duì)列,實(shí)現(xiàn)借鑒了Kafka設(shè)計(jì)。

Redis Stream的結(jié)構(gòu)如上圖所示,它有一個(gè)消息鏈表,將所有加入的消息都串起來,每個(gè)消息都有一個(gè)唯一的ID和對(duì)應(yīng)的內(nèi)容。消息是持久化的,Redis重啟后,內(nèi)容還在。

每個(gè)Stream都有唯一的名稱,它就是Redis的key,在我們首次使用xadd指令追加消息時(shí)自動(dòng)創(chuàng)建。

每個(gè)Stream都可以掛多個(gè)消費(fèi)組,每個(gè)消費(fèi)組會(huì)有個(gè)游標(biāo)last_delivered_id在Stream數(shù)組之上往前移動(dòng),表示當(dāng)前消費(fèi)組已經(jīng)消費(fèi)到哪條消息了。每個(gè)消費(fèi)組都有一個(gè)Stream內(nèi)唯一的名稱,消費(fèi)組不會(huì)自動(dòng)創(chuàng)建,它需要單獨(dú)的指令xgroup create進(jìn)行創(chuàng)建,需要指定從Stream的某個(gè)消息ID開始消費(fèi),這個(gè)ID用來初始化last_delivered_id變量。

每個(gè)消費(fèi)組(Consumer Group)的狀態(tài)都是獨(dú)立的,相互不受影響。也就是說同一份Stream內(nèi)部的消息會(huì)被每個(gè)消費(fèi)組都消費(fèi)到。

同一個(gè)消費(fèi)組(Consumer Group)可以掛接多個(gè)消費(fèi)者(Consumer),這些消費(fèi)者之間是競(jìng)爭(zhēng)關(guān)系,任意一個(gè)消費(fèi)者讀取了消息都會(huì)使游標(biāo)last_delivered_id往前移動(dòng)。每個(gè)消費(fèi)者者有一個(gè)組內(nèi)唯一名稱。

消費(fèi)者(Consumer)內(nèi)部會(huì)有個(gè)狀態(tài)變量pending_ids,它記錄了當(dāng)前已經(jīng)被客戶端讀取的消息,但是還沒有ack。如果客戶端沒有ack,這個(gè)變量里面的消息ID會(huì)越來越多,一旦某個(gè)消息被ack,它就開始減少。這個(gè)pending_ids變量在Redis官方被稱之為PEL,也就是Pending Entries List,這是一個(gè)很核心的數(shù)據(jù)結(jié)構(gòu),它用來確保客戶端至少消費(fèi)了消息一次,而不會(huì)在網(wǎng)絡(luò)傳輸?shù)闹型緛G失了沒處理。

優(yōu)點(diǎn)

  • 高性能:可以在非常短的時(shí)間內(nèi)處理大量的消息。
  • 持久化:支持?jǐn)?shù)據(jù)持久化,即使Redis服務(wù)器宕機(jī),也可以恢復(fù)之前的消息。
  • 順序性:保證消息的順序性,即使是并發(fā)的消息也會(huì)按照發(fā)送順序排列。
  • 靈活性:可以方便地?cái)U(kuò)展和分布式部署,可以滿足不同場(chǎng)景下的需求。

缺點(diǎn)

  • 功能相對(duì)簡(jiǎn)單:Redis Stream相對(duì)于其他的消息隊(duì)列,功能相對(duì)簡(jiǎn)單,無法滿足一些復(fù)雜的需求。
  • 不支持消息回溯:即消費(fèi)者無法獲取之前已經(jīng)消費(fèi)過的消息。
  • 不支持多消費(fèi)者分組:無法實(shí)現(xiàn)多個(gè)消費(fèi)者并發(fā)消費(fèi)消息的功能。

實(shí)戰(zhàn)

自動(dòng)ack消費(fèi)者

@Slf4j
@Component
public class AutoAckStreamConsumeListener implements StreamListener<String, MapRecord<String, String, String>> {
    //分組名
    public static final String GROUP = "autoack_stream";
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    @Override
    public void onMessage(MapRecord<String, String, String> message) {
        String stream = message.getStream();
        RecordId id = message.getId();
        Map<String, String> map = message.getValue();
        log.info("[自動(dòng)ACK]接收到一個(gè)消息 stream:[{}],id:[{}],value:[{}]", stream, id, map);
        redisTemplate.opsForStream().delete(GROUP, id.getValue());
    }
}

手動(dòng)ack消費(fèi)者

@Slf4j
@Component
public class BasicAckStreamConsumeListener implements StreamListener<String, MapRecord<String, String, String>> {
    //分組名
    public static final String GROUP = "basicack_stream";
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    @Override
    public void onMessage(MapRecord<String, String, String> message) {
        String stream = message.getStream();
        RecordId id = message.getId();
        Map<String, String> map = message.getValue();
        log.info("[手動(dòng)ACK]接收到一個(gè)消息 stream:[{}],id:[{}],value:[{}]", stream, id, map);
        redisTemplate.opsForStream().acknowledge(stream, GROUP, id.getValue());
        //消費(fèi)完畢刪除該條消息
        redisTemplate.opsForStream().delete(GROUP, id.getValue());
    }
}

綁定關(guān)系

@Slf4j
@Configuration
public class RedisStreamConfiguration {
    @Autowired
    private RedisConnectionFactory redisConnectionFactory;
    @Autowired
    private AutoAckStreamConsumeListener autoAckStreamConsumeListener;
    @Autowired
    private BasicAckStreamConsumeListener basicAckStreamConsumeListener;
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Bean(initMethod = "start", destroyMethod = "stop")
    public StreamMessageListenerContainer<String, MapRecord<String, String, String>> streamMessageListenerContainer() {
        AtomicInteger index = new AtomicInteger(1);
        int processors = Runtime.getRuntime().availableProcessors();
        ThreadPoolExecutor executor = new ThreadPoolExecutor(processors, processors, 0, TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(), r -> {
            Thread thread = new Thread(r);
            thread.setName("async-stream-consumer-" + index.getAndIncrement());
            thread.setDaemon(true);
            return thread;
        });
        StreamMessageListenerContainer.StreamMessageListenerContainerOptions<String, MapRecord<String, String, String>> options =
                StreamMessageListenerContainer.StreamMessageListenerContainerOptions
                        .builder()
                        // 一次最多獲取多少條消息
                        .batchSize(3)
                        // 運(yùn)行 Stream 的 poll task
                        .executor(executor)
                        // Stream 中沒有消息時(shí),阻塞多長(zhǎng)時(shí)間,需要比 `spring.redis.timeout` 的時(shí)間小
                        .pollTimeout(Duration.ofSeconds(3))
                        // 獲取消息的過程或獲取到消息給具體的消息者處理的過程中,發(fā)生了異常的處理
                        .errorHandler(new ErrorHandler() {
                            @Override
                            public void handleError(Throwable t) {
                                log.info("出現(xiàn)異常就來這里了" + t);
                            }
                        })
                        .build();

        StreamMessageListenerContainer<String, MapRecord<String, String, String>> streamMessageListenerContainer =
                StreamMessageListenerContainer.create(redisConnectionFactory, options);
        // 獨(dú)立消費(fèi)

        // 消費(fèi)組A,自動(dòng)ack
        // 從消費(fèi)組中沒有分配給消費(fèi)者的消息開始消費(fèi)
        if (!isStreamGroupExists(StreamRedisQueue.KEY,AutoAckStreamConsumeListener.GROUP)){
            redisTemplate.opsForStream().createGroup(StreamRedisQueue.KEY,AutoAckStreamConsumeListener.GROUP);
        }
        streamMessageListenerContainer.receiveAutoAck(Consumer.from(AutoAckStreamConsumeListener.GROUP, "AutoAckConsumer"),
                StreamOffset.create(StreamRedisQueue.KEY, ReadOffset.lastConsumed()), autoAckStreamConsumeListener);

        // 消費(fèi)組B,不自動(dòng)ack
        if (!isStreamGroupExists(StreamRedisQueue.KEY,BasicAckStreamConsumeListener.GROUP)){
            redisTemplate.opsForStream().createGroup(StreamRedisQueue.KEY,BasicAckStreamConsumeListener.GROUP);
        }
        streamMessageListenerContainer.receive(Consumer.from(BasicAckStreamConsumeListener.GROUP, "BasicAckConsumer"),
                StreamOffset.create(StreamRedisQueue.KEY, ReadOffset.lastConsumed()), basicAckStreamConsumeListener);

        return streamMessageListenerContainer;

    }

    /**
     * 判斷該消費(fèi)組是否存在
     * @param streamKey
     * @param groupName
     * @return
     */
    public boolean isStreamGroupExists(String streamKey, String groupName) {
        RedisStreamCommands commands = redisConnectionFactory.getConnection().streamCommands();
        //首先檢查Stream Key是否存在,否則下面代碼可能會(huì)因?yàn)閲L試檢查不存在的Stream Key而導(dǎo)致異常
        if (!redisTemplate.hasKey(streamKey)){
            return false;
        }
        //獲取streamKey下的所有g(shù)roups
        StreamInfo.XInfoGroups xInfoGroups = commands.xInfoGroups(streamKey.getBytes());
        AtomicBoolean exists= new AtomicBoolean(false);
        xInfoGroups.forEach(xInfoGroup -> {
            if (xInfoGroup.groupName().equals(groupName)){
                exists.set(true);
            }
        });

        return exists.get();
    }
}

生產(chǎn)工具

@Slf4j
@Service
public class StreamRedisQueue {
    //隊(duì)列名
    public static final String KEY = "stream_queue";
    @Autowired
    private RedisTemplate&lt;String, Object&gt; redisTemplate;

    public String  produce(Map&lt;String, String&gt; value) {
        return redisTemplate.opsForStream().add(KEY, value).getValue();
    }

    public void createGroup(String key, String group){
        redisTemplate.opsForStream().createGroup(key, group);
    }

}

測(cè)試

生產(chǎn)消息

@Slf4j
@RestController
@RequestMapping(value = "/queue")
public class RedisMQController {

    @Autowired
    private StreamRedisQueue streamRedisQueue;
    @RequestMapping(value = "/stream/produce", method = RequestMethod.GET)
    public void streamProduce() {
        Map<String, String> map = new HashMap<>();
        map.put("劉德華", "大家好我是劉德華");
        map.put("周杰倫", "周杰倫");
        map.put("time", DateUtil.now());
        String result = streamRedisQueue.produce(map);
        log.info("返回結(jié)果:{}", result);
    }
}

只要有消息,消費(fèi)者就會(huì)消費(fèi)

到此這篇關(guān)于springboot使用Redis隊(duì)列實(shí)戰(zhàn)的文章就介紹到這了,更多相關(guān)springboot Redis隊(duì)列實(shí)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 淺談Java中的interface應(yīng)用與面向接口編程

    淺談Java中的interface應(yīng)用與面向接口編程

    這篇文章主要介紹了淺談Java中的interface應(yīng)用與面向接口編程,Java的關(guān)鍵字interface應(yīng)用,一個(gè)接口,多個(gè)實(shí)現(xiàn)類,面向接口編程,把業(yè)務(wù)邏輯線提取出來作為接口,具體的業(yè)務(wù)實(shí)現(xiàn)通過該接口的實(shí)現(xiàn)類來完成,需要的朋友可以參考下
    2023-10-10
  • 淺談Java多進(jìn)程程序的運(yùn)行模式

    淺談Java多進(jìn)程程序的運(yùn)行模式

    這篇文章主要介紹了淺談Java多進(jìn)程程序的運(yùn)行模式,包括對(duì)進(jìn)程阻塞問題的討論等,需要的朋友可以參考下
    2015-11-11
  • Spring中的@RefreshScope注解作用

    Spring中的@RefreshScope注解作用

    這篇文章主要介紹了Spring中的@RefreshScope注解作用詳解,@RefreshScope注解是Spring Cloud中的一個(gè)重要注解,用于實(shí)現(xiàn)動(dòng)態(tài)刷新配置的功能,當(dāng)我們?cè)趹?yīng)用程序中使用@Value注解獲取配置屬性時(shí),如果配置發(fā)生變化,需要重啟應(yīng)用程序才能生效,需要的朋友可以參考下
    2023-10-10
  • SpringBoot Admin2.0 集成Arthas的實(shí)現(xiàn)步驟

    SpringBoot Admin2.0 集成Arthas的實(shí)現(xiàn)步驟

    這篇文章主要介紹了SpringBoot Admin2.0 集成Arthas的實(shí)現(xiàn)步驟,幫助大家更好的理解和學(xué)習(xí)使用SpringBoot框架,感興趣的朋友可以了解下
    2021-04-04
  • java文件上傳下載代碼實(shí)例

    java文件上傳下載代碼實(shí)例

    這篇文章主要介紹了java文件上傳下載,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03
  • 使用Java+FFmpeg實(shí)現(xiàn)視頻分片合并(生成list.txt自動(dòng)合并)

    使用Java+FFmpeg實(shí)現(xiàn)視頻分片合并(生成list.txt自動(dòng)合并)

    在很多視頻處理場(chǎng)景中,我們會(huì)將一個(gè)錄制任務(wù)拆成多個(gè)小片段,多個(gè)分片合并成一個(gè)完整視頻是一個(gè)常見需求,所以本文給大家介紹了Java 調(diào)用 FFmpeg 生成用于合并的視頻列表文件 list.txt,并執(zhí)行合并命令生成最終視頻,需要的朋友可以參考下
    2026-02-02
  • spring boot 添加admin監(jiān)控的方法

    spring boot 添加admin監(jiān)控的方法

    這篇文章主要介紹了spring boot 添加admin監(jiān)控的相關(guān)知識(shí),非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下
    2018-02-02
  • mybatis 獲取無數(shù)據(jù)的字段不顯示的問題

    mybatis 獲取無數(shù)據(jù)的字段不顯示的問題

    這篇文章主要介紹了mybatis 獲取無數(shù)據(jù)的字段不顯示的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • java設(shè)計(jì)模式學(xué)習(xí)之裝飾模式

    java設(shè)計(jì)模式學(xué)習(xí)之裝飾模式

    這篇文章主要為大家詳細(xì)介紹了java設(shè)計(jì)模式學(xué)習(xí)之裝飾模式的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-10-10
  • MyBatis-Plus @TableId注解之主鍵配置與常見問題分析

    MyBatis-Plus @TableId注解之主鍵配置與常見問題分析

    MyBatis-Plus的@TableId注解用于標(biāo)識(shí)實(shí)體類的主鍵字段,可配置主鍵列名、生成策略、是否保持全局格式等屬性
    2026-03-03

最新評(píng)論

富锦市| 搜索| 清丰县| 乐安县| 枞阳县| 全州县| 石河子市| 呼伦贝尔市| 聊城市| 鲁山县| 满洲里市| 彭州市| 固阳县| 扬州市| 安阳市| 集贤县| 宁化县| 政和县| 东丽区| 孟连| 灌南县| 罗源县| 磴口县| 米林县| 金昌市| 乐山市| 巩义市| 文成县| 新巴尔虎右旗| 新泰市| 宜城市| 海丰县| 东明县| 中山市| 西乡县| 江西省| 西昌市| 余姚市| 确山县| 呈贡县| 珲春市|