91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

進擊的 Java ,云原生時代的蛻變

發布時間:2020-07-20 21:35:21 來源:網絡 閱讀:520 作者:阿里系統軟件技術 欄目:云計算

進擊的 Java ,云原生時代的蛻變作者| 易立 阿里云資深技術專家

導讀:云原生時代的來臨,與Java 開發者到底有什么聯系?有人說,云原生壓根不是為了 Java 存在的。然而,本文的作者卻認為云原生時代,Java 依然可以勝任“巨人”的角色。作者希望通過一系列實驗,開拓同學視野,提供有益思考。

在企業軟件領域,Java 依然是絕對王者,但它讓開發者既愛又恨。一方面因為其豐富的生態和完善的工具支持,可以極大提升了應用開發效率;但在運行時效率方面,Java 也背負著”內存吞噬者“,“CPU 撕裂者“的惡名,持續受到 NodeJS、Python、Golang 等新老語言的挑戰。

在技術社區,我們經常看到有人在唱衰 Java 技術,認為其不再符合云原生計算發展的趨勢。我們先拋開這些觀點,首先思考一下云原生對應用運行時的不同需求。

  • 體積更小 - 對于微服務分布式架構而言,更小的體積意味著更少的下載帶寬,更快的分發下載速度。
  • 啟動速度更快 - 對于傳統單體應用,啟動速度與運行效率相比不是一個關鍵的指標。原因是,這些應用重啟和發布頻率相對較低。然而對于需要快速迭代、水平擴展的微服務應用而言,更快的的啟動速度就意味著更高的交付效率,和更加快速的回滾。尤其當你需要發布一個有數百個副本的應用時,緩慢的啟動速度就是時間殺手。對于Serverless 應用而言,端到端的冷啟動速度則更為關鍵,即使底層容器技術可以實現百毫秒資源就緒,如果應用無法在500ms內完成啟動,用戶就會感知到訪問延遲。
  • 占用資源更少 - 運行時更低的資源占用,意味著更高的部署密度和更低的計算成本。同時,在JVM啟動時需要消耗大量CPU資源對字節碼進行編譯,降低啟動時資源消耗,可以減少資源爭搶,更好保障其他應用SLA。
  • 支持水平擴展 - JVM的內存管理方式導致其對大內存管理的相對低效,一般應用無法通過配置更大的heap size實現性能提升,很少有Java應用能夠有效使用16G內存或者更高。另一方面,隨著內存成本的下降和虛擬化的流行,大內存配比已經成為趨勢。所以我們一般是采用水平擴展的方式,同時部署多個應用副本,在一個計算節點中可能運行一個應用的多個副本來提升資源利用率。

熱身準備

熟悉Spring框架的開發者大多對?Spring Petclinic?不會陌生。本文將借助這個著名示例應用來演示如何讓我們的Java應用變得更小,更快,更輕,更強大!
進擊的 Java ,云原生時代的蛻變我們fork了IBM的Michael Thompson的示例,并做了一些調整。

$ git clone https://github.com/denverdino/adopt-openj9-spring-boot
$ cd adopt-openj9-spring-boot

首先,我們會為PetClinic應用構建一個Docker鏡像。在Dockerfile中,我們利用OpenJDK作為基礎鏡像,安裝Maven,下載、編譯、打包Spring PetClinic應用,最后設置鏡像的啟動參數完成鏡像構建。

$ cat Dockerfile.openjdk
FROM adoptopenjdk/openjdk8
RUN sed -i 's/archive.ubuntu.com/mirrors.aliyun.com/' /etc/apt/sources.list
RUN apt-get update
RUN apt-get install -y \
    git \
    maven
WORKDIR /tmp
RUN git clone https://github.com/spring-projects/spring-petclinic.git
WORKDIR /tmp/spring-petclinic
RUN mvn install
WORKDIR /tmp/spring-petclinic/target
CMD ["java","-jar","spring-petclinic-2.1.0.BUILD-SNAPSHOT.jar"]

構建鏡像并執行

