[JAVA] Ich möchte die Aggregationsverarbeitung mit Spring-Batch durchführen

Hintergrund

In einer mit Spring-Boot erstellten WEB-Anwendung Ich möchte einen solchen Stapelprozess implementieren, der den Inhalt der vorbereiteten API-Ausführungsprotokolldatei aggregiert und das Ergebnis in der Datenbank speichert. Es gibt jedoch keine Website, die als Referenz für die Umsetzung dieser Anforderung verwendet werden könnte, und sie war lächerlich verstopft, sodass ich sie als Memorandum belassen werde. (Ich weiß nicht, ob das das Beste ist)

Hauptanforderungen

Vorausgesetzte Daten (Beispiel)

Vorher: Protokollformat, das aggregiert werden soll

Die URL / HTTP-Methode / HTTP-Statuscode / Ausführungszeit / Ausführungsdatum und -zeit zum Zeitpunkt des Zugriffs ist im TSV-Format aufgeführt. Beispiel: / api / ・ ・ ・ GET 200 48 2020/08/14 11:05:42 701 / api / ET ・ ・ GET 200 27 2020/08/14 11:05:43 352 / api / ・ ・ ・ / 41 DELETE 401 10 2020/08/14 11:05:46 780 / api / ・ ・ ・ / 42 PUT 200 108 2020/08/14 11:06:16 824 / api / ST ・ ・ POST 500 806 2020/08/14 11:06:30 252 ・ ・ ・

Nachher: Format beim Speichern der Datenbank

Implementierung

Politik

Punkt

Code

Dto Klasse

@Data
public class LogCollectedDto {
  //API-Name
  private String apiName;
  //HTTP-Methode
  private String httpMethod;
  //HTTP-Statuscode
  private String httpCode;
  //Ausführungszeit(ms)
  private String executionTime;
  //Datum und Uhrzeit der Aggregation
  private String collectedDate;
}

Reader

In Bean definiert

  @Bean
  public FlatFileItemReader<LogCollectedDto> reader() {

    final String READ_FILE_PATH = <Protokolldateiname zum Lesen>;

    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

In eine andere Klasse ausschneiden

public class CustomItemProcessor implements ItemProcessor<LogCollectedDto, LogCollectedDto> {

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

    //Validierung des abgerufenen Elements, überspringen Sie diese Zeile, wenn sie falsch ist
    if (!validateItem(item)) {
      return null;
    }

    //Halten Sie den erworbenen Gegenstand einmal in einer anderen Variablen, um ihn später zu verarbeiten
    //(Wenn Sie das Argument direkt ändern, wird das beim Unterbrechen und Neustarten erfasste Element möglicherweise zu den verarbeiteten Daten.)
    LogCollectedDto afterItem = item;
    //Dateninhaltsverarbeitung (separate Methode weggelassen)
    afterItem.setApiName(getApiName(・ ・ ・));

    return afterItem;
  }

//(Weggelassen)

}

Writer

In eine andere Klasse ausschneiden

@RequiredArgsConstructor
public class CustomItemWriter extends JpaItemWriter<LogCollectedDto> {

  private final JpaItemWriter<Log> jpaItemWriter;

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

   //Aggregieren Sie die vom Prozessor empfangenen Elemente und übergeben Sie sie an eine andere Writer-Instanz
    Map<String, List<LogCollectedDto>> groupingMap = groupingItems(items);
    jpaItemWriter.write(collectItems(groupingMap));
  }

  /**
   *Gruppieren von vom Prozessor empfangenen Elementen
   *API-Name und HTTP-Status als zusammengesetzter Schlüssel
   *
   * @param list
   * @return
   */
  private Map<String, List<LogCollectedDto>> groupingItems(List<? extends LogCollectedDto> list) {
    //Erstellen Sie einen zusammengesetzten Schlüssel
    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;
  }

  /**
   *Generieren Sie eine Liste von Entitäten, indem Sie gruppierte Elemente zusammenfassen
   *
   * @param groupingMap
   * @return
   */
  private List<Log> collectItems(Map<String, List<LogCollectedDto>> groupingMap) {

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

    for (List<LogCollectedDto> dtoList : groupingMap.values()) {
      //Instanziierung von Entitätsklassen
      Log record = new Log();
      //Aggregationsverarbeitung
      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;
  }
}

Besorgnis, Sorge

Zusammenfassung

Es gibt Bedenken, aber ich persönlich denke, es ist die sauberste Art zu schreiben. Wir sind immer auf der Suche nach Meinungen. Fazit: Stream API ist die stärkste! !! !!

