I Build a simple microservice project

Posted by NathanLedet on Sun, 13 Feb 2022 14:16:56 +0100

I Build a simple microservice project

1. Evolution from monomer architecture to microservice architecture

The single architecture we first came into contact with has only one project for the whole system. The packaging is often made into a war package and then deployed to a single tomcat. This is the single architecture, as shown in the figure:

Advantages of single architecture

1. Simple structure and deployment

2. Less hardware resources required

3. Cost savings

shortcoming

1. Version iteration is slow, and often changing a code will affect the overall situation

2. Cannot satisfy certain concurrent access

3. It is difficult to maintain the code. All the codes in one project are at risk of being modified by others

With the expansion of business and the development of the company, the single architecture can not meet our needs slowly. We need to change the architecture. The simplest way we can think of is to add machines and expand the application horizontally. As shown in the figure:

This architecture seems to solve our problem temporarily, but after the number of users increases slowly, we can only solve it by adding machines horizontally. There will still be problems of slow version iteration and difficult code maintenance. It is impossible for users to expand the capacity of the whole module because there are too many users who need to expand the capacity of the whole module. Therefore, it is impossible for users to expand the capacity of the whole module. Therefore, it is necessary to split the whole project according to modules. The split architecture diagram is as follows:

After the module is split, the communication between modules needs to be carried out through interface call, and the load balancing between modules is carried out through shunting software. This architecture solves the problems of resource waste and code management in front, because we split the system, and each module has a separate project. For example, if I modify the commodity module, I don't need to worry about whether it will affect the shopping cart module. However, this architecture expansion is very troublesome. Once you need to add machines horizontally or reduce machines, you need to modify the nginx configuration. Once there are more machines, the amount of nginx configuration is an impossible task. OK, then the SOA Service governance framework came into being. The architecture diagram is as follows:

The SOA framework based on the registry is very convenient to expand, because there is no need to maintain the shunting tool, but when we start the application, we will register the service to the registry through http.

There are generally three roles in SOA framework: 1. Registry 2. Service provider 3. Service consumer

1. Registration Center

A list of services is maintained in the registry

2. Service provider

When the service provider starts, it will register itself in the registration center

3. Service consumer

When the service consumer starts, the list of the services of the registration center is obtained, and then one of them is called from the service list when it is called.

Characteristics of microservice Engineering:

1. Flexible expansion

2. Each application is small in scale

3. Clear service boundary and perform their respective duties

4. With more packaged applications, CI continuous integration tools are often needed

2. Simple microservice engineering construction

1. Establishment of Registration Center (eureka server: Netflix eureka server)

In spring cloud, we choose eureka as the registry. The spring cloud project is based on the springboot project. pom. jar package dependency in XML:

<!--parent-->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.2.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>
<!--Springcloud Version of-->
<properties>
    <java.version>1.8</java.version>
    <spring-cloud.version>Hoxton.SR1</spring-cloud.version>
</properties>

<!--Eureka Server initiator import-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<!--Springcloud Dependent warehouse import-->
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>${spring-cloud.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

application.properties configuration file (cannot be called bootstrap.properties)

server.port=8763
eureka.instance.hostname=localhost
#Register with eureka
eureka.client.registerWithEureka=false
#Whether to pull the registration information from eureka
eureka.client.fetchRegistry=false
##Expose the address of eureka service
# http://localhost:8763/eureka/
eureka.client.serviceUrl.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka/

#In the self-protection mode, when there are network partitions and eureka loses too many clients in a short time, it will enter the self-protection mode, that is, if a service does not send heartbeat for a long time, eureka will not delete it. The default is true
eureka.server.enable-self-preservation=true

#The time interval for eureka server to clean up invalid nodes. The default is 60000 milliseconds, that is, 60 seconds
eureka.server.eviction-interval-timer-in-ms=60000

#Service manual offline delete request http://localhost:8763/eureka/apps/MICRO-ORDER/localhost:xxx:8084

Startup class

package len.hgy;

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

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

see

http://localhost:8763/

2. Service provider (eureka client: Netflix eureka client)

Pom's jar package depends on. Everything else is the same as eureka server, except that the service provider needs to register the service with eureka server, so the service provider is eureka's client, so it needs to import the initiator of eureka client.

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

bootstrap.properties

spring.application.name=micro-order
server.port=8084

eureka.client.serviceUrl.defaultZone=http://localhost:8763/eureka/
# There is a default configuration
#Service renewal, heartbeat interval
eureka.instance.lease-renewal-interval-in-seconds=30
#If you do not receive a new heartbeat within 90 seconds from the previous heartbeat time, call the rejection service
eureka.instance.lease-expiration-duration-in-seconds=90
#It indicates how long the eureka client takes to pull the service registration information. The default is 30 seconds
eureka.client.registry-fetch-interval-seconds=30

Startup class

package len.hgy;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@SpringBootApplication(scanBasePackages = {"len.hgy"})
// Register with eureka
@EnableEurekaClient
@MapperScan("len.hgy.dao")
public class MicroOrderApplication {

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

}

3. Reeuka client: reeuka client

pom is basically the same as the property configuration file. The consumer is responsible for calling the service provider, so the client needs to be called

rely on

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

bootstrap.properties

#Register with eureka
eureka.client.registerWithEureka=true
#Whether to pull the registration information from eureka
eureka.client.fetchRegistry=true
eureka.client.serviceUrl.defaultZone=http://localhost:8763/eureka/

Startup class

package len.hgy;

import len.hgy.service.feign.StudentService;
import len.hgy.service.feign.TeacherServiceFeign;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication(scanBasePackages = {"len.hgy"})
//Register with eureka
@EnableEurekaClient
//Turn on the function of circuit breaker
//@EnableCircuitBreaker
//Enable feign support. clients specifies which class to enable feign
//@EnableFeignClients(clients = {StudentService.class,TeacherServiceFeign.class})
public class MicroWebApplication {

    @Bean
    // Load balancing annotation
    @LoadBalanced
    RestTemplate restTemplate() {
        return new RestTemplate(); // Use this bean instance to load balance
    }

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

When a service is called, it is called according to the service name of the service provider

public static String SERVIER_NAME = "micro-order";

@Override
public List<ConsultContent> queryContents() {
    s.incrementAndGet();
    List<ConsultContent> results = restTemplate.getForObject("http://"                                                             + SERVIER_NAME + "/user/queryContent", List.class);
    return results;
}

The name of the service provider and the interface name of the service provider can complete the call. When the service provider and service consumer start, they will register the service in the service registry. The eureka server can also view the service registration through the interface:

http://localhost:8763/

Call web interface test

// controller
package len.hgy.controller;

import len.hgy.bean.ConsultContent;
import len.hgy.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

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

    @Autowired
    private UserService userService;

    @RequestMapping("/queryUser")
    public List<ConsultContent> queryUser() {
        return userService.queryContents();
    }

    @RequestMapping("/queryMonitor")
    public String queryMonitor() {
        return userService.queryMonitor();
    }
}

http://localhost:8083/user/queryUser

3. Eureka user authentication

When connecting to eureka, you need to bring the user name and password of the connection

Eureka server transformation

Add security initiator

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId> // spring-boot
</dependency>

Turn off csrf authentication

package hgy.security;

import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@EnableWebSecurity
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //Turn off csrf
        http.csrf().disable();
        //Enable authentication: login in URL format must be httpBasic
        http.authorizeRequests().anyRequest().authenticated().and().httpBasic();
    }
}

application.properties configuration

# Turn on basic verification and set login user name and password
security.basic.enabled=true
spring.security.user.name=admin
spring.security.user.password=admin

Eureka client transformation

When connecting with eureka, you should bring your user name and password

#eureka.client.serviceUrl.defaultZone=http://localhost:9876/eureka/
eureka.client.serviceUrl.defaultZone=http://admin:admin@localhost:9876/eureka/

At this time, the login page needs to enter the user password

http://localhost:9876/

4. Service renewal

After the client starts to register its own service list with eureka, it needs to send a heartbeat to the eureka server every other period of time to prove that it is still alive. When eureka receives the heartbeat request, it will know that the client is still alive and maintain the service list information of the client. Once the client fails to send heartbeat to eureka server on time due to some reasons, eureka may think that your client has hung up, and it may delete the service from the service list.

Configuration of renewal and maintenance

Client configuration

