Вот еще один оптимизированный способ сделать это:
import org.apache.spark.sql.functions._
val cols = freq.columns.drop(1).toSeq
val selections = Seq(col("id")) ++ cols.map(c => when(col(c).isNotNull, lit(1)).otherwise(col(c)).alias(c))
val freq2 = freq.select(selections : _*)
freq2.show
// +---+----+----+----+----+
// | id| a1| a2| a3| a4|
// +---+----+----+----+----+
// |101|null| 1| 1|null|
// |102| 1|null| 1| 1|
// |103| 1| 1|null| 1|
// |104| 1|null| 1|null|
// +---+----+----+----+----+
Вы можете попытаться сравнить планы выполнения для обоих:
scala> newfreq.explain(true)
== Parsed Logical Plan ==
'Project [id#10, a1#20, a2#26, a3#32, CASE WHEN isnotnull('a4) THEN 1 ELSE 'a4 END AS a4#38]
+- AnalysisBarrier
+- Project [id#10, a1#20, a2#26, CASE WHEN isnotnull(a3#13) THEN 1 ELSE a3#13 END AS a3#32, a4#14]
+- Project [id#10, a1#20, CASE WHEN isnotnull(a2#12) THEN 1 ELSE a2#12 END AS a2#26, a3#13, a4#14]
+- Project [id#10, CASE WHEN isnotnull(a1#11) THEN 1 ELSE a1#11 END AS a1#20, a2#12, a3#13, a4#14]
+- Relation[id#10,a1#11,a2#12,a3#13,a4#14] csv
== Analyzed Logical Plan ==
id: int, a1: int, a2: int, a3: int, a4: int
Project [id#10, a1#20, a2#26, a3#32, CASE WHEN isnotnull(a4#14) THEN 1 ELSE a4#14 END AS a4#38]
+- Project [id#10, a1#20, a2#26, CASE WHEN isnotnull(a3#13) THEN 1 ELSE a3#13 END AS a3#32, a4#14]
+- Project [id#10, a1#20, CASE WHEN isnotnull(a2#12) THEN 1 ELSE a2#12 END AS a2#26, a3#13, a4#14]
+- Project [id#10, CASE WHEN isnotnull(a1#11) THEN 1 ELSE a1#11 END AS a1#20, a2#12, a3#13, a4#14]
+- Relation[id#10,a1#11,a2#12,a3#13,a4#14] csv
== Optimized Logical Plan ==
Project [id#10, CASE WHEN isnotnull(a1#11) THEN 1 ELSE a1#11 END AS a1#20, CASE WHEN isnotnull(a2#12) THEN 1 ELSE a2#12 END AS a2#26, CASE WHEN isnotnull(a3#13) THEN 1 ELSE a3#13 END AS a3#32, CASE WHEN isnotnull(a4#14) THEN 1 ELSE a4#14 END AS a4#38]
+- Relation[id#10,a1#11,a2#12,a3#13,a4#14] csv
== Physical Plan ==
*(1) Project [id#10, CASE WHEN isnotnull(a1#11) THEN 1 ELSE a1#11 END AS a1#20, CASE WHEN isnotnull(a2#12) THEN 1 ELSE a2#12 END AS a2#26, CASE WHEN isnotnull(a3#13) THEN 1 ELSE a3#13 END AS a3#32, CASE WHEN isnotnull(a4#14) THEN 1 ELSE a4#14 END AS a4#38]
+- *(1) FileScan csv [id#10,a1#11,a2#12,a3#13,a4#14] Batched: false, Format: CSV, Location: InMemoryFileIndex[file:.../test.data], PartitionFilters: [], PushedFilters: [], ReadSchema: struct<id:int,a1:int,a2:int,a3:int,a4:int>
scala> freq2.explain(true)
== Parsed Logical Plan ==
'Project [unresolvedalias('id, None), CASE WHEN isnotnull('a1) THEN 1 ELSE 'a1 END AS a1#46, CASE WHEN isnotnull('a2) THEN 1 ELSE 'a2 END AS a2#47, CASE WHEN isnotnull('a3) THEN 1 ELSE 'a3 END AS a3#48, CASE WHEN isnotnull('a4) THEN 1 ELSE 'a4 END AS a4#49]
+- AnalysisBarrier
+- Relation[id#10,a1#11,a2#12,a3#13,a4#14] csv
== Analyzed Logical Plan ==
id: int, a1: int, a2: int, a3: int, a4: int
Project [id#10, CASE WHEN isnotnull(a1#11) THEN 1 ELSE a1#11 END AS a1#46, CASE WHEN isnotnull(a2#12) THEN 1 ELSE a2#12 END AS a2#47, CASE WHEN isnotnull(a3#13) THEN 1 ELSE a3#13 END AS a3#48, CASE WHEN isnotnull(a4#14) THEN 1 ELSE a4#14 END AS a4#49]
+- Relation[id#10,a1#11,a2#12,a3#13,a4#14] csv
== Optimized Logical Plan ==
Project [id#10, CASE WHEN isnotnull(a1#11) THEN 1 ELSE a1#11 END AS a1#46, CASE WHEN isnotnull(a2#12) THEN 1 ELSE a2#12 END AS a2#47, CASE WHEN isnotnull(a3#13) THEN 1 ELSE a3#13 END AS a3#48, CASE WHEN isnotnull(a4#14) THEN 1 ELSE a4#14 END AS a4#49]
+- Relation[id#10,a1#11,a2#12,a3#13,a4#14] csv
== Physical Plan ==
*(1) Project [id#10, CASE WHEN isnotnull(a1#11) THEN 1 ELSE a1#11 END AS a1#46, CASE WHEN isnotnull(a2#12) THEN 1 ELSE a2#12 END AS a2#47, CASE WHEN isnotnull(a3#13) THEN 1 ELSE a3#13 END AS a3#48, CASE WHEN isnotnull(a4#14) THEN 1 ELSE a4#14 END AS a4#49]
+- *(1) FileScan csv [id#10,a1#11,a2#12,a3#13,a4#14] Batched: false, Format: CSV, Location: InMemoryFileIndex[file:.../test.data], PartitionFilters: [], PushedFilters: [], ReadSchema: struct<id:int,a1:int,a2:int,a3:int,a4:int>
Оптимизированный логический логический планы одинаковы для обоих, но это более чистый способ сделать.
Чтобы полностью ответить на ваш вопрос (докеризировать проект Spring Boot и просмотреть соответствующее веб-приложение в локальном браузере, скажем, на этапе разработки), необходимо выполнить три независимых задачи:
Dockerfile
, который использует механизм кэширования Docker (чтобы избежать повторной загрузки зависимостей Maven с нуля каждый раз и тем самым ускорить сборку)
Убедитесь, что приложение Spring Boot прослушивает указанный порт для 0.0.0.0
специального IP , а не localhost
;
И, наконец, опубликуйте данный порт, чтобы вы могли запустить, например:
$ xdg-open http://localhost:8080/index
Шаг 3 хорошо объяснен в ответе @ Poger's, поэтому я подробнее остановлюсь только на шагах 1 и 2:
Я предложил Dockerfile
в этой теме SO: Как кэшировать зависимости maven в Docker , вдохновленный этой статьей в блоге , это применимо для проектов Java / Maven в гене ral (не только проекты Spring Boot):
# our base build image
FROM maven:3.5-jdk-8 as maven
WORKDIR /app
# copy the Project Object Model file
COPY ./pom.xml ./pom.xml
# fetch all dependencies
RUN mvn dependency:go-offline -B
# copy your other files
COPY ./src ./src
# build for release
# NOTE: my-project-* should be replaced with the proper prefix
RUN mvn package && cp target/my-project-*.jar app.jar
# smaller, final base image
FROM openjdk:8u171-jre-alpine
# OPTIONAL: copy dependencies so the thin jar won't need to re-download them
# COPY --from=maven /root/.m2 /root/.m2
# set deployment directory
WORKDIR /app
# copy over the built artifact from the maven image
COPY --from=maven /app/app.jar ./app.jar
# set the startup command to run your binary
CMD ["java", "-jar", "/app/app.jar"]
, но для дальнейшего уточнения обратите внимание, что рекомендуется передать дополнительное системное свойство java.security.egd
:
CMD ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app/app.jar"]
или, если вы предпочитаете ENTRYPOINT
, чем CMD
:
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app/app.jar"]
Как упомянуто в ветке SO Как получить доступ к приложению Spring, запущенному в докере container? , в контексте контейнерного приложения, localhost
следует избегать и заменять на 0.0.0.0
большую часть времени (а именно, если приложение должно действовать как веб-служба, отвечающая на входящие запросы извне контейнера ).
Подводя итог, вы должны просто попытаться добавить следующую строку в ваш файл application.properties
:
server.address=0.0.0.0
Когда вы запускаете Docker-контейнер, все порты, которые прослушиваются в нем любым приложением, по умолчанию не публикуются.
Чтобы опубликовать порт, вам нужно указать его при запуске контейнера с использованием вашего изображения. Для получения более подробной информации о том, как это сделать, вы можете проверить раздел «EXPOSE» в документации команды docker run : https://docs.docker.com/engine/reference/run /
Короче говоря, вы хотите добавить еще одну опцию во время работы вашего контейнера:
docker run --name mycontainer1 -p 8080:8080 myimage1
Я не уверен, что вы хотите добиться этого, добавив
[ 111]в вашем Dockerfile. На самом деле это не означает, что порт будет открыт, когда образ используется для запуска контейнера. Как вы можете найти в Справочник по Dockerfile :
Инструкция EXPOSE фактически не публикует порт. Он функционирует как тип документации между человеком, который создает образ, и человеком, который запускает контейнер, о том, какие порты предназначены для публикации. Чтобы фактически опубликовать порт при запуске контейнера, используйте флаг -p на панели запуска Docker, чтобы опубликовать и сопоставить один или несколько портов, или флаг -P, чтобы опубликовать все открытые порты и сопоставить их с портами высокого порядка.
BLOCKQUOTE>