SpringCloud study notes (Part 1): Service registration and discovery: Eureka, Zookeeper, Consul+ load balancing service call: Ribbon

 1. Zero foundation

1. Introduction to the zero-based theory of microservice architecture

SpringCloud = a one-stop solution for distributed microservice architecture. It is a collection of various microservice architecture implementation technologies, commonly known as the microservice family bucket.

2. Let’s start with version 2.2.x and H

springboot version selection:

Git source code address: https://github.com/spring-projects/spring-boot/releases/

New features of SpringBoot2.0: https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0-Release

springcloud version selection:

git source code address: https://github.com/spring-projects/spring-cloud

Official website: https://spring.io/projects/spring-cloud

Check the cloud version on the official website

Dependencies between springcloud and springboot: https://spring.io/projects/spring-cloud#overview

3. Regarding the suspension/upgrade/replacement of various Cloud components

Service registration in sequence: Eureka (hanging). zookeeper. Consul. Nacos (recommended by millions).

Service call 1: Ribbon. LoadBalancer.

Service call 2: Feign (hang). OpenFeign.

Service downgrade: Hystrix (hanging). resilience4j. sentienl (recommended).

Service gateway: Zuul (hanging). Zuul2 (not released yet). gateway (recommended).

Service configuration: Config (hung). Nacos (recommended).

Service Bus: Bus (hang). Nacos (recommended).

References:

1. Spring Cloud:https://cloud.spring.io/spring-cloud-static/Hoxton.SR1/reference/htmlsingle/

2. Spring Cloud Chinese documentation: https://www.bookstack.cn/read/spring-cloud-docs/docs-index.md

3. Spring Boot:https://docs.spring.io/spring-boot/docs/2.2.2.RELEASE/reference/htmlsingle/

4. Microservice architecture coding and construction

4.1 Introducing dependencies

<!-- 统一管理jar包版本 -->
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <junit.version>4.12</junit.version>
        <log4j.version>1.2.17</log4j.version>
        <lombok.version>1.16.18</lombok.version>
        <mysql.version>5.1.47</mysql.version>
        <druid.version>1.1.16</druid.version>
        <mybatis.spring.boot.version>1.3.0</mybatis.spring.boot.version>
    </properties>
<!-- 子模块继承之后,提供作用:锁定版本+子modlue不用写groupId和version  -->
    <dependencyManagement>
        <dependencies>
            <!--spring boot 2.2.2-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.2.2.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!--spring cloud Hoxton.SR1-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Hoxton.SR1</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!--spring cloud alibaba 2.1.0.RELEASE-->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>2.1.0.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>${mysql.version}</version>
            </dependency>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid</artifactId>
                <version>${druid.version}</version>
            </dependency>
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>${mybatis.spring.boot.version}</version>
            </dependency>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>${junit.version}</version>
            </dependency>
            <dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
                <version>${log4j.version}</version>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>${lombok.version}</version>
                <optional>true</optional>
            </dependency>
        </dependencies>
    </dependencyManagement>
<build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <version>2.2.1.RELEASE</version>
        <configuration>
          <fork>true</fork>
          <addResources>true</addResources>
        </configuration>
      </plugin>
    </plugins>
  </build>

The difference between DependencyManagement and Dependencies:

<DependencyManagement> is the coordinate version number defined by the parent class. It is used in the parent project. It is similar to the specification that unifies the versions of the entire project. It is only responsible for definition and not for introducing implementation. What actually introduces dependencies is the version number defined by the subclass <dependencies>. If the subclass does not specify a version number, the default is to use the parent class. If the subclass specifies a version number, the version number is specified by the subclass (if the dependency is not declared in the subproject, it will not be inherited from the parent project. As long as the dependency is written in the subproject and no specific version is specified , the item will be inherited from the parent project, and both version and scope are read from the parent pom).

Skip unit testing in maven:

4.2 Payment module construction

1. Build a module: Right-click the project and create a new module

2.Change POM

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid-spring-boot-starter</artifactId>
        <version>1.1.10</version>
    </dependency>
    <!--mysql-connector-java-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
    <!--jdbc-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jdbc</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <scope>runtime</scope>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

3. Write YML

Create the application.yml file under the resources of cloud-provider-payment8001. Note that it should turn into green leaves:

server:
  port: 8081

spring:
  application:
    name: cloud-payment-service
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/db2019?useUnicode=true&characterEncoding=utf-8&usrSSL=false
    username: root
    password: 111111

