搶先一步
VMware 提供培訓和認證,助您加速進步。
瞭解更多今天早些時候,我釋出了 Spring Sync 的第一個里程碑版本。這是一個透過採用基於補丁的交換方式,解決客戶端應用程式和 Spring 後端之間高效通訊的新專案。由於這是一個新專案,我認為現在是時候向您展示 Spring Sync 的功能了。
這裡給出的示例參考了 Spring REST Todos 示例和/或該示例專案中的 Todo
類。
在最低層,Spring Sync 提供了一個庫,用於生成和應用 Java 物件的補丁。Patch
類是該庫的核心,它捕獲可以應用於物件以使其與另一個物件同步的更改。
Patch
類旨在通用,不直接與任何特定的補丁表示形式關聯。也就是說,它受到 JSON Patch 的啟發,Spring Sync 支援將 Patch
例項建立並序列化為 JSON Patch。Spring Sync 的未來版本可能包含對其他補丁表示形式的支援。
建立補丁最簡單的方法是計算兩個 Java 物件之間的差異
Todo original = ...;
Todo modified = ...;
Patch patch = Diff.diff(original, modified);
在這裡,Diff.diff()
方法將比較兩個 Todo
物件,並生成一個描述它們之間差異的 Patch
。
一旦有了 Patch
,可以透過將物件傳遞給 apply()
方法將其應用於物件
Todo patched = patch.apply(original, Todo.class);
請注意,diff()
和 apply()
方法是互逆的。因此,在將補丁應用於原始物件後,這些示例中打了補丁的 Todo
應該與修改後的 Todo
完全相同。
如前所述,Patch
與任何特定的補丁表示形式是解耦的。但 Spring Sync 提供 JsonPatchMaker
作為工具類,用於將 Patch
物件與 Jackson JsonNode
例項進行相互轉換,其中 JsonNode
是一個包含零個或多個操作的 ArrayNode
,符合 JSON Patch 規範。例如,將 Patch
轉換為包含 JSON Patch 的 JsonNode
JsonNode jsonPatchNode = JsonPatchMaker.toJsonNode(patch);
類似地,可以從 JsonNode
建立 Patch
物件,如下所示
Patch patch = JsonPatchMaker.fromJsonNode(jsonPatchNode);
請注意,JsonPatchMaker
是用於將 Patch
物件與 JSON Patch 進行(反)序列化的臨時解決方案。它將在後續版本中被更永久的解決方案取代。
建立補丁需要您擁有物件的“之前”和“之後”例項,以便計算差異。雖然它沒有將它們稱為“之前”和“之後”,但 Neil Fraser 在一篇論文中描述的 差異同步 (Differential Synchronization) 演算法本質上定義了一種控制方式,透過該方式可以在兩個或多個網路節點(可能是客戶端和伺服器,但不一定僅適用於客戶端-伺服器場景)之間建立、共享和應用補丁。
應用差異同步時,每個節點都維護資源的兩個副本
節點可以對其本地資源副本進行任何所需的修改。節點會定期透過比較本地節點與為遠端節點維護的影子副本生成補丁。然後將補丁傳送到遠端節點。一旦傳送了補丁,節點就會將其本地副本複製到影子副本上,假設遠端節點將應用該補丁,因此其對遠端節點資源的理解與本地資源同步。
收到補丁後,節點必須將該補丁應用於它為傳送補丁的節點保留的影子副本以及它自己的本地副本(本地副本本身可能也發生了變化)。
Spring Sync 透過其 DiffSync
類支援差異同步。要建立 DiffSync
,必須為其提供 ShadowStore
和它可以應用補丁的物件型別
ShadowStore shadowStore = new MapBasedShadowStore();
shadowStore.setRemoteNodeId("remoteNode");
DiffSync diffSync = new DiffSync(shadowStore, Todo.class);
一旦有了 DiffSync
,您就可以使用它將 Patch
應用於物件
Todo patched = diffSync.apply(patch, todo);
apply()
方法將補丁應用於給定物件以及該物件的影子副本。如果尚未建立影子副本,它將透過深度克隆給定物件來建立一個。
ShadowStore
是 DiffSync
維護遠端節點影子副本的地方。對於任何給定節點,可能有多個影子儲存,每個遠端節點一個。如示例所示,其 remoteNodeId
屬性用於唯一標識遠端節點。在客戶端-伺服器拓撲中,伺服器可以使用會話 ID 來標識遠端節點。同時,客戶端(可能僅與一箇中心伺服器共享資源)可以使用任何想要的識別符號來標識伺服器節點。
DiffSync
也可以用於從儲存的影子副本建立 Patch
Patch patch = diffSync.diff(todo);
建立補丁時,將從 ShadowStore
中檢索儲存的影子副本並與給定物件進行比較。為了與差異同步流程保持一致,一旦生成補丁,給定物件將被複制到影子副本上。
值得注意的是,DiffSync
使用 Patch
物件,這些物件與任何特定的補丁表示形式是解耦的。因此,DiffSync
本身也與補丁表示形式解耦。
在單個節點上建立和應用補丁意義不大。差異同步真正發揮作用的地方在於,當兩個或多個節點共享和操作同一資源時,並且您需要每個節點(在合理範圍內)保持同步。因此,Spring Sync 還提供了 DiffSyncController
,這是一個處理 HTTP PATCH 請求的 Spring MVC 控制器,將差異同步應用於資源。
配置 DiffSyncController
最簡單的方法是建立一個用 @EnableDifferentialSynchronization
註解並擴充套件 DiffSyncConfigurerAdapter
類的 Spring 配置類
@Configuration
@EnableDifferentialSynchronization
public class DiffSyncConfig extends DiffSyncConfigurerAdapter {
@Autowired
private PagingAndSortingRepository<Todo, Long> repo;
@Override
public void addPersistenceCallbacks(PersistenceCallbackRegistry registry) {
registry.addPersistenceCallback(new JpaPersistenceCallback<Todo>(repo, Todo.class));
}
}
除其他事項外,@EnableDifferentialSynchronization
聲明瞭一個 DiffSyncController
bean,併為其提供了 PersistenceCallbackRegistry
和 ShadowStore
。
PersistenceCallbackRegistry
是一個 PersistenceCallback
物件的登錄檔,DiffSyncController
透過它檢索和持久化它所打補丁的資源。PersistenceCallback
介面使得 DiffSyncController
可以與資源的應用特定持久化選擇解耦。例如,以下是 PersistenceCallback
的一個實現,它使用 Spring Data 的 CrudRepository
來持久化 Todo
物件
package org.springframework.sync.diffsync.web;
import java.util.List;
import org.springframework.data.repository.CrudRepository;
import org.springframework.sync.diffsync.PersistenceCallback;
class JpaPersistenceCallback<T> implements PersistenceCallback<T> {
private final CrudRepository<T, Long> repo;
private Class<T> entityType;
public JpaPersistenceCallback(CrudRepository<T, Long> repo, Class<T> entityType) {
this.repo = repo;
this.entityType = entityType;
}
@Override
public List<T> findAll() {
return (List<T>) repo.findAll();
}
@Override
public T findOne(String id) {
return repo.findOne(Long.valueOf(id));
}
@Override
public void persistChange(T itemToSave) {
repo.save(itemToSave);
}
@Override
public void persistChanges(List<T> itemsToSave, List<T> itemsToDelete) {
repo.save(itemsToSave);
repo.delete(itemsToDelete);
}
@Override
public Class<T> getEntityType() {
return entityType;
}
}
對於提供給 DiffSyncController
的 ShadowStore
,預設將是 MapBasedShadowStore
。但您可以覆蓋 DiffSyncConfigurerAdapter
中的 getShadowStore()
方法來指定不同的影子儲存實現。例如,您可以像這樣配置基於 Redis 的影子儲存
@Autowired
private RedisOperations<String, Object> redisTemplate;
@Override
public ShadowStore getShadowStore() {
return new RedisShadowStore(redisTemplate);
}
無論您選擇哪種 ShadowStore
實現,都將宣告一個會話範圍的 bean,確保每個客戶端都將擁有自己的影子儲存例項。
由於它處理 PATCH 請求,DiffSyncController
將應用差異同步流程的一個週期
就像 Patch
和 DiffSync
一樣,DiffSyncController
與任何特定的補丁格式是解耦的。然而,Spring Sync 確實提供了 JsonPatchHttpMessageConverter
,這樣在內容型別為 "application/json-patch+json" 的情況下,DiffSyncController
可以接收和響應 JSON Patch 格式的補丁。
正如您在此處所見,Spring Sync 旨在提供一種在客戶端和伺服器(或任何共享資源的節點集)之間進行高效通訊和同步的方法。它提供了用於生成和應用補丁的低階支援,以及用於差異同步的高階支援。雖然它支援 JSON Patch,但它很大程度上獨立於任何特定的補丁格式。
這僅僅是個開始。除其他事項外,我們正在考慮...
DiffSyncController
基於 HTTP 的差異同步,實現全雙工補丁通訊。請關注該專案並告訴我們您的想法。歡迎隨時 提交錯誤報告和改進建議,我們也非常歡迎您 分叉程式碼 並提交拉取請求。
如果您想了解更多關於 Spring Sync 的資訊,請檢視以下資源