Learning Objectives

  • Understand the evolution of the system architecture
  • Understand the difference between RPC and Http
  • Know what SpringCloud is
  • Independently set up the Eureka registry
  • Configure Robbin load balancing independently

1. Evolution of system architecture

With the development of the Internet, the scale of web applications is expanding. The surge in demand has brought technological pressure. The system architecture is constantly evolving, upgrading and iterating. From single application, to vertical split, to distributed services, to SOA, and now the hot microservices architecture, and the surge of Service Mesh led by Google. Should we sail away on microservices, or just sit tight?

In fact, life is not only the present casual, there are poems and distant. So today we’re going to go back and look at the evolution of systems architecture; Grasp the present, learning the most popular technical architecture; Looking to the future, I will strive to become an excellent Java engineer.

1.1. Centralized architecture

When site traffic is low, only one application is needed to deploy all functions together to reduce deployment nodes and costs. At this point, the data access framework (ORM) for simplifying the workload of adding, deleting, modifying and checking is the key to affecting project development.

Existing problems:

  • Code coupling, development and maintenance is difficult
  • You can’t optimize for different modules
  • Unable to scale horizontally
  • Low single point fault tolerance and poor concurrency

1.2. Vertical split

When the traffic volume gradually increased and a single application could not meet the demand, in order to cope with higher concurrency and business requirements, we split the system according to business functions:

Advantages:

  • System splitting implements traffic sharing and solves concurrency problems
  • It can be optimized for different modules
  • Convenient horizontal expansion, load balancing, improved fault tolerance rate

Disadvantages:

  • The system is independent from each other, and there will be a lot of repeated development work, which affects the development efficiency

1.3. Distributed services

With the increasing number of vertical applications, the interaction between applications is inevitable. Core businesses are extracted as independent services, gradually forming a stable service center, so that front-end applications can respond to changing market demands more quickly. At this point, distributed invocation for business reuse and integration is key.

Advantages:

  • The basic services are extracted, and the systems call each other, which improves the code reuse and development efficiency

Disadvantages:

  • The coupling degree between systems becomes high, and the call relationship is complicated and difficult to maintain

1.4. Mobile Computing Architecture (SOA)

SOA: Service-oriented architecture

As the number of services increases, problems such as capacity evaluation and waste of small service resources gradually emerge. In this case, a scheduling center needs to be added to manage cluster capacity in real time based on access pressure to improve cluster utilization. At this point, a resource scheduling and Governance center (SOA) for improving machine utilization is key

What went wrong before?

  • There are more and more services, and you need to manage the address of each service
  • The call relationships are too complex to untangle dependencies
  • Too many services make service status difficult to manage and cannot be dynamically managed according to service status

What does service governance do?

  • Service registry, realize automatic service registration and discovery, without manual record service address
  • Automatic service subscription, automatic service list push, service invocation transparency, no need to care about dependencies
  • Dynamically monitor service status monitoring reports to manually control service status

Disadvantages:

  • There will be dependencies between services, once a link goes wrong, it will have a big impact
  • Service relationships are complex, and operation and maintenance, test and deployment are difficult, which does not conform to DevOps

1.5. The service

The English translation of SOA is service orientation. Microservices, which seem to be services, are systems that are unbundled. So it’s very confusing, but there are some differences:

Features of microservices:

  • Single responsibility: Each service in microservices corresponds to a unique business capability to achieve a single responsibility
  • Micro: Microservices have a very small granularity of service separation. For example, a user management can be used as a service. Each service is small, but “all the five organs”.
  • Service Orientation: Service orientation means that each service exposes a REST-style service interface API. It does not care about the technical implementation of the service, which is platform – and language-independent, and is not limited to what technology is used, as long as the Rest interface is provided.
  • Autonomy: Autonomy means that services are independent of each other
    • Team independence: Each service should be an independent development team, not too many people.
    • Technology independence: Because it is service-oriented and provides Rest interfaces, what technology can be used without interference from others
    • Separation of the front and back ends: The front and back ends are developed separately to provide unified Rest interfaces, eliminating the need to develop different interfaces for the PC and mobile segment
    • Database separation: Each service uses its own data source
    • The deployment is independent. Although there are calls between services, the restart of services should not affect other services. Facilitate continuous integration and continuous delivery. Each service is an independent component, reusable, replaceable, less coupled, and easy to maintain

Microservice structure diagram:

2. Service invocation mode

2.1. The RPC and HTTP

Both microservices and SOA face remote invocations between services. So what are the remote invocations between services?

There are two common remote invocation methods:

  • RPC: Remote Produce Call Remote procedure Call, similar to RMI. Custom data format, based on native TCP communication, fast speed, high efficiency. The early WebServices, and now the popular Dubbo, are typical examples of RPC

  • Http: Http is a network transport protocol based on TCP that defines the format of data transmission. At present, the communication between the client browser and the server is basically using Http protocol, and it can also be used for remote service invocation. The disadvantage is that the message encapsulation is bloated, and the advantage is that there is no technical limitation on the provider and caller of the service, which is free and flexible and more in line with the concept of microservices.

    The popular Rest style can be implemented through the HTTP protocol.

If your company adopts the Java technology stack entirely, then using Dubbo as a microservices architecture is a good choice.

Conversely, if your company’s technology stack is diverse and you prefer the Spring family, SpringCloud is the place to build microservices. In our project, we will choose the SpringCloud suite, so we will use Http for inter-service invocation.

2.2.Http client tools

Now that the microservice has chosen Http, we need to consider implementing our own handling of requests and responses. However, the open source world already has a number of HTTP client tools that can help us do these things, such as:

  • HttpClient
  • OKHttp
  • URLConnection

Now, these different clients, they have different apis

2.3. The Spring RestTemplate

Spring provides a RestTemplate template utility class that encapsulates http-based clients and facilitates object and JSON serialization and deserialization. The RestTemplate does not define the Http client type, but rather abstracts it, supporting all three of the commonly used types:

  • HttpClient
  • OkHttp
  • JDK native URLConnection (default)

First register a RestTemplate object in the project, which can be registered at the start class location:

@SpringBootApplication
public class HttpDemoApplication {

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

	@Bean
	public RestTemplate restTemplate(a) {
   
		return newRestTemplate(); }}Copy the code

Inject directly @autowired in the test class:

@RunWith(SpringRunner.class)
@SpringBootTest(classes = HttpDemoApplication.class)
public class HttpDemoApplicationTests {

	@Autowired
	private RestTemplate restTemplate;

	@Test
	public void httpGet(a) {
        // Invoke the REST interface in the SpringBoot case
		User user = this.restTemplate.getForObject("http://localhost/user/1", User.class); System.out.println(user); }}Copy the code
  • With the getForObject() method of RestTemplate, passing the URL and the bytecode of the entity class, RestTemplate automatically initiates the request, receives the response, and deserializes the response for us.

Now that you’ve learned about the Http client tools, you’re ready to learn about microservices.

3. SpringCloud person

Microservices is an architectural approach that will ultimately require a technical architecture to implement.

There are many ways to implement microservices, but Spring Cloud is the most popular. Why is that?

  • Background: As a member of the Spring family, I have a strong background with the whole Spring family backing me.
  • Strong technology: Spring, as a predecessor in the Java field, can be said to have profound skills. There is a strong technical team support, ordinary people really can not compare
  • Good audience: Most programmers have grown up with the Spring framework, so how many companies don’t use Spring today? SpringCloud integrates seamlessly with Spring’s various frameworks, and everything is a familiar recipe and taste to everyone.
  • Easy to use: I believe that everyone has realized the convenience SpringBoot brings to our development, and SpringCloud fully supports the development of SpringBoot, with very little configuration can complete the construction of micro-service framework

3.1. Introduction

SpringCloud is one of Spring’s project, the website address: http://projects.spring.io/spring-cloud/

What Spring does best is integration, taking the best frameworks in the world and integrating them into your own projects.

The same is true of SpringCloud, which integrates some of the most popular technologies such as configuration management, service discovery, intelligent routing, load balancing, fuses, control bus, cluster state, and more. Its main components include:

  • Eureka: Service governance component, including service registry, implementation of service registration and discovery mechanism. (Service governance, service registration/discovery)
  • Zuul: gateway component that provides intelligent routing and access filtering functions
  • Ribbon: Service Invocation Components for Client Load Balancing (Client Load)
  • Feign: Service Invocation, declarative service invocation component for Ribbon and Hystrix (Declarative service invocation)
  • Hystrix: Fault tolerant management component that implements circuit breaker mode to help with delays in service dependencies and provide strong fault tolerance for failures. (Fuse, circuit breaker, fault tolerance)

Architecture diagram:

These are just some of them.

Version 3.2.

Because Spring Cloud is different from other independent projects, it has large projects with many subprojects. So its version is the version name + version number (such as angel.sr6).

Version name: London underground

Version number: SR (Service Releases) is fixed, which roughly means stable version. It will be followed by an increasing number.

So Edgware.SR3 is the third Release of Edgware.

We’re going to use Finchley’s version in the project.

The components are also available in their respective versions, as shown in the following table:

Component Edgware.SR3 Finchley.RC1 Finchley.BUILD-SNAPSHOT
spring-cloud-aws 1.2.2. RELEASE . 2.0.0 RC1 2.0.0. BUILD – the SNAPSHOT
spring-cloud-bus 1.3.2. RELEASE . 2.0.0 RC1 2.0.0. BUILD – the SNAPSHOT
spring-cloud-cli 1.4.1. RELEASE . 2.0.0 RC1 2.0.0. BUILD – the SNAPSHOT
spring-cloud-commons 1.3.3. RELEASE . 2.0.0 RC1 2.0.0. BUILD – the SNAPSHOT
spring-cloud-contract 1. RELEASE . 2.0.0 RC1 2.0.0. BUILD – the SNAPSHOT
spring-cloud-config 1.4.3. RELEASE . 2.0.0 RC1 2.0.0. BUILD – the SNAPSHOT
spring-cloud-netflix 1.4.4. RELEASE . 2.0.0 RC1 2.0.0. BUILD – the SNAPSHOT
spring-cloud-security 1.2.2. RELEASE . 2.0.0 RC1 2.0.0. BUILD – the SNAPSHOT
spring-cloud-cloudfoundry 1.1.1. RELEASE . 2.0.0 RC1 2.0.0. BUILD – the SNAPSHOT
spring-cloud-consul 1.3.3. RELEASE . 2.0.0 RC1 2.0.0. BUILD – the SNAPSHOT
spring-cloud-sleuth 1.3.3. RELEASE . 2.0.0 RC1 2.0.0. BUILD – the SNAPSHOT
spring-cloud-stream Ditmars.SR3 Elmhurst.RELEASE Elmhurst.BUILD-SNAPSHOT
spring-cloud-zookeeper 1.2.1. RELEASE . 2.0.0 RC1 2.0.0. BUILD – the SNAPSHOT
spring-boot 1.5.10. RELEASE 2.0.1. RELEASE 2.0.0. BUILD – the SNAPSHOT
spring-cloud-task 1.2.2. RELEASE . 2.0.0 RC1 . 2.0.0 RELEASE
spring-cloud-vault 1.1.0. RELEASE . 2.0.0 RC1 2.0.0. BUILD – the SNAPSHOT
spring-cloud-gateway The 1.0.1 RELEASE . 2.0.0 RC1 2.0.0. BUILD – the SNAPSHOT
spring-cloud-openfeign . 2.0.0 RC1 2.0.0. BUILD – the SNAPSHOT

Next, let’s take a look at the key components in SpringCloud.

4. Micro-service scenario simulation

First, we need to simulate a service invocation scenario and build two projects: ITcast-service-provider (service provider) and ITcast-service-consumer (service caller). This will help you learn about microservices architecture later

Service provider: Use Mybatis to operate the database and realize the increase, deletion, change and check of data; In addition, rest interface services are provided externally.

Service consumer: Use the restTemplate to remotely invoke the REST interface service of the service provider to obtain data.

4.1. Service providers

Create a new project: ITcast-service-provider, which provides a service to query users by ID.

4.1.1.Spring scaffolding creation project

With the help of Spring’s quick build tools:

Next –> Fill in the project information:

Next –> Add a Web dependency:

Add mybatis dependency:

Next –> Fill in the project location:

Generated by the project structure, already contains the bootstrap class (itcastServiceProviderApplication) :

Dependencies have all been automatically introduced:

<?xml version="1.0" encoding="UTF-8"? >
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>cn.itcast.service</groupId>
    <artifactId>itcast-service-provider</artifactId>
    <version>0.0.1 - the SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>itcast-service-provider</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.6. RELEASE</version>
        <relativePath/> <! -- lookup parent from repository -->
    </parent>

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

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.2</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <! Spring does not include this dependency -->
        <dependency>
            <groupId>tk.mybatis</groupId>
            <artifactId>mapper-spring-boot-starter</artifactId>
            <version>2.0.4</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>
Copy the code

Of course, since we’re using a generic Mapper, we need to manually add a dependency:

<dependency>
    <groupId>tk.mybatis</groupId>
    <artifactId>mapper-spring-boot-starter</artifactId>
    <version>2.0.4</version>
</dependency>
Copy the code

Very fast!

4.1.2. Write code

4.1.2.1. Configuration

Properties file, here we use yamL syntax instead of properties:

server:
  port: 8081
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mybatis # the database address you used when learning Mybatis
    username: root
    password: root
mybatis:
  type-aliases-package: cn.itcast.service.pojo
Copy the code

4.1.2.2) entity class

