原生grpc使用第二篇内容:流式服务,这种讯用方式非常适合大流量或长尾服务的场景。话不多说,见下面代码例子。
syntax = "proto3";
import "google/protobuf/wrappers.proto";
package ecommerce;
service OrderManagement {
rpc addOrder(Order) returns (google.protobuf.StringValue);
rpc getOrder(google.protobuf.StringValue) returns (Order);
rpc searchOrders(google.protobuf.StringValue) returns (stream Order);
rpc updateOrders(stream Order) returns (google.protobuf.StringValue);
rpc processOrders(stream google.protobuf.StringValue) returns (stream CombinedShipment);
}
message Order {
string id = 1;
repeated string items = 2;
string description = 3;
float price = 4;
string destination = 5;
}
message CombinedShipment {
string id = 1;
string status = 2;
repeated Order ordersList = 3;
}
即上一章节实现的内容,一问一答类似http的机制。
客户端
OrderManagementGrpc.OrderManagementBlockingStub stub = OrderManagementGrpc.newBlockingStub(channel);
OrderManagementGrpc.OrderManagementStub asyncStub = OrderManagementGrpc.newStub(channel);
OrderManagementOuterClass.Order order = OrderManagementOuterClass.Order
.newBuilder()
.setId("101")
.addItems("iPhone XS").addItems("Mac Book Pro")
.setDestination("San Jose, CA")
.setPrice(2300)
.build();
StringValue result = stub.addOrder(order);
服务端
public void addOrder(OrderManagementOuterClass.Order request, StreamObserver responseObserver) {
logger.info("Order Added - ID: " + request.getId() + ", Destination : " + request.getDestination());
orderMap.put(request.getId(), request);
StringValue id = StringValue.newBuilder().setValue("100500").build();
responseObserver.onNext(id);
responseObserver.onCompleted();
// ToDo Handle errors
// responseObserver.onError();
}
@Override
public void searchOrders(StringValue request, StreamObserver responseObserver) {
for (Map.Entry orderEntry : orderMap.entrySet()) {
OrderManagementOuterClass.Order order = orderEntry.getValue();
int itemsCount = order.getItemsCount();
for (int index = 0; index < itemsCount; index++) {
String item = order.getItems(index);
if (item.contains(request.getValue())) {
logger.info("Item found " + item);
responseObserver.onNext(order); //持续写入流,这是一个持续发送的过程
break;
}
}
}
responseObserver.onCompleted(); //发送流结束标记
}
// Search Orders
StringValue searchStr = StringValue.newBuilder().setValue("Google").build();
Iterator matchingOrdersItr;
matchingOrdersItr = stub.searchOrders(searchStr);
while (matchingOrdersItr.hasNext()) {
OrderManagementOuterClass.Order matchingOrder = matchingOrdersItr.next();
logger.info("Search Order Response -> Matching Order - " + matchingOrder.getId());
logger.info(" Order : " + order.getId() + "\n "
+ matchingOrder.toString());
}
// Client Streaming
@Override
public StreamObserver updateOrders(StreamObserver responseObserver) {
return new StreamObserver() {
StringBuilder updatedOrderStrBuilder = new StringBuilder().append("Updated Order IDs : ");
@Override
public void onNext(OrderManagementOuterClass.Order value) {
if (value != null) {
orderMap.put(value.getId(), value);
updatedOrderStrBuilder.append(value.getId()).append(", ");
logger.info("Order ID : " + value.getId() + " - Updated");
}
}
@Override
public void onError(Throwable t) {
logger.info("Order ID update error " + t.getMessage());
}
@Override
public void onCompleted() {
logger.info("Update orders - Completed");
StringValue updatedOrders = StringValue.newBuilder().setValue(updatedOrderStrBuilder.toString()).build();
responseObserver.onNext(updatedOrders);
responseObserver.onCompleted();
}
};
}
private static void invokeOrderUpdate(OrderManagementGrpc.OrderManagementStub asyncStub) {
OrderManagementOuterClass.Order updOrder1 = OrderManagementOuterClass.Order.newBuilder()
.setId("102")
.addItems("Google Pixel 3A").addItems("Google Pixel Book")
.setDestination("Mountain View, CA")
.setPrice(1100)
.build();
OrderManagementOuterClass.Order updOrder2 = OrderManagementOuterClass.Order.newBuilder()
.setId("103")
.addItems("Apple Watch S4").addItems("Mac Book Pro").addItems("iPad Pro")
.setDestination("San Jose, CA")
.setPrice(2800)
.build();
OrderManagementOuterClass.Order updOrder3 = OrderManagementOuterClass.Order.newBuilder()
.setId("104")
.addItems("Google Home Mini").addItems("Google Nest Hub").addItems("iPad Mini")
.setDestination("Mountain View, CA")
.setPrice(2200)
.build();
final CountDownLatch finishLatch = new CountDownLatch(1);
StreamObserver updateOrderResponseObserver = new StreamObserver() {
@Override
public void onNext(StringValue value) {
logger.info("Update Orders Res : " + value.getValue());
}
@Override
public void onError(Throwable t) {
}
@Override
public void onCompleted() {
logger.info("Update orders response completed!");
finishLatch.countDown();
}
};
StreamObserver updateOrderRequestObserver = asyncStub.updateOrders(updateOrderResponseObserver);
updateOrderRequestObserver.onNext(updOrder1);
updateOrderRequestObserver.onNext(updOrder2);
updateOrderRequestObserver.onNext(updOrder3);
updateOrderRequestObserver.onNext(updOrder3);
if (finishLatch.getCount() == 0) {
logger.warning("RPC completed or errored before we finished sending.");
return;
}
updateOrderRequestObserver.onCompleted();
// Receiving happens asynchronously
try {
if (!finishLatch.await(10, TimeUnit.SECONDS)) {
logger.warning("FAILED : Process orders cannot finish within 10 seconds");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public StreamObserver processOrders(StreamObserver responseObserver) {
return new StreamObserver() {
int batchMarker = 0;
@Override
public void onNext(StringValue value) {
logger.info("Order Proc : ID - " + value.getValue());
OrderManagementOuterClass.Order currentOrder = orderMap.get(value.getValue());
if (currentOrder == null) {
logger.info("No order found. ID - " + value.getValue());
return;
}
// Processing an order and increment batch marker to
batchMarker++;
String orderDestination = currentOrder.getDestination();
OrderManagementOuterClass.CombinedShipment existingShipment = combinedShipmentMap.get(orderDestination);
if (existingShipment != null) {
existingShipment = OrderManagementOuterClass.CombinedShipment.newBuilder(existingShipment).addOrdersList(currentOrder).build();
combinedShipmentMap.put(orderDestination, existingShipment);
} else {
OrderManagementOuterClass.CombinedShipment shipment = OrderManagementOuterClass.CombinedShipment.newBuilder().build();
shipment = shipment.newBuilderForType()
.addOrdersList(currentOrder)
.setId("CMB-" + new Random().nextInt(1000)+ ":" + currentOrder.getDestination())
.setStatus("Processed!")
.build();
combinedShipmentMap.put(currentOrder.getDestination(), shipment);
}
if (batchMarker == BATCH_SIZE) {
// Order batch completed. Flush all existing shipments.
for (Map.Entry entry : combinedShipmentMap.entrySet()) {
responseObserver.onNext(entry.getValue());
}
// Reset batch marker
batchMarker = 0;
combinedShipmentMap.clear();
}
}
@Override
public void onError(Throwable t) {
}
@Override
public void onCompleted() {
for (Map.Entry entry : combinedShipmentMap.entrySet()) {
responseObserver.onNext(entry.getValue());
}
responseObserver.onCompleted();
}
};
}
private static void invokeOrderProcess(OrderManagementGrpc.OrderManagementStub asyncStub) {
final CountDownLatch finishLatch = new CountDownLatch(1);
StreamObserver orderProcessResponseObserver = new StreamObserver() {
@Override
public void onNext(OrderManagementOuterClass.CombinedShipment value) {
logger.info("Combined Shipment : " + value.getId() + " : " + value.getOrdersListList());
}
@Override
public void onError(Throwable t) {
}
@Override
public void onCompleted() {
logger.info("Order Processing completed!");
finishLatch.countDown();
}
};
StreamObserver orderProcessRequestObserver = asyncStub.processOrders(orderProcessResponseObserver);
orderProcessRequestObserver.onNext(StringValue.newBuilder().setValue("102").build());
orderProcessRequestObserver.onNext(StringValue.newBuilder().setValue("103").build());
orderProcessRequestObserver.onNext(StringValue.newBuilder().setValue("104").build());
orderProcessRequestObserver.onNext(StringValue.newBuilder().setValue("101").build());
if (finishLatch.getCount() == 0) {
logger.warning("RPC completed or errored before we finished sending.");
return;
}
orderProcessRequestObserver.onCompleted();
try {
if (!finishLatch.await(120, TimeUnit.SECONDS)) {
logger.warning("FAILED : Process orders cannot finish within 60 seconds");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}