+11

Microservices với Spring Boot và Spring Cloud (Phần 2) - Cấu hình dự án ban đầu bằng Eureka Server, Spring Cloud Gateway, Spring Cloud Config

Phiên bản của các ứng dụng Spring Boot mà chúng ta sẽ tạo là: 3.0.6 , Java 17

Các bài viết tham khảo:

Link github để các bạn tham khảo code : https://github.com/akitectio/microservices-spring-boot-3

Ta có kiến trúc hệ thống triển khai như sau:

Eureka — JWT Auth — Zuul — ELK.drawio.png

Eureka Server

Eureka Server là một máy chủ đăng ký dịch vụ trong hệ thống Microservices. Nó đảm nhiệm việc đặt tên cho mỗi microservice. Tại sao cần đặt tên? Bởi vì khi có nhiều microservices được triển khai và hoạt động trên nhiều instance khác nhau, không cần phải mã hóa địa chỉ IP cứng của mỗi service, thay vào đó, chúng ta có thể sử dụng tên service đã đăng ký trên Eureka Server để tìm kiếm và truy cập các dịch vụ này. Điều này giúp cho việc quản lý và mở rộng các dịch vụ một cách dễ dàng và hiệu quả hơn.

Vì vậy, mỗi dịch vụ đăng ký với Eureka và gửi yêu cầu ping tới Eureka server để thông báo rằng nó đang hoạt động.

Nếu Eureka server không nhận được bất kỳ thông báo nào từ một dịch vụ, dịch vụ đó sẽ bị hủy đăng ký tự động từ Eureka server.

Bạn sử dụng Spring Initializr dể tạo một Project Eureka Server và thêm dependencies: Web, Eureka Server, DevTools

Ta chọn và điền phần Project Metadata theo như cầu dự án của mình

Sau đó nhập vào Screenshot 2023-04-23 at 19.45.35.png để tạo project

Tiếp tục tạo thư mục mới tên là microservices-project và coppy thư mục vừa vào tạo và tạo thêm file pom.xml với nội dung

<?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>3.0.6</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>com.duytran</groupId>
    <artifactId>microservices-system</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>pom</packaging>
    <name>microservices System</name>
    <description>Microservices System</description>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>17</java.version>
    </properties>

    <modules>
        <module>eureka-server</module>
    </modules>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

Screenshot 2023-04-23 at 19.48.31.png

Tiếp theo, trong tệp application.properties, chúng ta cần thiết lập một số cấu hình.

# Give a name to the eureka server
spring.application.name=eureka-server

# default port for eureka server
server.port=8761

# eureka by default will register itself as a client. So, we need to set it to false.
# What's a client server? See other microservices (image, gallery, auth, etc).
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false

Cuối cùng, trong lớp ứng dụng chính của Spring Boot, chúng ta cần kích hoạt Eureka server bằng cách sử dụng chú thích @EnableEurekaServer.

package com.duytran.eurekaserver;

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

@SpringBootApplication
@EnableEurekaServer	// Enable eureka server

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

Tới đây thì bạn đã khởi tạo thành công Eureka Server

Service 01 - EnableDiscoveryClient

Service Eureka client là một dịch vụ độc lập trong kiến trúc microservice. Nó có thể được sử dụng cho thanh toán, tài khoản, thông báo, xác thực, cấu hình, v.v.

Chúng ta tiếp tục sử dụng Spring Initializr dể tạo một Project Eureka Client mang tên là Service01 và thêm dependencies: **Eureka Discovery Client,Rest Repositories **

Ta chọn và điền phần Project Metadata theo như cầu dự án của mình

Screenshot 2023-04-23 at 22.14.11.png

Sau đó nhập vào Screenshot 2023-04-23 at 19.45.35.png để tạo project

Tiếp tục tạo thư mục mới tên là microservices-project và coppy thư mục vừa vào tạo và thêm config vào file pom.xml với nội dung

    <modules>
        <module>eureka-server</module>
        <module>service01</module> #<- Thêm vào đây
    </modules>

Screenshot 2023-04-23 at 22.18.35.png

Trong tệp application.properties, chúng ta xác định các cấu hình

# serivce name
spring.application.name=service01
# port
server.port=8801
# eureka server url
eureka.client.service-url.default-zone=http://localhost:8761/eureka

Screenshot 2023-04-23 at 22.23.18.png

