SpringCloud Microservice Architecture---Basics

1. Understand microservices

1.1. Service Architecture Evolution

1.1.1. Monolithic architecture

Monolithic Architecture : All functions of the business are developed in one project and packaged into one package for deployment.

insert image description here

The advantages and disadvantages of monolithic architecture are as follows:

advantage:

  • simple structure
  • Low deployment cost

shortcoming:

  • High degree of coupling (difficult to maintain and upgrade)

1.1.2, distributed architecture

Distributed architecture : The system is split according to business functions, and each business function module is developed as an independent project, called a service.

insert image description here

Advantages and disadvantages of distributed architecture:

advantage:

  • Reduce service coupling
  • Conducive to service upgrade and expansion

shortcoming:

  • Service call relationship is intricate

Although the distributed architecture reduces service coupling, there are still many issues to consider when splitting services:

  • How to define the granularity of service splitting?
  • How to maintain the service cluster address?
  • How to implement remote calls between services?
  • How to perceive the service health status?

People need to develop a set of effective standards to constrain the distributed architecture.

1.1.3. Microservices

Architectural characteristics of microservices:

  • Single Responsibility: The granularity of microservice splitting is smaller, and each service corresponds to a unique business capability, so as to achieve a single responsibility
  • Autonomy: independent team, independent technology, independent data, independent deployment and delivery
  • Service-oriented: services provide a unified standard interface, independent of language and technology
  • Strong isolation: service calls are isolated, fault-tolerant, and degraded to avoid cascading problems

insert image description here

The above characteristics of microservices are actually setting a standard for distributed architecture, further reducing the coupling between services, and providing independence and flexibility of services. Achieve high cohesion and low coupling.

Therefore, microservices can be considered as a distributed architecture solution with well-designed architecture .

But how should the plan be implemented? What technology stack to choose? Internet companies around the world are actively trying their own microservice implementation solutions.

Among them, the most eye-catching one in the field of Java is the solution provided by Spring Cloud.

1.1.4. Summary

Monolithic architecture features?

  • Simple and convenient, highly coupled, poor scalability, suitable for small projects. Example: Student Management System

Distributed architecture characteristics?

  • Loose coupling and good scalability, but the structure is complex and difficult. Suitable for large-scale Internet projects, such as JD.com and Taobao

Microservices: A Good Distributed Architecture Solution

  • Advantages: smaller split granularity, more independent services, and lower coupling
  • Disadvantages: the structure is very complex, and the difficulty of operation and maintenance, monitoring and deployment is increased
  • SpringCloud is a one-stop solution for microservice architecture, integrating various excellent microservice functional components

1.2. Comparison of microservice technologies

1.2.1. Microservice structure

The microservice solution requires a technical framework to implement it. Internet companies around the world are actively trying their own microservice implementation technology. The most well-known in China are SpringCloud and Alibaba's Dubbo.

insert image description here

1.2.2. Comparison of microservices

insert image description here

insert image description here

1.3、SpringCloud

SpringCloud is currently the most widely used microservice framework in China. Official website address: https://spring.io/projects/spring-cloud.

SpringCloud integrates various microservice functional components, and realizes the automatic assembly of these components based on SpringBoot, thus providing a good out-of-the-box experience:

insert image description here

2. Microservice splitting and remote calling

2.1. Principles of service splitting

  • Different microservices, do not develop the same business repeatedly
  • Microservice data is independent, do not access the database of other microservices
  • Microservices can expose their business as interfaces for other microservices to call

insert image description here

2.2. Example of service splitting

cloud-demo: parent project, manage dependencies

  • order-service: order microservice, responsible for order-related business
  • user-service: user microservice, responsible for user-related business

Require:

  • Both the order microservice and the user microservice must have their own databases, independent of each other
  • Both order service and user service expose Restful interfaces to the outside world
  • If the order service needs to query user information, it can only call the Restful interface of the user service, and cannot query the user database

2.2.1. Import Sql statement

2.2.2. Import demo project

3. Eureka Registration Center

3.1, Eureka principle

insert image description here

How should consumers obtain specific information about service providers?

  • The service provider registers its own information with eureka when it starts
  • eureka saves this information
  • Consumers pull provider information from eureka according to the service name

If there are multiple service providers, how should consumers choose?

  • The service consumer uses the load balancing algorithm to select a service from the service list

How do consumers perceive the health status of service providers?

  • The service provider will send a heartbeat request to EurekaServer every 30 seconds to report the health status
  • eureka will update the record service list information, and the abnormal heartbeat will be removed
  • Consumers can pull the latest information

In the Eureka architecture, there are two types of microservice roles:

  • EurekaServer: server, registration center

    • Record service information
    • Heartbeat monitoring
  • EurekaClient: client

    • Provider: service provider, such as user-service in the case

      • Register your own information to EurekaServer
      • Send a heartbeat to EurekaServer every 30 seconds
    • consumer: service consumer, such as order-service in the case

      • Pull the service list from EurekaServer according to the service name

      • Do load balancing based on the service list, and initiate a remote call after selecting a microservice

3.2. Build EurekaServer

3.2.1. Create eureka-server service

3.2.2. Introduce eureka dependency

Introduce the starter dependency provided by SpringCloud for eureka:

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

3.2.3, write startup class

To write a startup class for the eureka-server service, be sure to add a @EnableEurekaServer annotation to enable the registration center function of eureka:

package cn.test.eureka;

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

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

3.2.4. Writing configuration files

Write an application.yml file with the following content:

server:
  port: 10086
spring:
  application:
    name: eureka-server
eureka:
  client:
    service-url: 
      defaultZone: http://127.0.0.1:10086/eureka

3.2.5. Start service

Start the microservice, and then visit it in the browser: http://127.0.0.1:10086

See the following result should be successful:

insert image description here

3.3. Service Registration

