Software Development

Microservices Architecture Tutorial

1. Introduction

Microservices architecture builds an application with many loosely coupled and independently deployable artifacts. In this example, I will demonstrate a simplified microservices architecture with two services.

microservices architecture - simplified msa
Figure 1 Simplified MSA
  • The course service runs at port 9001.
  • The student service runs at port 9002.
  • The student service depends on the course service.
  • The Eureka Server runs at port 8761.

2. Technologies Used

The example code in this article was built and run using:

  • Java 11
  • Maven 3.3.9
  • Spring Tools Suite (STS) 4
  • Spring Boot, Spring Cloud, Spring Data, and Spring Web
  • Postman

3. Spring Cloud Eureka Server

Spring Cloud Eureka server provides service registration and discovery. It makes microservices easier to manage as each microservice registers with a server and can find the needed services without knowing the service’s location.

In this step, I will create a maven project from https://start.spring.io/ with a Eureka Server dependency. Then import the generated project into STS workspace. Finally alter the generated MsServiceregistraapplication.java with @EnableEurekaServer and configure a Eureka server.

microservices architecture - eureka server
Figure 2 Eureka Server Project

3.1 Pom.xml

There is no change at the generated pom.xml file.

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.5.2</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>org.jcg.zheng.ms.service-registry</groupId>
	<artifactId>ms-service-registry</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>ms-service-registry</name>
	<description>Demo project for Eureka Server</description>
	<properties>
		<java.version>11</java.version>
		<spring-cloud.version>2020.0.3</spring-cloud.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>
	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>${spring-cloud.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

3.2 Service Registry Application

In this step, I will update the generated MsServiceRegistryApplication.java file by adding the @EnableEurekaServer annotation.

MsServiceRegistryApplication.java

package org.jcg.zheng.ms.serviceregistry.msserviceregistry;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@SpringBootApplication
@EnableEurekaServer
public class MsServiceRegistryApplication {

	public static void main(String[] args) {
		SpringApplication.run(MsServiceRegistryApplication.class, args);
	}

}
  • line 8 – @EnableEurekaServer annotation marks it as a Eureka Server.

3.3 Configuration

In this step, I will add the Eureka Server configuration in the application.properties file.

application.properties

server.port=8761
euraka.client.register-with-eureka=false
euraka.client.fetch-registry=false

spring.application.name=EUREKA-SERVER

3.4 Start the Server

In this step, I will start the server and verify the server log.

Server.log

[2m2021-07-17 20:18:07.766[0;39m [32m INFO[0;39m [35m59992[0;39m [2m---[0;39m [2m[       Thread-9][0;39m [36mc.n.e.registry.AbstractInstanceRegistry [0;39m [2m:[0;39m Registered instance EUREKA-SERVER/USA-X1082608.ctl.intranet:EUREKA-SERVER:8761 with status UP (replication=true)
[2m2021-07-17 20:18:07.766[0;39m [32m INFO[0;39m [35m59992[0;39m [2m---[0;39m [2m[       Thread-9][0;39m [36mc.n.e.r.PeerAwareInstanceRegistryImpl   [0;39m [2m:[0;39m Got 1 instances from neighboring DS node
[2m2021-07-17 20:18:07.766[0;39m [32m INFO[0;39m [35m59992[0;39m [2m---[0;39m [2m[       Thread-9][0;39m [36mc.n.e.r.PeerAwareInstanceRegistryImpl   [0;39m [2m:[0;39m Renew threshold is: 1
[2m2021-07-17 20:18:07.767[0;39m [32m INFO[0;39m [35m59992[0;39m [2m---[0;39m [2m[       Thread-9][0;39m [36mc.n.e.r.PeerAwareInstanceRegistryImpl   [0;39m [2m:[0;39m Changing status to UP
[2m2021-07-17 20:18:07.779[0;39m [32m INFO[0;39m [35m59992[0;39m [2m---[0;39m [2m[       Thread-9][0;39m [36me.s.EurekaServerInitializerConfiguration[0;39m [2m:[0;39m Started Eureka Server

3.5 Eureka Server Dashboard

Open a web browser and navigate to the Eureka Server dashboard at http://localhost:8761. It should display the service’s details.

Next, I will create two maven projects from https://start.spring.io/: ms-course-services and ms-student-services. Both projects have the following dependencies: Eureka Discovery Client, Spring Web, Spring Data JPA, and H2 Database. Here is the ms-student-services project creation screenshot.

microservices architecture - web project
Figure 3 Microservices Web Project

4. Course Services

In this step, I will import ms-course-services into STS and add a simple Rest Course service to save and retrieve a course via the Spring web library.

4.1 Pom.xml

There is no change at the generated pom.xml file.

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.5.2</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>org.jcg.zheng.ms.course</groupId>
	<artifactId>ms-course-services</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>ms-course-services</name>
	<description>Demo project for Course Service</description>
	<properties>
		<java.version>11</java.version>
		
		<!--for Spring Cloud -->
		<spring-cloud.version>2020.0.3</spring-cloud.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		
		<!--for Spring Cloud -->
	 	<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
		</dependency> 

		<dependency>
			<groupId>com.h2database</groupId>
			<artifactId>h2</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>
	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>${spring-cloud.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
				<configuration>
					<excludes>
						<exclude>
							<groupId>org.projectlombok</groupId>
							<artifactId>lombok</artifactId>
						</exclude>
					</excludes>
				</configuration>
			</plugin>
		</plugins>
	</build>

</project>

4.2 Course Services Application

In this step, I will update the generated MsCourseServicesApplication.java file by adding the @EnableEurekaClient and @EnableJpaRepositories annotations.

MsCourseServicesApplication.java

package org.jcg.zheng.ms.course;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;

@SpringBootApplication
@EnableJpaRepositories(basePackages = "org.jcg.zheng.ms.course")
@EnableEurekaClient
public class MsCourseServicesApplication {

	public static void main(String[] args) {
		SpringApplication.run(MsCourseServicesApplication.class, args);
	}

}
  • line 10 – @EnableEurekaClient marks it as a Eureka Client.

4.3 Configuration

In this step, I will add the Eureka client configuration in the application.properties file.

application.properties

server.port=9001
spring.jackson.serialization.fail-on-empty-beans=false

spring.application.name=COURSE-SERVICE

euraka.client.register-with-eureka=true
euraka.client.fetch-registry=true
euraka.client.service-url.defaultZone=http://localhost:8761/euraka/
euraka.instance.hostname=localhost
  • line 4 – Defines an application name so can be used to find service.
  • line 8 – Eureka server URL.

4.4 Course Web Controller

In this step, I will create a Rest service via Spring web annotations: @RestController, @RequestMapping, @RequestBody, @PathVariable, @PostMapping, and @GetMapping.

CourseController.javao

package org.jcg.zheng.ms.course.controller;

import org.jcg.zheng.ms.course.service.CourseService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/courses")
public class CourseController {

	@Autowired
	private CourseService courseService;

	@PostMapping("/")
	public CourseDO saveCourse(@RequestBody CourseDO course) {
		return courseService.save(course);
	}

	@GetMapping("/{id}")
	public CourseDO getCourse(@PathVariable("id") Long courseId) {
		return courseService.findById(courseId);
	}
}

The Rest service transforms the CourseDO from the Course Entity.

CourseDO.java

package org.jcg.zheng.ms.course.controller;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

@JsonIgnoreProperties(ignoreUnknown = true)
public class CourseDO {

	private static final long serialVersionUID = 1L;

	private Long id;
	private String description;
	private String name;

	public Long getId() {
		return id;
	}

	public void setId(Long id) {
		this.id = id;
	}

	public String getDescription() {
		return description;
	}

	public void setDescription(String description) {
		this.description = description;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
}

4.5 Course Service

In this step, I will create a CourseService class which autowired a CourseRepository to save and retrieve course information. Please reference Spring data JPA for detail about how to use Spring data JPA.

CourseService.java

package org.jcg.zheng.ms.course.service;

import org.jcg.zheng.ms.course.controller.CourseDO;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class CourseService {

	@Autowired
	private CourseRepository courseRepo;

	public CourseDO save(CourseDO course) {
		Course courseDto = new Course();
		BeanUtils.copyProperties(course, courseDto);
		courseDto = courseRepo.save(courseDto);
		course.setId(courseDto.getId());
		return course;
	}

	public CourseDO findById(Long courseId) {
		CourseDO courseDo = new CourseDO();
		try {
			Course courseDto = courseRepo.getById(courseId);
			BeanUtils.copyProperties(courseDto, courseDo);
		} catch (Exception e) {
			e.printStackTrace();
		}

		return courseDo;

	}

}

Here is the CourseRepostiory.java interface.

CourseRepository.java

package org.jcg.zheng.ms.course.service;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface CourseRepository extends JpaRepository<Course, Long> {

}

Here is the Course entity class.

Course.java

package org.jcg.zheng.ms.course.service;

import java.io.Serializable;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
public class Course implements Serializable {

	public Long getId() {
		return id;
	}

	public void setId(Long id) {
		this.id = id;
	}

	public String getDescription() {
		return description;
	}

	public void setDescription(String description) {
		this.description = description;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	private static final long serialVersionUID = 1L;
	@Id
	@GeneratedValue
	private Long id;
	private String description;
	private String name;
}

4.6 Start the Server

In this step, I will start the ms-course-service and verify the server log.

Server Log

2021-07-18 20:19:09.317[0;39m [32m INFO[0;39m [35m59992[0;39m [2m---[0;39m [2m[nio-8761-exec-7][0;39m [36mc.n.e.registry.AbstractInstanceRegistry [0;39m [2m:[0;39m Registered instance COURSE-SERVICE/USA-X1082608.ctl.intranet:COURSE-SERVICE:9001 with status UP (replication=true)
[2m2021-07-18 20:19:27.516[0;39m [32m INFO[0;39m [35m59992[0;39m [2m---[0;39m [2m[a-EvictionTimer][0;39m [36mc.n.e.registry.AbstractInstanceRegistry [0;39m [2m:[0;39m Running the evict task with compensationTime 0ms

4.7 Test with Postman

In this step, I will create a course with with Postman.

save a math course via POST

POST /courses/ HTTP/1.1
Host: localhost:9001
Content-Type: application/json
Cache-Control: no-cache
Postman-Token: 393a505b-4c05-9d41-99f1-a504ebfc5fe1

{
"name":"math",
"description":"advance math 1001 for freshman"

}

retrieve a course via GET

GET /courses/1 HTTP/1.1
Host: localhost:9001
Content-Type: application/json
Cache-Control: no-cache
Postman-Token: fdfac0f0-3e07-16ae-a961-45bd684310c8

Get course service’s response.

{
    "id": 1,
    "description": "advance math 1001 for freshman",
    "name": "math"
}

5. Student Service

In this step, I will create a simple Rest service to get the student details via the Spring web library. This service also depends on the course service created at step 4.

5.1 Pom.xml

There is no change at the generated pom.xml file.

Pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.5.2</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>org.jcg.zheng.ms.student</groupId>
	<artifactId>ms-student-services</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>ms-student-services</name>
	<description>Demo project for Student Service</description>
	<properties>
		<java.version>11</java.version>
		<spring-cloud.version>2020.0.3</spring-cloud.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
	 	<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
		</dependency>

		<dependency>
			<groupId>com.h2database</groupId>
			<artifactId>h2</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>
	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>${spring-cloud.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
				<configuration>
					<excludes>
						<exclude>
							<groupId>org.projectlombok</groupId>
							<artifactId>lombok</artifactId>
						</exclude>
					</excludes>
				</configuration>
			</plugin>
		</plugins>
	</build>

</project>

5.2 Student Service Application

In this step, I will update the generated MsStudentServicesApplication.java file by adding @EnableEurekaClient and @EnableJpaRepositories annotations. I will add a RestTemplate bean to access the student service.

MsStudentServicesApplication.java

package org.jcg.zheng.ms.student;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
@EnableJpaRepositories(basePackages = "org.jcg.zheng.ms.student")
@EnableEurekaClient
public class MsStudentServicesApplication {

	public static void main(String[] args) {
		SpringApplication.run(MsStudentServicesApplication.class, args);
	}

	@Bean
	@LoadBalanced
	public RestTemplate restTemplate() {
		return new RestTemplate();
	}
}

5.3 Configuration

In this step, I will add the Eureka Client and course service endpoint configuration in the application.properties file.

application.properties

server.port=9002
spring.jackson.serialization.fail-on-empty-beans=false

spring.application.name=STUDENT-SERVICE

# host:port is connecting to courseService directly
#courseservice.uri=http://localhost:9001/courses/

# host:port is connecting to courseService directly
courseservice.uri=http://COURSE-SERVICE/courses/

euraka.client.register-with-eureka=true
euraka.client.fetch-registry=true
euraka.client..service-url.defaultZone=http://localhost:8761/euraka/
euraka.instance.hostname=localhost
  • line 7 – The course service is specified with hostname and port. Commented as line 10 is better way to find a service.
  • line 10 – The course service is discovered by Eureka Server via the application name.

5.4 Student Web Controller

In this step, I will create a Rest controller which creates and retrieves a student’s data.

StudentController.java

package org.jcg.zheng.ms.student.controller;

import org.jcg.zheng.ms.student.service.StudentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/students")
public class StudentController {

	@Autowired
	private StudentService studentService;

	@GetMapping("/{id}")
	public StudentDO get(@PathVariable("id") Long studentId) {
		return studentService.findById(studentId);
	}

	@PostMapping("/")
	public StudentDO save(@RequestBody StudentDO student) {
		return studentService.save(student);
	}

}

StudentDO.java

package org.jcg.zheng.ms.student.controller;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

@JsonIgnoreProperties(ignoreUnknown = true)
public class StudentDO {

	private static final long serialVersionUID = 1L;

	private Long id;

	private String firstName;

	private String lastName;

	private Long courseId;

	private Course courseDetail;


	public Course getCourseDetail() {
		return courseDetail;
	}

	public Long getCourseId() {
		return courseId;
	}

	public String getFirstName() {
		return firstName;
	}

	public Long getId() {
		return id;
	}

	public String getLastName() {
		return lastName;
	}

	public void setCourseDetail(Course coureDetail) {
		this.courseDetail = coureDetail;
	}
	public void setCourseId(Long courseId) {
		this.courseId = courseId;
	}
	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}

	public void setId(Long id) {
		this.id = id;
	}

	public void setLastName(String lastName) {
		this.lastName = lastName;
	}

}

Here is the Course class which should be same as the CourseDO class in the ms-course-services project. Microservices reuse the source code to cut the dependencies among projects.

Course.java

package org.jcg.zheng.ms.student.controller;

public class Course {

	private static final long serialVersionUID = 1L;

	private Long id;

	private String description;

	private String name;

	public String getDescription() {
		return description;
	}

	public Long getId() {
		return id;
	}

	public String getName() {
		return name;
	}

	public void setDescription(String description) {
		this.description = description;
	}
	public void setId(Long id) {
		this.id = id;
	}
	public void setName(String name) {
		this.name = name;
	}

}

5.5 Student Service

In this step, I will create a StudentService class which uses StudentRepository to save and find a student.

StudentRepository.java

package org.jcg.zheng.ms.student.service;

import org.jcg.zheng.ms.student.controller.Course;
import org.jcg.zheng.ms.student.controller.StudentDO;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

@Service
public class StudentService {

	@Autowired
	private StudentRepository courseRepo;

	@Autowired
	private RestTemplate restTemplate;

	@Value("${courseservice.uri}")
	private String courseServiceUri;

	public StudentDO findById(Long studentId) {
		StudentDO studentDO = new StudentDO();
		try {
			Student student = courseRepo.getById(studentId);
			BeanUtils.copyProperties(student, studentDO);
			if (student.getCourseId() != null) {
				String courseGetUrl = courseServiceUri + student.getCourseId().longValue();
				System.out.println("courseServiceUri:" + courseServiceUri);
				System.out.println("Get Course-Service endpoint:" + courseGetUrl);
				Course c = restTemplate.getForObject(courseGetUrl, Course.class);
				studentDO.setCourseDetail(c);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}

		return studentDO;

	}

	public StudentDO save(StudentDO student) {
		Student stuDto = new Student();
		BeanUtils.copyProperties(student, stuDto);
		stuDto = courseRepo.save(stuDto);
		student.setId(stuDto.getId());
		return student;
	}

}

Here is the StudentRepostitory interface.

StudentRepository.java

package org.jcg.zheng.ms.student.service;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface StudentRepository extends JpaRepository<Student, Long> {

}

Here is the Student entity.

Student.java

package org.jcg.zheng.ms.student.service;

import java.io.Serializable;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
public class Student implements Serializable {

	private static final long serialVersionUID = 1L;

	@Id
	@GeneratedValue
	private Long id;

	private String firstName;

	private String lastName;

	private Long courseId;

	public Long getCourseId() {
		return courseId;
	}

	public String getFirstName() {
		return firstName;
	}

	public Long getId() {
		return id;
	}


	public String getLastName() {
		return lastName;
	}
	public void setCourseId(Long courseId) {
		this.courseId = courseId;
	}
	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}
	public void setId(Long id) {
		this.id = id;
	}
	public void setLastName(String lastName) {
		this.lastName = lastName;
	}
}

5.6 Start the Server

Start the ms-student-service spring boot application and verify the server log.

ms-student-server server log

2021-07-19 21:10:14.818[0;39m [32m INFO[0;39m [35m101236[0;39m [2m---[0;39m [2m[           main][0;39m [36mo.s.b.w.embedded.tomcat.TomcatWebServer [0;39m [2m:[0;39m Tomcat started on port(s): 9002 (http) with context path ''
[2m2021-07-19 21:10:14.819[0;39m [32m INFO[0;39m [35m101236[0;39m [2m---[0;39m [2m[           main][0;39m [36m.s.c.n.e.s.EurekaAutoServiceRegistration[0;39m [2m:[0;39m Updating port to 9002
[2m2021-07-19 21:10:14.894[0;39m [32m INFO[0;39m [35m101236[0;39m [2m---[0;39m [2m[nfoReplicator-0][0;39m [36mcom.netflix.discovery.DiscoveryClient   [0;39m [2m:[0;39m DiscoveryClient_STUDENT-SERVICE/USA-X1082608.ctl.intranet:STUDENT-SERVICE:9002 - registration status: 204
[2m2021-07-19 21:10:15.895[0;39m [32m INFO[0;39m [35m101236[0;39m [2m---[0;39m [2m[           main][0;39m [36mo.j.z.m.s.MsStudentServicesApplication  [0;39m [2m:[0;39m Started MsStudentServicesApplication in 17.152 seconds (JVM running for 23.527)

5.7 Eureka Server Dashboard

Navigate to http://localhost:8761. It should display both course and student services now.

Figure 4 Eureka Server with Two Services

6. Postman Test

6.1 Save a Student

Open a Postman and enter a POST request to save a student.

POST /students/ HTTP/1.1
Host: localhost:9002
Accept: application/json
Content-Type: application/json
Cache-Control: no-cache
Postman-Token: 67fccb7f-1326-f31b-d404-f848e6e871e5

{
"firstName":"Mary",
"lastName":"Zheng",
"courseId":1

}

You should receive a 200 ok status.

6.2 Find a Student

Open a Postman and enter a GET request to retrieve a student along with its course information.

GET /students/1 HTTP/1.1
Host: localhost:9002
Accept: application/json
Content-Type: application/json
Cache-Control: no-cache
Postman-Token: 76339b48-80ac-38f7-2334-0ae4aa30c600

You should get 200 ok status along with the student’s course information which comes from the course service.

Get Student Service Response

{
    "id": 1,
    "firstName": "Mary",
    "lastName": "Zheng",
    "courseId": 1,
    "courseDetail": {
        "id": 1,
        "description": "advance math 1001 for freshman",
        "name": "math"
    }
}

Verify the ms-student-service server log, you should see the Course service URL is http://COURSE-SERVICE instead of http://localhost:9001.

ms-student-service log

2021-07-20 22:47:51.835[0;39m [32m INFO[0;39m [35m49164[0;39m [2m---[0;39m [2m[nio-9002-exec-1][0;39m [36mo.s.web.servlet.DispatcherServlet       [0;39m [2m:[0;39m Completed initialization in 4 ms
courseServiceUri:http://COURSE-SERVICE/courses/
Get Course-Service endpoint:http://COURSE-SERVICE/courses/1

7. Summary

The key principle of microservices architecture is the independent deployment. In this tutorial, I created three maven projects.

Figure 5 Three Projects

I demonstrated how to:

  • Build two simple microservices via Spring web library.
  • Create a spring-cloud Eureka server.
  • Register microservices with a Eureka server.
  • Find a microservice from a Eureka server based on the service name.

8. Download the Source Code

Download
You can download the full source code of this example here: Microservices Architecture Tutorial

Mary Zheng

Mary has graduated from Mechanical Engineering department at ShangHai JiaoTong University. She also holds a Master degree in Computer Science from Webster University. During her studies she has been involved with a large number of projects ranging from programming and software engineering. She works as a senior Software Engineer in the telecommunications sector where she acts as a leader and works with others to design, implement, and monitor the software solution.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

3 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
zahid
3 years ago

Just the architectural demo I was looking for

zahid
3 years ago

You have typos in your application.properties file causing errors
eureka is misspelled euraka

Back to top button