Overview of the service invocation API building block
Using service invocation, your application can reliably and securely communicate with other applications using the standard gRPC or HTTP protocols.
In many microservice-based applications, multiple services need the ability to communicate with one another. This inter-service communication requires that application developers handle problems like:
Service discovery. How do I discover my different services?
Standardizing API calls between services. How do I invoke methods between services?
Secure inter-service communication. How do I call other services securely with encryption and apply access control on the methods?
Mitigating request timeouts or failures. How do I handle retries and transient errors?
Implementing observability and tracing. How do I use tracing to see a call graph with metrics to diagnose issues in production?
Service invocation API
Dapr addresses these challenges by providing a service invocation API that acts similar to a reverse proxy with built-in service discovery, while leveraging built-in distributed tracing, metrics, error handling, encryption and more.
Dapr uses a sidecar architecture. To invoke an application using Dapr:
You use the invoke API on the Dapr instance.
Each application communicates with its own instance of Dapr.
The Dapr instances discover and communicate with each other.
Dapr forwards the message to Service B’s Dapr sidecar
Note: All calls between Dapr sidecars go over gRPC for performance. Only calls between services and Dapr sidecars can be either HTTP or gRPC.
Service B’s Dapr sidecar forwards the request to the specified endpoint (or method) on Service B. Service B then runs its business logic code.
Service B sends a response to Service A. The response goes to Service B’s sidecar.
Dapr forwards the response to Service A’s Dapr sidecar.
Service A receives the response.
You can also call non-Dapr HTTP endpoints using the service invocation API. For example, you may only use Dapr in part of an overall application, may not have access to the code to migrate an existing application to use Dapr, or simply need to call an external HTTP service. Read “How-To: Invoke Non-Dapr Endpoints using HTTP” for more information.
Features
Service invocation provides several features to make it easy for you to call methods between applications or to call external HTTP endpoints.
HTTP and gRPC service invocation
HTTP: If you’re already using HTTP protocols in your application, using the Dapr HTTP header might be the easiest way to get started. You don’t need to change your existing endpoint URLs; just add the dapr-app-id header and you’re ready to go. For more information, see Invoke Services using HTTP.
gRPC: Dapr allows users to keep their own proto services and work natively with gRPC. This means that you can use service invocation to call your existing gRPC apps without having to include any Dapr SDKs or include custom gRPC services. For more information, see the how-to tutorial for Dapr and gRPC.
Service-to-service security
With the Dapr Sentry service, all calls between Dapr applications can be made secure with mutual (mTLS) authentication on hosted platforms, including automatic certificate rollover.
In the event of call failures and transient errors, service invocation provides a resiliency feature that performs automatic retries with backoff time periods. To find out more, see the Resiliency article here.
Tracing and metrics with observability
By default, all calls between applications are traced and metrics are gathered to provide insights and diagnostics for applications. This is especially important in production scenarios, providing call graphs and metrics on the calls between your services. For more information read about observability.
Access control
With access policies, applications can control:
Which applications are allowed to call them.
What applications are authorized to do.
For example, you can restrict sensitive applications with personnel information from being accessed by unauthorized applications. Combined with service-to-service secure communication, you can provide for soft multi-tenancy deployments.
You can scope applications to namespaces for deployment and security and call between services deployed to different namespaces. For more information, read the Service invocation across namespaces article.
Round robin load balancing with mDNS
Dapr provides round robin load balancing of service invocation requests with the mDNS protocol, for example with a single machine or with multiple, networked, physical machines.
The diagram below shows an example of how this works. If you have 1 instance of an application with app ID FrontEnd and 3 instances of application with app ID Cart and you call from FrontEnd app to Cart app, Dapr round robins’ between the 3 instances. These instance can be on the same machine or on different machines. .
Note: App ID is unique per application, not application instance. Regardless how many instances of that application exist (due to scaling), all of them will share the same app ID.
Swappable service discovery
Dapr can run on a variety of hosting platforms. To enable swappable service discovery with service invocation, Dapr uses name resolution components. For example, the Kubernetes name resolution component uses the Kubernetes DNS service to resolve the location of other applications running in the cluster.
Self-hosted machines can use the mDNS name resolution component. As an alternative, you can use the SQLite name resolution component to run Dapr on single-node environments and for local development scenarios. Dapr sidecars that are part of the cluster store their information in a SQLite database on the local machine.
The Consul name resolution component is particularly suited to multi-machine deployments and can be used in any hosting environment, including Kubernetes, multiple VMs, or self-hosted.
Streaming for HTTP service invocation
You can handle data as a stream in HTTP service invocation. This can offer improvements in performance and memory utilization when using Dapr to invoke another service using HTTP with large request or response bodies.
The diagram below demonstrates the six steps of data flow.
Request: “App A” to “Dapr sidecar A”
Request: “Dapr sidecar A” to “Dapr sidecar B”
Request: “Dapr sidecar B” to “App B”
Response: “App B” to “Dapr sidecar B”
Response: “Dapr sidecar B” to “Dapr sidecar A”
Response: “Dapr sidecar A” to “App A”
Example Architecture
Following the above call sequence, suppose you have the applications as described in the Hello World tutorial, where a python app invokes a node.js app. In such a scenario, the python app would be “Service A” , and a Node.js app would be “Service B”.
The diagram below shows sequence 1-7 again on a local machine showing the API calls:
The Node.js app has a Dapr app ID of nodeapp. The python app invokes the Node.js app’s neworder method by POSTing http://localhost:3500/v1.0/invoke/nodeapp/method/neworder, which first goes to the python app’s local Dapr sidecar.
Dapr discovers the Node.js app’s location using name resolution component (in this case mDNS while self-hosted) which runs on your local machine.
Dapr forwards the request to the Node.js app’s sidecar using the location it just received.
The Node.js app’s sidecar forwards the request to the Node.js app. The Node.js app performs its business logic, logging the incoming message and then persist the order ID into Redis (not shown in the diagram).
The Node.js app sends a response to the Python app through the Node.js sidecar.
Dapr forwards the response to the Python Dapr sidecar.
The Python app receives the response.
Try out service invocation
Quickstarts & tutorials
The Dapr docs contain multiple quickstarts that leverage the service invocation building block in different example architectures. To get a straight-forward understanding of the service invocation api and it’s features we recommend starting with our quickstarts:
This tutorial walks through using Dapr in kubernetes and covers both the service invocation and state management building blocks as well.
Start using service invocation directly in your app
Want to skip the quickstarts? Not a problem. You can try out the service invocation building block directly in your application to securely communicate with other services. After Dapr is installed, you can begin using the service invocation API in the following ways.
Invoke services using:
HTTP and gRPC service invocation (recommended set up method)
HTTP - Allows you to just add the dapr-app-id header and you’re ready to get started. Read more on this here, Invoke Services using HTTP.
gRPC - For gRPC based applications, the service invocation API is also available. Run the gRPC server, then invoke services using the Dapr CLI. Read more on this in Configuring Dapr to use gRPC and Invoke services using gRPC.
Direct call to the API - In addition to proxying, there’s also an option to directly call the service invocation API to invoke a GET endpoint. Just update your address URL to localhost:<dapr-http-port> and you’ll be able to directly call the API. You can also read more on this in the Invoke Services using HTTP docs linked above under HTTP proxying.
SDKs - If you’re using a Dapr SDK, you can directly use service invocation through the SDK. Select the SDK you need and use the Dapr client to invoke a service. Read more on this in Dapr SDKs.
For quick testing, try using the Dapr CLI for service invocation:
Dapr CLI command - Once the Dapr CLI is set up, use dapr invoke --method <method-name> command along with the method flag and the method of interest. Read more on this in Dapr CLI.
Take a look at observability. Here you can dig into Dapr’s monitoring tools like tracing, metrics and logging.
Read up on our security practices around mTLS encryption, token authentication, and endpoint authorization.
2 - How-To: Invoke services using HTTP
Call between services using service invocation
This article demonstrates how to deploy services each with an unique application ID for other services to discover and call endpoints on them using service invocation over HTTP.
Dapr allows you to assign a global, unique ID for your app. This ID encapsulates the state for your application, regardless of the number of instances it may have.
dapr run --app-id checkout --dapr-http-port 3500 -- go run .
dapr run --app-id order-processor --app-port 6006 --app-protocol http --dapr-http-port 3501 -- go run .
If your app uses a TLS, you can tell Dapr to invoke your app over a TLS connection by setting --app-protocol https:
dapr run --app-id checkout --dapr-http-port 3500 --app-protocol https -- go run .
dapr run --app-id order-processor --app-port 6006 --dapr-http-port 3501 --app-protocol https -- go run .
Set an app-id when deploying to Kubernetes
In Kubernetes, set the dapr.io/app-id annotation on your pod:
If your app uses a TLS connection, you can tell Dapr to invoke your app over TLS with the app-protocol: "https" annotation (full list here). Note that Dapr does not validate TLS certificates presented by the app.
Invoke the service
To invoke an application using Dapr, you can use the invoke API on any Dapr instance. The sidecar programming model encourages each application to interact with its own instance of Dapr. The Dapr sidecars discover and communicate with one another.
Below are code examples that leverage Dapr SDKs for service invocation.
#dependenciesimportrandomfromtimeimportsleepimportloggingimportrequests#codelogging.basicConfig(level=logging.INFO)whileTrue:sleep(random.randrange(50,5000)/1000)orderId=random.randint(1,1000)#Invoke a serviceresult=requests.post(url='%s/orders'%(base_url),data=json.dumps(order),headers=headers)logging.basicConfig(level=logging.INFO)logging.info('Order requested: '+str(orderId))logging.info('Result: '+str(result))
//dependencies
importaxiosfrom"axios";//code
constdaprHost="127.0.0.1";varmain=function(){for(vari=0;i<10;i++){sleep(5000);varorderId=Math.floor(Math.random()*(1000-1)+1);start(orderId).catch((e)=>{console.error(e);process.exit(1);});}}//Invoke a service
constresult=awaitaxios.post('order-processor',"orders/"+orderId,axiosConfig);console.log("Order requested: "+orderId);console.log("Result: "+result.config.data);functionsleep(ms){returnnewPromise(resolve=>setTimeout(resolve,ms));}main();
//dependenciesusingSystem;usingSystem.Collections.Generic;usingSystem.Net.Http;usingSystem.Net.Http.Headers;usingSystem.Threading.Tasks;usingMicrosoft.AspNetCore.Mvc;usingSystem.Threading;//codenamespaceEventService{classProgram{staticasyncTaskMain(string[]args){while(true){awaitTask.Delay(5000)varrandom=newRandom();varorderId=random.Next(1,1000);//Using Dapr SDK to invoke a methodvarorder=newOrder(orderId.ToString());varhttpClient=DaprClient.CreateInvokeHttpClient();varresponse=awaithttpClient.PostAsJsonAsync("http://order-processor/orders",order);varresult=awaitresponse.Content.ReadAsStringAsync();Console.WriteLine("Order requested: "+orderId);Console.WriteLine("Result: "+result);}}}}
//dependenciesimportjava.io.IOException;importjava.net.URI;importjava.net.http.HttpClient;importjava.net.http.HttpRequest;importjava.net.http.HttpResponse;importjava.time.Duration;importjava.util.HashMap;importjava.util.Map;importjava.util.Random;importjava.util.concurrent.TimeUnit;//code@SpringBootApplicationpublicclassCheckoutServiceApplication{privatestaticfinalHttpClienthttpClient=HttpClient.newBuilder().version(HttpClient.Version.HTTP_2).connectTimeout(Duration.ofSeconds(10)).build();publicstaticvoidmain(String[]args)throwsInterruptedException,IOException{while(true){TimeUnit.MILLISECONDS.sleep(5000);Randomrandom=newRandom();intorderId=random.nextInt(1000-1)+1;// Create a Map to represent the request bodyMap<String,Object>requestBody=newHashMap<>();requestBody.put("orderId",orderId);// Add other fields to the requestBody Map as neededHttpRequestrequest=HttpRequest.newBuilder().POST(HttpRequest.BodyPublishers.ofString(newJSONObject(requestBody).toString())).uri(URI.create(dapr_url)).header("Content-Type","application/json").header("dapr-app-id","order-processor").build();HttpResponse<String>response=httpClient.send(request,HttpResponse.BodyHandlers.ofString());System.out.println("Order passed: "+orderId);TimeUnit.MILLISECONDS.sleep(1000);log.info("Order requested: "+orderId);log.info("Result: "+response.body());}}}
packagemainimport("fmt""io""log""math/rand""net/http""os""time")funcmain(){daprHttpPort:=os.Getenv("DAPR_HTTP_PORT")ifdaprHttpPort==""{daprHttpPort="3500"}client:=&http.Client{Timeout:15*time.Second,}fori:=0;i<10;i++{time.Sleep(5000)orderId:=rand.Intn(1000-1)+1url:=fmt.Sprintf("http://localhost:%s/checkout/%v",daprHttpPort,orderId)req,err:=http.NewRequest(http.MethodGet,url,nil)iferr!=nil{panic(err)}// Adding target app id as part of the headerreq.Header.Add("dapr-app-id","order-processor")// Invoking a serviceresp,err:=client.Do(req)iferr!=nil{log.Fatal(err.Error())}b,err:=io.ReadAll(resp.Body)iferr!=nil{panic(err)}fmt.Println(string(b))}}
To avoid changing URL paths as much as possible, Dapr provides the following ways to call the service invocation API:
Change the address in the URL to localhost:<dapr-http-port>.
Add a dapr-app-id header to specify the ID of the target service, or alternatively pass the ID via HTTP Basic Auth: http://dapr-app-id:<service-id>@localhost:3602/path.
You can also append a query string or a fragment to the end of the URL and Dapr will pass it through unchanged. This means that if you need to pass some additional arguments in your service invocation that aren’t part of a payload or the path, you can do so by appending a ? to the end of the URL, followed by the key/value pairs separated by = signs and delimited by &. For example:
curl 'http://dapr-app-id:checkout@localhost:3602/checkout/100?basket=1234&key=abc' -X POST
Namespaces
When running on namespace supported platforms, you include the namespace of the target app in the app ID. For example, following the <app>.<namespace> format, use checkout.production.
Using this example, invoking the service with a namespace would look like:
curl http://localhost:3602/v1.0/invoke/checkout.production/method/checkout/100 -X POST
This article describe how to use Dapr to connect services using gRPC.
By using Dapr’s gRPC proxying capability, you can use your existing proto-based gRPC services and have the traffic go through the Dapr sidecar. Doing so yields the following Dapr service invocation benefits to developers:
Mutual authentication
Tracing
Metrics
Access lists
Network level resiliency
API token based authentication
Dapr allows proxying all kinds of gRPC invocations, including unary and stream-based ones.
Step 1: Run a gRPC server
The following example is taken from the “hello world” grpc-go example. Although this example is in Go, the same concepts apply to all programming languages supported by gRPC.
packagemainimport("context""log""net""google.golang.org/grpc"pb"google.golang.org/grpc/examples/helloworld/helloworld")const(port=":50051")// server is used to implement helloworld.GreeterServer.typeserverstruct{pb.UnimplementedGreeterServer}// SayHello implements helloworld.GreeterServerfunc(s*server)SayHello(ctxcontext.Context,in*pb.HelloRequest)(*pb.HelloReply,error){log.Printf("Received: %v",in.GetName())return&pb.HelloReply{Message:"Hello "+in.GetName()},nil}funcmain(){lis,err:=net.Listen("tcp",port)iferr!=nil{log.Fatalf("failed to listen: %v",err)}s:=grpc.NewServer()pb.RegisterGreeterServer(s,&server{})log.Printf("server listening at %v",lis.Addr())iferr:=s.Serve(lis);err!=nil{log.Fatalf("failed to serve: %v",err)}}
This Go app implements the Greeter proto service and exposes a SayHello method.
Run the gRPC server using the Dapr CLI
dapr run --app-id server --app-port 50051 -- go run main.go
Using the Dapr CLI, we’re assigning a unique id to the app, server, using the --app-id flag.
Step 2: Invoke the service
The following example shows you how to discover the Greeter service using Dapr from a gRPC client.
Notice that instead of invoking the target service directly at port 50051, the client is invoking its local Dapr sidecar over port 50007 which then provides all the capabilities of service invocation including service discovery, tracing, mTLS and retries.
packagemainimport("context""log""time""google.golang.org/grpc"pb"google.golang.org/grpc/examples/helloworld/helloworld""google.golang.org/grpc/metadata")const(address="localhost:50007")funcmain(){// Set up a connection to the server.conn,err:=grpc.Dial(address,grpc.WithInsecure(),grpc.WithBlock())iferr!=nil{log.Fatalf("did not connect: %v",err)}deferconn.Close()c:=pb.NewGreeterClient(conn)ctx,cancel:=context.WithTimeout(context.Background(),time.Second*2)defercancel()ctx=metadata.AppendToOutgoingContext(ctx,"dapr-app-id","server")r,err:=c.SayHello(ctx,&pb.HelloRequest{Name:"Darth Tyrannus"})iferr!=nil{log.Fatalf("could not greet: %v",err)}log.Printf("Greeting: %s",r.GetMessage())}
The following line tells Dapr to discover and invoke an app named server:
The dapr.io/app-protocol: "grpc" annotation tells Dapr to invoke the app using gRPC.
If your app uses a TLS connection, you can tell Dapr to invoke your app over TLS with the app-protocol: "grpcs" annotation (full list here). Note that Dapr does not validate TLS certificates presented by the app.
Namespaces
When running on namespace supported platforms, you include the namespace of the target app in the app ID: myApp.production
For example, invoking the gRPC server on a different namespace:
The example above showed you how to directly invoke a different service running locally or in Kubernetes. Dapr outputs metrics, tracing and logging information allowing you to visualize a call graph between services, log errors and optionally log the payload body.
For more information on tracing and logs see the observability article.
Proxying of streaming RPCs
When using Dapr to proxy streaming RPC calls using gRPC, you must set an additional metadata option dapr-stream with value true.
Currently, resiliency policies are not supported for service invocation via gRPC.
When proxying streaming gRPCs, due to their long-lived nature, resiliency policies are applied on the “initial handshake” only. As a consequence:
If the stream is interrupted after the initial handshake, it will not be automatically re-established by Dapr. Your application will be notified that the stream has ended, and will need to recreate it.
Retry policies only impact the initial connection “handshake”. If your resiliency policy includes retries, Dapr will detect failures in establishing the initial connection to the target app and will retry until it succeeds (or until the number of retries defined in the policy is exhausted).
Likewise, timeouts defined in resiliency policies only apply to the initial “handshake”. After the connection has been established, timeouts do not impact the stream anymore.
Watch this video on how to use Dapr’s gRPC proxying capability:
4 - How-To: Invoke Non-Dapr Endpoints using HTTP
Call Non-Dapr endpoints from Dapr applications using service invocation
This article demonstrates how to call a non-Dapr endpoint using Dapr over HTTP.
Using Dapr’s service invocation API, you can communicate with endpoints that either use or do not use Dapr. Using Dapr to call endpoints that do not use Dapr not only provides a consistent API, but also the following Dapr service invocation benefits:
Ability to apply resiliency policies
Call observability with tracing & metrics
Security access control through scoping
Ability to utilize middleware pipeline components
Service discovery
Authentication through the use of headers
HTTP service invocation to external services or non-Dapr endpoints
Sometimes you need to call a non-Dapr HTTP endpoint. For example:
You may choose to only use Dapr in part of your overall application, including brownfield development
You may not have access to the code to migrate an existing application to use Dapr
You need to call an external HTTP service.
By defining an HTTPEndpoint resource, you declaratively define a way to interact with a non-Dapr endpoint. You then use the service invocation URL to invoke non-Dapr endpoints. Alternatively, you can place a non-Dapr Fully Qualified Domain Name (FQDN) endpoint URL directly into the service invocation URL.
Order of precedence between HttpEndpoint, FQDN URL, and appId
When using service invocation, the Dapr runtime follows a precedence order:
Is this a named HTTPEndpoint resource?
Is this an FQDN URL with anhttp:// or https:// prefix?
Is this an appID?
Service invocation and non-Dapr HTTP endpoint
The diagram below is an overview of how Dapr’s service invocation works when invoking non-Dapr endpoints.
Service A makes an HTTP call targeting Service B, a non-Dapr endpoint. The call goes to the local Dapr sidecar.
Dapr discovers Service B’s location using the HTTPEndpoint or FQDN URL then forwards the message to Service B.
Service B sends a response to Service A’s Dapr sidecar.
Service A receives the response.
Using an HTTPEndpoint resource or FQDN URL for non-Dapr endpoints
There are two ways to invoke a non-Dapr endpoint when communicating either to Dapr applications or non-Dapr applications. A Dapr application can invoke a non-Dapr endpoint by providing one of the following:
A named HTTPEndpoint resource, including defining an HTTPEndpoint resource type. See the HTTPEndpoint reference guide for an example.
Using appId when calling Dapr enabled applications
AppIDs are always used to call Dapr applications with the appID and my-method. Read the How-To: Invoke services using HTTP guide for more information. For example:
Using the HTTPEndpoint resource allows you to use any combination of a root certificate, client certificate and private key according to the authentication requirements of the remote endpoint.
Watch this video on how to use service invocation to call non-Dapr endpoints.
5 - How to: Service invocation across namespaces
Call between services deployed to different namespaces
In this article, you’ll learn how you can call between services deployed to different namespaces. By default, service invocation supports invoking services within the same namespace by simply referencing the app ID (nodeapp):
Service invocation also supports calls across namespaces. On all supported hosting platforms, Dapr app IDs conform to a valid FQDN format that includes the target namespace. You can specify both:
The app ID (nodeapp), and
The namespace the app runs in (production).
Example 1
Call the neworder method on the nodeapp in the production namespace:
When calling an application in a namespace using service invocation, you qualify it with the namespace. This proves useful in cross-namespace calls in a Kubernetes cluster.
Example 2
Call the ping method on myapp scoped to the production namespace:
Call the same ping method as example 2 using a curl command from an external DNS address (in this case, api.demo.dapr.team) and supply the Dapr API token for authentication: