From 45c7c10d828da7b91fd4a90a15b9899f772cfc93 Mon Sep 17 00:00:00 2001 From: Nick Greenfield Date: Tue, 6 Jun 2023 13:21:47 -0700 Subject: [PATCH] Address PR feedback Signed-off-by: Nick Greenfield --- .../integrations/gRPC-integration.md | 259 +----------------- .../quickstarts/workflow-quickstart.md | 37 +-- 2 files changed, 6 insertions(+), 290 deletions(-) diff --git a/daprdocs/content/en/developing-applications/integrations/gRPC-integration.md b/daprdocs/content/en/developing-applications/integrations/gRPC-integration.md index c7999a637..c59d9b634 100644 --- a/daprdocs/content/en/developing-applications/integrations/gRPC-integration.md +++ b/daprdocs/content/en/developing-applications/integrations/gRPC-integration.md @@ -1,258 +1 @@ ---- -type: docs -title: "How to: Use the gRPC interface in your Dapr application" -linkTitle: "How to: gRPC interface" -weight: 6000 -description: "Use the Dapr gRPC API in your application" -type: docs ---- - -Dapr implements both an HTTP and a gRPC API for local calls. [gRPC](https://grpc.io/) is useful for low-latency, high performance scenarios and has language integration using the proto clients. - -[Find a list of auto-generated clients in the Dapr SDK documentation]({{< ref sdks >}}). - -The Dapr runtime implements a [proto service](https://github.com/dapr/dapr/blob/master/dapr/proto/runtime/v1/dapr.proto) that apps can communicate with via gRPC. - -In addition to calling Dapr via gRPC, Dapr supports service-to-service calls with gRPC by acting as a proxy. [Learn more in the gRPC service invocation how-to guide]({{< ref howto-invoke-services-grpc.md >}}). - -This guide demonstrates configuring and invoking Dapr with gRPC using a Go SDK application. - -## Configure Dapr to communicate with an app via gRPC - -{{< tabs "Self-hosted" "Kubernetes">}} - -{{% codetab %}} - -When running in self-hosted mode, use the `--app-protocol` flag to tell Dapr to use gRPC to talk to the app. - -```bash -dapr run --app-protocol grpc --app-port 5005 node app.js -``` - -This tells Dapr to communicate with your app via gRPC over port `5005`. - -{{% /codetab %}} - - -{{% codetab %}} - -On Kubernetes, set the following annotations in your deployment YAML: - -```yaml -apiVersion: apps/v1 -kind: Deployment -metadata: - name: myapp - namespace: default - labels: - app: myapp -spec: - replicas: 1 - selector: - matchLabels: - app: myapp - template: - metadata: - labels: - app: myapp - annotations: - dapr.io/enabled: "true" - dapr.io/app-id: "myapp" - dapr.io/app-protocol: "grpc" - dapr.io/app-port: "5005" -... -``` - -{{% /codetab %}} - -{{< /tabs >}} - -## Invoke Dapr with gRPC - -The following steps show how to create a Dapr client and call the `SaveStateData` operation on it. - -1. Import the package: - - ```go - package main - - import ( - "context" - "log" - "os" - - dapr "github.com/dapr/go-sdk/client" - ) - ``` - -1. Create the client: - - ```go - // just for this demo - ctx := context.Background() - data := []byte("ping") - - // create the client - client, err := dapr.NewClient() - if err != nil { - log.Panic(err) - } - defer client.Close() - ``` - - 3. Invoke the `SaveState` method: - - ```go - // save state with the key key1 - err = client.SaveState(ctx, "statestore", "key1", data) - if err != nil { - log.Panic(err) - } - log.Println("data saved") - ``` - -Now you can explore all the different methods on the Dapr client. - -## Create a gRPC app with Dapr - -The following steps will show how to create an app that exposes a server for with which Dapr can communicate. - -1. Import the package: - - ```go - package main - - import ( - "context" - "fmt" - "log" - "net" - - "github.com/golang/protobuf/ptypes/any" - "github.com/golang/protobuf/ptypes/empty" - - commonv1pb "github.com/dapr/dapr/pkg/proto/common/v1" - pb "github.com/dapr/go-sdk/dapr/proto/runtime/v1" - "google.golang.org/grpc" - ) - ``` - -1. Implement the interface: - - ```go - // server is our user app - type server struct { - pb.UnimplementedAppCallbackServer - } - - // EchoMethod is a simple demo method to invoke - func (s *server) EchoMethod() string { - return "pong" - } - - // This method gets invoked when a remote service has called the app through Dapr - // The payload carries a Method to identify the method, a set of metadata properties and an optional payload - func (s *server) OnInvoke(ctx context.Context, in *commonv1pb.InvokeRequest) (*commonv1pb.InvokeResponse, error) { - var response string - - switch in.Method { - case "EchoMethod": - response = s.EchoMethod() - } - - return &commonv1pb.InvokeResponse{ - ContentType: "text/plain; charset=UTF-8", - Data: &any.Any{Value: []byte(response)}, - }, nil - } - - // Dapr will call this method to get the list of topics the app wants to subscribe to. In this example, we are telling Dapr - // To subscribe to a topic named TopicA - func (s *server) ListTopicSubscriptions(ctx context.Context, in *empty.Empty) (*pb.ListTopicSubscriptionsResponse, error) { - return &pb.ListTopicSubscriptionsResponse{ - Subscriptions: []*pb.TopicSubscription{ - {Topic: "TopicA"}, - }, - }, nil - } - - // Dapr will call this method to get the list of bindings the app will get invoked by. In this example, we are telling Dapr - // To invoke our app with a binding named storage - func (s *server) ListInputBindings(ctx context.Context, in *empty.Empty) (*pb.ListInputBindingsResponse, error) { - return &pb.ListInputBindingsResponse{ - Bindings: []string{"storage"}, - }, nil - } - - // This method gets invoked every time a new event is fired from a registered binding. The message carries the binding name, a payload and optional metadata - func (s *server) OnBindingEvent(ctx context.Context, in *pb.BindingEventRequest) (*pb.BindingEventResponse, error) { - fmt.Println("Invoked from binding") - return &pb.BindingEventResponse{}, nil - } - - // This method is fired whenever a message has been published to a topic that has been subscribed. Dapr sends published messages in a CloudEvents 0.3 envelope. - func (s *server) OnTopicEvent(ctx context.Context, in *pb.TopicEventRequest) (*pb.TopicEventResponse, error) { - fmt.Println("Topic message arrived") - return &pb.TopicEventResponse{}, nil - } - - ``` - -1. Create the server: - - ```go - func main() { - // create listener - lis, err := net.Listen("tcp", ":50001") - if err != nil { - log.Fatalf("failed to listen: %v", err) - } - - // create grpc server - s := grpc.NewServer() - pb.RegisterAppCallbackServer(s, &server{}) - - fmt.Println("Client starting...") - - // and start... - if err := s.Serve(lis); err != nil { - log.Fatalf("failed to serve: %v", err) - } - } - ``` - - This creates a gRPC server for your app on port 50001. - -## Run the application - -{{< tabs "Self-hosted" "Kubernetes">}} - -{{% codetab %}} - -To run locally, use the Dapr CLI: - -```bash -dapr run --app-id goapp --app-port 50001 --app-protocol grpc go run main.go -``` - -{{% /codetab %}} - - -{{% codetab %}} - -On Kubernetes, set the required `dapr.io/app-protocol: "grpc"` and `dapr.io/app-port: "50001` annotations in your pod spec template, as mentioned above. - -{{% /codetab %}} - -{{< /tabs >}} - - -## Other languages - -You can use Dapr with any language supported by Protobuf, and not just with the currently available generated SDKs. - -Using the [protoc](https://developers.google.com/protocol-buffers/docs/downloads) tool, you can generate the Dapr clients for other languages like Ruby, C++, Rust, and others. - - ## Related Topics -- [Service invocation building block]({{< ref service-invocation >}}) -- [Service invocation API specification]({{< ref service_invocation_api.md >}}) +d \ No newline at end of file diff --git a/daprdocs/content/en/getting-started/quickstarts/workflow-quickstart.md b/daprdocs/content/en/getting-started/quickstarts/workflow-quickstart.md index 2738e03f1..0cb7186e0 100644 --- a/daprdocs/content/en/getting-started/quickstarts/workflow-quickstart.md +++ b/daprdocs/content/en/getting-started/quickstarts/workflow-quickstart.md @@ -282,10 +282,13 @@ In a new terminal window, navigate to the `order-processor` directory: ```bash cd workflows/python/sdk/order-processor ``` + Install the Dapr Python SDK package: ```bash pip3 install -r requirements.txt +``` + ### Step 3: Run the order processor app In the terminal, start the order processor app alongside a Dapr sidecar: @@ -296,39 +299,23 @@ dapr run --app-id order-processor --resources-path ../../../components/ -- pytho > **Note:** Since Python3.exe is not defined in Windows, you may need to use `python workflow.py` instead of `python3 workflow.py`. - This starts the `order-processor` app with unique workflow ID and runs the workflow activities. Expected output: -``` -== APP == *** Welcome to the Dapr Workflow console app sample! -== APP == *** Using this app, you can place orders that start workflows. -== APP == 2023-06-06 09:35:52.892 durabletask-worker INFO: Starting gRPC worker that connects to 127.0.0.1:65406 -== APP == item: InventoryItem(item_name=Paperclip, per_item_cost=5, quantity=100) -== APP == item: InventoryItem(item_name=Cars, per_item_cost=15000, quantity=100) -== APP == item: InventoryItem(item_name=Computers, per_item_cost=500, quantity=100) -== APP == ==========Begin the purchase of item:========== +```bash == APP == Starting order workflow, purchasing 11 of cars == APP == 2023-06-06 09:35:52.945 durabletask-worker INFO: Successfully connected to 127.0.0.1:65406. Waiting for work items... -== APP == 2023-06-06 09:35:55.960 durabletask-worker INFO: f4e1926e-3721-478d-be8a-f5bebd1995da: Waiting for 1 task(s) and 0 event(s). == APP == INFO:NotifyActivity:Received order f4e1926e-3721-478d-be8a-f5bebd1995da for 11 cars at $165000 ! -== APP == 2023-06-06 09:35:56.001 durabletask-worker INFO: f4e1926e-3721-478d-be8a-f5bebd1995da: Waiting for 1 task(s) and 0 event(s). == APP == INFO:VerifyInventoryActivity:Verifying inventory for order f4e1926e-3721-478d-be8a-f5bebd1995da of 11 cars == APP == INFO:VerifyInventoryActivity:There are 100 Cars available for purchase -== APP == 2023-06-06 09:35:56.035 durabletask-worker INFO: f4e1926e-3721-478d-be8a-f5bebd1995da: Waiting for 1 task(s) and 0 event(s). == APP == INFO:RequestApprovalActivity:Requesting approval for payment of 165000 USD for 11 cars -== APP == 2023-06-06 09:35:56.071 durabletask-worker INFO: f4e1926e-3721-478d-be8a-f5bebd1995da: Waiting for 1 task(s) and 1 event(s). == APP == 2023-06-06 09:36:05.969 durabletask-worker INFO: f4e1926e-3721-478d-be8a-f5bebd1995da Event raised: manager_approval -== APP == 2023-06-06 09:36:05.969 durabletask-worker INFO: f4e1926e-3721-478d-be8a-f5bebd1995da: Waiting for 2 task(s) and 0 event(s). == APP == INFO:NotifyActivity:Payment for order f4e1926e-3721-478d-be8a-f5bebd1995da has been approved! -== APP == 2023-06-06 09:36:06.000 durabletask-worker INFO: f4e1926e-3721-478d-be8a-f5bebd1995da: Waiting for 2 task(s) and 0 event(s). == APP == INFO:ProcessPaymentActivity:Processing payment: f4e1926e-3721-478d-be8a-f5bebd1995da for 11 cars at 165000 USD == APP == INFO:ProcessPaymentActivity:Payment for request ID f4e1926e-3721-478d-be8a-f5bebd1995da processed successfully -== APP == 2023-06-06 09:36:06.035 durabletask-worker INFO: f4e1926e-3721-478d-be8a-f5bebd1995da: Waiting for 2 task(s) and 0 event(s). == APP == INFO:UpdateInventoryActivity:Checking inventory for order f4e1926e-3721-478d-be8a-f5bebd1995da for 11 cars == APP == INFO:UpdateInventoryActivity:There are now 89 cars left in stock -== APP == 2023-06-06 09:36:06.071 durabletask-worker INFO: f4e1926e-3721-478d-be8a-f5bebd1995da: Waiting for 2 task(s) and 0 event(s). == APP == INFO:NotifyActivity:Order f4e1926e-3721-478d-be8a-f5bebd1995da has completed! == APP == 2023-06-06 09:36:06.106 durabletask-worker INFO: f4e1926e-3721-478d-be8a-f5bebd1995da: Orchestration completed with status: COMPLETED == APP == Workflow completed! Result: Completed @@ -343,7 +330,7 @@ If you have Zipkin configured for Dapr locally on your machine, you can view the ### What happened? -When you ran `dapr run --app-id order-processor --components-path ../../../components/ -- python3 app.py`: +When you ran `dapr run --app-id order-processor --resources-path ../../../components/ -- python3 app.py`: 1. A unique order ID for the workflow is generated (in the above example, `f4e1926e-3721-478d-be8a-f5bebd1995da`) and the workflow is scheduled. 1. The `NotifyActivity` workflow activity sends a notification saying an order for 11 cars has been received. @@ -365,11 +352,6 @@ In the application's program file: ```python class WorkflowConsoleApp: def main(self): - print("*** Welcome to the Dapr Workflow console app sample!", flush=True) - print("*** Using this app, you can place orders that start workflows.", flush=True) - # Wait for the sidecar to become available - sleep(5) - # Register workflow and activities workflowRuntime = WorkflowRuntime(settings.DAPR_RUNTIME_HOST, settings.DAPR_GRPC_PORT) workflowRuntime.register_workflow(order_processing_workflow) @@ -380,15 +362,6 @@ class WorkflowConsoleApp: workflowRuntime.register_activity(update_inventory_activity) workflowRuntime.start() - # Instantiate Dapr client - daprClient = DaprClient(address=f'{settings.DAPR_RUNTIME_HOST}:{settings.DAPR_GRPC_PORT}') - baseInventory = {} - baseInventory["paperclip"] = InventoryItem("Paperclip", 5, 100) - baseInventory["cars"] = InventoryItem("Cars", 15000, 100) - baseInventory["computers"] = InventoryItem("Computers", 500, 100) - - self.restock_inventory(daprClient, baseInventory) - print("==========Begin the purchase of item:==========", flush=True) item_name = default_item_name order_quantity = 11