
gRPC - 客戶端呼叫
gRPC 客戶端支援兩種型別的客戶端呼叫,即客戶端如何呼叫伺服器。以下是兩種方式:
阻塞式客戶端呼叫
非同步客戶端呼叫
在本章中,我們將逐一檢視它們。
阻塞式客戶端呼叫
gRPC 支援阻塞式客戶端呼叫。這意味著,一旦客戶端向服務發出呼叫,客戶端將不會繼續執行其餘程式碼,直到從伺服器收到響應。請注意,對於單向呼叫和伺服器流式呼叫,阻塞式客戶端呼叫是可能的。
請注意,對於單向呼叫和伺服器流式呼叫,阻塞式客戶端呼叫是可能的。
這是一個單向阻塞式客戶端呼叫的示例。
示例
package com.tp.bookstore; import io.grpc.Channel; import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; import io.grpc.StatusRuntimeException; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; import com.tp.bookstore.BookStoreOuterClass.Book; import com.tp.bookstore.BookStoreOuterClass.BookSearch; import com.tp.greeting.GreeterGrpc; import com.tp.greeting.Greeting.ServerOutput; import com.tp.greeting.Greeting.ClientInput; public class BookStoreClientUnaryBlocking { private static final Logger logger = Logger.getLogger(BookStoreClientUnaryBlocking.class.getName()); private final BookStoreGrpc.BookStoreBlockingStubblockingStub; public BookStoreClientUnaryBlocking(Channel channel) { blockingStub = BookStoreGrpc.newBlockingStub(channel); } public void getBook(String bookName) { logger.info("Querying for book with title: " + bookName); BookSearch request = BookSearch.newBuilder().setName(bookName).build(); Book response; try { response = blockingStub.first(request); } catch (StatusRuntimeException e) { logger.log(Level.WARNING, "RPC failed: {0}", e.getStatus()); return; } logger.info("Got following book from server: " + response); } public static void main(String[] args) throws Exception { String bookName = args[0]; String serverAddress = "localhost:50051"; ManagedChannel channel = ManagedChannelBuilder.forTarget(serverAddress) .usePlaintext() .build(); try { BookStoreClientUnaryBlocking client = new BookStoreClientUnaryBlocking(channel); client.getBook(bookName); } finally { channel.shutdownNow().awaitTermination(5, TimeUnit.SECONDS); } } }
在上面的示例中,我們有:
public BookStoreClientUnaryBlocking(Channel channel) { blockingStub = BookStoreGrpc.newBlockingStub(channel); }
這意味著我們將使用阻塞式 RPC 呼叫。
然後,我們有:
BookSearch request = BookSearch.newBuilder().setName(bookName).build(); Book response; response = blockingStub.first(request);
在這裡,我們使用 **blockingStub** 呼叫 RPC **method first()** 來獲取圖書詳情。
類似地,對於伺服器流式,我們可以使用阻塞式存根:
logger.info("Querying for book with author: " + author); BookSearch request = BookSearch.newBuilder().setAuthor(author).build(); Iterator<Book> response; try { response = blockingStub.searchByAuthor(request); while(response.hasNext()) { logger.info("Found book: " + response.next()); }
我們在這裡呼叫 RPC 方法 **searchByAuthor** 方法,並迭代響應,直到伺服器流結束。
非阻塞式客戶端呼叫
gRPC 支援非阻塞式客戶端呼叫。這意味著當客戶端向服務發出呼叫時,它不需要等待伺服器響應。為了處理伺服器響應,客戶端可以簡單地傳入觀察者,該觀察者決定在收到響應時該做什麼。請注意,對於單向呼叫和流式呼叫,非阻塞式客戶端呼叫都是可能的。但是,我們將特別關注伺服器流式呼叫的情況,以便將其與阻塞式呼叫進行比較。
請注意,對於單向呼叫和流式呼叫,非阻塞式客戶端呼叫都是可能的。但是,我們將特別關注伺服器流式呼叫的情況,以便將其與阻塞式呼叫進行比較。
這是一個伺服器流式非阻塞式客戶端呼叫的示例
示例
package com.tp.bookstore; import io.grpc.Channel; import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; import io.grpc.StatusRuntimeException; import io.grpc.stub.StreamObserver; import java.util.Iterator; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; import com.tp.bookstore.BookStoreOuterClass.Book; import com.tp.bookstore.BookStoreOuterClass.BookSearch; import com.tp.greeting.GreeterGrpc; import com.tp.greeting.Greeting.ServerOutput; import com.tp.greeting.Greeting.ClientInput; public class BookStoreClientServerStreamingNonBlocking { private static final Logger logger = Logger.getLogger(BookStoreClientServerStreamingNonBlocking.class.getName()); private final BookStoreGrpc.BookStoreStub nonBlockingStub; public BookStoreClientServerStreamingNonBlocking(Channelchannel) { nonBlockingStub = BookStoreGrpc.newStub(channel); } public StreamObserver<Book> getServerResponseObserver(){ StreamObserver<Book> observer = new StreamObserver<Book>(){ @Override public void onNext(Book book) { logger.info("Server returned following book: " +book); } @Override public void onError(Throwable t) { logger.info("Error while reading response fromServer: " + t); } @Override public void onCompleted() { logger.info("Server returned following book: " + book); } }; return observer; } public void getBook(String author) { logger.info("Querying for book with author: " + author); BookSearch request = BookSearch.newBuilder().setAuthor(author).build(); try { nonBlockingStub.searchByAuthor(request,getServerResponseObserver()); } catch (StatusRuntimeException e) { logger.log(Level.WARNING, "RPC failed: {0}",e.getStatus()); return; } } public static void main(String[] args) throws Exception { String authorName = args[0]; String serverAddress = "localhost:50051"; ManagedChannel channel =ManagedChannelBuilder.forTarget(serverAddress) .usePlaintext() .build(); try { BookStoreClientServerStreamingNonBlocking client = new BookStoreClientServerStreamingNonBlocking(channel); client.getBook(authorName); } finally { channel.shutdownNow().awaitTermination(5, TimeUnit.SECONDS); } } }
正如我們在上面的示例中看到的:
public BookStoreClientUnaryNonBlocking(Channel channel) { nonBlockingStub = BookStoreGrpc.newStub(channel); }
它定義了存根是非阻塞式的。類似地,以下程式碼用於處理我們從伺服器接收到的響應。一旦伺服器傳送響應,我們就記錄輸出。
public StreamObserver<Book> getServerResponseObserver(){ StreamObserver<Book> observer = new StreamObserver<Book>(){ .... .... return observer; }
以下 gRPC 呼叫是非阻塞式呼叫。
logger.info("Querying for book with author: " + author); BookSearch request = BookSearch.newBuilder().setAuthor(author).build(); try { nonBlockingStub.searchByAuthor(request, getServerResponseObserver()); }
這就是我們確保客戶端不需要等到伺服器完成 **searchByAuthor** 執行的方式。這將由流觀察者物件在伺服器返回 Book 物件時直接處理。