@Table(name = "tb_user")
public class User implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    / / user name
    private String userName;

    / / password
    private String password;

    / / name
    private String name;

    / / age
    private Integer age;

    // Gender, 1 male, 2 female
    private Integer sex;

    // Date of birth
    private Date birthday;

    // Create time
    private Date created;

    // Update time
    private Date updated;

    public Long getId(a) {
        return id;
    }

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

    public String getUserName(a) {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPassword(a) {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getName(a) {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge(a) {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Integer getSex(a) {
        return sex;
    }

    public void setSex(Integer sex) {
        this.sex = sex;
    }

    public Date getBirthday(a) {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public Date getCreated(a) {
        return created;
    }

    public void setCreated(Date created) {
        this.created = created;
    }

    public Date getUpdated(a) {
        return updated;
    }

    public void setUpdated(Date updated) {
        this.updated = updated; }}Copy the code

4.1.2.3. UserMapper

@Mapper
public interface UserMapper extends tk.mybatis.mapper.common.Mapper<User>{}Copy the code

4.1.2.4. UserService

@Service
public class UserService {

    @Autowired
    private UserMapper userMapper;

    public User queryById(Long id) {
        return this.userMapper.selectByPrimaryKey(id); }}Copy the code

4.1.2.5. UserController

Add an external query interface:

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

    @Autowired
    private UserService userService;

    @GetMapping("{id}")
    public User queryById(@PathVariable("id") Long id) {
        return this.userService.queryById(id); }}Copy the code

4.1.3. Start and test

Start the project, access interface: http://localhost:8081/user/1

4.2. Service caller

Set up itcast-service-consumer service consumer project.

4.2.1. Create a project

Similarly, we will not repeat it here, but note that we call the decoupling of itcast-service-provider to get the data, so there is no need for myBatis dependency.

Pom:

<?xml version="1.0" encoding="UTF-8"? >
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>cn.itcast.service</groupId>
    <artifactId>itcast-service-consumer</artifactId>
    <version>0.0.1 - the SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>itcast-service-consumer</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.4. RELEASE</version>
        <relativePath/> <! -- lookup parent from repository -->
    </parent>

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

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>


</project>

Copy the code

4.2.2. Write code

First register the RestTemplate in the boot class:

@SpringBootApplication
public class ItcastServiceConsumerApplication {

    @Bean
    public RestTemplate restTemplate(a) {
        return new RestTemplate();
    }

    public static void main(String[] args) { SpringApplication.run(ItcastServiceConsumerApplication.class, args); }}Copy the code

Write configuration (application.yml) :

server:
  port: 80
Copy the code

Write a UserController:

@Controller
@RequestMapping("consumer/user")
public class UserController {

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping
    @ResponseBody
    public User queryUserById(@RequestParam("id") Long id){
        User user = this.restTemplate.getForObject("http://localhost:8081/user/" + id, User.class);
        returnuser; }}Copy the code

Pojo object (User) :

public class User implements Serializable {

    private static final long serialVersionUID = 1L;

    private Long id;

    / / user name
    private String userName;

    / / password
    private String password;

    / / name
    private String name;

    / / age
    private Integer age;

    // Gender, 1 male, 2 female
    private Integer sex;

    // Date of birth
    private Date birthday;

    // Create time
    private Date created;

    // Update time
    private Date updated;

    public Long getId(a) {
        return id;
    }

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

    public String getUserName(a) {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPassword(a) {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getName(a) {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge(a) {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Integer getSex(a) {
        return sex;
    }

    public void setSex(Integer sex) {
        this.sex = sex;
    }

    public Date getBirthday(a) {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public Date getCreated(a) {
        return created;
    }

    public void setCreated(Date created) {
        this.created = created;
    }

    public Date getUpdated(a) {
        return updated;
    }

    public void setUpdated(Date updated) {
        this.updated = updated; }}Copy the code

4.2.3. Start the test

Because we don’t have to configure port, then the default is 8080, we visit: http://localhost/consumer/user? id=1

A simple remote service invocation case is implemented.

4.3. Any questions?

Just to recap, what we just wrote:

  • Itcast-service-provider: a micro-service that queries users by ID.
  • Itcast-service-consumer: A service caller that remotely invokes the itcast-service-provider from the RestTemplate.

What’s the problem?

  • In Consumer, we hard-coded url addresses into the code to make them difficult to maintain
  • The consumer needs to remember the provider’s address, and if it changes, it may not be notified and the address will become invalid
  • The Consumer does not know the status of the provider, nor does the service outage
  • The Provider has only one service and does not provide high availability
  • Even when providers cluster, consumers need to implement their own load balancing

In fact, the problems mentioned above are summarized as the inevitable problems of distributed services:

  • Service management
    • How to automatically register and discover
    • How to implement state policing
    • How to implement dynamic routing
  • How do services achieve load balancing
  • How do services solve DISASTER recovery problems
  • How can services be uniformly configured

All of these questions will be answered in SpringCloud.

5.Eureka Registry

5.1. Eureka

First of all, let’s solve the first problem, service management.

Problem analysis

In the preceding example, the ITcast-service-provider provides services externally and needs to expose its own address. The consumer (caller) needs to record the address of the service provider. Address changes in the future, but also need to update in time. This didn’t seem like much when there were fewer services, but in today’s increasingly complex Internet environment, a project is bound to split into dozens or even dozens of micro services. If you manage the address manually at this point, it will not only be difficult to develop, but also difficult to test and launch in the future, which is the opposite of DevOps.

online car-hailing

It’s like before the advent of ride-hailing, when people went out to get a taxi. Some private cars want to be hired but are not qualified, so they are called black cars. And many people want to offer a car, but there are too few taxis, not convenient. There are many private cars but dare not stop them, and there are many cars on the street, who knows which one is willing to take people. One wants, one is willing to give, is lack of lead, lack of management ah.

At this time, a ride-hailing platform like Didi appeared. All private cars that want to pick up passengers should register with Didi and record their vehicle type (service type) and identity information (contact information). Such private cars can be found on Didi at a glance.

If you want to hail a car, you just need to open the APP, enter your destination, select the car type (service type), and Didi will automatically arrange a car that meets your needs to serve you. Perfect!

What does Eureka do?

Eureka, like Didi, manages and records information about service providers. Instead of finding the service themselves, service invokers tell Eureka what they need, and Eureka tells you what services fit your needs.

At the same time, service providers and Eureka monitor each other through a “heartbeat” mechanism. When a service provider has problems, Eureka will remove it from the service list naturally.

This enables automatic registration, discovery, and status monitoring of services.

5.2. The principle diagram

Basic architecture:

  • Eureka: A service registry (which can be a cluster) that exposes its address to the outside world
  • Provider: After startup, register your information with Eureka (address, what services are provided)
  • Consumer: Subscribe to Eureka, and Eureka sends the consumer a list of all provider addresses for the corresponding service, which is updated periodically
  • Heartbeat (Renewal) : The provider periodically refreshes its status to Eureka via HTTP

5.3. Introductory cases

Build EurekaServer 5.3.1)

Next we create a project to launch a EurekaServer:

Still use spring’s quick build tools:

Select dependencies: EurekaServer- service registry dependencies, Eureka Discovery- service provider and service consumer. For Eureka, both the service provider and the service consumer are clients

Complete Pom file:

<?xml version="1.0" encoding="UTF-8"? >
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>cn.itcast.eureka</groupId>
    <artifactId>itcast-eureka</artifactId>
    <version>0.0.1 - the SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>itcast-eureka</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.6. RELEASE</version>
        <relativePath/> <! -- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <spring-cloud.version>Finchley.RC2</spring-cloud.version>
    </properties>

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

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>
Copy the code

Write the application.yml configuration:

server:
  port: 10086 # port
spring:
  application:
    name: eureka-server The app name will be displayed in Eureka
eureka:
  client:
    service-url: # EurekaServer's address is now its own address. If it is a cluster, it needs to add the address of other servers.
      defaultZone: {http://127.0.0.1:$server port} / eureka
Copy the code

Modify the bootstrap class by adding @enableeurekaserver annotation to the class:

@SpringBootApplication
@EnableEurekaServer // Declare the current SpringBoot application to be a Eureka service center
public class ItcastEurekaApplication {

    public static void main(String[] args) { SpringApplication.run(ItcastEurekaApplication.class, args); }}Copy the code

Start the service and go to: http://127.0.0.1:10086

5.3.2. Register with Eureka

To register a service, you add Eureka’s client dependencies to the service. The client code automatically registers the service with EurekaServer.

Modify the itcast-service-Provider project

  1. In pom.xml, add springCloud’s dependencies.
  2. In application.yml, add springCloud dependencies.
  3. Add annotations to the bootstrap class to inject the service into the Eureka registry.

Specific operation

5.3.2.1. Pom. XML

See itcast-eureka to add SpringCloud dependencies:

<! -- SpringCloud dependencies -->
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Finchley.SR2</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
Copy the code

Then there is the Eureka client:

<! -- Eureka client -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
Copy the code

Complete pom. XML:

<?xml version="1.0" encoding="UTF-8"? >
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>cn.itcast.service</groupId>
    <artifactId>itcast-service-provider</artifactId>
    <version>0.0.1 - the SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>itcast-service-provider</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.6. RELEASE</version>
        <relativePath/> <! -- lookup parent from repository -->
    </parent>

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

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.2</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>tk.mybatis</groupId>
            <artifactId>mapper-spring-boot-starter</artifactId>
            <version>2.0.4</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Finchley.SR1</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

</project>
Copy the code

5.3.2.2. Application. Yml

server:
  port: 8081
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/heima
    username: root
    password: root
    driverClassName: com.mysql.jdbc.Driver
  application:
    name: service-provider The name of the service registered with Eureka
mybatis:
  type-aliases-package: cn.itcast.service.pojo
eureka:
  client:
    service-url: # EurekaServer address
      defaultZone: http://127.0.0.1:10086/eureka
Copy the code

Note:

  • Here we add the spring.application.name attribute to specify the application name, which will be used as the application ID in the future.

5.3.2.3. Guide the class

Enable Eureka client functionality on the boot class

Add @enableDiscoveryClient to enable the Eureka client function

@SpringBootApplication
@EnableDiscoveryClient
public class ItcastServiceProviderApplication {

    public static void main(String[] args) { SpringApplication.run(ItcastServiceApplication.class, args); }}Copy the code

Restart the project and visit the Eureka monitoring page

We found that the service-provider service has been registered successfully

5.3.3. Obtain services from Eureka

Next we modify itcast-service-consumer to try to get the service from EurekaServer.

In a similar way to consumers, you simply add EurekaClient dependencies to your project and get information by service name!

  1. pom.xml
<?xml version="1.0" encoding="UTF-8"? >
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>cn.itcast.service</groupId>
    <artifactId>itcast-service-consumer</artifactId>
    <version>0.0.1 - the SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>itcast-service-consumer</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.6. RELEASE</version>
        <relativePath/> <! -- lookup parent from repository -->
    </parent>

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

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <! -- Eureka client -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

    <! -- SpringCloud dependencies -->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Finchley.SR2</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

Copy the code
  1. Modify the configuration
server:
  port: 80
spring:
  application:
    name: service-consumer
eureka:
  client:
    service-url:
      defaultZone: http://localhost:10086/eureka
Copy the code
  1. Start the Eureka client in the startup class
@SpringBootApplication
@EnableDiscoveryClient // Start Eureka client
public class ItcastServiceConsumerApplication {

    @Bean
    public RestTemplate restTemplate(a){
        return new RestTemplate();
    }

    public static void main(String[] args) { SpringApplication.run(ItcastServiceConsumerApplication.class, args); }}Copy the code
  1. Change the UserController code and use the DiscoveryClient method to obtain the service instance based on the service name:
@Controller
@RequestMapping("consumer/user")
public class UserController {

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private DiscoveryClient discoveryClient; // The eureka client can obtain information about the services in eureka

    @GetMapping
    @ResponseBody
    public User queryUserById(@RequestParam("id") Long id){
        // Get the service instance based on the service name. It's probably a cluster, so it's a collection of service instances
        List<ServiceInstance> instances = discoveryClient.getInstances("service-provider");
        // Because there is only one service-provider. So get the first instance
        ServiceInstance instance = instances.get(0);
        // Obtain the IP address and port information and combine them into service addresses
        String baseUrl = "http://" + instance.getHost() + ":" + instance.getPort() + "/user/" + id;
        User user = this.restTemplate.getForObject(baseUrl, User.class);
        returnuser; }}Copy the code

5) Debug trace running:

Generated URL:

Results:

5.4. Eureka explanation

Next, we will explain the principle and configuration of Eureka in detail.

5.4.1. Infrastructure

Three central roles in Eureka’s architecture:

  • Service Registry

    The Eureka server application, which provides service registration and discovery, is the itcast-Eureka we just created.

  • Service provider

    The application that provides the service can be a SpringBoot application or any other technology implementation, as long as the external service is restful. In this example, we implement itcast-service-provider.

  • Service consumer

    The consuming application gets the list of services from the registry to know information about each server and where to invoke the server. In this case, we implement itcast-service-consumer.

5.4.2. Highly available Eureka Server

EurekaServer is a registry of services. In our case, we have only one EurekaServer. In fact, EurekaServer can also be a cluster, forming a highly available Eureka center.

Service synchronization

Multiple Eureka Servers also register with each other as services. When a service provider registers with a node in the Eureka Server cluster, the node synchronizes the service information to each node in the cluster to achieve data synchronization. Therefore, no matter the client accesses any node in the Eureka Server cluster, the complete service list information can be obtained.

Build a highly available EurekaServer

Let’s say we want to run two clusters of EurekaServer, with ports 10086 and 10087. You only need to start itcast-Eureka twice.

1) Start the first eurekaServer, we modify the original eurekaServer configuration:

server:
  port: 10086 # port
spring:
  application:
    name: eureka-server The app name will be displayed in Eureka
eureka:
  client:
    service-url: # configure the address of other Eureka services instead of your own, such as 10087
      defaultZone: http://127.0.0.1:10087/eureka
Copy the code

The so-called high availability registry actually registers EurekaServer itself as a service, so that multiple EurekaServer can discover each other and form clusters. So we made the following changes:

  • Changed the value of service-URL to the address of another EurekaServer, not itself

Startup error, very normal. Because 10087 service is not started:

2) Start the second eurekaServer and modify itcast-Eureka configuration again:

server:
  port: 10087 # port
spring:
  application:
    name: eureka-server The app name will be displayed in Eureka
eureka:
  client:
    service-url: # configure the address of other Eureka services instead of your own, such as 10087
      defaultZone: http://127.0.0.1:10086/eureka
Copy the code

Note: The same application in IDEA cannot be started twice. We need to reconfigure an initiator:

Then start.

3) Access cluster, test:

4) The client registers the service with the cluster

Since there is more than one EurekaServer, the service-URL parameter needs to change when registering the service:

eureka:
  client:
    service-url: # EurekaServer address, multiple addresses separated by ','
      defaultZone: http://127.0.0.1:10086/eureka,http://127.0.0.1:10087/eureka
Copy the code

10086:

10087:

5.4.3. Service providers

The service provider should register the service with EurekaServer and complete the service renewal.

The service registry

The service provider checks if the eureka.client.register-with-eureka=true parameter in the configuration property is correct at startup, which is true by default. If the value is indeed true, a Rest request is made to EurekaServer with its own metadata information, which EurekaServer stores in a two-tier Map structure.

  • The Key of the layer 1 Map is the service ID, which is usually configuredspring.application.nameattribute
  • The key of the layer 2 Map is the instance ID of the service. Host + serviceId + port, for example:locahost:service-provider:8081
  • The value is an instance object of a service, that is, a service can start multiple instances at the same time to form a cluster.

The service contract

After registering the service, the service provider maintains a heartbeat (a periodic Rest request to EurekaServer) telling EurekaServer, “I’m still alive.” This we call service renewal;

There are two important parameters that modify the behavior of service renewal:

eureka:
  instance:
    lease-expiration-duration-in-seconds: 90
    lease-renewal-interval-in-seconds: 30
Copy the code
  • Lease -renewal- interval-seconds: renew interval. The default value is 30 seconds
  • Lease – expiration-durations-in-seconds: specifies the expiration time of the service. The default value is 90 seconds

That is, by default the service sends a heartbeat to the registry every 30 seconds to prove that it is still alive. If the heartbeat is not sent within 90 seconds, EurekaServer considers the service to be down and removes it from the service list. Do not change these two values in the production environment. The default values are enough.

But in development, this value is a little too long, and often when we shut down a service, Eureka still thinks the service is alive. So we can adjust it in the development phase.

eureka:
  instance:
    lease-expiration-duration-in-seconds: 10 # 10 seconds will expire
    lease-renewal-interval-in-seconds: 5 A heartbeat every 5 seconds
Copy the code

5.4.4. Service to consumers

Getting a list of services

When the service consumer starts, the value of the eureka.client.fetch-registry=true parameter is detected, and if true, a read-only backup of the list of Eureka Server services is pulled and cached locally. The data is retrieved and updated every 30 seconds. We can modify it with the following parameters:

eureka:
  client:
    registry-fetch-interval-seconds: 5
Copy the code

In production, we do not need to change this value.

However, in order to quickly get the latest status of the service in the development environment, we can make it smaller.

5.4.5. Failure elimination and self-protection

Service offline

When a service is normally shut down, it triggers a REST request to Eureka Server for the service to go offline, telling the service registry, “I’m going offline.” After the service center receives the request, it takes the service offline.

Failure to eliminate

In some cases, our service provider may not go offline normally, or the service may not work properly due to memory overflow or network failure. Eureka Server needs to remove such services from the service list. So it starts a scheduled task to weed out all failed services (no response for more than 90 seconds) every 60 seconds.

It can be modified in milliseconds using the Eureka. Server. Eviction – interval-timer-in-MS parameter, do not modify in production environment.

This would be a huge change for our development. You restart the service and it takes 60 seconds for Eureka to react. The development phase can be adjusted appropriately, for example: 10 seconds

To protect themselves

When we shut down a service, we see a warning on the Eureka panel:

This is what triggers Eureka’s self-protection mechanism. When a service fails to renew its heartbeat on time, Eureka calculates whether more than 85% of service instances have failed in the last 15 minutes. In a production environment, the percentage of failed heartbeat instances is likely to exceed the threshold due to network latency, but it is not appropriate to remove services from the list at this point because they may not be down. Eureka will protect the registration information of the current instance. This works well in a production environment, ensuring that most services are still available.

But this caused problems for our development, so we turned off self-protection mode during development :(itcast-eureka)

eureka:
  server:
    enable-self-preservation: false # Turn off self-protection mode (default: turn on)
    eviction-interval-timer-in-ms: 1000 # Interval between scanning failed services (default: 60*1000ms)
Copy the code

6. Load Balancing Ribbon

In this case, we started an ITcast-service-provider, and then used DiscoveryClient to get the service instance information, and then the IP and port to access it.

However, in the actual environment, many ITcast-service-provider clusters will be enabled. There will be more than one service in the list. Which one should we access?

In this case, we need to write a load balancing algorithm that selects from a list of instances.

However, Eureka has integrated a load balancing component called the Ribbon, which can be used with simple code modifications.

What is Ribbon:

Next, let’s use the Ribbon to implement load balancing.

6.1. Start two service instances

First consult itcast – eureka start two ItcastServiceProviderApplication instance, a 8081, a 8082.

Eureka Monitoring Panel:

6.2. Enable load balancing

Because the Ribbon is already integrated with Eureka, we don’t need to introduce new dependencies and change the code directly.

Modify the itcast-service-consumer bootstrap class to add @loadBalanced to the RestTemplate configuration method:

@Bean
@LoadBalanced
public RestTemplate restTemplate(a) {
    return new RestTemplate();
}
Copy the code

Change the invocation mode, instead of manually obtaining the IP and port, call directly from the service name:

@Controller
@RequestMapping("consumer/user")
public class UserController {

    @Autowired
    private RestTemplate restTemplate;

    //@Autowired
    //private DiscoveryClient discoveryClient; // Inject discoveryClient to obtain the service list

    @GetMapping
    @ResponseBody
    public User queryUserById(@RequestParam("id") Long id){
        // Get the list of service providers from client. Here we have only one
        // ServiceInstance instance = discoveryClient.getInstances("service-provider").get(0);
        String baseUrl = "http://service-provider/user/" + id;
        User user = this.restTemplate.getForObject(baseUrl, User.class);
        returnuser; }}Copy the code

Visit the page to view the results:

Perfect!

6.3. Source tracking

Why do we just enter the service name to access it? You have to get the IP and port before that.

Apparently someone helped us get the IP and port of the service instance based on the service name. It is LoadBalancerInterceptor

At the following code break point:

All the source code tracking: RestTemplate. GetForObject – > RestTemplate. Execute – > RestTemplate. The doExecute:

Click to enter AbstractClientHttpRequest. Execute – > AbstractBufferingClientHttpRequest. ExecuteInternal – > InterceptingClientHttpRequest.executeInternal –> InterceptingClientHttpRequest.execute:

Continue to enter: LoadBalancerInterceptor. Intercept method

Continue with the execute method: the service with port 8082 is found

The next time, I get 8081:

6.4. Load balancing Policy

The Ribbon’s default load balancing strategy is simple polling. Let’s test this:

RibbonLoadBalanceClient is used for load balancing. If the RibbonLoadBalanceClient is used for load balancing, the RibbonLoadBalanceClient is used for load balancing.

Now this is how load balancing gets instances.

We inject an object of this class and test it:

Test content:

@RunWith(SpringRunner.class)
@SpringBootTest(classes = ItcastServiceConsumerApplication.class)
public class LoadBalanceTest {

    @Autowired
    private RibbonLoadBalancerClient client;

    @Test
    public void testLoadBalance(a){
        for (int i = 0; i < 100; i++) {
            ServiceInstance instance = this.client.choose("service-provider");
            System.out.println(instance.getHost() + ":"+instance.getPort()); }}}Copy the code

Results:

In line with our expectations, it is indeed polling.

Can we change the load balancing policy?

Continuing to trace the source code, I found this code:

Let’s see who this rule is:

The default rule is a RoundRobinRule.

That’s what polling means.

Note that this class implements the interface IRule.

Defines the rule interface for load balancing.

It has the following implementation:

SpringBoot also provides a configuration entry for modifying load balancing rules. Add the following configuration to the itcast-service-consumer application.yml:

server:
  port: 80
spring:
  application:
    name: service-consumer
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka
service-provider:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
Copy the code

Format is: {service name}. Ribbon. NFLoadBalancerRuleClassName, value is IRule implementation class.

When tested again, it turned out to be random: