目前已經更新完《Java並發編程》,《Docker教程》和《JVM性能優化》,歡迎關注【后端精進之路】,輕松閱讀全部文章。
![]()
Java並發編程:
- Java並發編程系列-(1) 並發編程基礎
- Java並發編程系列-(2) 線程的並發工具類
- Java並發編程系列-(3) 原子操作與CAS
- Java並發編程系列-(4) 顯式鎖與AQS
- Java並發編程系列-(5) Java並發容器
- Java並發編程系列-(6) Java線程池
- Java並發編程系列-(7) Java線程安全
- Java並發編程系列-(8) JMM和底層實現原理
- Java並發編程系列-(9) JDK 8/9/10中的並發
Docker教程:
JVM性能優化:
上一篇文章介紹了docker鏡像的制作與發布,本文主要介紹實際docker工程部署中經常用到的docker-compose工具,以及docker的網絡配置和負載均衡。
Docker-compose介紹
實際開發過程中,在一個項目中,我們常將不同的模塊放在單獨的docker中,方便維護和擴展。比如我們一個項目可能有MySQL鏡像、Nginx鏡像、Spring Boot后端鏡像,我們在實際部署中可以采用上篇文章介紹的方法,分別打包,分別啟動;但是這樣太費精力,而且還容易出錯。
因此Docker-compose工具應運而生,就是為了解決工程部署中的多個Docker鏡像的管理問題。
docker-compose.yaml 文件
類似於build鏡像需要Dockerfile一樣,使用docker-compose時也需要類似的配置文件,叫做docker-compose.yaml。
下面是一個Docker-file的例子:

一般docker-compose文件中需要注意如下幾點:
- version: '3': 表示使用第三代語法,每代的語法稍有不同,我們選擇最新的就好。
- services: 表示compose需要啟動的服務,一般一個docker鏡像就可以作為一個單獨的service。
- container_name: 容器名稱
- environment: 容器環境變量,可以使用這個參數往容器傳遞一些變量,這個在我們切換不同環境配置時非常有用,不用每次去改變代碼中的配置
- ports: 對外開放的端口
- restart: always表示如果服務啟動不成功一直嘗試。
- volumes: 加載本地目錄到容器目標路徑,也就是將本地路徑共享給容器,方便進行數據交互,也可以利用這個功能,將docker的log輸出到本機指定的位置。
- depends_on:依賴服務,先啟動 depends_on 服務。
- command: mvn clean spring-boot:run : 表示以這個命令來啟動項目
上面提到了depends_on,通常用於需要順序啟動不同鏡像,比如后台需要等待數據庫初始化完畢以后才啟動,但是這個depends_on並不能保證前序容器完全初始化,意思是只要前序容器啟動了,就會嘗試啟動當前容器。這樣還是會造成前序容器未初始化完成,后續容器啟動后出現連接失敗等錯誤。
因此最好的方式是官方(https://docs.docker.com/compose/startup-order/)比較推薦的wait腳本,腳本的詳細使用可以看這里 https://github.com/vishnubob/wait-for-it,
wait-for-it.sh host:port [-s] [-t timeout] [-- command args]
-h HOST | --host=HOST Host or IP under test
-p PORT | --port=PORT TCP port under test
Alternatively, you specify the host and port as host:port
-s | --strict Only execute subcommand if the test succeeds
-q | --quiet Don't output any status messages
-t TIMEOUT | --timeout=TIMEOUT
Timeout in seconds, zero for no timeout
-- COMMAND ARGS Execute command with args after the test finishes
一般也可以在制作鏡像的時候指定等待一定時間,在下面的例子中,在運行test.jar之前,會一直等到elasticsearch初始化完成。
FROM openjdk:8
ADD ["target/test-1.0.0.jar", "test.jar"]
ADD ["wait-for-it.sh", "wait-for-it.sh"]
RUN ["chmod", "+x", "wait-for-it.sh"]
EXPOSE 1234
ENTRYPOINT ["./wait-for-it.sh", "elastic:9200", "--", "java", "-jar", "/test.jar"]
關於更多docker-compose文件的詳細介紹,可以參考官方的文檔 https://docs.docker.com/compose/gettingstarted/。
docker-compose的啟動/停止
docker-compose的啟動與停止非常簡單,直接使用docker-compose up
啟動,docker-compose down
關閉工程,同時會刪除所有容器。
Docker的網絡通信
docker的網絡方面是相對較弱的,新手如果不清楚,經常會出現為什么容器A訪問不到容器B的問題。
容器間的通信
有個很常見的場景,我們需要在本機同時調試前端和后端程序,通常我們會在localhost某個端口啟動前端,比如8080;同時在local的另一端口比如1234啟動后端。如果不用容器,前端直接在代碼里訪問localhost:1234的所有接口都是可以的,沒有任何問題。
但是如果前端和后端都封裝在不同的容器中,這個時候前端直接訪問localhost:1234,會報錯,無法訪問。原因就是兩個容器之間是獨立的,無法直接localhost訪問。
官方推薦的解決辦法就是,先新建一個網絡,然后啟動前后端容器的時候,分別指定在該網絡中運行。
docker network create mynet
docker run --network mynet --name frontend ...
docker run --network mynet --name backend ...
現在前端就可以通過 http://backend:1234 訪問后端的接口了。
注意:有些博客指出,可以通過
docker inspect [container_name]
來獲取后端容器的ip,然后直接訪問ip加端口就可以了。這個在Linux上也許可行,但是mac下由於容器的實現原理不同,無法直接通過ip訪問。
docker-compose下的網絡環境
默認情況下,當你利用docker-compose啟動多個容器時,docker-compose會自動幫你創建一個網絡,並將多個service在這個網絡中運行。因此不同容器之間,直接利用容器名+端口就可以訪問了。
Docker-compose實現負載均衡
實際生產環境中,肯定會啟動多個后端service,同時進行負載均衡以保證服務質量。利用docker-compose我們可以方便的實現這種功能。

下面是一個很簡單的docker-compose文件,啟動之后可以在本地打開localhost:5000,來訪問后端的service;在front容器內部,我們也可以通過backend:5000來訪問backend service。
version: "3"
services:
front:
image: front:latest
backend:
image: "backend:latest"
ports:
- "5000:5000"
為了啟動多個后端程序以實現負載均衡,docker-compose已經為我們提供--scale
參數,利用這個參數可以輕松實現同時啟動多個service。
下面是該參數的使用說明。
--scale SERVICE=NUM Scale SERVICE to NUM instances. Overrides the
`scale` setting in the Compose file if present.
在這里,我們直接在啟動的時候,指定生成3個后端的service,
docker-compose up --scale backend=3
但是這樣會報錯,因為本機上的5000端口,只能分配給一個service,如果你想同時在本機上調試,可以將上面的ports改為5000,修改后的docker-compose文件如下:
version: "3"
services:
front:
image: front:latest
backend:
image: "backend:latest"
ports:
- "5000"
這樣按照上面同樣的指令啟動之后,你你可以看到如下的輸出結果,有3個不同的本機端口綁定到容器的5000端口,
Name Command State Ports
--------------------------------------------------------------------------------------------
example_backend_1 /usr/bin/dumb-init -- /sbi ... Up (healthy) 0.0.0.0:32776->5000/tcp
example_backend_2 /usr/bin/dumb-init -- /sbi ... Up (healthy) 0.0.0.0:32775->5000/tcp
example_backend_3 /usr/bin/dumb-init -- /sbi ... Up (healthy) 0.0.0.0:32777->5000/tcp
當然,如果你只想從容器內部訪問后端的5000端口,你可以去掉port配置,改成如下的配置:
version: "3"
services:
front:
image: front:latest
backend:
image: "backend:latest"
這樣docker-compose up --scale backend=3
啟動后,我們就只能在容器內部訪問http:\\backend:5000
來訪問后端service,因為我們啟動了三個,前端的請求會隨機的分配到后端的三個service上,保證了整體的服務質量。
參考鏈接:
- https://stackoverflow.com/questions/48062134/connection-refused-with-two-microservices-in-docker
- https://pspdfkit.com/blog/2018/how-to-use-docker-compose-to-run-multiple-instances-of-a-service-in-development/
本文由『后端精進之路』原創,首發於博客 http://teckee.github.io/ , 轉載請注明出處
搜索『后端精進之路』關注公眾號,立刻獲取最新文章和價值2000元的BATJ精品面試課程。