Sau đó, kích hoạt eureka client bằng cách sử dụng chú thích @EnableDiscoveryClient.

package com.duytran.service01.controllers;

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

@SpringBootApplication
@EnableDiscoveryClient 	// Enable eureka client. It inherits from @EnableDiscoveryClient.
public class SpringEurekaImageApp {
	public static void main(String[] args) {
		SpringApplication.run(SpringEurekaImageApp.class, args);
	}
}

Tiếp tục thêm HomeControllerentities Image

HomeController

package com.duytran.service01.controllers;

import java.util.Arrays;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.duytran.service01.entities.Image;

@RestController
@RequestMapping("/")
public class HomeController {
    @Autowired
    private Environment env;

    @RequestMapping("/service01")
    public List<Image> getImages() {
        List<Image> images = Arrays.asList(
                new Image(1, "Treehouse of Horror V", "https://www.imdb.com/title/tt0096697/mediaviewer/rm3842005760"),
                new Image(2, "The Town", "https://www.imdb.com/title/tt0096697/mediaviewer/rm3698134272"),
                new Image(3, "The Last Traction Hero", "https://www.imdb.com/title/tt0096697/mediaviewer/rm1445594112"));
        return images;
    }
}

Screenshot 2023-04-23 at 22.34.05.png

entities Image

package com.duytran.service01.entities;


public class Image {
    private int id;
    private String name;
    private String url;

    public Image(int id, String name, String url) {
        this.id = id;
        this.name = name;
        this.url = url;
    }

    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getUrl() {
        return url;
    }
    public void setUrl(String url) {
        this.url = url;
    }

}

Screenshot 2023-04-23 at 22.35.44.png

Service 02 - EnableDiscoveryClient

Để gọi tới một REST client hoặc nhiều services khác bạn có thể dùng:

  1. RestTemplate là một đối tượng trong Spring Framework cung cấp khả năng gửi các yêu cầu HTTP đến các dịch vụ REST API khác. Nó hỗ trợ nhiều phương thức HTTP như GET, POST, PUT, DELETE, PATCH, ... để gửi các yêu cầu đến các địa chỉ URL khác nhau và xử lý các phản hồi trả về từ các dịch vụ đó.
  2. FeignClient là một annotation trong Spring Cloud, được sử dụng để tạo ra một client đại diện cho một RESTful service. Thay vì sử dụng RestTemplate, FeignClient sử dụng cấu hình interface và annotation để định nghĩa các method và URL endpoint cho các request. FeignClient cũng cung cấp khả năng load balancing và fault tolerance cho các service.

Cả hai cách điều cân bằng tải trên tất cả dịch vụ.

Thế nào là cân bằng tải (load balancing)?

  • Khi một dịch vụ có nhiều hơn một phiên bản đang chạy trên các cổng khác nhau, ta cần phải cân bằng tải các yêu cầu giữa các phiên bản của dịch vụ đó.
  • Khi sử dụng phương pháp "Ribbon" (mặc định), các yêu cầu sẽ được phân phối đều giữa các phiên bản của dịch vụ đó.

Chúng ta tiếp tục sử dụng Spring Initializr dể tạo một Project Eureka Client mang tên là Service02 và thêm dependencies: **Eureka Discovery Client,Rest Repositories **

Ta chọn và điền phần Project Metadata theo như cầu dự án của mình

Sau đó nhập vào Screenshot 2023-04-23 at 19.45.35.png để tạo project

Tiếp tục tạo thư mục mới tên là microservices-project và coppy thư mục vừa vào tạo và thêm config vào file pom.xml với nội dung

    <modules>
        <module>eureka-server</module>
        <module>service01</module>
        <module>service02</module> #<- Thêm vào đây
    </modules>

Tiếp theo là tập tin application.properties.

spring.application.name=service02
server.port=8802
eureka.client.service-url.default-zone=http://localhost:8761/eureka

Trong class chính của ứng dụng Spring Boot, bên cạnh việc kích hoạt Eureka client, chúng ta cần tạo một bean cho RestTemplate để gọi đến service01.

Tệp Service02Application.java:

package com.duytran.service02;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
@EnableDiscoveryClient
public class Service02Application {
	public static void main(String[] args) {
		SpringApplication.run(Service02Application.class, args);
	}

}

@Configuration
class RestTemplateConfig {

