作者:京東科技 李君
公司最近一年在推進(jìn)降本增效,在用盡各種手段之后,發(fā)現(xiàn)應(yīng)用太多,每個(gè)應(yīng)用都做跨機(jī)房容災(zāi)部署,則最少需要 4 臺(tái)機(jī)器(稱為容器更合適)。那么,將相近應(yīng)用做一個(gè)合并,減少維護(hù)項(xiàng)目,提高機(jī)器利用率就是一個(gè)可選方案。
?
經(jīng)過(guò)前后三次不同的折騰,最后探索出來(lái)一個(gè)可行方案。記錄一下,分享出來(lái),希望對(duì)有相關(guān)需求的研發(fā)童鞋有所幫助。下面按照四種可能的方案,分別做介紹。另外,為了方便做演示,專門整了兩個(gè)演示項(xiàng)目:
?
??diguage/merge-demo-boot — 合并項(xiàng)目,下面簡(jiǎn)稱為 boot。
??diguage/merge-demo-web — 被合并項(xiàng)目,下面簡(jiǎn)稱為 web。
?
一、Jar 包引用
?
這個(gè)方式,可能是給人印象最容易的方式。仔細(xì)思考一下,從維護(hù)性的角度來(lái)看,這個(gè)方式反而是最麻煩的方式,理由如下:
?
1.web 項(xiàng)目每次更新,都需要重新打包發(fā)布新版; boot 項(xiàng)目也需要跟著更新發(fā)布。拉一次屎,脫兩次褲子。屬實(shí)麻煩。
2.還需要考慮 web 項(xiàng)目的加載問(wèn)題,類似下面要描述的,是否共用容器:共用容器 — 這是最容器想到的方式。但是這種方式,需要解決 Bean 沖突的問(wèn)題。不共用容器 — 這種方式需要處理 web 容器如何加載的問(wèn)題。默認(rèn)應(yīng)該是無(wú)法識(shí)別。
3.?
基于這些考慮,這種方式直接被拋棄了。
?
二、倉(cāng)庫(kù)合并,公用一套容器
?
這是第一次嘗試使用的方案。也是遇到問(wèn)題最多的方案。
?
1.將兩個(gè)倉(cāng)庫(kù)做合并。
1.將 web 倉(cāng)庫(kù)的地址配置到 boot 項(xiàng)目里: git remote add web git@github.com:diguage/merge-demo-web.git;
2.在 boot 項(xiàng)目里,切出來(lái)一個(gè)分支: git switch -c web;
3.將 web 分支的提交清空: git update-ref -d HEAD,然后做一次提交;
4.將 web 項(xiàng)目的代碼克隆到 web 分支上: git pull --rebase --allow-unrelated-histories web master;注意,這里需要加 --allow-unrelated-histories 參數(shù),以允許不相干的倉(cāng)庫(kù)進(jìn)行合并。
5.從 boot 項(xiàng)目的 master 分支上,切出來(lái)一個(gè)合并分支: git switch -c merge;
6.將 web 項(xiàng)目向 boot 項(xiàng)目合并: git merge --allow-unrelated-histories web;注意,這里需要加 --allow-unrelated-histories 參數(shù),以允許不相干的倉(cāng)庫(kù)進(jìn)行合并。
7.處理代碼沖突,完成合并即可。
2.配置文件的合并于歸整。為了防止同名配置文件沖突,需要把 web 項(xiàng)目的配置文件調(diào)整到一個(gè)文件夾下,這里設(shè)定為 web 目錄。然后,需要把 web 項(xiàng)目的配置文件,讓 boot 可以加載到。這個(gè)調(diào)整相對(duì)簡(jiǎn)單,只需要一個(gè)注解即可 @ImportResource({"classpath:web/spring-cfg.xml"})。
3.調(diào)整完配置文件,接著遇到的問(wèn)題就是上面提到的 Bean 沖突的問(wèn)題。由于兩個(gè)項(xiàng)目都訪問(wèn)相同的數(shù)據(jù)庫(kù), Dao 及 Service 層很多很多類都是同名的。另外,在 web 項(xiàng)目里,Dao 是基于 iBATIS 開發(fā)的,而在 boot 項(xiàng)目里,DAO 是基于 MyBATIS 開發(fā)的。所以,只能給 web 項(xiàng)目的相關(guān)代碼做重命名(嚴(yán)謹(jǐn)一點(diǎn)是給 Spring Bean 的 beanName 做重命名操作)。這又帶來(lái)了新問(wèn)題:原來(lái)的項(xiàng)目里,注入方式是根據(jù)名稱注入的,就需要改動(dòng)大量的代碼,給相關(guān)的 Bean 變量做重命名操作。這無(wú)形中增加了很多的復(fù)雜度和不確定性。
?
經(jīng)過(guò)不斷折騰,這種方式被迫放棄。
?
三、倉(cāng)庫(kù)合并,Spring Boot 父子容器
?
在經(jīng)過(guò)上述方式折騰后,就想到了另外一個(gè)方案:可以考慮使用父子容器的方式來(lái)搞。接著就查到了這篇文章: Context Hierarchy with the Spring Boot Fluent Builder API。感覺(jué)這種方式挺不錯(cuò),就嘗試了一下。
?
1.代碼合并及文件調(diào)整,跟上述步驟類似,這個(gè)后面就不再贅述。
2.按照文章中的介紹,使用父子容器的方式來(lái)加載兩個(gè)項(xiàng)目。代碼如下:
3.原以為,這種方式屬于父子兩個(gè)容器,即使有同名的 Bean 應(yīng)該也沒(méi)有影響。但是,經(jīng)過(guò)實(shí)踐才發(fā)現(xiàn),上面這個(gè)猜測(cè)是錯(cuò)誤的。Spring Boot 在啟動(dòng)的時(shí)候,它背后做了檢查,如果兩個(gè)容器有同名的 Bean,它也會(huì)報(bào)錯(cuò)。也會(huì)帶來(lái)像上述方式那樣的大量重命名。折騰一兩天,最后還是放棄了這種寄予厚望的方式。
package com.diguage.demo.boot; import org.springframework.boot.WebApplicationType; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.ImportResource; /** * @author D瓜哥 · https://www.diguage.com */ public class DemoBootApplication { public static void main(String[] args) { new SpringApplicationBuilder() .parent(BootConfig.class).web(WebApplicationType.NONE) .child(WebConfig.class) // 如果有第三個(gè)項(xiàng)目,可以作為子容器的兄弟容器加載。 // .sibling(SiblingConfig.class) .run(args); } @Configuration @ImportResource({"classpath:spring-cfg.xml"}) @ComponentScan(basePackages = "com.diguage.demo.boot") public static class BootConfig { } @Configuration @ImportResource({"classpath:web/spring-cfg.xml"}) public static class WebConfig { } }
? |
Spring Boot 背后是否做了檢查,這個(gè)是根據(jù)報(bào)錯(cuò)信息的猜測(cè),沒(méi)有翻看代碼,所以這個(gè)猜測(cè)有一定的不確定性。有機(jī)會(huì)翻一下代碼,查看一下具體原因。 |
革命尚未成功,且聽下回分解……
下文:Spring 應(yīng)用合并之路(二):峰回路轉(zhuǎn),柳暗花明?
審核編輯 黃宇
-
spring
+關(guān)注
關(guān)注
0文章
337瀏覽量
14290
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論