Spring boot redis->スレッドプール->メッセージキュー->スレッドプール


Spring boot redis->スレッドプール->メッセージキュー->スレッドプール
久しぶりにブログを書くと、この習慣はなかなか身につけられないようですね.
私がネット上で探しているフロントエンドプロジェクトのデータフォーマットに合うように、私は元のmodel類Postの属性String imgUrlをList imgUrlsに変更しました.通常の考え方では、imgUrldsを格納するためにテーブルを新規作成したに違いありませんが、これは優雅ではありませんか.私が当初どうしてもっと強いxmlを捨てて使わないのかと思って、注釈でデータをselectするのは、注釈がもっと優雅だからではないでしょうか.しかし、私がこれをやったのは、かえって優雅ではない.
だからどうするの?redisを思い浮かべました.redisはKeyValue形式のnosqlデータベースであることはよく知られていますが、redisはValueに複数のデータ型を提供しています.そのうちの1つが配列なので、私はなぜredisを使わずにこのimgUrlsを保存しますか?
redisメモリ読み込みimgUrls list
やると言ったらすぐやる
まず通常の準備作業
依存の追加
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-data-redisartifactId>
        dependency>

yml構成
spring:
  redis:
    database: 0
    host: 127.0.0.1
    port: 6379
    timeout: 3000
    lettuce:
      pool:
        max-active: 8
        max-wait: -1
        max-idle: 8
        min-idle: 0

そしてredisTemplate push,selectができて、すべて順調です
    @Test
    public void testRedisIns(){
     
        List<String> postIds= Arrays.asList("235", "456", "908","123");
//        List postIds= Arrays.asList("123");
        List<String> imgUrls=Arrays.asList("https://images.pexels.com/photos/2584055/pexels-photo-2584055.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500",
                "https://images.pexels.com/photos/998904/pexels-photo-998904.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500",
                "https://images.pexels.com/photos/2793453/pexels-photo-2793453.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500",
                "https://images.pexels.com/photos/4099385/pexels-photo-4099385.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500",
                "https://images.pexels.com/photos/3755553/pexels-photo-3755553.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500");
        for(String postId:
                postIds){
     
            Integer randomCount=(int)(1+Math.random()*3);
            log.info("random count ");
            log.info(String.valueOf(randomCount));
            for (int i = 0; i < randomCount; i++) {
     
            //   List         
                redisTemplate.opsForList().rightPush(postId,imgUrls.get((int)(Math.random()*imgUrls.size())));
            }


        }

    }

List<String> findImgUrlsByItemId(Long itemId){
     
    //   range list   
        List<String> urls=redisTemplate.opsForList().range(String.valueOf(itemId),0,-1);
        return urls;
    }

しかし、まだ終わっていないので、私の頭の瓜子はまた、MySQLとredisからデータを抽出するのはお互いに依存していない以上、どうして私はマルチスレッドを作って少しスピードアップしないのですか?
スレッドプールの速度を上げる
やると言ったらやる、まずはスレッドプール構成から始めましょう!
import com.example.business_server.utils.Constant;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;

@Configuration
@EnableAsync
//       
public class AsyncConfig {
     
    @Bean("dbExcutor")
    public Executor dbExcutor(){
     
        ThreadPoolTaskExecutor executor=new ThreadPoolTaskExecutor();

        //      :              
        executor.setCorePoolSize(Constant.INIT_THREAD_NUM);
        //      :         ,                         
        executor.setMaxPoolSize(Constant.MAX_THREAD_NUM);
        //     :           
        executor.setQueueCapacity(Constant.QUEUE_CAPACITY);
       //          60 :                           
      executor.setKeepAliveSeconds(Constant.ALIVED_SECONDS);
       //        :                         
   executor.setThreadNamePrefix (Constant.DB_EXCUTOR_PREFIX);
        //              :       (      )
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());
        executor.initialize();

        return executor;
    }
}


非同期をオンにする方法も簡単で、対応する方法に@Asyncを追加すればいいです.
    @Async
    List<String> findImgUrlsByItemId(Long itemId){
     
        List<String> urls=redisTemplate.opsForList().range(String.valueOf(itemId),0,-1);
        return urls;
    }

しかし、長い間、私は突然、非同期メソッドの戻り値を取得するには、戻りたいデータ型にCompletableFutureをセットしなければならないことに気づいた.
    @Async
    CompletableFuture<List<String>> findImgUrlsByItemId(Long itemId){
     
        List<String> urls=redisTemplate.opsForList().range(String.valueOf(itemId),0,-1);
        return CompletableFuture.completedFuture(urls);
    }

しかしredis側はまだ言いやすいですが、mysql側はmybatisを使って直接抽象的な方法に注釈をつけています.私はどうして戻りタイプを変えることができますか.しかし、私はどうしてこんなにあきらめやすい人なのか、私が前に偶然聞いたニュースキューにも似たような機能があるような気がします.
メッセージキューの試行
やると言ったらやる!
まずerlang環境を構成し、rabbitMQをダウンロードします.
そして依存を加えてymlを配合します
        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-amqpartifactId>
        dependency>
spring:
  rabbitmq:
    host: 127.0.0.1
    port: 5672
    #           
    username: guest
    password: guest

次にコンフィギュレーションクラスも書きますが、どうせ今はこの場所でしか使われていないので、fanout、topicは必要ありません.直接最も普通のqueueに行きます.
@Configuration
public class RabbitConfig {
     
    @Bean
    public Queue dbQueue(){
     
        return new Queue("dbQueue");
    }
}

