This is the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

Service invocation

Perform direct, secure, service-to-service method calls

1 - Service invocation overview

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.

The following overview video and demo demonstrates how Dapr service invocation works.

The diagram below is an overview of how Dapr’s service invocation works between two Dapr-ized applications.

Diagram showing the steps of service invocation
  1. Service A makes an HTTP or gRPC call targeting Service B. The call goes to the local Dapr sidecar.
  2. Dapr discovers Service B’s location using the name resolution component which is running on the given hosting platform.
  3. 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.
  4. 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.
  5. Service B sends a response to Service A. The response goes to Service B’s sidecar.
  6. Dapr forwards the response to Service A’s Dapr sidecar.
  7. 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.

For more information read the service-to-service security article.

Resiliency including retries

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.

For more information read the access control allow lists for service invocation article.

Namespace scoping

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. .

Diagram showing the steps of service invocation

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.

Diagram showing the steps of service invocation described in the table below
  1. Request: “App A” to “Dapr sidecar A”
  2. Request: “Dapr sidecar A” to “Dapr sidecar B”
  3. Request: “Dapr sidecar B” to “App B”
  4. Response: “App B” to “Dapr sidecar B”
  5. Response: “Dapr sidecar B” to “Dapr sidecar A”
  6. 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:

  1. 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.
  2. 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.
  3. Dapr forwards the request to the Node.js app’s sidecar using the location it just received.
  4. 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).
  5. The Node.js app sends a response to the Python app through the Node.js sidecar.
  6. Dapr forwards the response to the Python Dapr sidecar.
  7. 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:

Quickstart/tutorial Description
Service invocation quickstart This quickstart gets you interacting directly with the service invocation building block.
Hello world tutorial This tutorial shows how to use both the service invocation and state management building blocks all running locally on your machine.
Hello world kubernetes tutorial 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)
  • 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.

Next steps

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.

Diagram showing service invocation of example service

Choose an ID for your service

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 --app-protocol http --dapr-http-port 3500 -- python3 checkout/app.py

dapr run --app-id order-processor --app-port 8001  --app-protocol http --dapr-http-port 3501 -- python3 order-processor/app.py

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 --app-protocol https --dapr-http-port 3500 -- python3 checkout/app.py

dapr run --app-id order-processor --app-port 8001 --app-protocol https --dapr-http-port 3501 -- python3 order-processor/app.py
dapr run --app-id checkout --app-protocol http --dapr-http-port 3500 -- npm start

dapr run --app-id order-processor --app-port 5001  --app-protocol http --dapr-http-port 3501 -- npm start

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 -- npm start

dapr run --app-id order-processor --app-port 5001 --dapr-http-port 3501 --app-protocol https -- npm start
dapr run --app-id checkout --app-protocol http --dapr-http-port 3500 -- dotnet run

dapr run --app-id order-processor --app-port 7001 --app-protocol http --dapr-http-port 3501 -- dotnet 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 -- dotnet run

dapr run --app-id order-processor --app-port 7001 --dapr-http-port 3501 --app-protocol https -- dotnet run
dapr run --app-id checkout --app-protocol http --dapr-http-port 3500 -- java -jar target/CheckoutService-0.0.1-SNAPSHOT.jar

dapr run --app-id order-processor --app-port 9001 --app-protocol http --dapr-http-port 3501 -- java -jar target/OrderProcessingService-0.0.1-SNAPSHOT.jar

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 -- java -jar target/CheckoutService-0.0.1-SNAPSHOT.jar

dapr run --app-id order-processor --app-port 9001 --dapr-http-port 3501 --app-protocol https -- java -jar target/OrderProcessingService-0.0.1-SNAPSHOT.jar
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:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: <language>-app
  namespace: default
  labels:
    app: <language>-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: <language>-app
  template:
    metadata:
      labels:
        app: <language>-app
      annotations:
        dapr.io/enabled: "true"
        dapr.io/app-id: "order-processor"
        dapr.io/app-port: "6001"
...

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.

#dependencies
import random
from time import sleep
import logging
import requests

