Microservice Learning - Microservice Framework

Nacos configuration management

Unified configuration management

  • Configuration change hot update

1

Steps to hand over the configuration to Nacos management:

  • Add configuration files in Nacos
  • Introduce nacos config dependencies in microservices
  • Add bootstrap.yml to the microservice, and configure the nacos address, current environment, service name, and file extension. These determine which file to read from nacos when the program starts
  1. Add configuration information in Nacos
  2. Fill in the configuration information in the pop-up form
    • Configuration file id: [service name]-[profile].[suffix name], such as userservice-dev.yaml
    • Grouping: default
    • Format: currently supports yaml and properties

2

  1. Introduce Nacos configuration management client dependencies:

    <!--nacos配置管理依赖-->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
    </dependency>
    
  2. Add a bootstrap.yml file to the resource directory in userservice. This file is a bootstrap file with a higher priority than application.yml:

    spring:
      application:
        name: userservice # 服务名称
      profiles:
        active: dev # 环境
      cloud:
        nacos:
          server-addr: localhost:8848 # nacos地址
          config:
            file-extension: yaml # 文件后缀名
    

Inject the pattern.dateformat attribute into UserController in user-service for testing:

@Value("${pattern.dateformat}")
private String dateFormat;

@GetMapping("/now")
public String now() {
    
    
    return LocalDateTime.now().format(DateTimeFormatter.ofPattern(dateFormat, Locale.CHINA));
}

Configure automatic refresh

After the configuration file in Nacos is changed, the microservice can perceive it without restarting. However, you need to implement the following two configurations:

  • Method 1: Add annotation @RefreshScope on the class where the variable injected by @Value is located

    @Slf4j
    @RestController
    @RefreshScope
    @RequestMapping("/user")
    public class UserController {
          
          
        @Value("${pattern.dateformat}")
        private String dateFormat;
        ...
    }
    
  • Method 2: Use @ConfigurationProperties annotation (directly injected into a member variable of a certain class)

    @Data
    @Component
    @ConfigurationProperties(prefix = "pattern")
    public class PatternProperties {
          
          
        private String dateFormat;
    }
    

Precautions:

  • Not all configurations are suitable to be placed in the configuration center, which is troublesome to maintain
  • It is recommended to put some key parameters and parameters that need to be adjusted at runtime into the nacos configuration center, which are generally custom configurations

Multi-environment shared configuration

When the microservice starts, it will read multiple configuration files from nacos:

  • [spring.application.name]-[spring.profiles.active].yaml,例如:users
    ervice-dev.yaml
  • [spring.application.name].yaml,例如: userservice.yaml

No matter how the profile changes, the [spring.application.name].yaml file will definitely be loaded,
so the multi-environment shared configuration can be written to this file

3

Priority of various configurations:

  • service-name-profile.yaml > service-name.yaml > local-configuration
  • userservice-dev-.yaml > userservice.yaml > local configuration file

Nacos cluster construction

In the Nacos production environment, it must be deployed as a cluster state

4