	// Create a bean for restTemplate to call services
	@Bean
	@LoadBalanced		// Load balance between service instances running at different ports.
	public RestTemplate restTemplate() {
		return new RestTemplate();
	}
}

Trong controller tạo HomeController, gọi serivce01 bằng RestTemplate và trả kết quả.

package com.duytran.service02.controllers;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import com.duytran.service02.entities.Gallery;

@RestController
@RequestMapping("/")
public class HomeController {
    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private Environment env;

    @RequestMapping("/")
    public String home() {
        // This is useful for debugging
        // When having multiple instance of gallery service running at different ports.
        // We load balance among them, and display which instance received the request.
        return "Hello from Gallery Service running at port: " + env.getProperty("local.server.port");
    }

    @RequestMapping("/{id}")
    public Gallery getGallery(@PathVariable final int id) {
        // create gallery object
        Gallery gallery = new Gallery();
        gallery.setId(id);

        // get list of available images
        List<Object> images = restTemplate.getForObject("http://service01/images/", List.class);
        gallery.setImages(images);

        return gallery;
    }

    // -------- Admin Area --------
    // This method should only be accessed by users with role of 'admin'
    // We'll add the logic of role based auth later
    @RequestMapping("/admin")
    public String homeAdmin() {
        return "This is the admin area of Gallery service running at port: " + env.getProperty("local.server.port");
    }
}

Screenshot 2023-04-24 at 00.18.49.png

Trong thư mục entities tạo Gallery.

package com.duytran.service02.entities;

import java.util.List;

public class Gallery {
    private int id;
    private List<Object> images;

    public Gallery() {
    }

    public Gallery(int galleryId) {
        this.id = galleryId;
    }

    public int getId() {
        return id;
    }

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

    public List<Object> getImages() {
        return images;
    }

    public void setImages(List<Object> images) {
        this.images = images;
    }

}

Một điều cần lưu ý. Vì chúng ta đang sử dụng restTemplate - sử dụng Eureka Server để đặt tên cho các dịch vụ và Ribbon để cân bằng tải. Vì vậy, chúng ta có thể sử dụng tên dịch vụ (như service01) thay vì localhost:port.

Gateway — Spring Cloud Gateway

Khi gọi bất kỳ dịch vụ nào từ trình duyệt, chúng ta không thể gọi bằng tên như chúng ta đã làm từ dịch vụ Service02 - Điều này được sử dụng nội bộ giữa các dịch vụ.

Và khi chúng ta tạo nhiều phiên bản của dịch vụ, mỗi phiên bản có một số cổng khác nhau. Vậy, câu hỏi là: Làm thế nào để gọi các dịch vụ từ trình duyệt và phân phối yêu cầu đến các phiên bản khác nhau đang chạy trên các cổng khác nhau?

Vâng, một giải pháp thông thường là sử dụng một Gateway.

Một Gateway là một điểm nhập cửa đơn giản vào hệ thống, được sử dụng để xử lý các yêu cầu bằng cách định tuyến chúng đến dịch vụ tương ứng. Nó cũng có thể được sử dụng cho xác thực, giám sát và nhiều hơn nữa.

Spring Cloud Gateway là gì?

Spring Cloud Gateway là một thư viện được sử dụng để xây dựng cổng API trên nền tảng Spring WebFlux. Dự án này nhằm mục đích cung cấp một cách đơn giản và hiệu quả để định tuyến đến các API và cung cấp các vấn đề liên quan đến bảo mật, giám sát/thống kê và tính chịu lỗi.

Nó là một proxy, cổng thông tin, một lớp trung gian giữa người dùng và dịch vụ của bạn.

Eureka server giải quyết vấn đề đặt tên cho các dịch vụ thay vì hardcode địa chỉ IP của chúng.

Tuy nhiên, chúng ta vẫn có thể có nhiều hơn một dịch vụ (instances) đang chạy trên các cổng khác nhau

  1. Spring Cloud Gateway sẽ ánh xạ giữa một tiền tố đường dẫn, ví dụ như /service02/ và một dịch vụ Service02. Nó sử dụng Eureka server để định tuyến dịch vụ được yêu cầu.
  2. Nó cân bằng tải (sử dụng Ribbon) giữa các phiên bản của dịch vụ đang chạy trên các cổng khác nhau.
  3. Chúng ta có thể lọc các yêu cầu( filter requests), thêm xác thực(authentication) và nhiều hơn nữa.