#code
logging.basicConfig(level = logging.INFO) 
while True:
    sleep(random.randrange(50, 5000) / 1000)
    orderId = random.randint(1, 1000)
        #Invoke a service
        result = 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
import axios from "axios";

//code
const daprHost = "127.0.0.1"; 

var main = function() {
    for(var i=0;i<10;i++) {
        sleep(5000);
        var orderId = Math.floor(Math.random() * (1000 - 1) + 1);
        start(orderId).catch((e) => {
            console.error(e);
            process.exit(1);
        });
    }
}

    //Invoke a service
    const result = await axios.post('order-processor' , "orders/" + orderId , axiosConfig);
    console.log("Order requested: " + orderId);
    console.log("Result: " + result.config.data);


function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

main();
//dependencies
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using System.Threading;

//code
namespace EventService
{
  class Program
   {
       static async Task Main(string[] args)
       {
          while(true) {
               await Task.Delay(5000)
               var random = new Random();
               var orderId = random.Next(1,1000);

               //Using Dapr SDK to invoke a method
               var order = new Order(orderId.ToString());

               var httpClient = DaprClient.CreateInvokeHttpClient();
               var response = await httpClient.PostAsJsonAsync("http://order-processor/orders", order);               
               var result = await response.Content.ReadAsStringAsync();
               
               Console.WriteLine("Order requested: " + orderId);
               Console.WriteLine("Result: " + result);
   	    }
       }
   }
}
//dependencies
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.TimeUnit;

//code
@SpringBootApplication
public class CheckoutServiceApplication {
    private static final HttpClient httpClient = HttpClient.newBuilder()
            .version(HttpClient.Version.HTTP_2)
            .connectTimeout(Duration.ofSeconds(10))
            .build();