3.3.1. Introducing dependencies

In the pom file of user-service, introduce the following eureka-client dependencies:

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

3.3.2. Configuration file

In user-service, modify the application.yml file and add the service name and eureka address:

spring:
  application:
    name: userservice
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka

3.3.3. Start multiple user-service instances

In order to demonstrate a scenario where a service has multiple instances, we add a SpringBoot startup configuration and start a user-service.

3.4. Service Discovery

3.4.1. Introducing dependencies

As mentioned before, service discovery and service registration are all encapsulated in eureka-client dependencies, so this step is consistent with service registration.

In the pom file of order-service, introduce the following eureka-client dependencies:

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

3.4.2. Configuration file

Service discovery also needs to know the eureka address, so the second step is consistent with service registration, which is to configure eureka information:

In order-service, modify the application.yml file and add the service name and eureka address:

spring:
  application:
    name: orderservice
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka

3.4.3. Service pull and load balancing

Finally, we are going to pull the instance list of user-service service from eureka-server and implement load balancing.

But we don't need to do these actions, we just need to add some annotations.

In the OrderApplication of order-service, add a @LoadBalanced annotation to the RestTemplate Bean:

Spring will automatically help us obtain the instance list from the eureka-server side according to the service name userservice, and then complete the load balancing.

3.5. Summary

Build Eureka Server

  • Introduce eureka-server dependency

  • Add @EnableEurekaServer annotation

  • Configure the eureka address in application.yml

service registration

  • Introduce eureka-client dependency

  • Configure the eureka address in application.yml

service discovery

  • Introduce eureka-client dependency
  • Configure the eureka address in application.yml
  • Add @LoadBalanced annotation to RestTemplate
  • Remote call with the service name of the service provider

4. The principle of Ribbon load balancing

4.1. Principle of load balancing

The bottom layer of Spring Cloud actually uses a component called Ribbon to implement load balancing.

insert image description here

4.2. Source code tracking

Why can we access it only by entering the service name? You also need to get the ip and port before.

Obviously someone helped us get the ip and port of the service instance based on the service name. That is LoadBalancerInterceptor, this class will intercept the RestTemplate request, then obtain the service list from Eureka according to the service id, and then use the load balancing algorithm to obtain the real service address information and replace the service id.

We do source code tracking:

4.2.1、LoadBalancerIntercepor

insert image description here
You can see that the intercept method here intercepts the user's HttpRequest request, and then does several things:

  • request.getURI(): Get the request uri, in this case it is http://user-service/user/8
  • originalUri.getHost(): Get the host name of the uri path, which is actually the service id.user-service
  • this.loadBalancer.execute(): Process service id, and user requests.

Here this.loadBalanceris LoadBalancerClientthe type, we continue to follow.

4.2.2、LoadBalancerClient

Continue to follow the execute method:

insert image description here
The code is like this:

  • getLoadBalancer(serviceId): Get ILoadBalancer according to the service id, and ILoadBalancer will take the service id to eureka to get the service list and save it.
  • getServer(loadBalancer): Use the built-in load balancing algorithm to select one from the service list. In this example, you can see that the service on port 8082 has been obtained

After release, visit and trace again, and found that the obtained is 8081:

insert image description here

Sure enough, load balancing is achieved.

4.2.3, load balancing strategy IRule

In the code just now, you can see that the access service uses a getServermethod to do load balancing:

insert image description here

We continue to follow up:
insert image description here

Continue to trace the source code chooseServer method, and found such a piece of code:

insert image description here

Let's see who this rule is:
insert image description here

The default value of the rule here is a RoundRobinRule, see the introduction of the class:

insert image description here
Isn't that what polling means.

At this point, the entire load balancing process is clear to us.

4.2.4. Summary

The bottom layer of SpringCloudRibbon uses an interceptor to intercept the request sent by RestTemplate and modify the address. To sum it up with a picture:

insert image description here

The basic process is as follows:

  • Intercept our RestTemplate request http://userservice/user/1
  • RibbonLoadBalancerClient will get the service name from the request url, which is user-service
  • DynamicServerListLoadBalancer pulls the service list from eureka according to user-service
  • eureka returns the list, localhost:8081, localhost:8082
  • IRule uses built-in load balancing rules, select one from the list, such as localhost:8081
  • RibbonLoadBalancerClient modifies the request address, replaces userservice with localhost:8081, gets http://localhost:8081/user/1, and initiates a real request

4.3. Load balancing strategy

4.3.1, load balancing strategy

The rules of load balancing are defined in the IRule interface, and IRule has many different implementation classes:

insert image description here

The meanings of the different rules are as follows:

Built-in load balancing rule class Rule description
RoundRobinRule Simply poll the list of services to select a server. It is the default load balancing rule of Ribbon.
AvailabilityFilteringRule Ignore the following two servers: (1) By default, if this server fails to connect 3 times, this server will be set to the "short circuit" state. The short-circuit state will last for 30 seconds, and if the connection fails again, the duration of the short-circuit will increase geometrically. (2) Servers with too high concurrency. If the number of concurrent connections of a server is too high, the client configured with the AvailabilityFilteringRule rule will also ignore it. The upper limit of the number of concurrent connections can be configured by the ..ActiveConnectionsLimit property of the client.
WeightedResponseTimeRule Assign a weight value to each server. The longer the server response time, the less weight this server has. This rule will randomly select a server, and this weight value will affect the server selection.
ZoneAvoidanceRule Server selection is based on the servers available in the region. Use Zone to classify servers. This Zone can be understood as a computer room, a rack, etc. Then poll multiple services in the Zone.
BestAvailableRule Ignore servers that are short-circuited and choose servers with lower concurrency.
RandomRule Randomly select an available server.
RetryRule Selection logic for the retry mechanism

The default implementation is ZoneAvoidanceRule, which is a polling scheme