Tính năng của Spring Cloud Gateway:

  1. Xây dựng trên Spring Framework 5, Project Reactor và Spring Boot 2.0
  2. Có thể khớp các tuyến đường với bất kỳ thuộc tính yêu cầu nào.
  3. Các Predicates và Filters được chỉ định cho từng tuyến đường cụ thể.
  4. Tích hợp Circuit Breaker.
  5. Tích hợp DiscoveryClient của Spring Cloud.
  6. Dễ dàng viết Predicates và Filters.
  7. Giới hạn tốc độ yêu cầu (Request Rate Limiting).
  8. Chuyển đổi lại đường dẫn (Path Rewriting).

Chúng ta tiếp tục sử dụng Spring Initializr dể tạo một Project Spring Cloud Gateway mang tên là Service Gatewayl và thêm dependencies: Spring Cloud Gateway,Spring Security,Spring Boot DevTools,Eureka Discovery Client

Ta chọn và điền phần Project Metadata theo như cầu dự án của mình

Screenshot 2023-04-24 at 10.07.42.png

Sau đó nhập vào Screenshot 2023-04-23 at 19.45.35.png để tạo project

Tiếp tục tạo thư mục mới tên là microservices-project và coppy thư mục vừa vào tạo và thêm config vào file pom.xml với nội dung

 <modules>
        <module>eureka-server</module>
        <module>service01</module>
        <module>service02</module>
        <module>gateway-service</module>  #<- Thêm vào đây
    </modules>

Tiếp tục trong file pom.xml trong project gateway-service thêm 1 dòng dependency

     <dependency>
         <groupId>org.springframework.cloud</groupId>
         <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
     </dependency>

	<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-actuator</artifactId>
			<version>3.0.6</version>
		</dependency>

		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
			<version>2.2.10.RELEASE</version>
		</dependency>

        <dependency>
			<groupId>io.jsonwebtoken</groupId>
			<artifactId>jjwt-api</artifactId>
			<version>0.11.1</version>
		</dependency>
		<dependency>
			<groupId>io.jsonwebtoken</groupId>
			<artifactId>jjwt-impl</artifactId>
			<version>0.11.1</version>
			<scope>runtime</scope>
		</dependency>

        <dependency>
			<groupId>javax.annotation</groupId>
			<artifactId>javax.annotation-api</artifactId>
			<version>1.3.2</version>
		</dependency>

Spring Cloud Gateway cũng hoạt động như một Eureka client. Vì vậy, chúng ta đặt tên cho nó, cổng và liên kết đến Eureka Server (giống như chúng ta đã làm với Service01).

server:
  port: 8762

spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true

jwt:
  secret: BvPHGM8C0ia4uOuxxqPD5DTbWC9F9TWvPStp3pb7ARo0oK2mJ3pd3YG4lxA9i8bj6OTbadwezxgeEByY

Tiếp tục ta thêm @EnableDiscoveryClient trong Application

package com.duytran.gatewayservice;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@SpringBootApplication
@EnableDiscoveryClient
public class GatewayServiceApplication {

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

}

Screenshot 2023-04-24 at 21.11.41.png

Trong thư mục controller tạo FallbackController.

package com.duytran.gatewayservice.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class FallbackController {

    @GetMapping("/fallback1")
    public String userFallback() {
        return "User service is not available";
    }

    @GetMapping("/auth-fallback")
    public String authFallback() {
        return "Auth service is not available";
    }
}

Trong thư mục config tạo AuthenticationFilter , GatewayConfig, JwtUtil, RouterValidator,

AuthenticationFilter


@RefreshScope
@Component
public class AuthenticationFilter implements GatewayFilter {

    @Autowired
    private RouterValidator routerValidator;
    @Autowired
    private JwtUtil jwtUtil;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();

