Dapr’s gRPC Interface

Use the Dapr gRPC API in your application

Dapr and gRPC

Dapr implements both an HTTP and a gRPC API for local calls. gRPC is useful for low-latency, high performance scenarios and has language integration using the proto clients.

You can find a list of auto-generated clients here.

The Dapr runtime implements a proto service that apps can communicate with via gRPC.

In addition to calling Dapr via gRPC, Dapr can communicate with an application via gRPC. To do that, the app needs to host a gRPC server and implements the Dapr appcallback service

Configuring Dapr to communicate with an app via gRPC

Self hosted

When running in self hosted mode, use the --app-protocol flag to tell Dapr to use gRPC to talk to the app:

  1. dapr run --app-protocol grpc --app-port 5005 node app.js

This tells Dapr to communicate with your app via gRPC over port 5005.

Kubernetes

On Kubernetes, set the following annotations in your deployment YAML:

  1. apiVersion: apps/v1
  2. kind: Deployment
  3. metadata:
  4. name: myapp
  5. namespace: default
  6. labels:
  7. app: myapp
  8. spec:
  9. replicas: 1
  10. selector:
  11. matchLabels:
  12. app: myapp
  13. template:
  14. metadata:
  15. labels:
  16. app: myapp
  17. annotations:
  18. dapr.io/enabled: "true"
  19. dapr.io/app-id: "myapp"
  20. dapr.io/app-protocol: "grpc"
  21. dapr.io/app-port: "5005"
  22. ...

Invoking Dapr with gRPC - Go example

The following steps show you how to create a Dapr client and call the SaveStateData operation on it:

  1. Import the package
  1. package main
  2. import (
  3. "context"
  4. "log"
  5. "os"
  6. dapr "github.com/dapr/go-sdk/client"
  7. )
  1. Create the client
  1. // just for this demo
  2. ctx := context.Background()
  3. data := []byte("ping")
  4. // create the client
  5. client, err := dapr.NewClient()
  6. if err != nil {
  7. logger.Panic(err)
  8. }
  9. defer client.Close()
  1. Invoke the Save State method
  1. // save state with the key key1
  2. err = client.SaveStateData(ctx, "statestore", "key1", "1", data)
  3. if err != nil {
  4. logger.Panic(err)
  5. }
  6. logger.Println("data saved")

Hooray!

Now you can explore all the different methods on the Dapr client.

Creating a gRPC app with Dapr

The following steps will show you how to create an app that exposes a server for Dapr to communicate with.

  1. Import the package
  1. package main
  2. import (
  3. "context"
  4. "fmt"
  5. "log"
  6. "net"
  7. "github.com/golang/protobuf/ptypes/any"
  8. "github.com/golang/protobuf/ptypes/empty"
  9. commonv1pb "github.com/dapr/go-sdk/dapr/proto/common/v1"
  10. pb "github.com/dapr/go-sdk/dapr/proto/runtime/v1"
  11. "google.golang.org/grpc"
  12. )
  1. Implement the interface
  1. // server is our user app
  2. type server struct {
  3. }
  4. // EchoMethod is a simple demo method to invoke
  5. func (s *server) EchoMethod() string {
  6. return "pong"
  7. }
  8. // This method gets invoked when a remote service has called the app through Dapr
  9. // The payload carries a Method to identify the method, a set of metadata properties and an optional payload
  10. func (s *server) OnInvoke(ctx context.Context, in *commonv1pb.InvokeRequest) (*commonv1pb.InvokeResponse, error) {
  11. var response string
  12. switch in.Method {
  13. case "EchoMethod":
  14. response = s.EchoMethod()
  15. }
  16. return &commonv1pb.InvokeResponse{
  17. ContentType: "text/plain; charset=UTF-8",
  18. Data: &any.Any{Value: []byte(response)},
  19. }, nil
  20. }
  21. // Dapr will call this method to get the list of topics the app wants to subscribe to. In this example, we are telling Dapr
  22. // To subscribe to a topic named TopicA
  23. func (s *server) ListTopicSubscriptions(ctx context.Context, in *empty.Empty) (*pb.ListTopicSubscriptionsResponse, error) {
  24. return &pb.ListTopicSubscriptionsResponse{
  25. Subscriptions: []*pb.TopicSubscription{
  26. {Topic: "TopicA"},
  27. },
  28. }, nil
  29. }
  30. // Dapr will call this method to get the list of bindings the app will get invoked by. In this example, we are telling Dapr
  31. // To invoke our app with a binding named storage
  32. func (s *server) ListInputBindings(ctx context.Context, in *empty.Empty) (*pb.ListInputBindingsResponse, error) {
  33. return &pb.ListInputBindingsResponse{
  34. Bindings: []string{"storage"},
  35. }, nil
  36. }
  37. // This method gets invoked every time a new event is fired from a registerd binding. The message carries the binding name, a payload and optional metadata
  38. func (s *server) OnBindingEvent(ctx context.Context, in *pb.BindingEventRequest) (*pb.BindingEventResponse, error) {
  39. fmt.Println("Invoked from binding")
  40. return &pb.BindingEventResponse{}, nil
  41. }
  42. // 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.
  43. func (s *server) OnTopicEvent(ctx context.Context, in *pb.TopicEventRequest) (*empty.Empty, error) {
  44. fmt.Println("Topic message arrived")
  45. return &empty.Empty{}, nil
  46. }
  1. Create the server
  1. func main() {
  2. // create listener
  3. lis, err := net.Listen("tcp", ":50001")
  4. if err != nil {
  5. log.Fatalf("failed to listen: %v", err)
  6. }
  7. // create grpc server
  8. s := grpc.NewServer()
  9. pb.RegisterAppCallbackServer(s, &server{})
  10. fmt.Println("Client starting...")
  11. // and start...
  12. if err := s.Serve(lis); err != nil {
  13. log.Fatalf("failed to serve: %v", err)
  14. }
  15. }

This creates a gRPC server for your app on port 4000.

  1. Run your app

To run locally, use the Dapr CLI:

  1. dapr run --app-id goapp --app-port 4000 --app-protocol grpc go run main.go

On Kubernetes, set the required dapr.io/app-protocol: "grpc" and dapr.io/app-port: "4000 annotations in your pod spec template as mentioned above.

Other languages

You can use Dapr with any language supported by Protobuf, and not just with the currently available generated SDKs. Using the protoc tool you can generate the Dapr clients for other languages like Ruby, C++, Rust and others.

Related Topics

Last modified March 18, 2021: Merge pull request #1321 from dapr/aacrawfi/logos (9a399d5)