4.3.2, custom load balancing strategy

The load balancing rules can be modified by defining the IRule implementation. There are two ways:

  1. Code method: In the OrderApplication class in order-service, define a new IRule:
@Bean
public IRule randomRule(){
    
    
    return new RandomRule();
}
  1. Configuration file method: In the application.yml file of order-service, adding new configurations can also modify the rules:
userservice: # 给某个微服务配置负载均衡规则,这里是userservice服务
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 负载均衡规则 

Note that the default load balancing rules are generally used without modification.

4.4. Lazy loading

Ribbon uses lazy loading by default, that is, the LoadBalanceClient is created only when it is accessed for the first time, and the request time will be very long.

Hunger loading will be created when the project starts to reduce the time-consuming for the first visit. Enable hunger loading through the following configuration:

ribbon:
  eager-load:
    enabled: true
    clients: userservice

4.5. Summary

Ribbon load balancing rules

  • The rule interface is IRule

  • The default implementation is ZoneAvoidanceRule, select the service list according to the zone, and then poll

Custom load balancing method

  • Code method: flexible configuration, but needs to be repackaged and released when modified

  • Configuration method: Intuitive, convenient, no need to repackage and publish, but global configuration is not possible

hunger loading

  • Enable starvation loading

  • Specify the microservice name for starvation loading

5. Nacose Registration Center

5.1. Understanding and installing Nacos

5.1.1, Windows installation

5.1.1.1. Download the installation package

On the GitHub page of Nacos, there is a download link to download the compiled Nacos server or source code:

GitHub homepage: https://github.com/alibaba/nacos

GitHub's Release download page: https://github.com/alibaba/nacos/releases

As shown in the picture:

insert image description here

5.1.1.2, Decompression

Unzip this package to any non-Chinese directory, as shown in the figure:

insert image description here

Directory description:

  • bin: startup script
  • conf: configuration file

5.1.1.3, port configuration

The default port of Nacos is 8848. If other processes on your computer occupy port 8848, please try to close the process first.

If you cannot close the process occupying port 8848 , you can also enter the conf directory of nacos and modify the port in the configuration file:

insert image description here

5.1.1.4, start

The startup is very simple, enter the bin directory, the structure is as follows:

insert image description here

Then execute the command:

  • windows command:

    startup.cmd -m standalone
    

The effect after execution is as follows:

insert image description here

5.1.1.5. Access

Enter the address in the browser: http://127.0.0.1:8848/nacos:

insert image description here
The default account and password are both nacos, after entering:

insert image description here

5.1.2, Linux installation

5.1.2.1, install JDK

Nacos relies on JDK to run, and JDK needs to be installed on index Linux.

Upload the jdk installation package:

Upload to a directory, for example:/usr/local/

Then unzip:

tar -xvf jdk-8u144-linux-x64.tar.gz

then rename it to java

Configure environment variables:

export JAVA_HOME=/usr/local/java
export PATH=$PATH:$JAVA_HOME/bin

Set environment variables:

source /etc/profile

5.1.2.2. Upload installation package

As shown in the picture:

insert image description here

Upload to a directory of the Linux server, for example, /usr/local/srcunder the directory:

insert image description here

5.1.2.3, Decompression

Command to decompress the installation package:

tar -xvf nacos-server-1.4.1.tar.gz

Then remove the installation package:

rm -rf nacos-server-1.4.1.tar.gz

The final style in the directory:
insert image description here

Inside the directory:
insert image description here

5.1.2.4, port configuration

Similar to windows

5.1.2.5, start

In the nacos/bin directory, enter the command to start Nacos:

sh startup.sh -m standalone

5.2. Nacos quick start

Nacos is a component of SpringCloudAlibaba, and SpringCloudAlibaba also follows the service registration and service discovery specifications defined in SpringCloud. Therefore, there is not much difference between using Nacos and using Eureka for microservices.

The main differences are:

  • depends on different
  • service address is different

5.2.1. Introducing dependencies

<dependencyManagement>Introduce the dependency of SpringCloudAlibaba in the pom file of the cloud-demo parent project :

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-alibaba-dependencies</artifactId>
    <version>2.2.6.RELEASE</version>
    <type>pom</type>
    <scope>import</scope>
</dependency>

Then introduce nacos-discovery dependencies in the pom files in user-service and order-service:

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

Note : Don't forget to comment out eureka's dependencies.

5.2.2. Configure nacos address

Add nacos address in application.yml of user-service and order-service:

spring:
  cloud:
    nacos:
      server-addr: localhost:8848

Note : Don't forget to comment out the address of eureka

5.2.3, restart

After restarting the microservice, log in to the nacos management page, and you can see the microservice information:

insert image description here

Summarize:

Nacos service construction

  • Download the installation package
  • decompress
  • Run the command in the bin directory: startup.cmd -m standalone

Nacos service registration or discovery

  • Introduce nacos.discovery dependency
  • Configure nacos address spring.cloud.nacos.server-addr

5.3, Nacos service hierarchical storage model

A service can have multiple instances , such as our user-service, can have:

  • 127.0.0.1:8081
  • 127.0.0.1:8082
  • 127.0.0.1:8083

If these instances are distributed in different computer rooms across the country, for example:

  • 127.0.0.1:8081, in Shanghai computer room
  • 127.0.0.1:8082, in Shanghai computer room
  • 127.0.0.1:8083, in Hangzhou computer room

Nacos divides the instances in the same computer room into a cluster .

In other words, user-service is a service. A service can contain multiple clusters, such as Hangzhou and Shanghai. Each cluster can have multiple instances, forming a hierarchical model, as shown in the figure:

insert image description here

When microservices access each other, they should access the same cluster instance as much as possible, because local access is faster. Only access other clusters when the cluster is unavailable. For example:

insert image description here

The order-service in the computer room in Hangzhou should give priority to access the user-service in the same computer room.