        if (routerValidator.isSecured.test(request)) {
            if (this.isAuthMissing(request))
                return this.onError(exchange, "Authorization header is missing in request", HttpStatus.UNAUTHORIZED);

            final String token = this.getAuthHeader(request);

            if (jwtUtil.isInvalid(token))
                return this.onError(exchange, "Authorization header is invalid", HttpStatus.UNAUTHORIZED);

            this.populateRequestWithHeaders(exchange, token);
        }
        return chain.filter(exchange);
    }


    /*PRIVATE*/

    private Mono<Void> onError(ServerWebExchange exchange, String err, HttpStatus httpStatus) {
        ServerHttpResponse response = exchange.getResponse();
        response.setStatusCode(httpStatus);
        return response.setComplete();
    }

    private String getAuthHeader(ServerHttpRequest request) {
        return request.getHeaders().getOrEmpty("Authorization").get(0);
    }

    private boolean isAuthMissing(ServerHttpRequest request) {
        return !request.getHeaders().containsKey("Authorization");
    }

    private void populateRequestWithHeaders(ServerWebExchange exchange, String token) {
        Claims claims = jwtUtil.getAllClaimsFromToken(token);
        exchange.getRequest().mutate()
                .header("id", String.valueOf(claims.get("id")))
                .header("role", String.valueOf(claims.get("role")))
                .build();
    }
}

Đây là một đoạn code để thực hiện xác thực JWT (JSON Web Token) trong Spring Cloud Gateway. Nó kiểm tra xem có yêu cầu được bảo mật hay không và xác minh tính hợp lệ của token JWT trong tiêu đề yêu cầu. Nếu token không hợp lệ hoặc thiếu, nó sẽ trả về lỗi UNAUTHORIZED (401) cho khách hàng. Nếu token hợp lệ, nó sẽ lấy thông tin được giải mã từ token và thêm chúng vào tiêu đề của yêu cầu để sử dụng cho các mục đích khác.

GatewayConfig

package com.duytran.gatewayservice.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableHystrix
public class GatewayConfig {

    @Autowired
    AuthenticationFilter filter;

    @Bean
    public RouteLocator routes(RouteLocatorBuilder builder) {
        return builder.routes()
                .route("user-service", r -> r.path("/users/**")
                        .filters(f -> f.filter(filter))
                        .uri("lb://user-service"))

                .route("auth-service", r -> r.path("/auth/**")
                        .filters(f -> f.filter(filter))
                        .uri("lb://auth-service"))
                .build();
    }

}

Đoạn code trên là cấu hình cho Gateway Service của Spring Cloud, nó khai báo một bean là routes là một RouteLocator, được tạo bằng cách sử dụng đối tượng RouteLocatorBuilder để xác định các routes cho Gateway. Ở đây, routes được cấu hình để định tuyến các yêu cầu gửi đến đường dẫn "/users/" đến service "user-service" và định tuyến các yêu cầu gửi đến đường dẫn "/auth/" đến service "auth-service", và áp dụng AuthenticationFilter cho tất cả các routes này.

JwtUtil

package com.duytran.gatewayservice.config;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.security.Key;
import java.util.Date;

@Component
public class JwtUtil {

    @Value("${jwt.secret}")
    private String secret;

    private Key key;

    @PostConstruct
    public void init(){
        this.key = Keys.hmacShaKeyFor(secret.getBytes());
    }

    public Claims getAllClaimsFromToken(String token) {
        return Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token).getBody();
    }

    private boolean isTokenExpired(String token) {
        return this.getAllClaimsFromToken(token).getExpiration().before(new Date());
    }

    public boolean isInvalid(String token) {
        return this.isTokenExpired(token);
    }

}

Đoạn code trên là một lớp tiện ích (utility class) để xử lý mã thông báo JWT (JSON Web Token). Lớp này cung cấp các phương thức để trích xuất thông tin từ mã thông báo và kiểm tra tính hợp lệ của nó. Nó sử dụng thư viện JJWT để xử lý mã thông báo. Phương thức init() được gọi sau khi đối tượng được tạo ra để khởi tạo khóa sử dụng trong việc tạo và xác minh mã thông báo.

RouterValidator

package com.duytran.gatewayservice.config;

import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.function.Predicate;

@Component
public class RouterValidator {

    public static final List<String> openApiEndpoints = List.of(
            "/auth/register",
            "/auth/login"
    );

    public Predicate<ServerHttpRequest> isSecured =
            request -> openApiEndpoints
                    .stream()
                    .noneMatch(uri -> request.getURI().getPath().contains(uri));

}

Đoạn code trên có chức năng kiểm tra đường dẫn yêu cầu của một request, nếu đường dẫn đó không nằm trong danh sách các endpoint công khai (openApiEndpoints) thì request sẽ được coi là yêu cầu bảo mật và phải đi kèm với token xác thực. Chức năng này được sử dụng trong AuthenticationFilter của Spring Cloud Gateway để kiểm tra xem request có yêu cầu bảo mật hay không.