mybatis:
  mapper-locations: classpath:mapper/*.xml #在mapper包下创建.xml配置文件
  type-aliases-package: com.atguigu.springcloud.entities #entities下创建实体类

4. Main startup

Create the PaymentMain8001 class under the src/main/java/com/atguigu/springcloud package, and add @SpringBootApplication as the main startup class:

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

5. Business type

5.1 Create table SQL

CREATE TABLE `payment`(
    `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID',
    `serial` varchar(200) DEFAULT '',
    PRIMARY KEY(`id`)
)ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8

5.2 entities entity class

Create the entities package under java/com/atguigu/springcloud and create the Payment class under it:

Main entity Payment code:

public class Payment implements Serializable {
    private Long id;
    private String serial;
    public Long getId() {return id;}
    public void setId(Long id) {this.id = id;}
    public String getSerial() {return serial;}
    public void setSerial(String serial) {this.serial = serial;}
    public Payment(Long id, String serial) {this.id = id;this.serial = serial;}
    public Payment() {}
}

Json package CommonResult

Create the CommonResult class under the entities package:

public class CommonResult<T> {
    private Integer code;
    private String message;
    private T data;
    public  CommonResult(Integer code,String message){this(code,message,null);}
    public CommonResult(Integer code, String message, T data) {this.code = code;this.message = message;this.data = data;}
    public CommonResult() {}
    public Integer getCode() {return code;}
    public void setCode(Integer code) {this.code = code;}
    public String getMessage() {return message;}
    public void setMessage(String message) {this.message = message;}
    public T getData() {return data;}
    public void setData(T data) {this.data = data;}
}

1. @DataIt is a Lombok annotation used to automatically generate common methods of Java classes, such as getter, setter, toString, equals and hashCode methods.

2. this(code, message, null) means calling another constructor of the current classCommonResult(Integer code, String message, Object data), and passing parameters code, message and null。

3. @AllArgsConstructorIt is one of the Lombok annotations, used to automatically generate a constructor containing all parameters.

5.3 knives

Create the dao package and create the PaymentDao interface under it. Remember to write the @Mapper annotation:

@Mapper
public interface PaymentDao {
    public int create(Payment payment);
    public Payment getPaymentById(@Param("id") Long id);
}

Mybatis mapping file:

Create the mapper package under resources and create the PaymentMapper.xml mapping file:

1. parameterType is used to specify the parameter type passed into the SQL statement.

2. resultMap is used to specify which Java object the query results are mapped to.

3. column is a database field, and property is a java object property.

4.keyProperty: Used to specify which attribute of the Java entity class the automatically generated primary key value is to be set to.

The resultMap mapping attribute is used to prevent errors caused by irregular naming:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.atguigu.springcloud.dao.PaymentDao">
    <insert id="create" parameterType="Payment" useGeneratedKeys="true" keyProperty="id">
        insert into payment(serial) values(#{serial});
    </insert>
    <resultMap id="BaseResultMap" type="com.atguigu.springcloud.entities.Payment">
        <id column="id" property="id" jdbcType="BIGINT"/>
        <id column="serial" property="serial" jdbcType="VARCHAR"/>
    </resultMap>
    <select id="getPaymentById" parameterType="Long" resultMap="BaseResultMap">
        select * from payment where id=#{id};
    </select>
</mapper>

5.4 service

Create a service package under springcloud and create a PaymentService interface:

public interface PaymentService{
    public int create(Payment payment);
    public Payment getPatmentById(@Param("id") Long id);
}

1. The @Param annotation is used to alias method parameters. The @Param annotation is used to alias method parameters. @Param("id") means to alias the method parameter id as "id". In this way, #{id} can be used to reference this parameter in the SQL mapping file.

Create the PaymentServiceImpl implementation class and remember to write the @Service annotation

1. The @Resource annotation is used to mark an object that requires dependency injection. Dependency injection can be done based on name or type.

@Service
public class PaymentServiceImpl implements PaymentService {
    @Resource
    private PaymentDao paymentDao;

    public int create(Payment payment) {
        return paymentDao.create(payment);
    }
    public Payment getPatmentById(Long id) {
        return paymentDao.getPaymentById(id);
    }
}

5.5 controller

Create the controller package under springcloud, and then create the PaymentController class:

1. The @GetMapping annotation is used to map HTTP GET requests to the corresponding processing methods.

2. The @PathVariable annotation is used to bind variable values ​​in the URL path to method parameters.

@RestController
public class PaymentController {
    @Resource
    private PaymentService paymentService;
    @PostMapping(value="/payment/create")
    public CommonResult create(Payment payment){
        System.out.println(payment.getSerial());
        int result = paymentService.create(payment);
        if(result>0){
            return new CommonResult(200,"插入数据库成功",result);
        }else{
            return new CommonResult(444,"插入数据库失败",null);
        }
    }
    @GetMapping(value="/payment/get/{id}")
    public CommonResult getPaymentById(@PathVariable("id") Long id)
    {
        Payment payment = paymentService.getPatmentById(id);
        if(payment != null){
            return new CommonResult(200,"查询成功",payment);
        }else{
            return new CommonResult(444,"没有对应记录,查询ID:"+id,null);
        }
    }
}

5.6 Testing

Start the startup class and enter localhost:8001/payment/get/31 in the address bar.

The browser is not suitable for sending post requests, so go to postman to send the request. The ID is auto-incremented, and you only need to send the serial:

4.3 Hot deployment

Devtools hot deployment, code changes, automatic restart.

Step 1: Add dependencies in cloud-provider-payment8001

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <scope>runtime</scope>
    <optional>true</optional>
</dependency>

 Step 2: Add plug-ins to the parent project cloud2020

 <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <version>2.2.1.RELEASE</version>
        <configuration>
          <fork>true</fork>
          <addResources>true</addResources>
        </configuration>
      </plugin>
    </plugins>
  </build>

Step 3: Enable automatic compilation permissions 

Step 4: Update values ​​and enable hot registration

Input shortcut key: ctrl + shift + alt + /

 

Step 5: Restart

(Failed) test: PaymentMain8001 is also started, then enters the controller under cloud-provider-payment8001, changes CommonResult to CommonResult<Payment>, it will be refreshed in real time (failed).

4.4 Consumer order module

1. Build module

Name: Cloud-consumer-order80, there are main and test under src, and java and resource under main.

2. Change pom

Web and acuator are graphical monitoring displays. You must first add the mybatis driver to run, and then delete the mybatis driver. Otherwise, the .yml will not become green leaves:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <scope>runtime</scope>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
    </dependency>
</dependencies>

3. Write YML

Create application.yml under resources and set port 80

server:
  port: 81

4. Main startup

Create the com.atguigu.springcloud.OrderMain80 class under java

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

5. Business type

5.1 entities copy payment8001

RestTemplate provides a variety of convenient methods to access remote HTTP services. It is a simple and convenient template class for accessing restful services. It is a client template tool set provided by Spring for accessing Rest services.

5.2 Create the config package under springcloud and create the ApplicationContextConfig class under the package:

@Configuration
public class ApplicationContextConfig{
    @Bean
    public RestTemplate getRestTemp(){
        return new RestTemplate();
    }
}

5.3 Create the OrderController class under the controller package:

@RestController
@Slf4j
public class OrderController{
    public static final String PAYMENT_URL = "http://localhost:8081";
    @Resource
    private RestTemplate restTemplate;
    @PostMapping("/consumer/payment/create")
    public CommonResult<Payment> create(Payment payment){
        return restTemplate.postForObject(PAYMENT_URL+"/payment/create",payment,CommonResult.class);
    }
    @GetMapping("/consumer/payment/get/{id}")
    public CommonResult<Payment> getPayment(@PathVariable("id") Long id){
        return restTemplate.getForObject(PAYMENT_URL+"/payment/get/"+id,CommonResult.class);
    }
}

  

However, when the create method is called at this time, the inserted value will be empty. Remember to add the @RequestBody annotation to the PaymentController class under the controller of cloud-provider-payment8001:

@RequestBodyAnnotations are used to map the request body part of the HTTP request to the parameters of the method.

The new version of Dashboard is called services

4.5 Refactoring

Create a new cloud-api-commons module as a general encapsulation class:

Introduce dependencies under the module:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <scope>runtime</scope>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-all</artifactId>
        <version>5.1.0</version>
    </dependency>
<dependencies>

Create the com.atguigu.springcloud.entities package in the java directory of the module and paste the entities directly below:

Click Maven on the right, select refresh and cloud-api-commons appears, enter Lifecycle, click clean and click install.

Delete the original entities package

Paste the following dependencies under the module where the entities package has been deleted:


<dependency>
    <groupId>com.atguigu.springcloud</groupId>
    <artifactId>cloud-api-commons</artifactId>
    <version>${project.version}</version>
</dependency>

2. Elementary level

5. Eureka service registration and discovery

7001 and 7002 simulate the Eureka server, 81 simulates the consumer, and 8081 and 8082 simulate the producer.

5.1 Basic knowledge

5.2 Server installation

1. Build module

ArtifactId:cloud-eureka-server7001,com.atguigu.springcloud

2. Change POM

<dependencies>
<!--eureka-server-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</deoendency>
<dependency>
    <groupId>org.atguigu.springcloud</groupId>
    <artifactId>cloud-api-common</artifactId>
    <version>${project.version}</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <scope>runtime</scope>
    <optional>true</optional>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>
</dependencies>

3. Write YML

Create application.yml under resources

server:
 port: 7001
eureka:
    instance:
      hostname: localhost #eureka服务端实例名称
    client:
        #false表示不向注册中心注册自己
        register-with-eureka: false
        #false表示自己端就是注册中心,我的职责就是维护服务实例,不需要检索服务
        fetch-registry: false
        service-url:
        #设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址
          defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
        

4. Main startup

Create EurekaMain7001 as the startup class:

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

If you successfully enter localhost:7001, the following words will appear: 

5.3 Payment microservice 8081 settled in eurekaServer

Introduced in 8081's Pom:

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

Add the following code to the application.yml file:

eureka:
  client:
    #表示是否将自己注册进Eurekaserver默认为true
    register-with-eureka: true
    #是否从EurekaServer抓取已有的注册信息默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡。
    fetchRegistry: true
    service-url:
      defaultZone: http://localhost:7001/eureka

Add annotation to the startup class: @EnableEurekaClient

If successful, the new Instances CLOUD-PAYMENT-SERVICE will appear in the Instances area:

5.4 Order microservice 81 settled in eurekaServer

Add the following code to Pom:

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

Add the following code to YML:

eureka:
  client:
    register-with-eureka: true
    fetchRegistry: true
    service-url:
      defaultZone: http://localhost:7001/eureka

 Add the following code to the startup class: @EnableEurekaClient

If successful, the following 2 appear:

Register-with-eureka is false to deny entry, and true to allow entry.

5.5 Description of cluster principle

Eureka Server: Service registration, register service information into the service center. Service discovery, obtaining service information from the registration center. In essence, save the key service name and get the value calling address.

Start the eureka registration center first. Start the service provider payment payment service. After the payment service is started, its own information (such as the service address is registered in eureka as an alias). When the consumer order service needs to call the interface, it uses the service alias to go to the registration center to obtain the actual RPC remote calling address. After the consumer or calling address, the bottom layer actually uses HttpClient technology to implement remote calls. After the consumer obtains the service address, it will be cached in the local JVM memory. By default, the service call address is updated every 30 seconds.

Eureka has high availability characteristics and can build an Eureka registration center cluster to achieve load balancing + fault tolerance. Avoid single points of failure. Register with each other and watch each other.

5.6 Cluster environment construction

建Module:cloud-eureka-server7002

Pom: directly copy 7001

YML:建application.yml

Main startup class: EurekaMain7002, annotated with 7001

Modify the configuration: hosts file in C:\Windows\System32\drivers\etc path.

Modify the .yml of 7001, modify hostname and defaultZone:

server:
  port: 7001
eureka:
  instance:
    hostname: eureka7001.com #eureka服务端实例名称
  client:
    #false表示不向注册中心注册自己
    register-with-eureka: false
    #false表示自己端就是注册中心,我的职责就是维护服务实例,不需要检索服务
    fetch-registry: false
    service-url:
      #设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址
      defaultZone: http://eureka7002.com:7002/eureka/

Modify the .yml file of 7002:

server:
  port: 7002
eureka:
  instance:
    hostname: eureka7002.com #eureka服务端实例名称
  client:
    #false表示不向注册中心注册自己
    register-with-eureka: false
    #false表示自己端就是注册中心,我的职责就是维护服务实例,不需要检索服务
    fetch-registry: false
    service-url:
      #设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址
      defaultZone: http://eureka7001.com:7001/eureka/

Enter eureka7002.com:7002==127.0.0.1:7002 and eureka7001.com:7001==127.0.0.1 to see the eureka homepage:

enter

locahost:7002 can point to 7001, locahost:7001 can point to 7002, which is considered a success:

5.7 The two order and payment microservices are registered in the Eureka cluster

YML: Add under eureka of 8081:

defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka

 YML: Added under eureka of 81:

eureka:
    client:
        register-with-eureka: true
        fetchRegistry: true
        service-url:
                defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka

start up:

First start EurekaServer, 7001/7002 service 

Restart the service provider provider, 8081

Restart the consumer, 81

Enter http://localhost:81/consumer/payment/get/31, if the query is successful

test:

eureka7001.com:7001 can see 81 and 8081 register and enter

eureka7002.com:7002 can also be seen

5.8 Payment microservice cluster configuration

Build Module: Create cloud-provider-payment8082

Change Pom: Paste 8081 into 8082

Write YML: paste 8081 into 8082 and change a port number

Main startup: Create PaymentMain8082, copy the startup class of 8081, paste the mapper package under resource to 8082, and paste the com package directly to 8082.

Change controller: Modify 8001 and 8002 and add the following content:

@Value("${server.port}")
private String serverPort;
return new CommonResult(200,"插入数据库成功,serverPort"+serverPort,result);
return new CommonResult(200,"查询成功,serverPort"+serverPort,payment);

Startup sequence: 7001, 7002, 8081, 8082, 81

Test: eureka7001.com:7001, eureka7002.com:7002, localhost:81/consumer/payment/get/31

Found a bug: the order service access address is hard-coded.

An error will be reported at this time because IDEA does not know which server under this address provides services.

Use the @LoadBalanced annotation to give RestTemplate the ability to load balance. The annotation is written under the ApplicationContextConfig of the config package:

At this time, when accessing localhost:81/consumer/payment/get/31, the port number will switch back and forth between 8081 and 8082:

5.9 Actuator microservice information is improved

Add instance: instance-id and prefer-ip-address: true (the access path displays the ip address)

5.10 Service DiscoveryDiscovery

Modify the PaymentController class of the 8081 controller:

@Slf4j
@RestController
public class PaymentController {
    @Resource
    private DiscoveryClient discoveryClient;
    @GetMapping(value="/payment/discovery")
    public Object discovery()
    {
        List<String> services = discoveryClient.getServices();
        for(String element : services){
            log.info("*****element: "+element);
        }
        List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
        for(ServiceInstance instance : instances){
            log.info(instance.getServiceId()+"\t"+instance.getHost()+"\t"+instance.getPort()+"\t"+instance.getUri());
        }
        return instances;
    }
}

5. 11 Eureka self-protection theory knowledge

Summary: If a certain microservice is unavailable at a certain moment, Eureka will not clean it up immediately, but will still save the information of the microservice.

In order to prevent EurekaClient from running normally but the network is blocked from EurekaServer, EurekaServer will not immediately remove the EurekaClient server.

5.12 Self-protection prohibited

Under eureka in the yml file of 7001, add the following code:

  server:
    #关闭自我保护机制,保证不可用服务被及时踢除
    enable-self-preservation: false
    eviction-interval-timer-in-ms: 2000

Test: First check if 8081 exists, close 8081, and see if the card is deleted immediately.

Eureka suspension update instructions

6. Zookeeper service registration and discovery

6.1 Installation of Zookeeper

Download zookeeper-3.4.9, the following is the installation address

https://archive.apache.org/dist/zookeeper/

In the Linux virtual machine, create the /opt/module directory and place the zookeeper gz compressed package in this directory.

Then unzip the compressed package to this directory:

tar -zxvf zookeeper-3.4.10.tar.gz -C /opt/module/

Then modify the configuration:

Modify zoo.sample.cfg in the path /opt/module/zookeeper-3.4.10/conf to zoo.cfg:

mv zoo_sample.cfg zoo.cfg

Create the zkData folder in the /opt/module/zookeeper-3.4.10 directory:

mkdir zkData

Open the zoo.cfg file and modify the dataDir path:

vim zoo.cfg

Modify the following content:

dataDir=/opt/module/zookeeper-3.4.10/zkData

Start the Zookeeper service

Enter: sudo /opt/module/zookeeper-3.4.9/bin/zkServer.sh start

Enter Zookeeper

Enter: sudo /opt/module/zookeeper-3.4.9/bin/zkCli.sh

Exit Zookeeper 

Enter: quit

Shut down the Zookeeper service:

sudo /opt/module/zookeeper-3.4.9/bin/zkServer.sh stop

6.2 Project environment configuration

Enter in the /opt/module/zookeeper-3.4.9/bin directory:

sudo systemctl stop firewalld turns off the firewall.

sudo systemctl status firewalld checks whether the firewall is turned off.

Make sure the virtual machine can ping with windows

建module:cloud-provider-payment8004

see:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <scope>runtime</scope>
    <optional>true</optional>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
</dependency>

YML: 

server:
  port: 8004
#服务别名 — — — — 注册zookeeper到注册中心名称
spring:
  application:
    name: cloud-provider-payment
  cloud:
    zookeeper:
      connect-string: 192.168.182.157:2181

Startup class:

com.atguigu.springcloud.PaymentMain8004 

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

Service type:

Build controller package, PaymentController under springcloud

@RestController
@Slf4j
public class PaymentController {
    @Value("${server.port}")
    private String serverPort;
    @RequestMapping(value="/payment/zk")
    public String paymentzk(){
        return "springcloud with zookeeper: "+serverPort+"\t"+ UUID.randomUUID().toString();
    }
}

Then start the Zookeeper service and enter zookeeper

Enter: sudo /opt/module/zookeeper-3.4.9/bin/zkServer.sh start

Enter: sudo /opt/module/zookeeper-3.4.9/bin/zkCli.sh

Input: get /zookeeper

Version conflict issues:

<dependency>
<!--添加zookeeper3.4.9版本-->
        <groupId>org.apache.zookeeper</groupId>
        <artifactId>zookeeper</artifactId>
        <version>3.4.9</version>
<!--先排除自带的zookeeper3.5.3-->
        <exclusions>
            <exclusion>
                <groupId>org.apache.zookeeper</groupId>
                <artifactId>zookeeper</artifactId>
            </exclusion>
        </exclusions>
</dependency>

Then click Start.

Test localhost:8004/payment/zk. If the next string of numbers changes every time it is refreshed, it will be successful:

Assign the json string to tool.lu/json and see the information successfully:

6.3 Temporary or persistent nodes

It is used temporarily. If you close 8004 for a period of time, zookeeper will kick it off. Restart and the serial number will change.

6.4 Register the order service into zookeeper

module:cloud-consumerzk-order81。

POM: Copy 8004, see previous section.

YML:

server:
  port: 81
#服务别名 — — — — 注册zookeeper到注册中心名称
spring:
  application:
    name: cloud-consumer-order
  cloud:
  #注册到zookeeper地址
    zookeeper:
      connect-string= 192.168.182.157:2181

Startup class:

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

config:

@Configuration
public class ApplicationContextConfig {
    @Bean
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }
}

controller:

@RestController
@Slf4j
public class OrderZKController {
    public static final String INVOKE_URL="http://cloud-provider-payment";
    @Resource
    private RestTemplate restTemplate;
    @GetMapping(value="/consumer/payment/zk")
    public String paymentInfo(){
        String result = restTemplate.getForObject(INVOKE_URL+"/payment/zk",String.class);
        return result;
    }
}

test:

7. Consul service registration and discovery

7.1 Introduction to Consul

7.2 Install and run Consul

download link:

https://developer.hashicorp.com/consul/downloads

 

Click the address bar, enter cmd, enter consul --version, and then enter consul agent -dev

Enter http://localhost:8500 to enter the interface and see the check mark:

7.3 Service provider registers into Consul

module:cloud-providerconsul-payment8006

see:

<dependency>
     <groupId>org.springframework.cloud</groupId>
     <artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <scope>runtime</scope>
    <optional>true</optional>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>

yml:

server:
  port: 8006
  
spring:
  application:
    name: consul-provider-payment

#consul注册中心地址
cloud:
  consul:
    host: localhost
    port: 8500
    discovery:
      service-name: ${spring.application.name}

Startup class:

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

Business class: copy 8004, slightly modified

@RestController
@Slf4j
public class PaymentController {
    @Value("${server.port}")
    private String serverPort;
    @RequestMapping(value="/payment/consul")
    public String paymentzk(){
        return "springcloud with consul: "+serverPort+"\t"+ UUID.randomUUID().toString();
    }
}

Test class: Start

7.4 Service consumers register into Consul

module: cloud-consumerconsul-order81

see:

<dependency>
     <groupId>org.springframework.cloud</groupId>
     <artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <scope>runtime</scope>
    <optional>true</optional>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

yml:

server:
  port: 81

spring:
  application:
    name: cloud-consumer-order

#consul注册中心地址
cloud:
  consul:
    host: localhost
    port: 8500
    discovery:
      service-name: ${spring.application.name}

Main startup class:

@SpringBootApplication
@EnableDiscoveryClient //该注解用于向使用consul或者zookeeper作为注册中心时注册服务
public class OrderConsulMain80 {
    public static void main(String[] args) {
        SpringApplication.run(OrderConsulMain80.class,args);
    }
}

Configure beans:

@Configuration
public class ApplicationContextConfig {
    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }
}

Controller:

@RestController
@Slf4j
public class OrderConsulController {
    public static final String INVOKE_URL = "http://consul-provider-payment";
    @Resource
    private RestTemplate restTemplate;

    @GetMapping(value = "/consumer/payment/consul")
    public String paymentInfo(){
        String result = restTemplate.getForObject(INVOKE_URL+"/payment/consul",String.class);
        return result;
    }
}

Validation test: start

Visit the test address:

7.5 Similarities and differences among the three registration centers

CAP:

AP is Eureka, which guarantees consistency.

 Data synchronization in different network partitions fails. In order to ensure availability, old values ​​are allowed to be returned, which violates the consistency requirement.

CP is Zookeeper and Consul. The data must be consistent. If it is inconsistent, an error will be reported.

8. Ribbon load balancing service call

8.1 Introduction to Ribbon

First restore the environment EurekaMain7001, EurekaMain7002, PaymentMain8081, PaymentMain8083, OrderMain81.

Enter eureka7001.com:7001

basic introduction:

What can be done:

centralized: 

Within the process: 

8.2 Ribbon load balancing and Rest call

At this time, I refreshed the web page and found that polling could be implemented, but Ribbon was not introduced. The actual reason is that Eureka already comes with Ribbon.

Add the following code to the OrderController of cloud-consumer-order81:

@GetMapping("/consumer/payment/getForEntity/{id}")
public CommonResult<Payment> getPayment2(@PathVariable("id") Long id)
{
    ResponseEntity<CommonResult> entity = restTemplate.getForEntity(PAYMENT_URL + "/payment/get/" + id, CommonResult.class);
    if(entity.getStatusCode().is2xxSuccessful()){
        return entity.getBody();
    }else{
        return new CommonResult<>(444,"操作失败");
    }
}

输入:localhost:81/consumer/payment/getForEntity/31

8.3 Ribbon’s default load rules

8.4 Ribbon load rule replacement

The ribbon replacement rules require that the configuration cannot be placed below the package one level above the Main package, that is, you must create one yourself outside the package.

Create a package called myrule under atguigu.

Create the MySelfRule class below myrule.

@Configuration
public class MySelfRule {
    @Bean
    public IRule myRule(){
        return new RandomRule();
    }
}

 Add the @RibbonClient annotation on OrderMain81:

@SpringBootApplication
@EnableEurekaClient
@RibbonClient(name="CLOUD-PAYMENT-SERVICE",configuration= MySelfRule.class)
public class OrderMain81 {
    public static void main(String[] args){
        SpringApplication.run(OrderMain81.class,args);
    }
}

 Input: localhost:81/consumer/payment/getForEntity/31, the access at this time is random

8.5 Ribbon default load polling algorithm principle

8.6 RoundRobinRule source code analysis

Shortcut key: alt+ctrl+b View source code

8.7 Ribbon's handwritten polling algorithm

1. Comment out @LoadBalance in the cloud-consumer-order81 configuration class

2. Create a new lb package under springcloud and create a new interface LoadBalancer

public interface LoadBalancer {
    ServiceInstance instances(List<ServiceInstance> serviceInstances);
}

3. Implement the class under the lb package: MyLB

@Component
public class MyLB implements  LoadBalancer{
    private AtomicInteger atomicInteger = new AtomicInteger(0);
    public final int getAndIncrement(){
        int current;
        int next;
        do{
            current = this.atomicInteger.get();
            next = current >= 2147483647 ? 0:current+1;
        }while(!this.atomicInteger.compareAndSet(current,next));
        System.out.println("*****第几次访问,次数next:"+next);
        return next;
    }
    @Override
    public ServiceInstance instances(List<ServiceInstance> serviceInstances){
        int index = getAndIncrement() % serviceInstances.size();
        return serviceInstances.get(index);
    }
}

Write the following code in OrderControlle under the control package:

@GetMapping(value="/consumer/payment/lb")
public String getPaymentLB(){
    List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
    if(instances == null || instances.size() <= 0){
        return null;
    }
    ServiceInstance serviceInstance = loadBalancer.instances(instances);
    URI uri = serviceInstance.getUri();
    System.out.println(uri);
    System.out.println(uri+"/payment/lb");
    return restTemplate.getForObject(uri+"/payment/lb",String.class);
}
@Resource
private LoadBalancer loadBalancer;
@Resource
private DiscoveryClient discoveryClient;

Write the following code in the PaymentController of 8081 and 8083:

@GetMapping(value="/payment/lb")
    public String getPaymentLB(){
        return serverPort;
}

Enter the address: localhost:81/consumer/payment/lb. The effect is that 8081 and 8083 appear alternately.

9. OpenFeign service interface call

9.1 What is OpenFeig?  

9.2 OpenFeign service call 

Interface + annotation: Microservice calling interface (the interface that matches the provider and the caller) + @FeignClient

module:cloud-consumer-feign-order81

see:

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    <dependency>
        <groupId>org.atguigu.springcloud</groupId>
        <artifactId>cloud-api-common</artifactId>
        <version>${project.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <scope>runtime</scope>
        <optional>true</optional>
    </dependency>
</dependencies>

yml:

server:
  port: 81
eureka:
  client:
    register-with-eureka: false
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/

Main startup:

com.atguigu.springcloud.OrderFeignMain81添加@EnableFeignClients

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

Business category:

Add the service package under springcloud, and then add the PaymentFeignService interface.

@Configuration
@FeignClient(value="CLOUD-PAYMENT-SERVICE")
public interface PaymentFeignService {
    @GetMapping(value="/payment/get/{id}")
    public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id);
}

Create OrerFeignController under controller

@RestController
@Slf4j
public class OrderFeignController {
    @Resource
    private PaymentFeignService paymentFeignService;
    @GetMapping(value="/consumer/payment/get/{id}")
    public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id){
        return paymentFeignService.getPaymentById(id);
    }
}

Test: localhost:81/consumer/payment/get/31, alternate results of 8081 and 8083 appear.

Summary: Feign is a declarative web service client that makes writing HTTP clients easier. We only need to create an interface and add annotations to the interface, and Feign will automatically provide the implementation. There is no need to manually create URLs, send HTTP requests, process HTTP responses, etc.

The client will send the request to a service named CLOUD-PAYMENT-SERVICE.

9.3 OpenFeign timeout control

Add the following code to the PaymentController under the 8081 controller:

@GetMapping(value="/payment/feign/timeout")
public String paymentFeignTimeout(){
    try{
        TimeUnit.SECONDS.sleep(3);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return serverPort;
}

Add the following code under the PaymentFeignService interface under the 81 service package:

@GetMapping(value="/payment/feign/timeout")
public String paymentFeignTimeout();

Under the controller package of 81

@GetMapping(value="/consumer/payment/feign/timeout")
public String paymentFeignTimeout(){
//客户端默认等1秒
return paymentFeignService.paymentFeignTimeout();
}

test:

Start 7001,8001

localhost:8081/payment/feign/timeout, normal access takes seconds

localhost:81/consumer/payment/feign/timeout timeout

solve:

server:
  port: 81
eureka:
  client:
    register-with-eureka: false
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/

#设置feign客户端超时时间(OpenFeign默认支持ribbon)
ribbon:
#指的是建立连接所用的时间,适用于网络状况正常的情况下,两段连接所用的时间
  ReadTimeout: 5000
#指的是建立连接后从服务器读取到可用资源所用的时间
  ConnectTimeout: 5000

Retest successfully.

9.4 OpenFeign log enhancement

Create config under springcloud under cloud-consumer-feign-order80 and configure the log bean:

@Configuration
public class FeignConfig {
    @Bean
    Logger.Level feignLoggerlevel()
    {
        return Logger.Level.FULL;
    }
}

 Add the following code to the YML file:

logging:
  level:
    com.atguigu.springcloud.service.PaymentFeignService: debug

Test: Start Openfeign81. localhost/consumer/payment/get/31.

View the call results on the console:

Guess you like

Origin blog.csdn.net/RuanFun/article/details/133966489