概述 下麵我們將學習如何創建多個 Spring boot 微服務以及如何使用 RestTemplate 類在多個微服務之間進行同步通信。 微服務通信有兩種風格: 同步通訊 非同步通信 同步通訊 在同步通信的情況下,客戶端發送請求並等待服務的響應。這裡重要的一點是協議(HTTP/HTTPS)是同步的,客 ...
概述
下麵我們將學習如何創建多個 Spring boot 微服務以及如何使用 RestTemplate 類在多個微服務之間進行同步通信。
微服務通信有兩種風格:
- 同步通訊
- 非同步通信
同步通訊
在同步通信的情況下,客戶端發送請求並等待服務的響應。這裡重要的一點是協議(HTTP/HTTPS)是同步的,客戶端代碼只有在收到 HTTP 伺服器響應時才能繼續其任務。
例如,Microservice1 作為客戶端發送請求並等待 Microservice2 的響應。
我們可以使用 RestTemplate 或 WebClient 或 Spring Cloud Open Feign 庫來同步通信多個微服務。
非同步通信
在非同步通信的情況下,客戶端發送請求並且不等待服務的響應。客戶端將繼續執行其任務 - 它不會等待服務的響應。
例如, 微服務1 作為客戶端發送請求,並不等待 微服務2 的響應。
我們可以使用RabbitMQ和Apache Kafka等消息代理在多個微服務之間進行非同步通信。
我們需要做什麼
下麵我們將創建兩個微服務,例如部門服務和用戶服務,並且我們將從用戶服務到部門服務進行 REST API 調用以獲取特定的用戶部門。
並且每個微服務創建一個單獨的 MySQL 資料庫。
在 IntelliJ IDEA 中創建並設置兩個 Spring boot 項目作為兩個微服務。
1.創建DepartmentService微服務
首先 在 IntelliJ IDEA 中創建並設置部門服務Spring boot 項目
1.在IntelliJ IDEA中創建並設置spring boot項目(部門服務)
我們使用 springinitializr創建一個 Spring boot 項目。
請查看下麵的屏幕截圖,在使用 springinitializr創建 Spring Boot 應用程式時輸入詳細信息 :
點擊“GENERATE”按鈕以 zip 文件形式下載 Spring boot 項目。解壓zip文件併在IntelliJ IDEA中導入Spring boot項目。
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.7.17</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>io.wz</groupId>
<artifactId>department-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>department-service</name>
<description>department-service</description>
<properties>
<java.version>1.8</java.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>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<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>
DepartmentService - 配置 MySQL 資料庫
由於我們使用 MySQL 作為資料庫,因此我們需要配置 URL、用戶名和密碼,以便 Spring boot 在啟動時可以與資料庫建立連接。
打開 src/main/resources/application.properties
文件並向其中添加以下屬性:
spring.datasource.url=jdbc:mysql://localhost:3306/department_db
spring.datasource.username=root
spring.datasource.password=Mysql@123
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect
spring.jpa.hibernate.ddl-auto=update
這裡註意修改的MySQL密碼,另外在 MySQL 中創建一個名為Department_db的資料庫 。
不需要創建任何表。Hibernate 將根據我們將在下一步中定義的Department實體自動創建這些表 。這是通過屬性 spring.jpa.hibernate.ddl-auto = update 自動實現的。
DepartmentService - 創建部門 JPA 實體
package io.wz.departmentservice.entity;
import javax.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Entity
@Table(name = "departments")
@NoArgsConstructor
@AllArgsConstructor
@Setter
@Getter
public class Department {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String departmentName;
private String departmentAddress;
private String departmentCode;
}
DepartmentService - 創建 Spring Data JPA 存儲庫
package io.wz.departmentservice.repository;
import io.wz.departmentservice.entity.Department;
import org.springframework.data.jpa.repository.JpaRepository;
public interface DepartmentRepository extends JpaRepository<Department, Long> {
}
DepartmentService - 創建服務層
DepartmentService
package io.wz.departmentservice.service;
import io.wz.departmentservice.entity.Department;
public interface DepartmentService {
Department saveDepartment(Department department);
Department getDepartmentById(Long departmentId);
}
DepartmentServiceImpl 類
package io.wz.departmentservice.service.impl;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import io.wz.departmentservice.entity.Department;
import io.wz.departmentservice.repository.DepartmentRepository;
import io.wz.departmentservice.service.DepartmentService;
import org.springframework.stereotype.Service;
@Service
@AllArgsConstructor
@Slf4j
public class DepartmentServiceImpl implements DepartmentService {
private DepartmentRepository departmentRepository;
@Override
public Department saveDepartment(Department department) {
return departmentRepository.save(department);
}
@Override
public Department getDepartmentById(Long departmentId) {
return departmentRepository.findById(departmentId).get();
}
}
DepartmentService - 創建Controller層
DepartmentController
package io.wz.departmentservice.controller;
import lombok.AllArgsConstructor;
import io.wz.departmentservice.entity.Department;
import io.wz.departmentservice.service.DepartmentService;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("api/departments")
@AllArgsConstructor
public class DepartmentController {
private DepartmentService departmentService;
@PostMapping
public ResponseEntity<Department> saveDepartment(@RequestBody Department department){
Department savedDepartment = departmentService.saveDepartment(department);
return new ResponseEntity<>(savedDepartment, HttpStatus.CREATED);
}
@GetMapping("{id}")
public ResponseEntity<Department> getDepartmentById(@PathVariable("id") Long departmentId){
Department department = departmentService.getDepartmentById(departmentId);
return ResponseEntity.ok(department);
}
}
DepartmentService - 啟動 Spring Boot 應用程式
我們可以通過兩種方式啟動獨立的 Spring boot 應用程式。
- 從應用程式的根目錄並鍵入以下命令來運行它 -
$ mvn spring-boot:run
- 從 IDE 中,將
DepartmentServiceApplication.main()
方法作為獨立 Java 類運行,該方法將在埠 8080 上啟動嵌入式 Tomcat 伺服器並將瀏覽器指向 http://localhost:8080/。
DepartmentService - 使用 Postman 客戶端測試 REST API
保存部門 REST API:
獲取單個部門 REST API:
2.創建UserService微服務
我們首先在 IntelliJ IDEA 中創建並設置UserService
Spring boot 項目
1.在IntelliJ IDEA中創建並設置spring boot項目(用戶服務)
使用 springinitializr創建一個 Spring boot 項目。
請參閱下麵的屏幕截圖,在使用 springinitializr創建 Spring Boot 應用程式時輸入詳細信息 :
單擊“GENRATE”按鈕以 zip 文件形式下載 Spring boot 項目。解壓zip文件併在IntelliJ IDEA中導入Spring boot項目。
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.7.17</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>io.wz</groupId>
<artifactId>user-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>user-service</name>
<description>user-service</description>
<properties>
<java.version>1.8</java.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>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<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>
UserService - 配置 MySQL 資料庫
打開 src/main/resources/application.properties
文件並向其中添加以下屬性:
spring.datasource.url=jdbc:mysql://localhost:3306/employee_db
spring.datasource.username=root
spring.datasource.password=Mysql@123
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect
spring.jpa.hibernate.ddl-auto=update
這裡註意修改的MySQL密碼,另外在 MySQL 中創建一個名為employee_db的資料庫 。
不需要創建任何表。Hibernate 將根據我們將在下一步中定義的User實體自動創建這些表 。這是通過屬性 spring.jpa.hibernate.ddl-auto = update 自動實現的。
UserService - 更改伺服器埠
註意,部門服務 Spring boot 項目運行在預設的 tomcat 伺服器埠 8080 上。
對於用戶服務,我們需要使用以下屬性將嵌入式 tomcat 伺服器埠更改為 8081:
server.port = 8081
UserService - 創建用戶 JPA 實體
package io.wz.userservice.entity;
import javax.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Entity
@Table(name = "users")
@Setter
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String firstName;
private String lastName;
@Column(nullable = false, unique = true)
private String email;
private String departmentId;
}
UserService - 創建 Spring Data JPA 存儲庫
package io.wz.userservice.repository;
import io.wz.userservice.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserRepository extends JpaRepository<User, Long> {
}
UserService - 創建 DTO 類
DepartmentDto
package io.wz.userservice.dto;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Setter
@Getter
@AllArgsConstructor
@NoArgsConstructor
public class DepartmentDto {
private Long id;
private String departmentName;
private String departmentAddress;
private String departmentCode;
}
UserDto
package io.wz.userservice.dto;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Setter
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class UserDto {
private Long id;
private String firstName;
private String lastName;
private String email;
}
ResponseDto
package io.wz.userservice.dto;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Setter
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class ResponseDto {
private DepartmentDto department;
private UserDto user;
}
UserService - 將 RestTemplate 配置為 Spring Bean
將 RestTemplate 類配置為 Spring bean,以便我們可以註入並使用它。
package io.wz.userservice;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
public class UserServiceApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceApplication.class, args);
}
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
UserService - 創建服務層
用戶服務介面
package io.wz.userservice.service;
import io.wz.userservice.dto.ResponseDto;
import io.wz.userservice.entity.User;
public interface UserService {
User saveUser(User user);
ResponseDto getUser(Long userId);
}
UserServiceImpl class
package io.wz.userservice.service.impl;
import lombok.AllArgsConstructor;
import io.wz.userservice.dto.DepartmentDto;
import io.wz.userservice.dto.ResponseDto;
import io.wz.userservice.dto.UserDto;
import io.wz.userservice.entity.User;
import io.wz.userservice.repository.UserRepository;
import io.wz.userservice.service.UserService;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
@Service
@AllArgsConstructor
public class UserServiceImpl implements UserService {
private UserRepository userRepository;
private RestTemplate restTemplate;
@Override
public User saveUser(User user) {
return userRepository.save(user);
}
@Override
public ResponseDto getUser(Long userId) {
ResponseDto responseDto = new ResponseDto();
User user = userRepository.findById(userId).get();
UserDto userDto = mapToUser(user);
ResponseEntity<DepartmentDto> responseEntity = restTemplate
.getForEntity("http://localhost:8080/api/departments/" + user.getDepartmentId(),
DepartmentDto.class);
DepartmentDto departmentDto = responseEntity.getBody();
System.out.println(responseEntity.getStatusCode());
responseDto.setUser(userDto);
responseDto.setDepartment(departmentDto);
return responseDto;
}
private UserDto mapToUser(User user){
UserDto userDto = new UserDto();
userDto.setId(user.getId());
userDto.setFirstName(user.getFirstName());
userDto.setLastName(user.getLastName());
userDto.setEmail(user.getEmail());
return userDto;
}
}
請註意,以上我們使用RestTemplate對部門服務進行 REST API 調用:
ResponseEntity<DepartmentDto> responseEntity = restTemplate
.getForEntity("http://localhost:8080/api/departments/" + user.getDepartmentId(),
DepartmentDto.class);
UserService - 創建控制器層:UserController
package io.wz.userservice.controller;
import lombok.AllArgsConstructor;
import io.wz.userservice.dto.ResponseDto;
import io.wz.userservice.entity.User;
import io.wz.userservice.service.UserService;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("api/users")
@AllArgsConstructor
public class UserController {
private UserService userService;
@PostMapping
public ResponseEntity<User> saveUser(@RequestBody User user){
User savedUser = userService.saveUser(user);
return new ResponseEntity<>(savedUser, HttpStatus.CREATED);
}
@GetMapping("{id}")
public ResponseEntity<ResponseDto> getUser(@PathVariable("id") Long userId){
ResponseDto responseDto = userService.getUser(userId);
return ResponseEntity.ok(responseDto);
}
}
UserService - 啟動 Spring Boot 應用程式
我們可以通過兩種方式啟動獨立的 Spring boot 應用程式。
- 從應用程式的根目錄並鍵入以下命令來運行它 -
$ mvn spring-boot:run
- 在 IDE 中,將
UserServiceApplication.main()
方法作為獨立 Java 類運行,該方法將在埠 8080 上啟動嵌入式 Tomcat 伺服器並將瀏覽器指向http://localhost:8081/
。
UserService - 使用 Postman 客戶端測試 REST API
保存用戶 REST API:
獲取用戶 REST API:
請註意,響應包含用戶的部門。這說明我們已成功從 UserService 到 DepartmentService 進行 REST API 調用。
結論
在本教程中,我們學習瞭如何創建多個 Spring boot 微服務以及如何使用RestTemplate類在多個微服務之間進行同步通信。
從 5.0 開始, RestTemplate 類處於維護模式,很快就會被棄用。因此 Spring 團隊推薦使用 org.springframework.web.reactive.client.WebClient
,它具有現代 API 並支持同步、非同步和流場景,下一篇文章繼續講解