Summarize:

Nacos service hierarchical storage model

  • The first level is a service, such as userservice
  • The second level is the cluster, such as Hangzhou or Shanghai
  • The third level is an instance, such as a server in a computer room in Hangzhou that deploys userservice

How to set cluster properties for an instance

  • Modify the application.yml file and add the spring.cloud.nacos.discovery.cluster-name property

5.3.1. Configure cluster for user-service

Modify the application.yml file of user-service and add the cluster configuration:

spring:
  cloud:
    nacos:
      server-addr: localhost:8848
      discovery:
        cluster-name: HZ # 集群名称

After restarting the two user-service instances, we can see the following results in the nacos console:
insert image description here
we copy a user-service startup configuration again and add properties:

-Dserver.port=8083 -Dspring.cloud.nacos.discovery.cluster-name=SH

The configuration is shown in the figure:
insert image description here

Check the nacos console again after starting UserApplication3:
insert image description here

5.3.2. Load balancing with the same cluster priority

By default, ZoneAvoidanceRuleit is not possible to achieve load balancing based on the priority of the same cluster.

Therefore, Nacos provides an NacosRuleimplementation that can preferentially select instances from the same cluster.

1) Configure cluster information for order-service

Modify the application.yml file of order-service and add the cluster configuration:

spring:
  cloud:
    nacos:
      server-addr: localhost:8848
      discovery:
        cluster-name: HZ # 集群名称

2) Modify the load balancing rules

Modify the application.yml file of order-service and modify the load balancing rules:

userservice:
  ribbon:
    NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule # 负载均衡规则 

NacosRule load balancing strategy

  • Prioritize the list of service instances in the same cluster
  • The local cluster cannot find the provider, so it goes to other clusters to look for it, and a warning will be reported
  • After determining the list of available instances, random load balancing is used to select instances

5.4. Weight Configuration

In actual deployment, such a scenario will appear:

The performance of server equipment is different. Some instances have better performance, while others have poorer performance. We hope that machines with better performance can bear more user requests.

But by default, NacosRule is randomly selected in the same cluster, without considering the performance of the machine.

Therefore, Nacos provides weight configuration to control the access frequency. The greater the weight, the higher the access frequency.

In the nacos console, find the user-service instance list, click Edit, and you can modify the weight:
insert image description here
In the pop-up editing window, modify the weight:
insert image description here

Note : If the weight is modified to 0, the instance will never be visited

5.5. Nacos environment isolation

Nacos provides namespace to implement environment isolation.

  • There can be multiple namespaces in nacos
  • There can be group, service, etc. under the namespace
  • Different namespaces are isolated from each other, for example, services in different namespaces are invisible to each other

insert image description here

5.5.1. Create a namespace

By default, all services, data, and groups are in the same namespace named public:

insert image description here

We can click the Add button on the page to add a namespace:

insert image description here
Then, fill out the form:

insert image description here

You can see a new namespace on the page:

insert image description here

5.5.2. Configure namespace for microservices

Configuring namespaces for microservices can only be achieved by modifying the configuration.

For example, modify the application.yml file of order-service:

spring:
  cloud:
    nacos:
      server-addr: localhost:8848
      discovery:
        cluster-name: HZ
        namespace: 492a7d5d-237b-46a1-a99a-fa8e98e4b0f9 # 命名空间,填ID

After restarting order-service, access the console, and you can see the following results:

insert image description here
insert image description here

At this time, when accessing order-service, because the namespace is different, the userservice will not be found, and the console will report an error:

insert image description here

Summarize:

Nacos environment isolation

  • Each namespace has a unique id
  • When the service sets the namespace, write the id instead of the name
  • Services under different namespaces are invisible to each other

5.6 The difference between Nacos and Eureka

Nacos service instances are divided into two types:

  • Temporary instance: If the instance is down for more than a certain period of time, it will be removed from the service list, the default type.

  • Non-temporary instance: If the instance goes down, it will not be removed from the service list, and it can also be called a permanent instance.

Configure a service instance as a permanent instance:

spring:
  cloud:
    nacos:
      discovery:
        ephemeral: false # 设置为非临时实例

The overall structure of Nacos and Eureka is similar, service registration, service pull, heartbeat waiting, but there are some differences:

insert image description here

  • What Nacos and eureka have in common

    • Both support service registration and service pull
    • Both support the service provider's heartbeat method for health detection
  • The difference between Nacos and Eureka

    • Nacos supports the server to actively detect the provider status: the temporary instance adopts the heartbeat mode, and the non-temporary instance adopts the active detection mode
    • Temporary instances with abnormal heartbeat will be removed, while non-temporary instances will not be removed
    • Nacos supports the message push mode of service list changes, and the service list updates more timely
    • The Nacos cluster adopts the AP mode by default. When there are non-temporary instances in the cluster, the CP mode is adopted; Eureka adopts the AP mode

6. Nacos configuration management

6.1. Unified configuration management

When more and more instances of microservices are deployed, reaching dozens or hundreds, modifying the configuration of microservices one by one will make people crazy and error-prone. We need a unified configuration management solution that can centrally manage the configuration of all instances.

insert image description here
On the one hand, Nacos can centrally manage the configuration, and on the other hand, when the configuration changes, it can notify the microservice in time to realize hot update of the configuration.

6.1.1. Add configuration files in nacos

How to manage configuration in nacos?
insert image description here

Then fill in the configuration information in the pop-up form:
insert image description here

Note: The core configuration of the project needs to be managed by nacos only when the hot update configuration is required. It is better to save some configurations that will not be changed locally in the microservice.

6.1.2. Pull configuration from microservices

The microservice needs to pull the configuration managed in nacos and merge it with the local application.yml configuration to complete the project startup.

But if application.yml has not been read yet, how do you know the address of nacos?

