您的應用程序需要可擴展。 這意味著它需要在集群中運行,而狀態是最難分配的。 如果您將狀態存儲的位置最小化,那么群集的復雜性也將最小化。 但是狀態應該存在,在這里擁有狀態就可以了:
- 數據庫–無論是SQL,NoSQL還是搜索引擎,它都是存儲狀態的主要內容。 應該是支持集群的東西,或者是處理來自多個其他“代碼”服務器的請求的大型專用機器。 該代碼與數據庫進行通信,但是代碼本身不會為一個以上的客戶請求存儲任何內容。
- 緩存-緩存相對容易分發(基本上是鍵值)。 有許多現成的解決方案,例如EhCache和memcached。 因此,您可以配置緩存并將結果存儲在內存中,而不是計算結果或根據每個請求從數據庫獲取結果。 但是,代碼仍然沒有存儲任何內容,它只是填充并查詢緩存。
- HTTP會話–在Web組件(控制器,托管Bean,無論您如何稱呼)中。 它與緩存非常相似,盡管它具有不同的目的–允許識別同一用戶的后續操作(http本身是無狀態的)。 但是,由于您的代碼在多臺計算機上運行,??因此負載均衡器可能并不總是將后續請求發送到同一服務器。 因此,會話也應該在所有服務器之間復制。 幸運的是,大多數容器都內置了該選項,因此您只需添加一條配置行。 另外,您可以指示負載均衡器使用“粘性會話”(根據會話cookie來確定發送請求的服務器),但是它也會將一些狀態管理也移到負載均衡器。 無論您選擇哪種選項,都不要在會話中放入太多數據
- 文件系統–存儲文件時,需要所有計算機都可以訪問它們。 這里有多個選項,包括SAN或使用Amazon S3之類的云存儲服務,可通過API訪問
所有這些都在代碼之外進行管理。 您的代碼僅通過API(會話API,緩存API,JDBC,S3 /文件系統API)使用它們。 如果代碼包含該狀態中的任何一個(作為對象的實例變量),則該應用程序將難以支持(您必須自己管理狀態),并且可伸縮性也會降低。 當然,在少數情況下,如果不將狀態存儲在代碼中就無法走開。 記錄這些,并確保它們不依賴于在集群中工作。
但是,如果將狀態存儲在執行業務邏輯的對象中,那會出錯嗎? 然后,您有兩個選擇:
- 同步對字段的訪問–這會降低性能,因為所有發出請求的用戶都必須排隊等待服務來管理其字段;
- 為每個HTTP請求創建類的新實例,并以某種方式管理實例。 管理這些實例是困難的部分。 人們可能傾向于選擇會話來執行此操作,這意味著會話會變得非常大且難以復制(跨多臺計算機共享大量數據的速度較慢,會話復制必須快速)。 更不用說不必要地增加了內存占用。
這是不做什么的瑣碎示例。 您應該將這些類型的值作為方法參數傳遞,而不是將其存儲在實例中:
class OrderService {double orderPrice;void processOrder(OrderDto order) {for (Entry entry : order.getEntries() {orderPrice += entry.getPrice();}boolean discounts = hasDiscounts(order);}boolean hasDiscounts(OrderDto order) {return order.getEntries().length > 5 && orderPrice > 200;}
}
因此,使您的所有代碼都為無狀態–這將確保至少某種程度的可伸縮性。
參考:我們的JCG合作伙伴 未將State 納入 代碼范圍 ? Bozho的技術博客中的 Bozhidar Bozhanov 。
翻譯自: https://www.javacodegeeks.com/2012/02/state-does-not-belong-in-code.html