|
| 1 | +--- |
| 2 | +type: docs |
| 3 | +title: "How to: Use the gRPC interface in your Dapr application" |
| 4 | +linkTitle: "gRPC interface" |
| 5 | +weight: 400 |
| 6 | +description: "Use the Dapr gRPC API in your application" |
| 7 | +--- |
| 8 | + |
| 9 | +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. |
| 10 | + |
| 11 | +[Find a list of auto-generated clients in the Dapr SDK documentation]({{< ref sdks >}}). |
| 12 | + |
| 13 | +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. |
| 14 | + |
| 15 | +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 >}}). |
| 16 | + |
| 17 | +This guide demonstrates configuring and invoking Dapr with gRPC using a Go SDK application. |
| 18 | + |
| 19 | +## Configure Dapr to communicate with an app via gRPC |
| 20 | + |
| 21 | +{{< tabs "Self-hosted" "Kubernetes">}} |
| 22 | +<!--selfhosted--> |
| 23 | +{{% codetab %}} |
| 24 | + |
| 25 | +When running in self-hosted mode, use the `--app-protocol` flag to tell Dapr to use gRPC to talk to the app. |
| 26 | + |
| 27 | +```bash |
| 28 | +dapr run --app-protocol grpc --app-port 5005 node app.js |
| 29 | +``` |
| 30 | + |
| 31 | +This tells Dapr to communicate with your app via gRPC over port `5005`. |
| 32 | + |
| 33 | +{{% /codetab %}} |
| 34 | + |
| 35 | +<!--k8s--> |
| 36 | +{{% codetab %}} |
| 37 | + |
| 38 | +On Kubernetes, set the following annotations in your deployment YAML: |
| 39 | + |
| 40 | +```yaml |
| 41 | +apiVersion: apps/v1 |
| 42 | +kind: Deployment |
| 43 | +metadata: |
| 44 | + name: myapp |
| 45 | + namespace: default |
| 46 | + labels: |
| 47 | + app: myapp |
| 48 | +spec: |
| 49 | + replicas: 1 |
| 50 | + selector: |
| 51 | + matchLabels: |
| 52 | + app: myapp |
| 53 | + template: |
| 54 | + metadata: |
| 55 | + labels: |
| 56 | + app: myapp |
| 57 | + annotations: |
| 58 | + dapr.io/enabled: "true" |
| 59 | + dapr.io/app-id: "myapp" |
| 60 | + dapr.io/app-protocol: "grpc" |
| 61 | + dapr.io/app-port: "5005" |
| 62 | +... |
| 63 | +``` |
| 64 | + |
| 65 | +{{% /codetab %}} |
| 66 | + |
| 67 | +{{< /tabs >}} |
| 68 | + |
| 69 | +## Invoke Dapr with gRPC |
| 70 | + |
| 71 | +The following steps show how to create a Dapr client and call the `SaveStateData` operation on it. |
| 72 | + |
| 73 | +1. Import the package: |
| 74 | + |
| 75 | + ```go |
| 76 | + package main |
| 77 | + |
| 78 | + import ( |
| 79 | + "context" |
| 80 | + "log" |
| 81 | + "os" |
| 82 | + |
| 83 | + dapr "github.com/dapr/go-sdk/client" |
| 84 | + ) |
| 85 | + ``` |
| 86 | + |
| 87 | +1. Create the client: |
| 88 | + |
| 89 | + ```go |
| 90 | + // just for this demo |
| 91 | + ctx := context.Background() |
| 92 | + data := []byte("ping") |
| 93 | + |
| 94 | + // create the client |
| 95 | + client, err := dapr.NewClient() |
| 96 | + if err != nil { |
| 97 | + log.Panic(err) |
| 98 | + } |
| 99 | + defer client.Close() |
| 100 | + ``` |
| 101 | + |
| 102 | + 3. Invoke the `SaveState` method: |
| 103 | + |
| 104 | + ```go |
| 105 | + // save state with the key key1 |
| 106 | + err = client.SaveState(ctx, "statestore", "key1", data) |
| 107 | + if err != nil { |
| 108 | + log.Panic(err) |
| 109 | + } |
| 110 | + log.Println("data saved") |
| 111 | + ``` |
| 112 | + |
| 113 | +Now you can explore all the different methods on the Dapr client. |
| 114 | + |
| 115 | +## Create a gRPC app with Dapr |
| 116 | + |
| 117 | +The following steps will show how to create an app that exposes a server for with which Dapr can communicate. |
| 118 | + |
| 119 | +1. Import the package: |
| 120 | + |
| 121 | + ```go |
| 122 | + package main |
| 123 | + |
| 124 | + import ( |
| 125 | + "context" |
| 126 | + "fmt" |
| 127 | + "log" |
| 128 | + "net" |
| 129 | + |
| 130 | + "github.com/golang/protobuf/ptypes/any" |
| 131 | + "github.com/golang/protobuf/ptypes/empty" |
| 132 | + |
| 133 | + commonv1pb "github.com/dapr/dapr/pkg/proto/common/v1" |
| 134 | + pb "github.com/dapr/dapr/pkg/proto/runtime/v1" |
| 135 | + "google.golang.org/grpc" |
| 136 | + ) |
| 137 | + ``` |
| 138 | + |
| 139 | +1. Implement the interface: |
| 140 | + |
| 141 | + ```go |
| 142 | + // server is our user app |
| 143 | + type server struct { |
| 144 | + pb.UnimplementedAppCallbackServer |
| 145 | + } |
| 146 | + |
| 147 | + // EchoMethod is a simple demo method to invoke |
| 148 | + func (s *server) EchoMethod() string { |
| 149 | + return "pong" |
| 150 | + } |
| 151 | + |
| 152 | + // This method gets invoked when a remote service has called the app through Dapr |
| 153 | + // The payload carries a Method to identify the method, a set of metadata properties and an optional payload |
| 154 | + func (s *server) OnInvoke(ctx context.Context, in *commonv1pb.InvokeRequest) (*commonv1pb.InvokeResponse, error) { |
| 155 | + var response string |
| 156 | + |
| 157 | + switch in.Method { |
| 158 | + case "EchoMethod": |
| 159 | + response = s.EchoMethod() |
| 160 | + } |
| 161 | + |
| 162 | + return &commonv1pb.InvokeResponse{ |
| 163 | + ContentType: "text/plain; charset=UTF-8", |
| 164 | + Data: &any.Any{Value: []byte(response)}, |
| 165 | + }, nil |
| 166 | + } |
| 167 | + |
| 168 | + // Dapr will call this method to get the list of topics the app wants to subscribe to. In this example, we are telling Dapr |
| 169 | + // To subscribe to a topic named TopicA |
| 170 | + func (s *server) ListTopicSubscriptions(ctx context.Context, in *empty.Empty) (*pb.ListTopicSubscriptionsResponse, error) { |
| 171 | + return &pb.ListTopicSubscriptionsResponse{ |
| 172 | + Subscriptions: []*pb.TopicSubscription{ |
| 173 | + {Topic: "TopicA"}, |
| 174 | + }, |
| 175 | + }, nil |
| 176 | + } |
| 177 | + |
| 178 | + // Dapr will call this method to get the list of bindings the app will get invoked by. In this example, we are telling Dapr |
| 179 | + // To invoke our app with a binding named storage |
| 180 | + func (s *server) ListInputBindings(ctx context.Context, in *empty.Empty) (*pb.ListInputBindingsResponse, error) { |
| 181 | + return &pb.ListInputBindingsResponse{ |
| 182 | + Bindings: []string{"storage"}, |
| 183 | + }, nil |
| 184 | + } |
| 185 | + |
| 186 | + // 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 |
| 187 | + func (s *server) OnBindingEvent(ctx context.Context, in *pb.BindingEventRequest) (*pb.BindingEventResponse, error) { |
| 188 | + fmt.Println("Invoked from binding") |
| 189 | + return &pb.BindingEventResponse{}, nil |
| 190 | + } |
| 191 | + |
| 192 | + // 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. |
| 193 | + func (s *server) OnTopicEvent(ctx context.Context, in *pb.TopicEventRequest) (*pb.TopicEventResponse, error) { |
| 194 | + fmt.Println("Topic message arrived") |
| 195 | + return &pb.TopicEventResponse{}, nil |
| 196 | + } |
| 197 | + |
| 198 | + ``` |
| 199 | + |
| 200 | +1. Create the server: |
| 201 | + |
| 202 | + ```go |
| 203 | + func main() { |
| 204 | + // create listener |
| 205 | + lis, err := net.Listen("tcp", ":50001") |
| 206 | + if err != nil { |
| 207 | + log.Fatalf("failed to listen: %v", err) |
| 208 | + } |
| 209 | + |
| 210 | + // create grpc server |
| 211 | + s := grpc.NewServer() |
| 212 | + pb.RegisterAppCallbackServer(s, &server{}) |
| 213 | + |
| 214 | + fmt.Println("Client starting...") |
| 215 | + |
| 216 | + // and start... |
| 217 | + if err := s.Serve(lis); err != nil { |
| 218 | + log.Fatalf("failed to serve: %v", err) |
| 219 | + } |
| 220 | + } |
| 221 | + ``` |
| 222 | + |
| 223 | + This creates a gRPC server for your app on port 50001. |
| 224 | + |
| 225 | +## Run the application |
| 226 | + |
| 227 | +{{< tabs "Self-hosted" "Kubernetes">}} |
| 228 | +<!--selfhosted--> |
| 229 | +{{% codetab %}} |
| 230 | + |
| 231 | +To run locally, use the Dapr CLI: |
| 232 | + |
| 233 | +```bash |
| 234 | +dapr run --app-id goapp --app-port 50001 --app-protocol grpc go run main.go |
| 235 | +``` |
| 236 | + |
| 237 | +{{% /codetab %}} |
| 238 | + |
| 239 | +<!--k8s--> |
| 240 | +{{% codetab %}} |
| 241 | + |
| 242 | +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. |
| 243 | + |
| 244 | +{{% /codetab %}} |
| 245 | + |
| 246 | +{{< /tabs >}} |
| 247 | + |
| 248 | + |
| 249 | +## Other languages |
| 250 | + |
| 251 | +You can use Dapr with any language supported by Protobuf, and not just with the currently available generated SDKs. |
| 252 | + |
| 253 | +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. |
| 254 | + |
| 255 | + ## Related Topics |
| 256 | +- [Service invocation building block]({{< ref service-invocation >}}) |
| 257 | +- [Service invocation API specification]({{< ref service_invocation_api.md >}}) |
0 commit comments