$ docker build -t petclinic-openjdk-hotspot -f Dockerfile.openjdk .
$ docker run --name hotspot -p 8080:8080 --rm petclinic-openjdk-hotspot
              |\      _,,,--,,_
             /,`.-'`'   ._  \-;;,_
  _______ __|,4-  ) )_   .;.(__`'-'__     ___ __    _ ___ _______
 |       | '---''(_/._)-'(_\_)   |   |   |   |  |  | |   |       |
 |    _  |    ___|_     _|       |   |   |   |   |_| |   |       | __ _ _
 |   |_| |   |___  |   | |       |   |   |   |       |   |       | \ \ \ \
 |    ___|    ___| |   | |      _|   |___|   |  _    |   |      _|  \ \ \ \
 |   |   |   |___  |   | |     |_|       |   | | |   |   |     |_    ) ) ) )
 |___|   |_______| |___| |_______|_______|___|_|  |__|___|_______|  / / / /
 ==================================================================/_/_/_/
...
2019-09-11 01:58:23.156  INFO 1 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2019-09-11 01:58:23.158  INFO 1 --- [           main] o.s.s.petclinic.PetClinicApplication     : Started PetClinicApplication in 7.458 seconds (JVM running for 8.187)

可以通過 http://localhost:8080/ 訪問應用界面。
檢查一下構建出的Docker鏡像, ”petclinic-openjdk-openj9“ 的大小為871MB,而基礎鏡像 ”adoptopenjdk/openjdk8“ 僅有 300MB!這貨也太膨脹了!

$ docker images petclinic-openjdk-hotspot
REPOSITORY                  TAG                 IMAGE ID            CREATED             SIZE
petclinic-openjdk-hotspot   latest              469f73967d03        26 hours ago        871MB

原因是:為了構建Spring應用,我們在鏡像中引入了一系列編譯時依賴,如 Git,Maven等,并產生了大量臨時的文件。然而這些內容在運行時是不需要的。
在著名的軟件12要素?第五條明確指出了,”Strictly separate build and run stages.“ 嚴格分離構建和運行階段,不但可以幫助我們提升應用的可追溯性,保障應用交付的一致性,同時也可以減少應用分發的體積,減少安全風險。

鏡像瘦身

Docker提供了Multi-stage Build(多階段構建),可以實現鏡像瘦身。
進擊的 Java ,云原生時代的蛻變我們將鏡像構建分成兩個階段:

  • 在 ”build“ 階段依然采用JDK作為基礎鏡像,并利用Maven進行應用構建;
  • 在最終發布的鏡像中,我們會采用JRE版本作為基礎鏡像,并從”build“ 鏡像中直接拷貝出生成的jar文件。這意味著在最終發布的鏡像中,只包含運行時所需必要內容,不包含任何編譯時依賴,大大減少了鏡像體積。
    $ cat Dockerfile.openjdk-slim
    FROM adoptopenjdk/openjdk8 AS build
    RUN sed -i 's/archive.ubuntu.com/mirrors.aliyun.com/' /etc/apt/sources.list
    RUN apt-get update
    RUN apt-get install -y \
    git \
    maven
    WORKDIR /tmp
    RUN git clone https://github.com/spring-projects/spring-petclinic.git
    WORKDIR /tmp/spring-petclinic
    RUN mvn install
    FROM adoptopenjdk/openjdk8:jre8u222-b10-alpine-jre
    COPY --from=build /tmp/spring-petclinic/target/spring-petclinic-2.1.0.BUILD-SNAPSHOT.jar spring-petclinic-2.1.0.BUILD-SNAPSHOT.jar
    CMD ["java","-jar","spring-petclinic-2.1.0.BUILD-SNAPSHOT.jar"]

    查看一下新鏡像大小,從 871MB 減少到 167MB!

    $ docker build -t petclinic-openjdk-hotspot-slim -f Dockerfile.openjdk-slim .
    ...
    $ docker images petclinic-openjdk-hotspot-slim
    REPOSITORY                       TAG                 IMAGE ID            CREATED             SIZE
    petclinic-openjdk-hotspot-slim   latest              d1f1ca316ec0        26 hours ago        167MB

    鏡像瘦身之后將大大加速應用分發速度,我們是否有辦法優化應用的啟動速度呢?

從 JIT 到 AOT - 啟動提速

為了解決Java啟動的性能瓶頸,我們首先需要理解JVM的實現原理。為了實現“一次編寫,隨處運行”的能力,Java程序會被編譯成實現架構無關的字節碼。JVM在運行時將字節碼轉換成本地機器碼執行。這個轉換過程決定了Java應用的啟動和運行速度。為了提升執行效率,JVM引入了JIT compiler(Just in Time Compiler,即時編譯器),其中Sun/Oracle公司的HotSpot是最著名JIT編譯器實現。它提供了自適應優化器,可以動態分析、發現代碼執行過程中的關鍵路徑,并進行編譯優化。HotSpot的出現極大提升了Java應用的執行效率,在Java 1.4以后成為了缺省的VM實現。但是HotSpot VM在啟動時才對字節碼進行編譯,一方面導致啟動時執行效率不高,一方面編譯和優化需要很多的CPU資源,拖慢了啟動速度。我們是否可以優化這個過程,提升啟動速度呢?
熟悉Java江湖歷史的同學應該會知道IBM J9 VM,它是用于IBM企業級軟件產品的一款高性能的JVM,幫助IBM奠定了商業應用平臺中間件的霸主地位。2017年9月,IBM 將 J9 捐獻給 Eclipse 基金會,并更名 Eclipse OpenJ9,開啟開源之旅。
OpenJ9 提供了Shared Class Cache (SCC 共享類緩存) 和 Ahead-of-Time (AOT 提前編譯) 技術,顯著減少了Java應用啟動時間。
SCC 是一個內存映射文件,包含了J9 VM對字節碼的執行分析信息和已經編譯生成的本地代碼。開啟 AOT 編譯后,會將JVM編譯結果保存在 SCC 中,在后續 JVM 啟動中可以直接重用。與啟動時進行的 JIT 編譯相比,從 SCC 加載預編譯的實現要快得多,而且消耗的資源要更少。啟動時間可以得到明顯改善。
我們開始構建一個包含AOT優化的Docker應用鏡像

$cat Dockerfile.openj9.warmed
FROM adoptopenjdk/openjdk8-openj9 AS build
RUN sed -i 's/archive.ubuntu.com/mirrors.aliyun.com/' /etc/apt/sources.list
RUN apt-get update
RUN apt-get install -y \
    git \
    maven
WORKDIR /tmp
RUN git clone https://github.com/spring-projects/spring-petclinic.git
WORKDIR /tmp/spring-petclinic
RUN mvn install
FROM adoptopenjdk/openjdk8-openj9:jre8u222-b10_openj9-0.15.1-alpine
COPY --from=build /tmp/spring-petclinic/target/spring-petclinic-2.1.0.BUILD-SNAPSHOT.jar spring-petclinic-2.1.0.BUILD-SNAPSHOT.jar
# Start and stop the JVM to pre-warm the class cache
RUN /bin/sh -c 'java -Xscmx50M -Xshareclasses -Xquickstart -jar spring-petclinic-2.1.0.BUILD-SNAPSHOT.jar &' ; sleep 20 ; ps aux | grep java | grep petclinic | awk '{print $1}' | xargs kill -1
CMD ["java","-Xscmx50M","-Xshareclasses","-Xquickstart", "-jar","spring-petclinic-2.1.0.BUILD-SNAPSHOT.jar"]

其中 Java 參數?-Xshareclasses?開啟SCC,-Xquickstart?開啟AOT。
在Dockerfile中,我們運用了一個技巧來預熱SCC。在構建過程中啟動JVM加載應用,并開啟SCC和AOT,在應用啟動后停止JVM。這樣就在Docker鏡像中包含了生成的SCC文件。
然后,我們來構建Docker鏡像并啟動測試應用,

$ docker build -t petclinic-openjdk-openj9-warmed-slim -f Dockerfile.openj9.warmed-slim .
$ docker run --name hotspot -p 8080:8080 --rm petclinic-openjdk-openj9-warmed-slim
...
2019-09-11 03:35:20.192  INFO 1 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2019-09-11 03:35:20.193  INFO 1 --- [           main] o.s.s.petclinic.PetClinicApplication     : Started PetClinicApplication in 3.691 seconds (JVM running for 3.952)
...

可以看到,啟動時間已經從之前的 8.2s 減少到 4s,提升近50%。
在這個方案中,我們一方面將耗時耗能的編譯優化過程轉移到構建時完成,一方面采用以空間換時間的方法,將預編譯的SCC緩存保存到Docker鏡像中。在容器啟動時,JVM可以直接使用內存映射文件來加載SCC,優化了啟動速度和資源占用。
這個方法另外一個優勢是:由于Docker鏡像采用分層存儲,同一個宿主機上的多個Docker應用實例會共享同一份SCC內存映射,可以大大減少在單機高密度部署時的內存消耗。
下面我們做一下資源消耗的比較,我們首先利用基于HotSpot VM的鏡像,同時啟動4個Docker應用實例,30s后利用docker stats查看資源消耗

$ ./run-hotspot-4.sh
...
Wait a while ...
CONTAINER ID        NAME                CPU %               MEM USAGE / LIMIT     MEM %               NET I/O             BLOCK I/O           PIDS
0fa58df1a291        instance4           0.15%               597.1MiB / 5.811GiB   10.03%              726B / 0B           0B / 0B             33
48f021d728bb        instance3           0.13%               648.6MiB / 5.811GiB   10.90%              726B / 0B           0B / 0B             33
a3abb10078ef        instance2           0.26%               549MiB / 5.811GiB     9.23%               726B / 0B           0B / 0B             33
6a65cb1e0fe5        instance1           0.15%               641.6MiB / 5.811GiB   10.78%              906B / 0B           0B / 0B             33
...

然后使用基于OpenJ9 VM的鏡像,同時啟動4個Docker應用實例,并查看資源消耗

$ ./run-openj9-warmed-4.sh
...
Wait a while ...
CONTAINER ID        NAME                CPU %               MEM USAGE / LIMIT     MEM %               NET I/O             BLOCK I/O           PIDS
3a0ba6103425        instance4           0.09%               119.5MiB / 5.811GiB   2.01%               1.19kB / 0B         0B / 446MB          39
c07ca769c3e7        instance3           0.19%               119.7MiB / 5.811GiB   2.01%               1.19kB / 0B         16.4kB / 120MB      39
0c19b0cf9fc2        instance2           0.15%               112.1MiB / 5.811GiB   1.88%               1.2kB / 0B          22.8MB / 23.8MB     39
95a9c4dec3d6        instance1           0.15%               108.6MiB / 5.811GiB   1.83%               1.45kB / 0B         102MB / 414MB       39
...

與HotSpot VM相比,OpenJ9的場景下應用內存占用從平均 600MB 下降到 120MB。驚喜不驚喜?
通常而言,HotSpot JIT比AOT可以進行更加全面和深入的執行路徑優化,從而有更高的運行效率。為了解決這個矛盾,OpenJ9 的AOT SCC只在啟動階段生效,在后續運行中會繼續利用JIT進行分支預測、代碼內聯等深度編譯優化。
更多關于 OpenJ9 SCC和AOT的技術介紹,請參考

  • https://www.ibm.com/developerworks/cn/java/j-class-sharing-openj9/index.html
  • https://www.ibm.com/developerworks/cn/java/j-optimize-jvm-startup-with-eclipse-openjj9/index.html
  • HotSpot在Class Data Sharing (CDS)和AOT方面也有了很大進展,但是IBM J9在這方面更加成熟。期待阿里的Dragonwell也提供相應的優化支持。

思考:與C/C++,Golang, Rust等靜態編譯語言不同,Java采用VM方式運行,提升了應用可移植性的同時犧牲了部分性能。我們是否可以將AOT做到極致?完全移除字節碼到本地代碼的編譯過程?

原生代碼編譯

為了將Java應用編譯成本地可執行代碼,我們首先要解決JVM和應用框架在運行時的動態性挑戰。JVM提供了靈活的類加載機制,Spring的依賴注入(DI,Dependency-injection)可以實現運行時動態類加載和綁定。在Spring框架中,反射,Annotation 運行時處理器等技術也被廣泛應用。這些動態性一方面提升了應用架構的靈活性和易用性,另一方面也降低了應用的啟動速度,使得AOT原生編譯和優化變得非常復雜。
為了解決這些挑戰,社區有很多有趣的探索,Micronaut?是其中一個優秀代表。與Spring框架序不同,Micronaut提供了編譯時的依賴注入和AOP處理能力,并最小化反射和動態代理的使用。Micronaut 應用有著更快的啟動速度和更低的內存占用。更加讓我們更感興趣的是Micronaut支持與Graal VM配合,可以將Java應用編譯成為本地執行代碼全速運行。注:GraalVM是Oracle推出的一種新型通用虛擬機,支持多種語言,可以將Java應用程序編譯為本地原生應用。進擊的 Java ,云原生時代的蛻變原圖
下面開始我們的探險,我們利用Mitz提供的Micronaut版本PetClinic示例工程并做了一點點調整。(使用Graal VM 19.2)

$ git clone https://github.com/denverdino/micronaut-petclinic
$ cd micronaut-petclinic

其中Docker鏡像的內容如下

$ cat Dockerfile
FROM maven:3.6.1-jdk-8 as build
COPY ./ /micronaut-petclinic/
WORKDIR /micronaut-petclinic
RUN mvn package
FROM oracle/graalvm-ce:19.2.0 as graalvm
RUN gu install native-image
WORKDIR /work
COPY --from=build /micronaut-petclinic/target/micronaut-petclinic-*.jar .
RUN native-image --no-server -cp micronaut-petclinic-*.jar
FROM frolvlad/alpine-glibc
EXPOSE 8080
WORKDIR /app
COPY --from=graalvm /work/petclinic .
CMD ["/app/petclinic"]

其中

  • 在 "build" 階段,利用Maven構建 Micronaut 版本的 PetClinic 應用,
  • 在 "graalvm" 階段,我們通過?native-image?將PetClinic jar文件轉化成可執行文件。
  • 在最終階段,將本地可執行文件加入一個Alpine Linux基礎鏡像

構建應用

$ docker-compose build

啟動測試數據庫

$ docker-compose up db

啟動測試應用

$ docker-compose up app
micronaut-petclinic_db_1 is up-to-date
Starting micronaut-petclinic_app_1 ... done
Attaching to micronaut-petclinic_app_1
app_1  | 04:57:47.571 [main] INFO  org.hibernate.dialect.Dialect - HHH000400: Using dialect: org.hibernate.dialect.PostgreSQL95Dialect
app_1  | 04:57:47.649 [main] INFO  org.hibernate.type.BasicTypeRegistry - HHH000270: Type registration [java.util.UUID] overrides previous : org.hibernate.type.UUIDBinaryType@5f4e0f0
app_1  | 04:57:47.653 [main] INFO  o.h.tuple.entity.EntityMetamodel - HHH000157: Lazy property fetching available for: com.example.micronaut.petclinic.owner.Owner
app_1  | 04:57:47.656 [main] INFO  o.h.e.t.j.p.i.JtaPlatformInitiator - HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
app_1  | 04:57:47.672 [main] INFO  io.micronaut.runtime.Micronaut - Startup completed in 159ms. Server Running: http://1285c42bfcd5:8080

應用啟動速度如閃電般提升至 159ms,僅有HotSpot VM的1/50!
Micronaut和Graal VM還在快速發展中,遷移一個Spring應用還有不少工作需要考慮。此外Graal VM的調試、監控等工具鏈還不夠完善。但是這已經讓我們看到了曙光,Java應用和Serverless的世界不再遙遠。由于篇幅有限,對Graal VM和Micronaut有興趣的同學可以參考

  • https://docs.micronaut.io/latest/guide/index.html#graal
  • https://www.exoscale.com/syslog/how-to-integrate-spring-with-micronaut/

總結與后記

作為進擊的巨人,Java技術在云原生時代也在不停地進化。在JDK 8u191和JDK 10之后,JVM增強了在Docker容器中對資源的感知。同時社區也在多個不同方向探索Java技術棧的邊界。JVM OpenJ9作為傳統VM的一員,在對現有Java應用保持高度兼容的同時,對啟動速度和內存占用做了細致的優化,比較適于與現有Spring等微服務架構配合使用。而Micronaut/Graal VM則另辟蹊徑,通過改變編程模型和編譯過程,將應用的動態性盡可能提前到編譯時期處理,極大優化了應用啟動時間,在Serverless領域前景可期。這些設計思路都值得我們借鑒。
在云原生時代,我們要能夠在橫向的應用開發生命周期中,將開發、交付、運維過程進行有效的分割和重組,提升研發協同效率;并且要能在整個縱向軟件技術棧中,在編程模型、應用運行時和基礎設施等多層面進行系統優化,實現radical simplification,提升系統效率。
本文完成于在參加阿里集團20周年的火車旅途上,9/10阿里年會是非常難忘的經歷。感謝馬老師,感謝阿里,感謝這個時代,感謝所有幫助和支持我們的小伙伴,感謝所有追夢的技術人,我們一起開拓云原生的未來。

“阿里巴巴云原生微信公眾號(ID:Alicloudnative)關注微服務、Serverless、容器、Service Mesh等技術領域、聚焦云原生流行技術趨勢、云原生大規模的落地實踐,做最懂云原生開發者的技術公眾號。”

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

邵东县| 延边| 英超| 丰城市| 梅州市| 泰和县| 嘉荫县| 专栏| 晋江市| 呼图壁县| 东兴市| 霍城县| 惠东县| 台中县| 全椒县| 大姚县| 且末县| 运城市| 杭锦旗| 开平市| 万州区| 宣汉县| 都匀市| 望谟县| 绵阳市| 新邵县| 喀喇沁旗| 太仆寺旗| 讷河市| 枣强县| 南充市| 永川市| 武陟县| 黄浦区| 米泉市| 莱西市| 西吉县| 尉氏县| 民乐县| 临江市| 卢氏县|