Config Service - Spring Cloud Config

Spring Cloud Config là một dịch vụ của Spring Cloud, cung cấp khả năng quản lý cấu hình trung tâm cho các ứng dụng phân tán. Nó cho phép các ứng dụng lấy cấu hình của chúng từ một vị trí tập trung thay vì phải lưu trữ cấu hình trực tiếp trong mã nguồn hoặc tệp tin. Các ứng dụng có thể truy cập cấu hình của chúng thông qua giao diện REST API, hoặc thông qua các thư viện khách được cung cấp bởi Spring Cloud Config. Sử dụng Spring Cloud Config, ta có thể dễ dàng quản lý và triển khai các ứng dụng phân tán một cách hiệu quả và an toàn hơn.

Các tính năng của Spring Cloud Config Server:

  • API dựa trên HTTP và tài nguyên cho cấu hình bên ngoài (các cặp tên-giá trị hoặc tương đương nội dung YAML).
  • Mã hóa và giải mã các giá trị thuộc tính (symmetric or asymmetric).
  • Có thể dễ dàng nhúng vào một ứng dụng Spring Boot bằng cách sử dụng @EnableConfigServer.

Các tính năng của Config Client (cho các ứng dụng Spring):

  • Kết nối đến Config Server và khởi tạo Spring Environment với các nguồn thuộc tính từ xa.
  • Mã hóa (Encrypt) và giải mã (decrypt) các giá trị thuộc tính (symmetric or asymmetric).

Chúng ta tiếp tục sử dụng Spring Initializr dể tạo một Project **Spring Cloud Config ** mang tên là Service Config và thêm dependencies: Config Server,Spring Boot DevTools,Eureka Discovery Client

Ta chọn và điền phần Project Metadata theo như cầu dự án của mình

Screenshot 2023-04-24 at 12.34.02.png

Sau đó nhập vào Screenshot 2023-04-23 at 19.45.35.png để tạo project

Tiếp tục tạo thư mục mới tên là microservices-project và coppy thư mục vừa vào tạo và thêm config vào file pom.xml với nội dung

    <modules>
        <module>eureka-server</module>
        <module>service01</module>
        <module>service02</module>
        <module>gateway-service</module>
        <module>config-service</module> # <- Thêm ở đây
    </modules>

Tiếp tục thêm config vào tiệp application.yml

server:
  port: 9297

spring:
  application:
    name: config-server
  cloud:
    config:
      server:
        git:
          uri: https://github.com/tdduydev/demo-config-server.git
          clone-on-start: true

Tiếp tục tạo thêm một tiệp bootstrap.yml

server:
  port: 9297

spring:
  application:
    name: config-server
  cloud:
    config:
      server:
        git:
          uri: https://github.com/tdduydev/demo-config-server.git
          clone-on-start: true

Tiếp tục ta thêm @EnableDiscoveryClient@EnableConfigServer trong Application

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.config.server.EnableConfigServer;

@SpringBootApplication
@EnableDiscoveryClient
@EnableConfigServer
public class ConfigServiceApplication {

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

}

Trong bài viết này, chúng ta đã tìm hiểu về các khái niệm và công nghệ liên quan đến thiết kế và triển khai hệ thống microservices. Chúng ta đã bắt đầu với một số khái niệm cơ bản như Eureka Server, một giải pháp để quản lý và phân phối các microservice. Sau đó, chúng ta đã tìm hiểu về Spring Cloud Gateway và Zuul, hai công nghệ được sử dụng để định tuyến và phân phối các yêu cầu đến các microservice.

Cuối cùng, chúng ta đã xem xét Spring Cloud Config, một công nghệ quản lý cấu hình tập trung cho các ứng dụng microservices. Với Spring Cloud Config, chúng ta có thể quản lý các cấu hình tập trung cho các ứng dụng và triển khai chúng đến các môi trường khác nhau với độ tin cậy cao.

Tóm lại, các công nghệ và giải pháp này đóng một vai trò quan trọng trong việc thiết kế và triển khai các hệ thống microservices hiệu quả và đáng tin cậy. Với kiến thức được tìm hiểu ở đây, chúng ta hy vọng bạn sẽ có thể bắt đầu xây dựng các hệ thống microservices của riêng mình một cách dễ dàng hơn.


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.