Therefore, spring introduces a new configuration file: bootstrap.yaml file, which will be read before application.yml. The process is as follows:

insert image description here

1) Introduce nacos-config dependency

First, in the user-service service, introduce the client dependency of nacos-config:

<!--nacos配置管理依赖-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>

2) Add bootstrap.yaml

Then, add a bootstrap.yaml file in user-service with the following content:

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

Here, the nacos address will be obtained according to spring.cloud.nacos.server-addr, and then according to

${spring.application.name}-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}As a file id, to read the configuration.

In this example, it is to read userservice-dev.yaml:

insert image description here

3) Read nacos configuration

Add business logic to UserController in user-service, read pattern.dateformat configuration:

insert image description here

Full code:

@Slf4j
@RestController
@RequestMapping("/user")
public class UserController {
    
    

    @Autowired
    private UserService userService;

    @Value("${pattern.dateformat}")
    private String dateformat;
    
    @GetMapping("now")
    public String now(){
    
    
        return LocalDateTime.now().format(DateTimeFormatter.ofPattern(dateformat));
    }
    // ...略
}

When visiting the page, you can see the effect:

insert image description here

Summarize:

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

6.2. Configuration hot update

Our ultimate goal is to modify the configuration in nacos, so that the microservice can make the configuration take effect without restarting, that is, configuration hot update .

To achieve configuration hot update, two methods can be used:

6.2.1. Method 1

Add the annotation @RefreshScope to the class where the variable injected by @Value is located:

insert image description here

6.2.2. Method 2

Use the @ConfigurationProperties annotation instead of the @Value annotation.

In the user-service service, add a class to read the patternern.dateformat property:

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

Use this class instead of @Value in UserController:

insert image description here

Full code:

@Slf4j
@RestController
@RequestMapping("/user")
public class UserController {
    
    

    @Autowired
    private UserService userService;

    @Autowired
    private PatternProperties patternProperties;

    @GetMapping("now")
    public String now(){
    
    
        return LocalDateTime.now().format(DateTimeFormatter.ofPattern(patternProperties.getDateformat()));
    }

    // 略
}

Summarize:

After the Nacos configuration is changed, microservices can be hot updated in the following ways:

  • Injected through the @Value annotation, combined with @RefreshScope to refresh
  • Injected through @ConfigurationProperties, automatically refreshed

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

6.3, configuration sharing

In fact, when the microservice starts, it will go to nacos to read multiple configuration files, for example:

  • [spring.application.name]-[spring.profiles.active].yaml, for example: userservice-dev.yaml

  • [spring.application.name].yaml, for example: userservice.yaml

Does not[spring.application.name].yaml contain environments, so can be shared by multiple environments.

Let's test the configuration sharing through the case

6.3.1. Add an environment sharing configuration

We add a userservice.yaml file in nacos:

insert image description here

6.3.2. Read shared configuration in user-service

In the user-service service, modify the PatternProperties class to read the newly added properties:
insert image description here

In the user-service service, modify UserController and add a method:
insert image description here

6.3.3. Run two UserApplications with different profiles

Modify the startup item UserApplication2 and change its profile value:

In this way, the profile used by UserApplication (8081) is dev, and the profile used by UserApplication2 (8082) is test.

Start UserApplication and UserApplication2

Visit http://localhost:8081/user/prop, the result:
insert image description here

Visit http://localhost:8082/user/prop, the result:
insert image description here

It can be seen that both the dev and test environments have read the value of the attribute envSharedValue.

6.3.4. Configure the priority of sharing

When nacos and service local have the same attribute at the same time, the priority is divided into high and low:
insert image description here
summary:

Configuration files read by microservices by default:

  • [service name]-[spring.profile.active].yaml, the default configuration
  • [service name].yaml, multi-environment sharing

Configuration files shared by different microservices:

  • Specified by shared-configs
  • Specified by extension-configs

priority:

  • Environment configuration > service name.yaml > extension-config > extension-configs > shared-configs > local configuration

6.2. Build a Nacos cluster

6.2.1. Cluster structure diagram

The official Nacos cluster diagram:

insert image description here

It contains 3 nacos nodes, and then a load balancer proxy 3 Nacos. Here the load balancer can use nginx.

Our planned cluster structure:

insert image description here

Addresses of three nacos nodes:

node ip port
nacos1 192.168.150.1 8845
nacos2 192.168.150.1 8846
nacos3 192.168.150.1 8847

6.2.2. Building a cluster

The basic steps to build a cluster:

  • Build the database and initialize the database table structure
  • Download nacos installation package
  • configure nacos
  • Start nacos cluster
  • nginx reverse proxy

6.2.2.1. Initialize the database

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 master-slave. Here we take a single-point database as an example to explain.

First create a new database, name it nacos, and then import the following 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');

6.2.2.2. Download nacos

nacos has a download address on GitHub: https://github.com/alibaba/nacos/tags, you can choose any version to download.

6.2.2.3, configure Nacos

Unzip this package to any non-Chinese directory, as shown in the figure:
insert image description here

Directory description:

  • bin: startup script
  • conf: configuration file

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

Then add 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=123

6.2.2.4, start

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

6.2.2.5, nginx reverse proxy

Find the nginx installation package and extract it to any non-Chinese directory:

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.

The application.yml file configuration in the code is as follows:

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

6.2.2.6. Optimization

  • In actual deployment, it is necessary to set a domain name for the nginx server that acts as a reverse proxy, so that there is no need to change the configuration of the nacos client if the server is migrated later.

  • Each node of Nacos should be deployed to multiple different servers for disaster recovery and isolation

Summarize:

Cluster construction steps:

  • Build a MySQL cluster and initialize the database table
  • Download and decompress nacos
  • Modify cluster configuration (node ​​information), database configuration
  • Start multiple nacos nodes separately
  • nginx reverse proxy

