領先一步
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 在一篇論文中描述的 差異同步 演算法並未將它們稱為「之前」和「之後」,但它本質上定義了一種控制器方式,藉此可以在兩個或多個網路節點之間(可能是用戶端和伺服器,但不一定僅適用於用戶端-伺服器情境)建立、共用和套用修補程式。
在套用差異同步時,每個節點維護資源的兩個副本
節點可以對其資源的本機副本進行任何需要的變更。節點會定期透過比較本機節點與其維護的遠端節點的陰影副本來產生修補程式。然後,它將修補程式傳送到遠端節點。一旦修補程式傳送出去,節點就會將其本機副本複製到陰影副本上,假設遠端節點將套用修補程式,因此它對遠端節點資源的理解與本機資源同步。
在收到修補程式後,節點必須將修補程式套用到它為傳送修補程式的節點保留的陰影副本,以及它自己的本機副本(可能本身已進行變更)。
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
物件一起運作,而 Patch
物件與任何特定的修補程式表示形式解耦。因此,DiffSync
本身也與修補程式表示形式解耦。
在單一節點上建立和套用修補程式有點毫無意義。當兩個或多個節點共用和操作相同的資源,並且您需要每個節點保持同步(在合理的範圍內)時,差異同步才會真正發揮作用。因此,Spring Sync 也提供了 DiffSyncController
,這是一個 Spring MVC 控制器,用於處理 HTTP PATCH 請求,將差異同步套用到資源。
配置 DiffSyncController
最簡單的方法是建立一個使用 @EnableDifferentialSynchronization
註解的 Spring 配置類別,並擴展 DiffSyncConfigurerAdapter
類別
@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
,以便 DiffSyncController
可以接收和響應 JSON Patch 格式的修補程式,前提是內容類型為 "application/json-patch+json"。
正如您在這裡看到的,Spring Sync 旨在提供一種用戶端和伺服器(或任何共用資源的節點集合)之間有效通訊和同步的方法。它為產生和套用修補程式提供低階支援,並為使用差異同步提供高階支援。儘管它附帶了對 JSON Patch 的支援,但在很大程度上它獨立於任何特定的修補程式格式。
這僅僅是開始。在其他事項中,我們正在尋求...
DiffSyncController
基於 HTTP 的差異同步,以實現全雙工修補程式通訊。請密切關注該專案,並告訴我們您的想法。歡迎隨時提交錯誤報告和改進建議,我們當然歡迎您fork 程式碼並提交 pull request。
如果您想閱讀更多關於 Spring Sync 的資訊,請查看以下資源