Async Datastore API

Async Datastore API 可讓您以平行、無阻斷的方式呼叫資料儲存庫,並在稍後處理要求時擷取這些呼叫的結果。本說明文件提供 Async Datastore API 下列方面的說明:

使用非同步資料儲存庫服務

您可以透過 Async Datastore API 使用 AsyncDatastoreService 介面的方法呼叫資料儲存庫。您可以呼叫 DatastoreServiceFactory 類別的 getAsyncDatastoreService() 類別方法來取得此物件。

import com.google.appengine.api.datastore.AsyncDatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;

// ...
AsyncDatastoreService datastore = DatastoreServiceFactory.getAsyncDatastoreService();

除了大多數方法會立刻傳回您可在稍後封鎖其結果的 Future 外,AsyncDatastoreService 支援的作業和 DatastoreService 一樣。舉例來說,DatastoreService.get() 會傳回實體,但是 AsyncDatastoreService.get() 會傳回 Future<Entity>

// ...

Key key = KeyFactory.createKey("Employee", "Max");
// Async call returns immediately
Future<Entity> entityFuture = datastore.get(key);

// Do other stuff while the get operation runs in the background...

// Blocks if the get operation has not finished, otherwise returns instantly
Entity entity = entityFuture.get();

注意:在您呼叫 get() 方法之前,不會擲回例外狀況。呼叫這個方法讓您可以驗證非同步作業是否已成功完成。

如果您有 AsyncDatastoreService,但是需要同步執行作業,請叫用適當的 AsyncDatastoreService 方法,然後再立即封鎖結果:

// ...

Entity entity = new Employee("Employee", "Alfred");
// ... populate entity properties

// Make a sync call via the async interface
Key key = datastore.put(key).get();

使用非同步交易

Async Datastore API 呼叫和同步呼叫一樣,都可在交易中使用。以下函式可調整 Employee 的薪資,並在與 Employee 相同的實體群組中寫入額外的 SalaryAdjustment 實體,所有作業都在單一交易下執行。

void giveRaise(AsyncDatastoreService datastore, Key employeeKey, long raiseAmount)
        throws Exception {
    Future<Transaction> txn = datastore.beginTransaction();

    // Async call to lookup the Employee entity
    Future<Entity> employeeEntityFuture = datastore.get(employeeKey);

    // Create and put a SalaryAdjustment entity in parallel with the lookup
    Entity adjustmentEntity = new Entity("SalaryAdjustment", employeeKey);
    adjustmentEntity.setProperty("adjustment", raiseAmount);
    adjustmentEntity.setProperty("adjustmentDate", new Date());
    datastore.put(adjustmentEntity);

    // Fetch the result of our lookup to make the salary adjustment
    Entity employeeEntity = employeeEntityFuture.get();
    long salary = (Long) employeeEntity.getProperty("salary");
    employeeEntity.setProperty("salary", salary + raiseAmount);

    // Re-put the Employee entity with the adjusted salary.
    datastore.put(employeeEntity);
    txn.get().commit(); // could also call txn.get().commitAsync() here
}

以上範例說明了「不使用交易的非同步呼叫」與「使用交易的非同步呼叫」之間的主要差異。若您不使用交易,則確保個別非同步呼叫已完成的唯一方法,便是擷取發出呼叫時 Future 的傳回值。當您使用交易時,呼叫 Transaction.commit() 即會封鎖自交易開始至修訂交易前發出之所有非同步呼叫的結果。

因此,在上述範例中,即使插入 SalaryAdjustment 實體的非同步呼叫在我們呼叫 commit() 時仍未解決,但除非完成插入,否則不會進行修訂。同樣地,如果您選擇呼叫 commitAsync() 而非 commit(),則會在 commitAsync() 傳回的 Future 上封鎖叫用 get(),直到未解決的所有非同步呼叫均已完成為止。