    public static void main(String[] args) throws InterruptedException, IOException {
        while (true) {
            TimeUnit.MILLISECONDS.sleep(5000);
            Random random = new Random();
            int orderId = random.nextInt(1000 - 1) + 1;

            // Create a Map to represent the request body
            Map<String, Object> requestBody = new HashMap<>();
            requestBody.put("orderId", orderId);
            // Add other fields to the requestBody Map as needed

            HttpRequest request = HttpRequest.newBuilder()
                    .POST(HttpRequest.BodyPublishers.ofString(new JSONObject(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());
        }
    }
}
package main

import (
	"fmt"
	"io"
	"log"
	"math/rand"
	"net/http"
	"os"
	"time"
)

func main() {
	daprHttpPort := os.Getenv("DAPR_HTTP_PORT")
	if daprHttpPort == "" {
		daprHttpPort = "3500"
	}

	client := &http.Client{
		Timeout: 15 * time.Second,
	}

	for i := 0; i < 10; i++ {
		time.Sleep(5000)
		orderId := rand.Intn(1000-1) + 1

		url := fmt.Sprintf("http://localhost:%s/checkout/%v", daprHttpPort, orderId)
		req, err := http.NewRequest(http.MethodGet, url, nil)
		if err != nil {
			panic(err)
		}

		// Adding target app id as part of the header
		req.Header.Add("dapr-app-id", "order-processor")

		// Invoking a service
		resp, err := client.Do(req)
		if err != nil {
			log.Fatal(err.Error())
		}

		b, err := io.ReadAll(resp.Body)
		if err != nil {
			panic(err)
		}

		fmt.Println(string(b))
	}
}

Additional URL formats

To invoke a ‘GET’ endpoint:

curl http://localhost:3602/v1.0/invoke/checkout/method/checkout/100

To avoid changing URL paths as much as possible, Dapr provides the following ways to call the service invocation API:

  1. Change the address in the URL to localhost:<dapr-http-port>.
  2. 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.

For example, the following command:

curl http://localhost:3602/v1.0/invoke/checkout/method/checkout/100

is equivalent to:

curl -H 'dapr-app-id: checkout' 'http://localhost:3602/checkout/100' -X POST

or:

curl 'http://dapr-app-id:checkout@localhost:3602/checkout/100' -X POST

Using CLI:

dapr invoke --app-id checkout --method checkout/100

Including a query string in the URL

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

See the Cross namespace API spec for more information on namespaces.

View traces and logs

Our example above showed you how to directly invoke a different service running locally or in Kubernetes. Dapr:

  • Outputs metrics, tracing, and logging information,
  • Allows you to visualize a call graph between services and log errors, and
  • Optionally, log the payload body.

For more information on tracing and logs, see the observability article.

3 - How-To: Invoke services using gRPC

Call between services using service invocation

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:

  1. Mutual authentication
  2. Tracing
  3. Metrics
  4. Access lists
  5. Network level resiliency
  6. 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.

package main

import (
	"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.
type server struct {
	pb.UnimplementedGreeterServer
}

// SayHello implements helloworld.GreeterServer
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
	log.Printf("Received: %v", in.GetName())
	return &pb.HelloReply{Message: "Hello " + in.GetName()}, nil
}

func main() {
	lis, err := net.Listen("tcp", port)
	if err != nil {
		log.Fatalf("failed to listen: %v", err)
	}
	s := grpc.NewServer()
	pb.RegisterGreeterServer(s, &server{})
	log.Printf("server listening at %v", lis.Addr())
	if err := 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.

package main

import (
	"context"
	"log"
	"time"

	"google.golang.org/grpc"
	pb "google.golang.org/grpc/examples/helloworld/helloworld"
	"google.golang.org/grpc/metadata"
)

const (
	address = "localhost:50007"
)

func main() {
	// Set up a connection to the server.
	conn, err := grpc.Dial(address, grpc.WithInsecure(), grpc.WithBlock())
	if err != nil {
		log.Fatalf("did not connect: %v", err)
	}
	defer conn.Close()
	c := pb.NewGreeterClient(conn)

	ctx, cancel := context.WithTimeout(context.Background(), time.Second*2)
	defer cancel()

	ctx = metadata.AppendToOutgoingContext(ctx, "dapr-app-id", "server")
	r, err := c.SayHello(ctx, &pb.HelloRequest{Name: "Darth Tyrannus"})
	if err != 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:

ctx = metadata.AppendToOutgoingContext(ctx, "dapr-app-id", "server")

All languages supported by gRPC allow for adding metadata. Here are a few examples:

Metadata headers = new Metadata();
Metadata.Key<String> jwtKey = Metadata.Key.of("dapr-app-id", "server");

GreeterService.ServiceBlockingStub stub = GreeterService.newBlockingStub(channel);
stub = MetadataUtils.attachHeaders(stub, header);
stub.SayHello(new HelloRequest() { Name = "Darth Malak" });
var metadata = new Metadata
{
	{ "dapr-app-id", "server" }
};

var call = client.SayHello(new HelloRequest { Name = "Darth Nihilus" }, metadata);
metadata = (('dapr-app-id', 'server'),)
response = stub.SayHello(request={ name: 'Darth Revan' }, metadata=metadata)
const metadata = new grpc.Metadata();
metadata.add('dapr-app-id', 'server');

client.sayHello({ name: "Darth Malgus" }, metadata)
metadata = { 'dapr-app-id' : 'server' }
response = service.sayHello({ 'name': 'Darth Bane' }, metadata)
grpc::ClientContext context;
context.AddMetadata("dapr-app-id", "server");

Run the client using the Dapr CLI

dapr run --app-id client --dapr-grpc-port 50007 -- go run main.go

View telemetry

If you’re running Dapr locally with Zipkin installed, open the browser at http://localhost:9411 and view the traces between the client and server.

Deploying to Kubernetes

Set the following Dapr annotations on your deployment:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: grpc-app
  namespace: default
  labels:
    app: grpc-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: grpc-app
  template:
    metadata:
      labels:
        app: grpc-app
      annotations:
        dapr.io/enabled: "true"
        dapr.io/app-id: "server"
        dapr.io/app-protocol: "grpc"
        dapr.io/app-port: "50051"
...

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:

ctx = metadata.AppendToOutgoingContext(ctx, "dapr-app-id", "server.production")

See the Cross namespace API spec for more information on namespaces.

Step 3: View traces and logs

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.

For example:

ctx = metadata.AppendToOutgoingContext(ctx, "dapr-app-id", "server")
ctx = metadata.AppendToOutgoingContext(ctx, "dapr-stream", "true")
Metadata headers = new Metadata();
Metadata.Key<String> jwtKey = Metadata.Key.of("dapr-app-id", "server");
Metadata.Key<String> jwtKey = Metadata.Key.of("dapr-stream", "true");
var metadata = new Metadata
{
	{ "dapr-app-id", "server" },
	{ "dapr-stream", "true" }
};
metadata = (('dapr-app-id', 'server'), ('dapr-stream', 'true'),)
const metadata = new grpc.Metadata();
metadata.add('dapr-app-id', 'server');
metadata.add('dapr-stream', 'true');
metadata = { 'dapr-app-id' : 'server' }
metadata = { 'dapr-stream' : 'true' }
grpc::ClientContext context;
context.AddMetadata("dapr-app-id", "server");
context.AddMetadata("dapr-stream", "true");

Streaming gRPCs and Resiliency

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.

Community call demo

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:

  1. Is this a named HTTPEndpoint resource?
  2. Is this an FQDN URL with anhttp:// or https:// prefix?
  3. 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.

Diagram showing the steps of service invocation to non-Dapr endpoints
  1. Service A makes an HTTP call targeting Service B, a non-Dapr endpoint. The call goes to the local Dapr sidecar.
  2. Dapr discovers Service B’s location using the HTTPEndpoint or FQDN URL then forwards the message to Service B.
  3. Service B sends a response to Service A’s Dapr sidecar.
  4. 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.

    localhost:3500/v1.0/invoke/<HTTPEndpoint-name>/method/<my-method>
    

    For example, with an HTTPEndpoint resource called “palpatine” and a method called “Order66”, this would be:

    curl http://localhost:3500/v1.0/invoke/palpatine/method/order66
    
  • A FQDN URL to the non-Dapr endpoint.

    localhost:3500/v1.0/invoke/<URL>/method/<my-method>
    

    For example, with an FQDN resource called https://darthsidious.starwars, this would be:

    curl http://localhost:3500/v1.0/invoke/https://darthsidious.starwars/method/order66
    

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:

localhost:3500/v1.0/invoke/<appID>/method/<my-method>
curl http://localhost:3602/v1.0/invoke/orderprocessor/method/checkout

TLS authentication

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.

Example using root certificate

apiVersion: dapr.io/v1alpha1
kind: HTTPEndpoint
metadata:
  name: "external-http-endpoint-tls"
spec:
  baseUrl: https://service-invocation-external:443
  headers:
  - name: "Accept-Language"
    value: "en-US"
  clientTLS:
    rootCA:
      secretKeyRef:
        name: dapr-tls-client
        key: ca.crt

Example using client certificate and private key

apiVersion: dapr.io/v1alpha1
kind: HTTPEndpoint
metadata:
  name: "external-http-endpoint-tls"
spec:
  baseUrl: https://service-invocation-external:443
  headers:
  - name: "Accept-Language"
    value: "en-US"
  clientTLS:
    certificate:
      secretKeyRef:
        name: dapr-tls-client
        key: tls.crt
    privateKey:
      secretKeyRef:
        name: dapr-tls-key
        key: tls.key

Community call demo

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):

localhost:3500/v1.0/invoke/nodeapp/method/neworder

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:

localhost:3500/v1.0/invoke/nodeapp.production/method/neworder

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:

https://localhost:3500/v1.0/invoke/myapp.production/method/ping

Example 3

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:

MacOS/Linux:

curl -i -d '{ "message": "hello" }' \
     -H "Content-type: application/json" \
     -H "dapr-api-token: ${API_TOKEN}" \
     https://api.demo.dapr.team/v1.0/invoke/myapp.production/method/ping