#java #apache-spark #code-cleanup
Вопрос:
В настоящее время я нахожусь на своей первой работе, связанной с Spark. Код написан на Java.
Моя текущая задача-продолжить то, что было оставлено предыдущим разработчиком.
Сама по себе задача не очень сложная, но контекст:
- Существует более 10 наборов данных, которые поступают из разных источников (файлы, конечная точка компании, таблицы и т. Д.).
- Эти наборы данных должны быть объединены в конце в уникальный набор данных.
- В этих наборах данных существуют сотни «Правил».
- Некоторые из этих правил могут применяться, например, только определенными соединениями из DsA DsB.
Предыдущий разработчик написал Правила, объединения только в одном конкретном классе (которые становятся действительно большими, например, тысячи строк) с огромным основным классом.
Я могу легко следовать тому же процессу и сделать эти классы еще больше, но я нахожу крайне раздражающим читать и поддерживать их в будущем.
На данный момент я реструктурировал проект в таких пакетах, как:
- Читать
- предварительный процесс
- расчеты
- писать
Но я также сомневаюсь в своей собственной методологии, и у меня нет старшего, на которого можно положиться в компании.
- Мои классы-это просто служебные классы со многими (но организованными ?) статическими методами, которые содержат правила, и я вызываю их из вспомогательного класса, чтобы связать все процессы. Я чувствую, что это своего рода «грязный» код, но я не могу придумать никакого другого решения
- У меня есть несколько классов, в которых я вводю набор данных в качестве параметра, чтобы этот набор данных служил ссылкой во всех методах внутри класса. Но все методы действительно «специфичны» , например:
public class CurrencyService { private final Dataset<Row> currencyReference; // constructor public Dataset<Row> applyCurrencyConversion(Dataset<Row> targetDataset){ // target joins reference and apply conversion } private Column conversion(Column targetColumn){ // conversion rules } }
- Как я должен обрабатывать ключи соединения? Каждый набор данных имеет другое имя для своего идентификатора. Я думал о том, чтобы заставить строку ключа из константы для каждого идентификатора на этапе предварительной обработки. Потому что в противном случае мне придется присоединяться к каждому набору данных вручную, каждый раз зная имя их столбца.
С помощью предварительно обработанных ключей соединения я могу сделать что-то подобноеdsA.join(dsB, Sequence of (joinKeys))
.
В конце концов, я чувствую, что еще больше усложняю код, в то время как предыдущий стиль «одного класса» было легче понять, но не поддерживать…
У меня есть опыт работы в POO, так что я отношусь к этому проекту слишком объектно-ориентированно?
Ответ №1:
Это действительно очень широкий и впечатляющий вопрос 🙂 — Я хотел бы, чтобы разработчики имели такой уровень зрелости, когда они начинают проект.
Трубопровод
Вы строите трубопровод, я рекомендую вам следовать:
- Принимайте (считывайте) данные
- Примените правила качества данных (убедитесь, что данные в порядке)
- Преобразуйте данные (сформируйте их так, как вы хотите)
- Выполните соединение
- Сохраните данные в качестве контрольной точки/конечного результата
Если вы будете следовать этой схеме, вам не следует применять правила после Золотой зоны, если только вы не работаете над конкретными подпроектами.
Преобразования
Для организации преобразований статические функции прекрасно подходят, особенно если вы наследуете существующий (рабочий?) проект. Я бы подумал немного по-другому о будущих правилах или проекте с нуля (вы могли бы создать небольшую систему правил, в которой вы добавляете правила в список и выполняете их сразу, например).
Присоединяется
Для присоединения, если у вас есть сложные соединения, которые вы использовали повторно, вы можете представить их изоляцию, но ваша идея изоляции столбцов идентификаторов хороша: она в большей степени зависит от метаданных. В некоторых проектах я создавал объект «Супердатафрам», в котором я мог добавить некоторые недостающие данные в фрейм данных. Вы также можете добавлять пользовательские метаданные в любые столбцы и использовать их в своих правилах: проверьте https://github.com/jgperrin/net.jgp.labs.spark/blob/master/src/main/java/net/jgp/labs/spark/l090_metadata/l000_add_metadata/AddMetadataApp.java. Это выглядит так:
Metadata metadata = new MetadataBuilder()
.putString("x-source", filename)
.putString("x-format", format)
.putLong("x-order", i )
.build();
df = df.withColumn(colName, col, metadata);
Общие рекомендации
- Избегайте UDF: они дороги для Catalyst (оптимизатор) видит их как черные ящики, поэтому он не может их оптимизировать. Придерживайтесь функций Spark как можно больше.
- Придерживайтесь фреймов данных (
Dataset<Row>
), а не наборов данных/RDDs для оптимизации производительности.
Ты на верном пути!