Seven, Feign remote call

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

There are following problems:

• Poor code readability and inconsistent programming experience

• URLs with complex parameters are difficult to maintain

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.

7.1, Feign replaces RestTemplate

The steps to use Fegin are as follows:

7.1.1. Introducing dependencies

We introduce the feign dependency in the pom file of the order-service service:

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

7.1.2. Add annotations

Add annotations to the startup class of order-service to enable the function of Feign:

insert image description here

7.1.3, write Feign client

Create a new interface in order-service with the following content:

@FeignClient("userservice")
public interface UserClient {
    
    
    @GetMapping("/user/{id}")
    User findById(@PathVariable("id") Long id);
}

This client is mainly based on SpringMVC annotations to declare remote call information, such as:

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

In this way, Feign can help us send http requests without using RestTemplate to send them ourselves.

7.1.4. Test

Modify the queryOrderById method in the OrderService class in order-service, and use Feign client instead of RestTemplate:

insert image description here

Doesn't it look more elegant.

7.1.5. Summary

Steps to use Feign:

① Introduce dependency

② Add @EnableFeignClients annotation

③ Write the FeignClient interface

④ Use the method defined in FeignClient instead of RestTemplate

7.2, custom configuration

Feign can support many custom configurations, as shown in the following table:

type effect illustrate
feign.Logger.Level Modify log level Contains four different levels: NONE, BASIC, HEADERS, FULL
feign.codec.Decoder Parser for the response result Parsing the results of http remote calls, such as parsing json strings into java objects
feign.codec.Encoder request parameter encoding Encode request parameters for sending via http requests
feign. Contract Supported Annotation Formats The default is the annotation of SpringMVC
feign. Retryer Failure retry mechanism The retry mechanism for request failure, the default is no, but Ribbon's retry will be used

Under normal circumstances, the default value is enough for us to use. If you want to customize it, you only need to create a custom @Bean to override the default Bean.

The following uses logs as an example to demonstrate how to customize the configuration.

7.2.1. Configuration file method

Modifying feign's log level based on the configuration file can target a single service:

feign:  
  client:
    config: 
      userservice: # 针对某个微服务的配置
        loggerLevel: FULL #  日志级别 

It is also possible to target all services:

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

The log level is divided into four types:

  • NONE: Do not record any log information, which is the default value.
  • BASIC: Only log the request method, URL, response status code and execution time
  • HEADERS: On the basis of BASIC, the header information of the request and response is additionally recorded
  • FULL: Record details of all requests and responses, including header information, request body, and metadata.

7.2.2, Java code method

You can also modify the log level based on Java code, first declare a class, and then declare a Logger.Level object:

public class DefaultFeignConfiguration  {
    
    
    @Bean
    public Logger.Level feignLogLevel(){
    
    
        return Logger.Level.BASIC; // 日志级别为BASIC
    }
}

If you want to take effect globally , put it in the @EnableFeignClients annotation of the startup class:

@EnableFeignClients(defaultConfiguration = DefaultFeignConfiguration .class) 

If it is locally effective , put it in the corresponding @FeignClient annotation:

@FeignClient(value = "userservice", configuration = DefaultFeignConfiguration .class) 

Summarize:

Feign's log configuration:

  • The first way is to configure the file, feign.client.config.xxx.loggerLevel

    • If xxx is default, it means global
    • If xxx is a service name, such as userservice, it represents a service
  • The second way is to configure the Logger.Level Bean in java code

    • If declared in the @EnableFeignClients annotation, it represents the global
    • If declared in the @FeignClient annotation, it represents a service

7.3, Feign usage optimization

The bottom layer of Feign initiates http requests and relies on other frameworks. Its underlying client implementation includes:

  • URLConnection: default implementation, does not support connection pooling

  • Apache HttpClient: support connection pool

  • OKHttp: support connection pool

Therefore, optimizing the performance of Feign mainly includes:

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

Here we use Apache's HttpClient to demonstrate.

7.3.1. Introducing dependencies

Introduce Apache's HttpClient dependency in the pom file of order-service:

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

7.3.2, configure the connection pool

Add configuration in application.yml of order-service:

feign:
  client:
    config:
      default: # default全局的配置
        loggerLevel: BASIC # 日志级别,BASIC就是基本的请求和响应信息
  httpclient:
    enabled: true # 开启feign对HttpClient的支持
    max-connections: 200 # 最大的连接数
    max-connections-per-route: 50 # 每个路径的最大连接数

Summarize

  • Try to use basic as the log level

  • Use HttpClient or OKHttp instead of URLConnection

    • Introduce feign-httpClient dependency

    • The configuration file enables the httpClient function and sets the connection pool parameters

7.4. Best Practices

The so-called recent practice refers to the experience summed up during the use process, the best way to use it.

Self-study observation shows that Feign's client is very similar to the controller code of the service provider:

feign client:

insert image description here

UserController:

insert image description here

Is there a way to simplify this repetitive coding?

7.4.1. Inheritance method

The same code can be shared through inheritance:

1) Define an API interface, use the definition method, and make a statement based on SpringMVC annotations.

2) Both the Feign client and the Controller integrate the interface
insert image description here

advantage:

  • Simple
  • code sharing

shortcoming:

  • Service provider and service consumer are tightly coupled

  • The annotation mapping in the parameter list will not be inherited, so the method, parameter list, and annotation must be declared again in the Controller

7.4.2. Extraction method

Extract Feign's Client 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.

For example, the default configurations of UserClient, User, and Feign are all extracted into a feign-api package, and all microservices can be used directly by referencing this dependency package.

insert image description here

Summarize:

Best practices for Feign:

  • Let controller and FeignClient inherit the same interface
  • Define the default configuration of FeignClient, POJO, and Feign into one project for all consumers to use

7.4.3. Implementing extraction-based best practices