The basic steps to build a cluster:

  1. Build the database and initialize the database table structure

    Nacos default data is stored in the embedded database Derby, which is not a production-available database. The officially recommended best practice is to use a high-availability database cluster with a master-slave. Here, a single-point database is used as an example.

    First create a new database, name it nacos, and then import the following SQL (the sql file is located in nacos\conf\nacos-mysql.sql)

    CREATE TABLE `config_info` (
      `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
      `data_id` varchar(255) NOT NULL COMMENT 'data_id',
      `group_id` varchar(255) DEFAULT NULL,
      `content` longtext NOT NULL COMMENT 'content',
      `md5` varchar(32) DEFAULT NULL COMMENT 'md5',
      `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
      `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
      `src_user` text COMMENT 'source user',
      `src_ip` varchar(50) DEFAULT NULL COMMENT 'source ip',
      `app_name` varchar(128) DEFAULT NULL,
      `tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段',
      `c_desc` varchar(256) DEFAULT NULL,
      `c_use` varchar(64) DEFAULT NULL,
      `effect` varchar(64) DEFAULT NULL,
      `type` varchar(64) DEFAULT NULL,
      `c_schema` text,
      PRIMARY KEY (`id`),
      UNIQUE KEY `uk_configinfo_datagrouptenant` (`data_id`,`group_id`,`tenant_id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_info';
    
    /******************************************/
    /*   数据库全名 = nacos_config   */
    /*   表名称 = config_info_aggr   */
    /******************************************/
    CREATE TABLE `config_info_aggr` (
      `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
      `data_id` varchar(255) NOT NULL COMMENT 'data_id',
      `group_id` varchar(255) NOT NULL COMMENT 'group_id',
      `datum_id` varchar(255) NOT NULL COMMENT 'datum_id',
      `content` longtext NOT NULL COMMENT '内容',
      `gmt_modified` datetime NOT NULL COMMENT '修改时间',
      `app_name` varchar(128) DEFAULT NULL,
      `tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段',
      PRIMARY KEY (`id`),
      UNIQUE KEY `uk_configinfoaggr_datagrouptenantdatum` (`data_id`,`group_id`,`tenant_id`,`datum_id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='增加租户字段';
    
    
    /******************************************/
    /*   数据库全名 = nacos_config   */
    /*   表名称 = config_info_beta   */
    /******************************************/
    CREATE TABLE `config_info_beta` (
      `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
      `data_id` varchar(255) NOT NULL COMMENT 'data_id',
      `group_id` varchar(128) NOT NULL COMMENT 'group_id',
      `app_name` varchar(128) DEFAULT NULL COMMENT 'app_name',
      `content` longtext NOT NULL COMMENT 'content',
      `beta_ips` varchar(1024) DEFAULT NULL COMMENT 'betaIps',
      `md5` varchar(32) DEFAULT NULL COMMENT 'md5',
      `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
      `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
      `src_user` text COMMENT 'source user',
      `src_ip` varchar(50) DEFAULT NULL COMMENT 'source ip',
      `tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段',
      PRIMARY KEY (`id`),
      UNIQUE KEY `uk_configinfobeta_datagrouptenant` (`data_id`,`group_id`,`tenant_id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_info_beta';
    
    /******************************************/
    /*   数据库全名 = nacos_config   */
    /*   表名称 = config_info_tag   */
    /******************************************/
    CREATE TABLE `config_info_tag` (
      `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
      `data_id` varchar(255) NOT NULL COMMENT 'data_id',
      `group_id` varchar(128) NOT NULL COMMENT 'group_id',
      `tenant_id` varchar(128) DEFAULT '' COMMENT 'tenant_id',
      `tag_id` varchar(128) NOT NULL COMMENT 'tag_id',
      `app_name` varchar(128) DEFAULT NULL COMMENT 'app_name',
      `content` longtext NOT NULL COMMENT 'content',
      `md5` varchar(32) DEFAULT NULL COMMENT 'md5',
      `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
      `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
      `src_user` text COMMENT 'source user',
      `src_ip` varchar(50) DEFAULT NULL COMMENT 'source ip',
      PRIMARY KEY (`id`),
      UNIQUE KEY `uk_configinfotag_datagrouptenanttag` (`data_id`,`group_id`,`tenant_id`,`tag_id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_info_tag';
    
    /******************************************/
    /*   数据库全名 = nacos_config   */
    /*   表名称 = config_tags_relation   */
    /******************************************/
    CREATE TABLE `config_tags_relation` (
      `id` bigint(20) NOT NULL COMMENT 'id',
      `tag_name` varchar(128) NOT NULL COMMENT 'tag_name',
      `tag_type` varchar(64) DEFAULT NULL COMMENT 'tag_type',
      `data_id` varchar(255) NOT NULL COMMENT 'data_id',
      `group_id` varchar(128) NOT NULL COMMENT 'group_id',
      `tenant_id` varchar(128) DEFAULT '' COMMENT 'tenant_id',
      `nid` bigint(20) NOT NULL AUTO_INCREMENT,
      PRIMARY KEY (`nid`),
      UNIQUE KEY `uk_configtagrelation_configidtag` (`id`,`tag_name`,`tag_type`),
      KEY `idx_tenant_id` (`tenant_id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_tag_relation';
    
    /******************************************/
    /*   数据库全名 = nacos_config   */
    /*   表名称 = group_capacity   */
    /******************************************/
    CREATE TABLE `group_capacity` (
      `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
      `group_id` varchar(128) NOT NULL DEFAULT '' COMMENT 'Group ID,空字符表示整个集群',
      `quota` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '配额,0表示使用默认值',
      `usage` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '使用量',
      `max_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个配置大小上限,单位为字节,0表示使用默认值',
      `max_aggr_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '聚合子配置最大个数,,0表示使用默认值',
      `max_aggr_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个聚合数据的子配置大小上限,单位为字节,0表示使用默认值',
      `max_history_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '最大变更历史数量',
      `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
      `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
      PRIMARY KEY (`id`),
      UNIQUE KEY `uk_group_id` (`group_id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='集群、各Group容量信息表';
    
    /******************************************/
    /*   数据库全名 = nacos_config   */
    /*   表名称 = his_config_info   */
    /******************************************/
    CREATE TABLE `his_config_info` (
      `id` bigint(64) unsigned NOT NULL,
      `nid` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
      `data_id` varchar(255) NOT NULL,
      `group_id` varchar(128) NOT NULL,
      `app_name` varchar(128) DEFAULT NULL COMMENT 'app_name',
      `content` longtext NOT NULL,
      `md5` varchar(32) DEFAULT NULL,
      `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
      `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
      `src_user` text,
      `src_ip` varchar(50) DEFAULT NULL,
      `op_type` char(10) DEFAULT NULL,
      `tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段',
      PRIMARY KEY (`nid`),
      KEY `idx_gmt_create` (`gmt_create`),
      KEY `idx_gmt_modified` (`gmt_modified`),
      KEY `idx_did` (`data_id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='多租户改造';
    
    
    /******************************************/
    /*   数据库全名 = nacos_config   */
    /*   表名称 = tenant_capacity   */
    /******************************************/
    CREATE TABLE `tenant_capacity` (
      `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
      `tenant_id` varchar(128) NOT NULL DEFAULT '' COMMENT 'Tenant ID',
      `quota` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '配额,0表示使用默认值',
      `usage` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '使用量',
      `max_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个配置大小上限,单位为字节,0表示使用默认值',
      `max_aggr_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '聚合子配置最大个数',
      `max_aggr_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个聚合数据的子配置大小上限,单位为字节,0表示使用默认值',
      `max_history_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '最大变更历史数量',
      `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
      `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
      PRIMARY KEY (`id`),
      UNIQUE KEY `uk_tenant_id` (`tenant_id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='租户容量信息表';
    
    
    CREATE TABLE `tenant_info` (
      `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
      `kp` varchar(128) NOT NULL COMMENT 'kp',
      `tenant_id` varchar(128) default '' COMMENT 'tenant_id',
      `tenant_name` varchar(128) default '' COMMENT 'tenant_name',
      `tenant_desc` varchar(256) DEFAULT NULL COMMENT 'tenant_desc',
      `create_source` varchar(32) DEFAULT NULL COMMENT 'create_source',
      `gmt_create` bigint(20) NOT NULL COMMENT '创建时间',
      `gmt_modified` bigint(20) NOT NULL COMMENT '修改时间',
      PRIMARY KEY (`id`),
      UNIQUE KEY `uk_tenant_info_kptenantid` (`kp`,`tenant_id`),
      KEY `idx_tenant_id` (`tenant_id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='tenant_info';
    
    CREATE TABLE `users` (
    	`username` varchar(50) NOT NULL PRIMARY KEY,
    	`password` varchar(500) NOT NULL,
    	`enabled` boolean NOT NULL
    );
    
    CREATE TABLE `roles` (
    	`username` varchar(50) NOT NULL,
    	`role` varchar(50) NOT NULL,
    	UNIQUE INDEX `idx_user_role` (`username` ASC, `role` ASC) USING BTREE
    );
    
    CREATE TABLE `permissions` (
        `role` varchar(50) NOT NULL,
        `resource` varchar(255) NOT NULL,
        `action` varchar(8) NOT NULL,
        UNIQUE INDEX `uk_role_permission` (`role`,`resource`,`action`) USING BTREE
    );
    
    INSERT INTO users (username, password, enabled) VALUES ('nacos', '$2a$10$EuWPZHzz32dJN7jexM34MOeYirDdFAZm2kuWj7VEOJhhZkDrxfvUu', TRUE);
    
    INSERT INTO roles (username, role) VALUES ('nacos', 'ROLE_ADMIN');
    
  2. Download nacos installation package

  3. configure nacos

    Enter the conf directory of nacos, modify the configuration file cluster.conf.example, rename it to cluster.conf, and add the content:

    127.0.0.1:8845
    127.0.0.1:8846
    127.0.0.1:8847
    

    Then modify the application.properties file and add the database configuration

    spring.datasource.platform=mysql
    
    db.num=1
    
    db.url.0=jdbc:mysql://127.0.0.1:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC
    db.user.0=root
    db.password.0=root
    
  4. Start nacos cluster

    Copy the nacos folder three times and name them: nacos1, nacos2, nacos3

    Then modify the application.properties in the three folders respectively,

    nacos1:

    server.port=8845
    

    nacos2:

    server.port=8846
    

    nacos3:

    server.port=8847
    

    Then start three nacos nodes respectively:

    startup.cmd
    
  5. nginx reverse proxy

    Modify the conf/nginx.conf file, the configuration is as follows:

    upstream nacos-cluster {
        server 127.0.0.1:8845;
    	server 127.0.0.1:8846;
    	server 127.0.0.1:8847;
    }
    
    server {
        listen       80;
        server_name  localhost;
    
        location /nacos {
            proxy_pass http://nacos-cluster;
        }
    }
    

    Then visit in the browser: http://localhost/nacos.

    Modify the application.yml file configuration in the code as follows:

    spring:
      cloud:
        nacos:
          server-addr: localhost:80 # Nacos地址
    

http client Feign

Problems with calling RestTemplate

Let's first look at the code we used to initiate remote calls using RestTemplate:

String url = "http: //userservice/user/" + order.getUserId();
User user = restTemplate.getFor0bject(url, User.class);

There are following problems:

  • Poor code readability and inconsistent programming experience
  • URLs with complex parameters are difficult to maintain

Feign's introduction

Feign is a declarative http client, official address: https://github.com/OpenFeign/feign

Its role is to help us elegantly implement the sending of http requests and solve the problems mentioned above.

Define and use Feign client

The steps to use Feign are as follows:

  1. Import dependencies:

    <!--feign客户端-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
    
  2. Add annotations to the startup class of order-service to enable the function of Feign:

    @MapperScan("cn.itcast.order.mapper")
    @SpringBootApplication
    @EnableFeignClients
    public class OrderApplication {
          
          
        ...
    }
    
  3. Write a Feign client:

    package cn.itcast.order.clients;
    
    import cn.itcast.order.pojo.User;
    import org.springframework.cloud.openfeign.FeignClient;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    
    @FeignClient("userservice")
    public interface UserClient {
          
          
        @GetMapping("/user/{id}")
        User FindById(@PathVariable("id") Long id);
    }
    
  4. Replace RestTemplate with Feign client

    @Autowired
    private UserClient userClient;
    
    public Order queryOrderById(Long orderId) {
          
          
        // 1.查询订单
        Order order = orderMapper.findById(orderId);
        // 2.用Feign远程调用
        User user = userClient.findById(order.getUserId());
        // 3.封装User到Order
        order.setUser(user);
        // 4.返回
        return order;
    }
    

It is mainly based on the annotations of SpringMVC to declare the information of remote adjustment, such as:

  • Service name: userservice
  • Request method: GET
  • Request path: /user/{id}
  • Request parameter: Long id
  • Return value type: User

Customize the configuration of Feign

Feign runs a custom configuration to override the default configuration. The configuration that can be modified is as follows:

5

Generally, what we need to configure is the log level.

There are two ways to configure Feign logs:

  • Method 1: configuration file method
  1. Globally effective:

    feign:
      client:
        config:
          default: #这里用default就是全局配置,如果是写服务名称,则是针对某个微服务的配置
            loggerLevel: FULL #日志级别
    
  2. partial effect

    feign:
      client:
        config:
          userservice: #这里用default就是全局配置,如果是写服务名称,则是针对某个微服务的配置
            loggerLevel: FULL #日志级别
    
  • Method 2: java code method

You need to declare a Bean first:

package cn.itcast.config;

import feign.Logger;
import org.springframework.context.annotation.Bean;

public class FeignClientConfiguration {
    
    
    @Bean
    public Logger.Level feignLogLevel() {
    
    
        return Logger.Level.BASIC;
    }
}
  1. Then if it is a global configuration, put it in the @EnableFeignClients annotation:
@MapperScan("cn.itcast.order.mapper")
@SpringBootApplication
@EnableFeignClients(defaultConfiguration = FeignClientConfiguration.class)
public class OrderApplication {
    
    
    ...
}
  1. If it is a partial configuration, put it in the @FeignClient annotation:
@FeignClient(value = "userservice", configuration= FeignClientConfiguration.class)
public interface UserClient {
    
    
    @GetMapping("/user/{id}")
    User findById(@PathVariable("id") Long id);
}

Feign performance optimization

The underlying client implementation of Feign:

  • URLConnection: default implementation, does not support connection pool
  • Apache HttpClient: support connection pool
  • OKHttp: support connection pool

Therefore, optimizing the performance of Feign mainly includes:

  1. Use a connection pool instead of the default URLConnection
  2. log level, preferably basic or none

Feign performance optimization - connection pool configuration

Feign adds Httpclient support:

Import dependencies:

<!--HttpClient依赖-->
<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-httpclient</artifactId>
</dependency>

Configure the connection pool:

feign:
  httpclient:
    enabled: true # 支持HttpClient的开关
    max-connections: 200 # 最大连接数
    max-connections-per-route: 50 #单个路径的最大连接数

Best Practices for Feign

Method 1 (inheritance): Define a unified parent interface for the consumer's FeignClient and the provider's controller as a standard.

6

  • Services Tightly Coupled
  • Mappings in parent interface parameter lists are not inherited

Method 2 (extraction): Extract FeignClient as an independent module, and put the POJO related to the interface and the default Feign configuration into this module, and provide it to all consumers

7

Extract FeignClient

The steps to implement Best Practice 2 are as follows:

  1. First create a module named feign-api, and then introduce feign's starter dependency

  2. Copy the UserClient, User, and DefaultFeignConfiguration written in order-service to the feign-api project

  3. Introduce the dependency of feign-api in order-service

  4. Modify all the import parts related to the above three components in order-service, and change them to import the package in feign-api

When the defined FeignClient is not in the scope of SpringBootApplication's scan package, these FeignClients cannot be used. There are two ways to solve it:

  • Method 1: Specify the package where FeignClient is located

    @EnableFeignclients(basePackages = "cn.itcast.feign.clients")
    
  • Method 2: specify FeignClient bytecode

    @EnableFeignclients(clients = {
          
          Userclient.class})
    

Unified Gateway Gateway

Why do you need a gateway

Gateway function:

  • Identity authentication and permission verification

  • Service routing, load balancing

  • request throttling

8

The technical realization of the gateway

The implementation of the gateway in Spring Cloud includes two types:

  • gateway
  • zul

Zuul is a Servlet-based implementation and belongs to blocking programming. Spring Cloud Gateway is based on WebFlux provided in Spring 5, which is an implementation of responsive programming and has better performance.

Build gateway service

Steps to build a gateway service:

  1. Create a new module and introduce the dependency of SpringCloudGateway and the service discovery dependency of nacos:

    <!--nacos服务注册发现依赖-->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    <!--网关gateway依赖-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>
    
  2. Write routing configuration and nacos address

    server:
      port: 10010 # 网关端口
    spring:
      application:
        name: gateway # 服务名称cloud :
      cloud:
        nacos:
          server-addr: localhost:8848 # nacos地址
        gateway:
          routes: # 网关路由配置
            - id: user-service # 路由id,自定义,必须唯一
    #          uri: http ://127.0.0.1:8081 # 路由的目标地址 http就是固定地址
              uri: lb://userservice # 路由的目标地址lb就是负载均衡,后面跟服务名称
              predicates: # 路由断言,判断请求是否符合规则
                - Path=/user/** # 路径断言,判断路径是否是以/user开头,如果是则符合
            - id: order-service
              uri: lb://orderservice
              predicates:
                - Path=/order/**
    

9

Gateway construction steps:

  1. Create a project, introduce nacos service discovery and gateway dependencies
  2. Configure application.yml, including basic service information, nacos address, routing

Routing configuration includes:

  1. route id: the unique identifier of the route
  2. Routing destination (uri): the destination address of the routing, http stands for fixed address, lb stands for
    load balancing based on service name
  3. Routing assertion (predicates): rules for judging routing,
  4. Routing filters (filters): process the request or response

Route Predicate Factory Route Predicate Factory

The content that can be configured in the gateway route includes:

  • route id: the unique identifier of the route
  • uri: routing destination, supports both lb and http
  • predicates: Routing assertion, to judge whether the request meets the requirements, if it meets the request, it will be forwarded to the routing destination
  • filters: route filters, processing requests or responses

The assertion rules we write in the configuration file are just strings, these strings will be read and processed by the Predicate Factory, and transferred to the conditions for the way out judgment

Spring provides 11 basic Predicate factories:

10

Route filter GatewayFilter

GatewayFilter is a filter provided in the gateway, which can process the requests entering the gateway and the responses returned by microservices :

11

Spring provides 31 different route filter factories. For example:

12

More filters can be found in the document Spring Cloud Gateway Chinese Documentation (springdoc.cn)

default filter

If you want to take effect for all routes, you can write the filter factory under default. The format is as follows:

server:
  port: 10010 # 网关端口
spring:
  application:
    name: gateway # 服务名称cloud :
  cloud:
    nacos:
      server-addr: localhost:8848 # nacos地址
    gateway:
      routes: # 网关路由配置
        - id: user-service # 路由id,自定义,必须唯一
#          uri: http ://127.0.0.1:8081 # 路由的目标地址 http就是固定地址
          uri: lb://userservice # 路由的目标地址lb就是负载均衡,后面跟服务名称
          predicates: # 路由断言,判断请求是否符合规则
            - Path=/user/** # 路径断言,判断路径是否是以/user开头,如果是则符合
        - id: order-service
          uri: lb://orderservice
          predicates:
            - Path=/order/**
#          filters: # 指定路由的过滤器
#            - AddRequestHeader=MyKey,MyValue
      default-filters: # 全局过滤器
        - AddRequestHeader=MyKey,MyValue

Receive parameters:

@GetMapping("/{id}")
public User queryById(@PathVariable("id") Long id,
                      @RequestHeader(value = "MyKey", required = false) String myKey) {
    
    
    System.out.println("myKey:" + myKey);
    return userService.queryById(id);
}

Global Filter GlobalFilter

The role of the global filter is also to process all requests and microservice responses entering the gateway, which is the same as the role of GatewayFilter.

The difference is that GatewayFilter is defined through configuration, and the processing logic is fixed. The logic of GlobalFilter needs to be implemented by writing code yourself.

The way of definition is to implement the GlobalFilter interface.

  • the case

Define a global filter to intercept and determine user identity

Requirements: Define a global filter, intercept requests, and determine whether the parameters of the request meet the following conditions.

  • Whether there is authorization in the parameter,
  • Whether the authorization parameter value is admin

If it is satisfied at the same time, let it go, otherwise block it

  • Step: Custom Filters

    Customize the class, implement the GlobalFilter interface, and set the priority:

    //@Order(-1) //设置优先级,也可以通过实现Ordered接口设置(此处采用接口设置)
    @Component
    public class AuthorizeFilter implements GlobalFilter, Ordered {
          
          
        @Override
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
          
          
            //1.获取请求参数
            ServerHttpRequest request = exchange.getRequest();
            MultiValueMap<String, String> params = request.getQueryParams();
            //2.获取参数中的authorization参数
            String author = params.getFirst("authorization");
            //3.判断参数值是否等于admin
            if ("admin".equals(author)) {
          
          
                //4.是,放行
                return chain.filter(exchange);
            }
            //5.否,拦截
            //5.1设置状态码
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            //5.2拦截请求
            return exchange.getResponse().setComplete();
        }
    
        //设置优先级
        @Override
        public int getOrder() {
          
          
            return -1;
        }
    }
    

filter execution order

When a request enters the gateway, it will encounter three types of filters: current route filter, DefaultFilter, GlobalFilter

After requesting routing, the current routing filter, DefaultFilter, and GlobalFilter will be merged into a filter chain (collection), and each filter will be executed in turn after sorting

  • Each filter must specify an int type order value, the smaller the order value, the higher the priority, and the higher the execution order .
  • GlobalFilter specifies the order value by implementing the Ordered interface or adding the @Order annotation, which is specified by ourselves
  • The order of routing filters and defaultFilter is specified by Spring, and the default is to increase from 1 according to the order of declaration.
  • When the order values ​​of the filters are the same, they will be executed in the order of defaultFilter > routing filter > GlobalFilter.

Cross domain problem handling

Cross-domain: Inconsistent domain names are cross-domain, mainly including:

  • Different domain names: www.taobao.com and www.taobao.org and www.jd.com and miaosha.jd.com
  • Same domain name, different ports: localhost:8080 and localhost:8081
  • Different protocols: http and https

Cross-domain problem: The browser prohibits the originator of the request from making a cross-domain ajax request with the server, and the request is intercepted by the browser

Solution: CORS

The gateway also adopts the CORS scheme for cross-domain processing, and it only needs simple configuration to achieve:

spring:
  cloud:
    gateway:
      globalcors: # 全局的跨域处理
        add-to-simple-url-handler-mapping: true # 解决options请求被拦截问题
        corsConfigurations:
          '[/**]':
            allowedOrigins: # 允许哪些网站的跨域请求
              - "http://localhost:8090"
              - "http://www.leyou.com"
            allowedMethods: # 允许的跨域ajax的请求方式
              - "GET"
              - "POST"
              - "DELETE"
              - "PUT"
              - "OPTIONS"
            allowedHeaders: "*" # 允许在请求中携带的头信息
            allowCredentials: true # 是否允许携带cookie
            maxAge: 360000 # 这次跨域检测的有效期

Guess you like

Origin blog.csdn.net/jihuaTEL/article/details/130271231