做者:小傅哥
博客:https://bugstack.cn - 原創系列專題文章
html
沉澱、分享、成長,讓本身和他人都能有所收穫!😄
場地和場景的重要性
java
射擊🏹須要去靶場學習、滑雪🏂須要去雪場體驗、開車🚗須要能上路實踐,而編程開發除了能完成產品的功能流程,還須要保證系統的可靠性能。就像你能聽到的一些系統監控指標;QPS
、TPS
、TP99
、TP999
、可用率
、響應時長
等等,而這些指標的總和評估就是一個系統的健康度。但若是你幾乎沒有聽到這樣的技術術語,也沒接觸過相似高併發場景,那麼就很像駕駛證的科目1考了100分,但不能上路。沒有這樣的技術場景給你訓練,讓你不斷的體會系統的脾氣秉性,即使你有再多的想法都無法實現。因此,若是真的想學習必定要去一個有實操的場景,下水試試才能學會狗刨。編程
你的視覺盲區有多大
設計模式
一樣一本書、一樣一條路、一樣一座城,你真的覺得生活有選擇嗎?有時候不少選項都是擺設,給你多少次機會你都選的如出一轍。這不是你選不選而是你的認知範圍決定了你下一秒作的事情,另外的一個下一秒又決定了再下一個下一秒。就像管中窺豹同樣,20%的面積在你視覺裏都是黑色的,甚至就老是忽略看不到,而這看不到的20%就是生命中的時運!但,人能夠學習,能夠成長,能夠脫胎換骨,能夠努力付出,經過一次次的蛻變而看到剩下的20%!緩存
沒有設計圖紙你敢蓋樓嗎
微信
編程開發中最好的什麼,是設計。運用架構思惟、經驗心得、才華靈感
,構建出最佳的系統。真正的研發會把本身寫的代碼當作做品來欣賞,你說這是一份工做,但在這樣的人眼裏這可不是一份工做,而是一份工匠精神。就像可能時而你也會爲本身由於一個niubility
的設計而豪邁萬丈,爲能上線一個扛得住每秒200萬訪問量的系統會神采飛揚。這樣的自豪感就是一次次壘磚同樣墊高腳底,不斷的把你的視野提升,讓你能看到上層設計也能知曉根基建設。能夠把控全局,也能夠治理細節。這一份份知識的沉澱,來幫助你繪製出一張系統架構藍圖。架構
bugstack蟲洞棧
,回覆源碼下載
獲取(打開獲取的連接,找到序號18)工程 | 描述 |
---|---|
itstack-demo-design-13-00 | 場景模擬工程;模擬一個上線流程審批的接口。 |
itstack-demo-design-13-01 | 使用一坨代碼實現業務需求 |
itstack-demo-design-13-02 | 經過設計模式優化改造代碼,產生對比性從而學習 |
擊鼓傳雷,看上圖你是否想起周星馳有一個電影,你們坐在海邊圍成一個圈,拿着一個點燃的炸彈,互相傳遞。併發
責任鏈模式的核心是解決一組服務中的前後執行處理關係,就有點像你沒錢花了,須要家庭財務支出審批,10塊錢如下找閨女審批,100塊錢先閨女審批在媳婦審批。你能夠理解想象成當你要跳槽的時候被安排的明明白白的被各個領導簽字放行。app
在本案例中咱們模擬在618大促期間的業務系統上線審批流程場景高併發
像是這些一線電商類的互聯網公司,阿里、京東、拼多多等,在618期間都會作一些運營活動場景以及提供的擴容備戰,就像過年期間百度的紅包同樣。可是全部開發的這些系統都須要陸續的上線,由於臨近618有時候也有一些緊急的調整的須要上線,但爲了保障線上系統的穩定性是儘量的減小上線的,也會相應的加強審批力度。就像一級響應、二級響應同樣。
而這審批的過程在隨着特定時間點會增長不一樣級別的負責人加入,每一個人就像責任鏈模式中的每個核心點。對於研發小夥伴並不須要關心具體的審批流程處理細節,只須要知道這個上線更嚴格,級別也更高,但對於研發人員來講一樣是點擊相同的提審按鈕,等待審覈。
接下來咱們就模擬這樣一個業務訴求場景,使用責任鏈的設計模式來實現此功能。
itstack-demo-design-13-00 └── src └── main └── java └── org.itstack.demo.design └── AuthService.java
public class AuthService { private static Map<String, Date> authMap = new ConcurrentHashMap<String, Date>(); public static Date queryAuthInfo(String uId, String orderId) { return authMap.get(uId.concat(orderId)); } public static void auth(String uId, String orderId) { authMap.put(uId.concat(orderId), new Date()); } }
queryAuthInfo
)、另一個是處理審覈(auth
)。這裏咱們先使用最直接的方式來實現功能
按照咱們的需求審批流程,日常系統上線只須要三級負責人審批就能夠,可是到了618大促時間點,就須要由二級負責以及一級負責人一塊兒加入審批系統上線流程。在這裏咱們使用很是直接的if判斷方式來實現這樣的需求。
itstack-demo-design-13-01 └── src └── main └── java └── org.itstack.demo.design └── AuthController.java
public class AuthController { private SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");// 時間格式化 public AuthInfo doAuth(String uId, String orderId, Date authDate) throws ParseException { // 三級審批 Date date = AuthService.queryAuthInfo("1000013", orderId); if (null == date) return new AuthInfo("0001", "單號:", orderId, " 狀態:待三級審批負責人 ", "王工"); // 二級審批 if (authDate.after(f.parse("2020-06-01 00:00:00")) && authDate.before(f.parse("2020-06-25 23:59:59"))) { date = AuthService.queryAuthInfo("1000012", orderId); if (null == date) return new AuthInfo("0001", "單號:", orderId, " 狀態:待二級審批負責人 ", "張經理"); } // 一級審批 if (authDate.after(f.parse("2020-06-11 00:00:00")) && authDate.before(f.parse("2020-06-20 23:59:59"))) { date = AuthService.queryAuthInfo("1000011", orderId); if (null == date) return new AuthInfo("0001", "單號:", orderId, " 狀態:待一級審批負責人 ", "段總"); } return new AuthInfo("0001", "單號:", orderId, " 狀態:審批完成"); } }
@Test public void test_AuthController() throws ParseException { AuthController authController = new AuthController(); // 模擬三級負責人審批 logger.info("測試結果:{}", JSON.toJSONString(authController.doAuth("小傅哥", "1000998004813441", new Date()))); logger.info("測試結果:{}", "模擬三級負責人審批,王工"); AuthService.auth("1000013", "1000998004813441"); // 模擬二級負責人審批 logger.info("測試結果:{}", JSON.toJSONString(authController.doAuth("小傅哥", "1000998004813441", new Date()))); logger.info("測試結果:{}", "模擬二級負責人審批,張經理"); AuthService.auth("1000012", "1000998004813441"); // 模擬一級負責人審批 logger.info("測試結果:{}", JSON.toJSONString(authController.doAuth("小傅哥", "1000998004813441", new Date()))); logger.info("測試結果:{}", "模擬一級負責人審批,段總"); AuthService.auth("1000011", "1000998004813441"); logger.info("測試結果:{}", "審批完成"); }
authController.doAuth
,是查看審批的流程節點、AuthService.auth
,是審批方法用於操做節點流程狀態。23:25:00.363 [main] INFO org.itstack.demo.design.test.ApiTest - 測試結果:{"code":"0001","info":"單號:1000998004813441 狀態:待三級審批負責人 王工"} 23:25:00.366 [main] INFO org.itstack.demo.design.test.ApiTest - 測試結果:模擬三級負責人審批,王工 23:25:00.367 [main] INFO org.itstack.demo.design.test.ApiTest - 測試結果:{"code":"0001","info":"單號:1000998004813441 狀態:待二級審批負責人 張經理"} 23:25:00.367 [main] INFO org.itstack.demo.design.test.ApiTest - 測試結果:模擬二級負責人審批,張經理 23:25:00.368 [main] INFO org.itstack.demo.design.test.ApiTest - 測試結果:{"code":"0001","info":"單號:1000998004813441 狀態:待一級審批負責人 段總"} 23:25:00.368 [main] INFO org.itstack.demo.design.test.ApiTest - 測試結果:模擬一級負責人審批,段總 23:25:00.368 [main] INFO org.itstack.demo.design.test.ApiTest - 測試結果:審批完成 Process finished with exit code 0
接下來使用裝飾器模式來進行代碼優化,也算是一次很小的重構。
責任鏈模式可讓各個服務模塊更加清晰,而每個模塊間能夠經過next
的方式進行獲取。而每個next
是由繼承的統一抽象類實現的。最終全部類的職責能夠動態的進行編排使用,編排的過程能夠作成可配置化。
itstack-demo-design-13-02 └── src └── main └── java └── org.itstack.demo.design ├── impl │ ├── Level1AuthLink.java │ ├── Level2AuthLink.java │ └── Level3AuthLink.java ├── AuthInfo.java └── AuthLink.java
責任鏈模式模型結構
AuthLink
的不一樣規則,再進行責任編排模擬出一條鏈路。這個鏈路就是業務中的責任鏈。public class AuthInfo { private String code; private String info = ""; public AuthInfo(String code, String ...infos) { this.code = code; for (String str:infos){ this.info = this.info.concat(str); } } // ...get/set }
public abstract class AuthLink { protected Logger logger = LoggerFactory.getLogger(AuthLink.class); protected SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");// 時間格式化 protected String levelUserId; // 級別人員ID protected String levelUserName; // 級別人員姓名 private AuthLink next; // 責任鏈 public AuthLink(String levelUserId, String levelUserName) { this.levelUserId = levelUserId; this.levelUserName = levelUserName; } public AuthLink next() { return next; } public AuthLink appendNext(AuthLink next) { this.next = next; return this; } public abstract AuthInfo doAuth(String uId, String orderId, Date authDate); }
AuthLink next
,重點在於能夠經過next
方式獲取下一個鏈路須要處理的節點。levelUserId
、levelUserName
,是責任鏈中的公用信息,標記每個審覈節點的人員信息。abstract AuthInfo doAuth
,這是每個實現者必須實現的類,不一樣的審覈級別處理不一樣的業務。Level1AuthLink
public class Level1AuthLink extends AuthLink { public Level1AuthLink(String levelUserId, String levelUserName) { super(levelUserId, levelUserName); } public AuthInfo doAuth(String uId, String orderId, Date authDate) { Date date = AuthService.queryAuthInfo(levelUserId, orderId); if (null == date) { return new AuthInfo("0001", "單號:", orderId, " 狀態:待一級審批負責人 ", levelUserName); } AuthLink next = super.next(); if (null == next) { return new AuthInfo("0000", "單號:", orderId, " 狀態:一級審批完成負責人", " 時間:", f.format(date), " 審批人:", levelUserName); } return next.doAuth(uId, orderId, authDate); } }
Level2AuthLink
public class Level2AuthLink extends AuthLink { private Date beginDate = f.parse("2020-06-11 00:00:00"); private Date endDate = f.parse("2020-06-20 23:59:59"); public Level2AuthLink(String levelUserId, String levelUserName) throws ParseException { super(levelUserId, levelUserName); } public AuthInfo doAuth(String uId, String orderId, Date authDate) { Date date = AuthService.queryAuthInfo(levelUserId, orderId); if (null == date) { return new AuthInfo("0001", "單號:", orderId, " 狀態:待二級審批負責人 ", levelUserName); } AuthLink next = super.next(); if (null == next) { return new AuthInfo("0000", "單號:", orderId, " 狀態:二級審批完成負責人", " 時間:", f.format(date), " 審批人:", levelUserName); } if (authDate.before(beginDate) || authDate.after(endDate)) { return new AuthInfo("0000", "單號:", orderId, " 狀態:二級審批完成負責人", " 時間:", f.format(date), " 審批人:", levelUserName); } return next.doAuth(uId, orderId, authDate); } }
Level3AuthLink
public class Level3AuthLink extends AuthLink { private Date beginDate = f.parse("2020-06-01 00:00:00"); private Date endDate = f.parse("2020-06-25 23:59:59"); public Level3AuthLink(String levelUserId, String levelUserName) throws ParseException { super(levelUserId, levelUserName); } public AuthInfo doAuth(String uId, String orderId, Date authDate) { Date date = AuthService.queryAuthInfo(levelUserId, orderId); if (null == date) { return new AuthInfo("0001", "單號:", orderId, " 狀態:待三級審批負責人 ", levelUserName); } AuthLink next = super.next(); if (null == next) { return new AuthInfo("0000", "單號:", orderId, " 狀態:三級審批負責人完成", " 時間:", f.format(date), " 審批人:", levelUserName); } if (authDate.before(beginDate) || authDate.after(endDate)) { return new AuthInfo("0000", "單號:", orderId, " 狀態:三級審批負責人完成", " 時間:", f.format(date), " 審批人:", levelUserName); } return next.doAuth(uId, orderId, authDate); } }
Level1AuthLink
、Level2AuthLink
、Level3AuthLink
,實現了不一樣的審覈級別處理的簡單邏輯。super.next();
,若是不存在下一個節點,則直接返回結果。next.doAuth(uId, orderId, authDate);
,有點像遞歸調用。@Test public void test_AuthLink() throws ParseException { AuthLink authLink = new Level3AuthLink("1000013", "王工") .appendNext(new Level2AuthLink("1000012", "張經理") .appendNext(new Level1AuthLink("1000011", "段總"))); logger.info("測試結果:{}", JSON.toJSONString(authLink.doAuth("小傅哥", "1000998004813441", new Date()))); // 模擬三級負責人審批 AuthService.auth("1000013", "1000998004813441"); logger.info("測試結果:{}", "模擬三級負責人審批,王工"); logger.info("測試結果:{}", JSON.toJSONString(authLink.doAuth("小傅哥", "1000998004813441", new Date()))); // 模擬二級負責人審批 AuthService.auth("1000012", "1000998004813441"); logger.info("測試結果:{}", "模擬二級負責人審批,張經理"); logger.info("測試結果:{}", JSON.toJSONString(authLink.doAuth("小傅哥", "1000998004813441", new Date()))); // 模擬一級負責人審批 AuthService.auth("1000011", "1000998004813441"); logger.info("測試結果:{}", "模擬一級負責人審批,段總"); logger.info("測試結果:{}", JSON.toJSONString(authLink.doAuth("小傅哥", "1000998004813441", new Date()))); }
AuthLink authLink = new Level3AuthLink("1000013", "王工") .appendNext(new Level2AuthLink("1000012", "張經理") .appendNext(new Level1AuthLink("1000011", "段總")));
經過把不一樣的責任節點進行組裝,構成一條完整業務的責任鏈。authLink.doAuth(...)
,經過返回結果對數據進行三、二、1級負責人審覈,直至最後審覈所有完成。23:49:46.585 [main] INFO org.itstack.demo.design.test.ApiTest - 測試結果:{"code":"0001","info":"單號:1000998004813441 狀態:待三級審批負責人 王工"} 23:49:46.590 [main] INFO org.itstack.demo.design.test.ApiTest - 測試結果:模擬三級負責人審批,王工 23:49:46.590 [main] INFO org.itstack.demo.design.test.ApiTest - 測試結果:{"code":"0001","info":"單號:1000998004813441 狀態:待二級審批負責人 張經理"} 23:49:46.590 [main] INFO org.itstack.demo.design.test.ApiTest - 測試結果:模擬二級負責人審批,張經理 23:49:46.590 [main] INFO org.itstack.demo.design.test.ApiTest - 測試結果:{"code":"0001","info":"單號:1000998004813441 狀態:待一級審批負責人 段總"} 23:49:46.590 [main] INFO org.itstack.demo.design.test.ApiTest - 測試結果:模擬一級負責人審批,段總 23:49:46.590 [main] INFO org.itstack.demo.design.test.ApiTest - 測試結果:{"code":"0000","info":"單號:1000998004813441 狀態:一級審批完成負責人 時間:2020-06-18 23:49:46 審批人:段總"} Process finished with exit code 0