7.4.3.1. Extraction

First create a module named feign-api:

In feign-api, then introduce feign's starter dependency

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

Then, the UserClient, User, and DefaultFeignConfiguration written in order-service are all copied to the feign-api project

7.4.3.2. Using feign-api in order-service

First, delete classes or interfaces such as UserClient, User, and DefaultFeignConfiguration in order-service.

Introduce the dependency of feign-api in the pom file of order-service:

<dependency>
    <groupId>cn.test.demo</groupId>
    <artifactId>feign-api</artifactId>
    <version>1.0</version>
</dependency>

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

7.4.3.3, restart test

After restarting, it was found that the service reported an error:

This is because UserClient is now under the cn.test.feign.clients package,

The @EnableFeignClients annotation of order-service is under the cn.test.order package, not in the same package, and UserClient cannot be scanned.

7.4.3.4, Solve the scanning package problem

method one:

Specify the packages that Feign should scan:

@EnableFeignClients(basePackages = "cn.test.feign.clients")

Method 2:

Specify the Client interface that needs to be loaded:

@EnableFeignClients(clients = {
    
    UserClient.class})

There are two ways to import FeignClient of different packages:

  • Add basePackages in the @EnableFeignClients annotation to specify the package where FeignClient is located
  • Add clients to the @EnableFeignClients annotation to specify the bytecode of the specific FeignClient

Summarize:

The steps to implement Best Practice Method 2 are as follows:

  • First create a module named feign-api, and then introduce feign's starter dependency
  • Copy the UserClient, User, and DefaultFeignConfiguration written in order-service to the feign-api project
  • Introduce the dependency of feign-api in order-service
  • Modify all the import parts related to the above three components in order-service, and change them to import the package in feign-api
  • restart test

8. Gateway Service Gateway

Spring Cloud Gateway is a new project of Spring Cloud, which is a gateway developed based on Spring 5.0, Spring Boot 2.0 and Project Reactor and other reactive programming and event flow technologies. It aims to provide a simple and effective unified API route management method.

8.1. Why do we need a gateway

Gateway is the gatekeeper of our services, the unified entrance of all microservices.

The core functional characteristics of the gateway :

  • request routing
  • access control
  • Limiting

Architecture diagram:

[External link image transfer failed, the source site may have an anti-leeching mechanism, it is recommended to save the image and upload it directly (img-jD806LhV-1681478872549)(assets/image-20210714210131152.png)]

Access control : As the entry point of microservices, the gateway needs to verify whether the user is eligible for the request, and intercept it if not.

Routing and load balancing : All requests must first pass through the gateway, but the gateway does not process business, but forwards the request to a microservice according to certain rules. This process is called routing. Of course, when there are multiple target services for routing, load balancing is also required.

Current limiting : When the request traffic is too high, the gateway will release the request according to the speed that the downstream microservice can accept, so as to avoid excessive service pressure.

There are two types of gateway implementations in Spring Cloud:

  • 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.

Summarize:

The role of the gateway:

  • Perform identity authentication and permission verification on user requests
  • Route user requests to microservices and implement load balancing
  • Limit user requests

8.2, gateway quick start

Next, we will demonstrate the basic routing function of the gateway. The basic steps are as follows:

  1. Create a SpringBoot project gateway and introduce gateway dependencies
  2. Write startup class
  3. Write basic configuration and routing rules
  4. Start the gateway service for testing

8.2.1. Create a gateway service and introduce dependencies

Create a service and introduce dependencies:

<!--网关-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--nacos服务发现依赖-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

8.2.2. Write startup class

@SpringBootApplication
public class GatewayApplication {
    
    

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

8.2.3. Write basic configuration and routing rules

Create an application.yml file with the following content:

server:
  port: 10010 # 网关端口
spring:
  application:
    name: gateway # 服务名称
  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/开头就符合要求

We will Pathproxy all requests that match the rules to urithe address specified by the parameter.

In this example, we proxy /user/**the initial request to, lb://userservicelb is load balancing, pull the service list according to the service name, and realize load balancing.

8.2.4, restart test

Restart the gateway, when accessing http://localhost:10010/user/1, it complies with /user/**the rules, the request is forwarded to uri: http://userservice/user/1, and the result is obtained:

8.2.5. Flow chart of gateway routing

The entire access process is as follows:

insert image description here

Summarize:

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 assertions (predicates): rules for judging routing,

  4. Route filters (filters): process the request or response

8.3. Assertion 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: routing filters, processing requests or responses

The assertion rules we write in the configuration file are just strings, which will be read and processed by the Predicate Factory and turned into conditions for routing judgments

For example, Path=/user/** is matched according to the path. This rule is determined by

org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactoryclass to

For processing, there are more than a dozen assertion factories like this in Spring Cloud Gateway:

name illustrate example
After is a request after a certain point in time - After=2037-01-20T17:42:47.789-07:00[America/Denver]
Before is a request before some point in time - Before=2031-04-13T15:14:47.433+08:00[Asia/Shanghai]
Between is a request before a certain two points in time - Between=2037-01-20T17:42:47.789-07:00[America/Denver], 2037-01-21T17:42:47.789-07:00[America/Denver]
Cookie Requests must contain certain cookies - Cookie=chocolate, ch.p
Header Requests must contain certain headers - Header=X-Request-Id, \d+
Host The request must be to access a certain host (domain name) - Host=.somehost.org,.anotherhost.org
Method The request method must be specified - Method=GET,POST
Path The request path must conform to the specified rules - Path=/red/{segment},/blue/**
Query The request parameters must contain the specified parameters - Query=name, Jack or - Query=name
RemoteAddr The requester's ip must be in the specified range - RemoteAddr=192.168.1.1/24
Weight weight processing

We only need to master the routing engineering of Path.

8.4. Filter factory

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

insert image description here

8.4.1. Types of routing filters

Spring provides 31 different route filter factories. For example:

name illustrate
AddRequestHeader Add a request header to the current request
RemoveRequestHeader Remove a request header from the request
AddResponseHeader Add a response header to the response result
RemoveResponseHeader There is a response header removed from the response result
RequestRateLimiter limit the amount of requests

8.4.2. Request header filter

Let's take AddRequestHeader as an example to explain.

Requirement : Add a request header to all requests entering userservice: Truth=test is freaking awesome!

Just modify the application.yml file of the gateway service and add route filtering:

spring:
  cloud:
    gateway:
      routes:
      - id: user-service 
        uri: lb://userservice 
        predicates: 
        - Path=/user/** 
        filters: # 过滤器
        - AddRequestHeader=Truth, test is freaking awesome! # 添加请求头

The current filter is written under the userservice route, so it is only valid for requests to access userservice.

8.4.3, default filter

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

spring:
  cloud:
    gateway:
      routes:
      - id: user-service 
        uri: lb://userservice 
        predicates: 
        - Path=/user/**
      default-filters: # 默认过滤项
      - AddRequestHeader=Truth, test is freaking awesome! 

8.4.4 Summary

What is the role of the filter?

① Process the routing request or response, such as adding a request header

② The filter configured under the route only takes effect for the request of the current route

What is the role of defaultFilters?

① A filter that is effective for all routes

8.5. Global filter

上次学习的过滤器,网关提供了31种,但每一种过滤器的作用都是固定的。如果我们希望拦截请求,做自己的业务逻辑则没办法实现。

8.5.1、全局过滤器作用

全局过滤器的作用也是处理一切进入网关的请求和微服务响应,与GatewayFilter的作用一样。区别在于GatewayFilter通过配置定义,处理逻辑是固定的;而GlobalFilter的逻辑需要自己写代码实现。

定义方式是实现GlobalFilter接口。

public interface GlobalFilter {
    
    
    /**
     *  处理当前请求,有必要的话通过{@link GatewayFilterChain}将请求交给下一个过滤器处理
     *
     * @param exchange 请求上下文,里面可以获取Request、Response等信息
     * @param chain 用来把请求委托给下一个过滤器 
     * @return {@code Mono<Void>} 返回标示当前过滤器业务结束
     */
    Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);
}

