Author Paul Osman offers this overview.

Paul Osman, a passionate advocate of open technology platforms and tools who has been building external and internal platforms for over 10 years, offers this primer.

In a real-world micro-service architecture, services frequently need to invoke other services in order to fulfill a user’s request. A typical user request will commonly create dozens of requests to services in your system.

In large-scale systems, problems arise less often in services themselves and more often in the communication between services. For this reason, you need to carefully consider various challenges in service-to-service communication.

When discussing service-to-service communication, it’s useful to visualize the flow of information in your system. Data flows in both directions–from the client (upstream) to the database, or event bus (downstream) in the form of requests, and back again in the form of responses.

When you refer to upstream services, you describe components of the system that are closer to the user in the flow of information. When you refer to downstream services, you describe components of the system that are further away from the user. In other words, the user makes a request that is routed to a service that then makes requests to other, downstream services, as shown in the following diagram:

In the preceding diagram, the originating user is upstream from the edge-proxy-service, which is upstream from the auth-service, attachment-service, and user-service.

In order to demonstrate the service-to-service communication, you’ll create a simple service that calls another service synchronously using the Spring Boot Java framework. You’ll create a message service that is responsible for sending messages. The message service has to invoke the social graph service in order to determine whether the sender and recipient of a message are friends before allowing a message to be sent. The following simplified diagram illustrates the relationship between services:

As you can see, a POST request comes in from the user to the /message endpoint, which is routed to message-service. The message-service service then makes an HTTP GET request to the social-service service using the /friendships/:idendpoint. The social-service service returns a JSON representation of friendships for a user.

  1. Create a new Java/Gradle project called message-service and add the following content to the build.gradle file:
group 'com.packtpub.microservices'
version '1.0-SNAPSHOT'

buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath group: 'org.springframework.boot', name: 'spring-boot-gradle-plugin', version: '1.5.9.RELEASE'
}
}

apply plugin: 'java'
apply plugin: 'org.springframework.boot'

sourceCompatibility = 1.8

repositories {
mavenCentral()
}

dependencies {
compile group: 'org.springframework.boot', name: 'spring-boot-starter-web'
testCompile group: 'junit', name: 'junit', version: '4.12'
}
  1. Create a new package called packtpub.microservices.ch03.message and a new class called Application. This will be your service’s entry point:
package com.packtpub.microservices.ch03.message;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
  1. Create the model. Create a package called packtpub.microservices.ch03.message.models and a class called Message. This is the internal representation of the message. There’s a lot missing here. You’re not actually persisting the message in this code, as it’s best to keep this example simple:
package com.packtpub.microservices.ch03.message.models;

public class Message {

    private String toUser;
    private String fromUser;
    private String body;

    public Message() {}

    public Message(String toUser, String fromUser, String body) {
        this.toUser = toUser;
        this.fromUser = fromUser;
        this.body = body;
    }

    public String getToUser() {
        return toUser;
    }

    public String getFromUser() {
        return fromUser;
    }

    public String getBody() {
        return body;
    }
}
  1. Create a new package called packtpub.microservices.ch03.message.controllers and a new class called MessageController. At the moment, your controller doesn’t do much except accept the request, parse the JSON, and return the message instance, as you can see from this code:
package com.packtpub.microservices.ch03.message.controllers;

import com.packtpub.microservices.models.Message;
import org.springframework.web.bind.annotation.*;

@RestController
public class MessageController {

    @RequestMapping(
            path="/messages",
            method=RequestMethod.POST,
            produces="application/json")
    public Message create(@RequestBody Message message) {
        return message;
    }
}
  1. Test this basic service by running it and trying to send a simple request:
$ ./gradlew bootRun
Starting a Gradle Daemon, 1 busy Daemon could not be reused, use --status for details

> Task :bootRun

  . ____ _ __ _ _
 /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/ ___)| |_)| | | | | || (_| | ) ) ) )
  ' |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot :: (v1.5.9.RELEASE)

...

Take a look at the following command line:

$ curl -H "Content-Type: application/json" -X POST http://localhost:8080/messages -d'{"toUser": "reader", "fromUser": "paulosman", "body": "Hello, World"}'

{"toUser":"reader","fromUser":"paulosman","body":"Hello, World"}

Now, you have a basic service working, but it’s pretty dumb and not doing much. Add some intelligence by checking with the social service to verify that your two users have a friendship before allowing the message to be sent. For the purposes of this example, imagine you have a working social service that allows you to check for relationships between users with requests:

GET /friendships?username=paulosman&filter=reader

{
  "username": "paulosman",
  "friendships": [
    "reader"
  ]
}
  1. Before you can consume this service, create a model to store its response. In the packtpub.microservices.ch03.message.models package, create a class called UserFriendships:
package com.packtpub.microservices.ch03.message.models;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

import java.util.List;

@JsonIgnoreProperties(ignoreUnknown = true)
public class UserFriendships {
    private String username;
    private List friendships;

    public UserFriendships() {}

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public List getFriendships() {
        return friendships;
    }

    public void setFriendships(List friendships) {
        this.friendships = friendships;
    }
}

 

  1. Modify MessageController, adding a method to get a list of friendships for a user, optionally filtering by a username. Note that you’re hardcoding the URL in this example, which is a bad practice. Take a look at the following code:
private List getFriendsForUser(String username, String filter) {
    String url = "http://localhost:4567/friendships?username=" + username + "&filter=" + filter;
    RestTemplate template = new RestTemplate();


UserFriendships friendships = template.getForObject(url, UserFriendships.class);   return friendships.getFriendships();}
  1. Modify the create If the users are friends, continue and return the message as before; if the users are not friends, the service will respond with a 403indicating that the request is forbidden:
@RequestMapping(
            path="/messages",
            method=RequestMethod.POST,
            produces="application/json")
    public ResponseEntity create(@RequestBody Message message) {
        List friendships = getFriendsForUser(message.getFromUser(), message.getToUser());

        if (friendships.isEmpty())
            return ResponseEntity.status(HttpStatus.FORBIDDEN).build();

        URI location = ServletUriComponentsBuilder
                .fromCurrentRequest().path("/{id}")
                .buildAndExpand(message.getFromUser()).toUri();

        return ResponseEntity.created(location).build();
    }

If you found this article interesting, check out Paul Osman’s Microservices Development Cookbook.   The Cookbook will help you work with a team to break a large, monolithic codebase into independently deployable and scalable micro-services.