Recommended Posts

Ich möchte die Aggregationsverarbeitung mit Spring-Batch durchführen
Ich möchte eine asynchrone Verarbeitung und periodische Ausführung mit Rail durchführen !!!
Ich möchte Group By-Verarbeitung mit Stream durchführen (Group-by-Count, Group-by-Sum, Group-by-Max).
[Java] Ich möchte mit dem Schlüssel im Objekt eindeutig arbeiten
Ich möchte Java8 für jeden mit Index verwenden
Ich möchte mit Firestore von Rails spielen
[Rails] Ich möchte CSS mit Webpacker laden
Ich möchte den Dunkelmodus mit der SWT-App verwenden
Ich möchte eine bestimmte Datei mit WatchService überwachen
Ich möchte Benutzer mit Devise + OmniAuth bei Rails authentifizieren
Ich möchte Bildschirmübergänge mit Kotlin und Java machen!
Ich möchte Zeichen konvertieren ...
Wechseln Sie dynamisch die Datenbank, zu der eine Verbindung hergestellt werden soll
Ersetzen Sie durch einen Wert entsprechend der Übereinstimmung mit einem regulären Java-Ausdruck
Ich möchte die Aggregationsverarbeitung mit Spring-Batch durchführen
Ich habe versucht, die Verarbeitungsgeschwindigkeit mit spiritueller Technik zu erhöhen
Ich möchte eine mit Rails 6 erstellte App an GitHub senden
Ich möchte mit Ruby (ABC177E) eine schnelle Primfaktorisierung durchführen.
Ich möchte eine Liste mit Kotlin und Java erstellen!
Ich möchte eine Funktion mit Kotlin und Java erstellen!
Selbst in Java möchte ich true mit == 1 && a == 2 && a == 3 ausgeben
Ich möchte manuell eine Autorisierungs-E-Mail mit Devise senden
Ich möchte verschiedene Funktionen mit Kotlin und Java implementieren!
Ich möchte den Startbefehl mit Docker-Compose an Postgres übergeben.
[Java] Ich möchte Standardeingabe und Standardausgabe mit JUnit testen
Ich habe versucht, mit Java zu interagieren
Führen Sie die Verarbeitung manuell entsprechend @ConfigurationProperties durch
Ich möchte mit link_to [Hinweis] eine Schaltfläche mit einem Zeilenumbruch erstellen.
Ich möchte SONY Kopfhörer WH-1000XM4 mit LDAC mit Ubuntu 20.04 verbinden! !!
Ich möchte eine Browsing-Funktion mit Ruby on Rails hinzufügen
Ich möchte den Ablauf der Spring-Verarbeitungsanforderungsparameter verstehen
Ich möchte mit Kotlin und Java zum vorherigen Bildschirm zurückkehren!
Ich möchte Spring Local Time mit MySQL Time (auch Millisekunden) einfügen.
Ich möchte OutOfMemory bei der Ausgabe großer Dateien mit POI vermeiden
[Rails] Ich möchte beim Übergang mit link_to Daten zu Params hinzufügen
Ich möchte mit einem regulären Ausdruck zwischen Zeichenketten extrahieren
Ich möchte eine Webanwendung entwickeln!
Ich möchte ein schönes build.gradle schreiben
Ich möchte doppelte Fehlermeldungen beseitigen
Ich habe versucht, Processing auf VS Code zu migrieren
Ich möchte eine ios.android App machen
Ich habe versucht, mit Web Assembly zu beginnen
Ich möchte im Dialogfeld mehrere Elemente mit einem benutzerdefinierten Layout auswählen
Selbst in Java möchte ich true mit == 1 && a == 2 && a == 3 ausgeben (PowerMockito Edition)
[iOS] Ich habe versucht, mit Swift eine insta-ähnliche Verarbeitungsanwendung zu erstellen
Ich möchte einen Unit Test schreiben!
Ich möchte PHP 7.2 unter Ubuntu 20.04 installieren.
[Hinweis] Ich möchte mit afterLast mit JdbcTemplate in umgekehrter Reihenfolge arbeiten
Ich möchte mit Jakarta EE 8 mit Java 11 ein dunkles Web-SNS erstellen
Ich möchte mit der Berechtigung des Windows-Verzeichnisses von WSL (Ubuntu) herumspielen.
Ich möchte ein chinesisches (koreanisches) PDF mit dünnen Berichten anzeigen
Ich war süchtig nach dem Spring-Batch-Test
Ich möchte Java-Updates insgesamt stoppen