[JAVA] Je souhaite effectuer un traitement d'agrégation avec spring-batch

Contexte

Dans une application WEB réalisée avec Spring-Boot Je souhaite implémenter un tel processus par lots qui agrège le contenu du fichier journal d'exécution de l'API préparé et stocke le résultat dans la base de données. Cependant, aucun site ne pourrait être utilisé comme référence pour la mise en œuvre de cette exigence, et il était ridiculement bouché, je vais donc le laisser comme un mémorandum. (Je ne sais pas si c'est le meilleur)

Principales exigences

Données prérequises (exemple)

Avant: format de journal à agréger

ʻURL / méthode HTTP / code d'état HTTP / heure d'exécution / date et heure d'exécutionau moment de l'accès sont répertoriés au format TSV Exemple: / api / ・ ・ ・ GET 200 48 14/08/2020 11:05:42 701 / api / ・ ・ ・ GET 200 27 2020/08/14 11:05:43 352 / api / ・ ・ ・ / 41 SUPPRIMER 401 10 2020/08/14 11:05:46 780 / api / ・ ・ ・ / 42 PUT 200108 2020/08/14 11:06:16 824 / api / ・ ・ ・ POST 500806 2020/08/14 11:06:30 252 ・ ・ ・ `

Après: Format lors du stockage de DB

la mise en oeuvre

politique

point

code

Classe Dto

@Data
public class LogCollectedDto {
  //Nom de l'API
  private String apiName;
  //Méthode HTTP
  private String httpMethod;
  //Code d'état HTTP
  private String httpCode;
  //Temps d'exécution(ms)
  private String executionTime;
  //Date et heure d'agrégation
  private String collectedDate;
}

Reader

Défini dans Bean

  @Bean
  public FlatFileItemReader<LogCollectedDto> reader() {

    final String READ_FILE_PATH = <Nom du fichier journal à lire>;

    FlatFileItemReader<LogCollectedDto> reader = new FlatFileItemReader<>();
    reader.setResource(new FileSystemResource(READ_FILE_PATH));
    reader.setEncoding(StandardCharsets.UTF_8.name());
    reader.setLinesToSkip(0);
    reader.setLineMapper(
        new DefaultLineMapper() {
          {
            setLineTokenizer(
                new DelimitedLineTokenizer(DelimitedLineTokenizer.DELIMITER_TAB) {
                  {
                    setNames(
                        new String[] {
                          "apiUrl", "httpMethod", "httpCode", "executionTime", "collectedDate"
                        });
                  }
                });
            setFieldSetMapper(
                new BeanWrapperFieldSetMapper<LogCollectedDto>() {
                  {
                    setTargetType(LogCollectedDto.class);
                  }
                });
          }
        });
    return reader;
  }

Processor

Couper à une autre classe

public class CustomItemProcessor implements ItemProcessor<LogCollectedDto, LogCollectedDto> {

  @Override
  public LogCollectedDto process(LogCollectedDto item) throws Exception {

    //Validation de l'élément récupéré, ignorez cette ligne si false
    if (!validateItem(item)) {
      return null;
    }

    //Conserver l'élément acquis une fois dans une autre variable pour un traitement ultérieur
    //(Si vous modifiez directement l'argument, l'élément acquis lors de l'interruption et du redémarrage peut devenir les données traitées)
    LogCollectedDto afterItem = item;
    //Traitement du contenu des données (méthode distincte omise)
    afterItem.setApiName(getApiName(・ ・ ・));

    return afterItem;
  }

//(Omis)

}

Writer

Couper à une autre classe

@RequiredArgsConstructor
public class CustomItemWriter extends JpaItemWriter<LogCollectedDto> {

  private final JpaItemWriter<Log> jpaItemWriter;

  @Override
  public void write(List<? extends LogCollectedDto> items) {

   //Regroupez les éléments reçus du processeur et transmettez-les à une autre instance de Writer
    Map<String, List<LogCollectedDto>> groupingMap = groupingItems(items);
    jpaItemWriter.write(collectItems(groupingMap));
  }

  /**
   *Regroupement des éléments reçus du processeur
   *Nom de l'API et statut HTTP en tant que clé composite
   *
   * @param list
   * @return
   */
  private Map<String, List<LogCollectedDto>> groupingItems(List<? extends LogCollectedDto> list) {
    //Créer une clé composite
    Function<LogCollectedDto, String> compositeKey =
        logDto -> {
          StringBuffer sb = new StringBuffer();
          sb.append(logDto.getApiName()).append("-").append(logDto.getHttpMethod());
          return sb.toString();
        };

    Map<String, List<LogCollectedDto>> grpByComplexKeys =
        list.stream().collect(Collectors.groupingBy(compositeKey));

    return grpByComplexKeys;
  }

  /**
   *Générer une liste d'entités en agrégeant des éléments groupés
   *
   * @param groupingMap
   * @return
   */
  private List<Log> collectItems(Map<String, List<LogCollectedDto>> groupingMap) {

    List<Log> recordList = new ArrayList<>();

    for (List<LogCollectedDto> dtoList : groupingMap.values()) {
      //Instanciation de classe d'entité
      Log record = new Log();
      //Traitement d'agrégation
      record.setApiName(dtoList.stream().findFirst().get().getApiName());
      record.setHttpCode(dtoList.stream().findFirst().get().getHttpCode());
      record.setHttpMethod(dtoList.stream().findFirst().get().getHttpMethod());
      record.setAccesses(dtoList.size());
      record.setAverageTime(
          dtoList.stream()
              .collect(
                  Collectors.averagingDouble(dto -> Double.parseDouble(dto.getExecutionTime()))));
      record.setCollectedTime(LocalDateTime.now());
      recordList.add(record);
    }

    return recordList;
  }
}

Préoccupation

Résumé

Il y a des inquiétudes, mais je pense personnellement que c'est la façon la plus propre d'écrire. Nous recherchons toujours des opinions. Conclusion: l'API Stream est la plus puissante! !! !!

Recommended Posts

Je souhaite effectuer un traitement d'agrégation avec spring-batch
Je souhaite effectuer un traitement asynchrone et une exécution périodique avec Rail !!!
Je souhaite effectuer un traitement Group By avec Stream (group-by-count, group-by-sum, group-by-max)
[Java] Je veux effectuer distinctement avec la clé dans l'objet
Je veux utiliser java8 forEach avec index
Je veux jouer avec Firestore de Rails
[Rails] Je veux charger du CSS avec webpacker
Je souhaite utiliser le mode sombre avec l'application SWT
Je souhaite surveiller un fichier spécifique avec WatchService
Je souhaite authentifier les utilisateurs auprès de Rails avec Devise + OmniAuth
Je veux faire des transitions d'écran avec kotlin et java!
Je veux convertir des caractères ...
Basculer dynamiquement la base de données à laquelle se connecter
Remplacer par une valeur selon la correspondance avec une expression régulière Java
Je souhaite effectuer un traitement d'agrégation avec spring-batch
J'ai essayé d'augmenter la vitesse de traitement avec l'ingénierie spirituelle
Je veux pousser une application créée avec Rails 6 vers GitHub
Je souhaite effectuer une factorisation prime rapide avec Ruby (ABC177E)
Je veux faire une liste avec kotlin et java!
Je veux créer une fonction avec kotlin et java!
Même en Java, je veux afficher true avec un == 1 && a == 2 && a == 3
Je souhaite envoyer manuellement un e-mail d'autorisation avec Devise
Je veux implémenter diverses fonctions avec kotlin et java!
Je veux passer la commande de démarrage à postgres avec docker-compose.
[Java] Je souhaite tester l'entrée standard et la sortie standard avec JUnit
J'ai essayé d'interagir avec Java
Effectuer manuellement un traitement équivalent à @ConfigurationProperties
Je veux créer un bouton avec un saut de ligne avec link_to [Note]
Je veux connecter un casque SONY WH-1000XM4 avec LDAC avec ubuntu 20.04! !!
Je souhaite ajouter une fonction de navigation avec ruby on rails
Je veux comprendre le flux des paramètres de demande de traitement Spring
Je veux revenir à l'écran précédent avec kotlin et java!
Je veux INSÉRER l'heure locale du printemps avec l'heure MySQL (également en millisecondes)
Je veux éviter OutOfMemory lors de la sortie de gros fichiers avec POI
[Rails] Je souhaite ajouter des données aux paramètres lors de la transition avec link_to
Je veux extraire entre des chaînes de caractères avec une expression régulière
Je souhaite développer une application web!
Je veux écrire un joli build.gradle
Je souhaite éliminer les messages d'erreur en double
J'ai essayé de migrer le traitement vers VS Code
Je veux créer une application ios.android
J'ai essayé de démarrer avec Web Assembly
Je souhaite sélectionner plusieurs éléments avec une disposition personnalisée dans la boîte de dialogue
Même en Java, je veux afficher true avec un == 1 && a == 2 && a == 3 (édition PowerMockito)
[iOS] J'ai essayé de créer une application de traitement de type insta avec Swift
Je veux écrire un test unitaire!
Je souhaite installer PHP 7.2 sur Ubuntu 20.04.
[Note] Je veux obtenir dans l'ordre inverse en utilisant afterLast avec JdbcTemplate
Je souhaite créer un SNS Web sombre avec Jakarta EE 8 avec Java 11
Je veux jouer avec l'autorisation du répertoire Windows de WSL (ubuntu)
Je souhaite afficher un PDF chinois (coréen) avec des rapports fins
J'étais accro au test Spring-Batch
Je veux arrêter complètement les mises à jour Java