上の配置クラスはqueueですが、このqueueも生産者が必要です.
package com.example.business_server.producer;

import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class RabbitProducer {
     
    private final AmqpTemplate template;

    public RabbitProducer(AmqpTemplate template) {
     
        this.template = template;
    }

    public void sendFetchDataQueue(Long itemId){
     
        // 
        this.template.convertAndSend("dbQueue",itemId);
    }
}


そして私の親愛なる消費者です
package com.example.business_server.service.impl;

import com.example.business_server.dao.PostMapper;
import com.example.business_server.model.domain.Post;
import com.example.business_server.model.recom.Recommendation;
import com.example.business_server.producer.RabbitProducer;
import com.example.business_server.service.PostService;
import lombok.SneakyThrows;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;


@Service
@RabbitListener(queues = "dbQueue")
public class PostServiceImpl implements PostService {
     
    @Autowired
    private RabbitProducer rabbitProducer;
    private final RedisTemplate<String,String> redisTemplate;
    private final PostMapper postMapper;

    public PostServiceImpl(PostMapper postMapper, RedisTemplate<String,String> redisTemplate) {
     
        this.postMapper = postMapper;
        this.redisTemplate = redisTemplate;
    }

    

    @RabbitHandler
    List<String> findImgUrlsByItemId(Long itemId){
     
        List<String> urls=redisTemplate.opsForList().range(String.valueOf(itemId),0,-1);
        return urls;
    }


}


最後にPostServiceImplで生産者を呼び出せばいいのですが、
    @Override
    public List<Post> findPostsByRecommendations(List<Recommendation> recommendations) {
     
        List<Post> posts=new ArrayList<>();

        for (Recommendation recommendation :
                recommendations) {
     
            // 
			Post post
        }

        return posts;
    }

しかし、私がPost postを打ったとき、私は突然この生産者が全然値を返していないことに気づいた(ああああああああああああ!!!!!!!!!)
仕方なく急いでもう一度メッセージキューを検索したら、気づいた...(後で)
最後に私はやはりマルチスレッドを使う方法に戻って、実はそのdao層がタイプを変えることができない問題を解決して、実はとても簡単で、
    @Async
	//  PostServiceImpl         dao ,        
    CompletableFuture<Post> findPostById(Long itemId){
     
        Post post=postMapper.findPostById(itemId);
        return CompletableFuture.completedFuture(post);
    }

Ok、すべてが終わったから、もう一度やり直しましょう.
ふくばん
まずこの新しいimgUrlsのために、それからredisを作りますか.
私の観点はこれが価値があるということです.
これは確かに複雑さを増していますが、これはもともと小さな変更であり、redisも相対的に簡単なので、mybatisで直接注釈を変更するよりも、mysqlデータベースで元のテーブルを修正したり、新しいテーブルを追加したりするよりも簡単ではありません.
一方redisのアクセスも速く、imgUrls valueでitemId keyを探す必要もありません.
だから総合的に以上のredisの速度はもっと速くて、需要をよく満たして、上手になるのももっと簡単で、それは明らかにこれは良い選択です.
次に、メッセージキューの使用シーンの問題です.いつ使用しますか.
いつ使うか聞く前に、まずいつ使えないか聞いてみましょう.
  • 私のように明らかに消費者の戻り値を獲得しなければならないのは、きっと使えないに違いない.
  • 私のような小型ではありませんが、学習目的を主とするプロジェクトでも使わないでください.これは明らかに複雑さを大きく増加させるからです(私は学習目的を主とするプロジェクトです.もちろん、より多くの技術を応用しようとしています)
  • では、いつ使いますか.
  • 非同期処理処理の場合に使用できます.例えば、あなたのアプリが登録に成功したときにメールとメールを送るなら、どちらも送信が完了してから登録に成功する必要はありません.直接登録に成功してから、メッセージキューを通じてメール、メール
  • を通知することができます.
  • アプリケーションデカップリング:メッセージキューではなくスレッドプールを最後に選択した最も重要な原因は、メッセージキューが結果を返さないことですが、これはメッセージキューの2番目のアプリケーションシーンの原因です.メッセージキューは戻り値を得ることができず、最後に成功したかどうかにも関心を持つ必要はなく、その成功の結果が何なのかにも関心を持つ必要はありません.彼はただ知らせただけで、それからすべてが終わったと思っています.私のことはありません.これはよくデカップリングの目的を達成しました.(もちろんこれもメッセージキューの欠陥です)
    名詞の解釈——結合とは何か
    私がここに来たとき、突然結合の概念がまだはっきりしていないことに気づいた.だから再びネットで検索して、私自身の理解を得ました.
    低結合とは?良い例は高校です.高校では、大家さんや奥さん、どんな物業と付き合う必要はありません.学校は私たちのために宿泊を手配してくれました.一日中複雑な外食システム、階下のホテル、自分で作る間に難しい選択をする必要はありません.学校は私たちのために食事を手配してくれました.さらに、嫌な同僚や覇道的な指導者、憎らしい義母に対処する必要はなく、学校には「人情世故」はない.学校は私たちのためにすべてを手配して、私たちはお金を払うだけで、それから3年は1つのことしかしません--勉強に専念します.
  • トラフィック削減ピーク:つまり、あなたのアプリケーションが短時間で大量のユーザーが同時にアクセス(例えば秒殺)すれば、メッセージキューはトラフィックを一定の長さに制御し、長さを超える要求を直接捨てるのに役立ちます
  • ユーザログのメッセージキューおよびメッセージ通信を受信するために使用されてもよい.