注意:交易與特定執行緒相關聯,而非 DatastoreServiceAsyncDatastoreService 的特定執行個體。這表示如果您使用 DatastoreService 初始化交易,而且使用 AsyncDatastoreService 執行非同步呼叫,則非同步呼叫可參與該交易。或者簡言之,DatastoreService.getCurrentTransaction()AsyncDatastoreService.getCurrentTransaction() 一律會傳回相同的 Transaction

使用 Futures

Future Javadoc 已針對順利使用 Async Datastore API 傳回的 Future,說明您必須知道的大部分事項,但您仍必須注意下列幾項 App Engine 的相關事宜:

非同步查詢

我們目前不開放對明確的非同步 API 進行查詢。不過,當您叫用 PreparedQuery.asIterable()PreparedQuery.asIterator()PreparedQuery.asList(FetchOptions fetchOptions) 時,DatastoreService 和 AsyncDatastoreService 都會立即傳回並且非同步預先擷取結果。這會讓應用程式在擷取查詢結果時還能平行執行工作。

// ...

Query q1 = new Query("Salesperson");
q1.setFilter(new FilterPredicate("dateOfHire", FilterOperator.LESS_THAN, oneMonthAgo));

// Returns instantly, query is executing in the background.
Iterable<Entity> recentHires = datastore.prepare(q1).asIterable();

Query q2 = new Query("Customer");
q2.setFilter(new FilterPredicate("lastContact", FilterOperator.GREATER_THAN, oneYearAgo));

// Also returns instantly, query is executing in the background.
Iterable<Entity> needsFollowup = datastore.prepare(q2).asIterable();

schedulePhoneCall(recentHires, needsFollowUp);

非同步資料儲存庫呼叫的使用時機

DatastoreService 介面顯示的這些項目為同步作業。舉例來說,當您呼叫 DatastoreService.get() 時,程式碼在呼叫資料儲存庫完成前都會維持封鎖。如果應用程式只需將 get() 的結果轉譯成 HTML,在呼叫完成之前封鎖程式碼是相當合理的做法。不過,如果應用程式需要 get() 的結果以及 Query 的結果才能轉譯回應,且 get()Query 之間不具有任何資料相依性,那麼等待 get() 完成才啟動 Query 是相當浪費時間的做法。下列提供可透過非同步 API 提升效能的程式碼範例:

DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
Key empKey = KeyFactory.createKey("Employee", "Max");

// Read employee data from the Datastore
Entity employee = datastore.get(empKey); // Blocking for no good reason!

// Fetch payment history
Query query = new Query("PaymentHistory");
PreparedQuery pq = datastore.prepare(query);
List<Entity> result = pq.asList(FetchOptions.Builder.withLimit(10));
renderHtml(employee, result);

您可以改用 AsyncDatastoreService 以非同步方式執行呼叫,如此一來,就無需等待 get() 完成:

AsyncDatastoreService datastore = DatastoreServiceFactory.getAsyncDatastoreService();
Key empKey = KeyFactory.createKey("Employee", "Max");

// Read employee data from the Datastore
Future<Entity> employeeFuture = datastore.get(empKey); // Returns immediately!

// Fetch payment history for the employee
Query query = new Query("PaymentHistory", empKey);
PreparedQuery pq = datastore.prepare(query);

// Run the query while the employee is being fetched
List<Entity> result = pq.asList(FetchOptions.Builder.withLimit(10));
// Implicitly performs query asynchronously
Entity employee = employeeFuture.get(); // Blocking!
renderHtml(employee, result); 

這個程式碼的同步版和非同步版的 CPU 使用量相差不多 (畢竟這兩個版本執行的工作量相同),但由於非同步版的程式碼可以讓兩個資料儲存庫作業並行執行,所以非同步版的延遲時間較短。一般來說,如果您需要執行多個沒有任何資料相依性的資料儲存庫作業,使用 AsyncDatastoreService 可以明顯改善延遲情形。

本頁內容對您是否有任何幫助?請提供意見:

傳送您對下列選項的寶貴意見...

這個網頁
Java 適用的 App Engine 標準環境