在filter中编写自定义逻辑,可以实现下列功能:

  • 登录状态判断
  • 权限校验
  • 请求限流等

8.5.2、自定义全局过滤器

需求:定义全局过滤器,拦截请求,判断请求的参数是否满足下面条件:

  • 参数中是否有authorization,

  • authorization参数值是否为admin

如果同时满足则放行,否则拦截

实现:

在gateway中定义一个过滤器:

@Order(-1)
@Component
public class AuthorizeFilter implements GlobalFilter {
    
    
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    
    
        // 1.获取请求参数
        MultiValueMap<String, String> params = exchange.getRequest().getQueryParams();
        // 2.获取authorization参数
        String auth = params.getFirst("authorization");
        // 3.校验
        if ("admin".equals(auth)) {
    
    
            // 放行
            return chain.filter(exchange);
        }
        // 4.拦截
        // 4.1.禁止访问,设置状态码
        exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
        // 4.2.结束处理
        return exchange.getResponse().setComplete();
    }
}

全局过滤器的作用是什么?

  • 对所有路由都生效的过滤器,并且可以自定义处理逻辑

实现全局过滤器的步骤?

  • 实现GlobalFilter接口
  • 添加@Order注解或实现Ordered接口
  • 编写处理逻辑

8.5.3、过滤器执行顺序

请求进入网关会碰到三类过滤器:当前路由的过滤器、DefaultFilter、GlobalFilter

请求路由后,会将当前路由过滤器和DefaultFilter、GlobalFilter,合并到一个过滤器链(集合)中,排序后依次执行每个过滤器:

insert image description here

排序的规则是什么呢?

  • 每一个过滤器都必须指定一个int类型的order值,order值越小,优先级越高,执行顺序越靠前
  • GlobalFilter通过实现Ordered接口,或者添加@Order注解来指定order值,由我们自己指定
  • 路由过滤器和defaultFilter的order由Spring指定,默认是按照声明顺序从1递增。
  • 当过滤器的order值一样时,会按照 defaultFilter > 路由过滤器 > GlobalFilter的顺序执行。

For details, you can view the source code:

org.springframework.cloud.gateway.route.RouteDefinitionRouteLocator#getFilters()The method is to load the defaultFilters first, then load the filters of a certain route, and then merge them.

org.springframework.cloud.gateway.handler.FilteringWebHandler#handle()The method will load the global filter, sort according to the order after merging with the previous filter, and organize the filter chain

Summary:
Execution order of routing filter, defaultFilter, and global filter?

  • The smaller the order value, the higher the priority
  • When the order value is the same, the order is the defaultFilter first, then the local routing filter, and finally the global filter

8.6. Cross-domain issues

8.6.1. What is a cross-domain problem

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 localhost8081

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, this should have been learned before, so I won't repeat it here. Friends who don’t know can check https://www.ruanyifeng.com/blog/2016/04/cors.html

8.6.2. Simulation of cross-domain problems

Access localhost:10010 from localhost:8090, the port is different, it is obviously a cross-domain request.

8.6.3. Solve cross-domain problems

In the application.yml file of the gateway service, add the following configuration:

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

8.7. Limiting filter

Current limiting: Limit the requests of the application server to avoid overloading or even downtime of the server due to too many requests. There are two common current limiting algorithms:

  • Counter algorithm, including window counter algorithm, sliding window counter algorithm
  • Leaky Bucket
  • Token Bucket Algorithm (Token Bucket)

insert image description here

insert image description here

insert image description here

Guess you like

Origin blog.csdn.net/shuai_h/article/details/130140455