# Client configuration
#Service renewal, heartbeat interval 
eureka.instance.lease-renewal-interval-in-seconds=30
#If no new heartbeat is received within 90 seconds from the previous heartbeat time, the service will be rejected 
eureka.instance.lease-expiration-duration-in-seconds=90
#It indicates how long the eureka client takes to pull the service registration information. The default is 30 seconds 
eureka.client.registry-fetch-interval-seconds=30

Server configuration

# Server configuration
#In the self-protection mode, when there are network partitions and eureka loses too many clients in a short time, it will enter the self-protection mode, that is, if a service does not send heartbeat for a long time, eureka will not delete it. The default is true
eureka.server.enable-self-preservation=true
#Eureka Server will count whether the heartbeat failure rate is lower than 85% within 15 minutes during operation. If it is lower than 85%, Eureka Server will protect these instances
eureka.server.renewal-percent-threshold=0.85
#The time interval for eureka server to clean up invalid nodes. The default is 60000 milliseconds, that is, 60 seconds 
eureka.server.eviction-interval-timer-in-ms=60000

5. Eureka health test

Eureka's default health detection is to check whether the service connection is UP or DOWN, and then the client will only call the service in UP status. However, in some cases, although the service connection is good, some interfaces of the service may not be normal, and the interface call may fail due to the need to connect to Redis, mongodb or DB, So theoretically, although the service can be called normally, it is not a healthy service. Therefore, it is necessary for us to do custom health detection for this situation.

application.properties configuration

Turn on health detection

#Health testing
eureka.client.healthcheck.enabled=true

Custom health detection code

@Configuration
public class MicroWebHealthIndicator implements HealthIndicator {

    @Override
    public Health health() {
        //This status is whether the database connection is OK
        if(UserController.canVisitDb) { // Business ID, which is more specific to the connection of db,redis, mysql, etc
            return new Health.Builder(Status.UP).build();
        } else {
            return new Health.Builder(Status.DOWN).build();
        }
    }
}

jar

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

6. Service offline

For example, in some cases, the service host goes down unexpectedly, which means that the service cannot give eureka heartbeat information. However, eureka relies on maintaining the service for 90s when it does not receive heartbeat. Within this 90s, a client may call the service, which may lead to call failure. Therefore, we must have a mechanism that can manually remove the down services from the eureka service list immediately to avoid being called by the service caller.

Call the interface of service offline: this interface is the interface of calling eureka server

http://localhost:9876/eureka/apps/micro-order/localhost:micro-order:8765

delete request

be careful:

If the service is not shut down during the test, there will always be a heartbeat, resulting in manual offline failure

In addition, manually stopping the idea service will directly remove the service and will not keep it alive for 90s, because it is a normal shutdown and the client automatically goes offline

You can simulate abnormal downtime by yourself through the following commands

windows

netstat -nao | findstr 8765
taskkill /f /pid  19016 # At this time, there are still services in eureka's service list, and eureka will delete them when the maintenance time passes

linux

ps -aux | grep 8765
kill -9 19016

Pause service interface

PUT http://localhost:9876/eureka/apps/micro-order/localhost:micro-order:8765?value=OUT_OF_SERVICE

Online service

PUT http://localhost:9876/eureka/apps/micro-order/localhost:micro-order:8765?value=UP

7. Eureka high availability

The architecture of Eureka hot backup is as follows:

There are multiple eureka services in the whole microservice. Each eureka service is copied to each other, and the services registered by the client will be copied to other nodes in the eureka cluster. In fact, it simply means that each node of eureka copies each other.

The specific configuration is as follows:

The eureka server with port 9878 registers itself with the eureka server with port 9877

# Multiple are separated by commas
server.port=9877
eureka.client.serviceUrl.defaultZone=http://localhost:9878/eureka/

The eureka server with port 9877 registers itself with the eureka server with port 9878

# Multiple are separated by commas
server.port=9878
eureka.client.serviceUrl.defaultZone=http://localhost:9877/eureka/

configuration file

application-9877.properties

application-9878.properties

When starting, start according to the specified configuration file

java -jar netflix-eureka-server-1.0-SNAPSHOT.jar --spring.profiles.active=9877 --server.port=9877

java -jar netflix-eureka-server-1.0-SNAPSHOT.jar --spring.profiles.active=9878 --server.port=9878

This is the configuration

eureka.client.serviceUrl.defaultZone=http://admin:admin@localhost:9877/eureka/

Topics: Java Microservices