springcloud

------------恢复内容开始------------

学习链接:尚硅谷SpringCloud框架开发教程(SpringCloudAlibaba微服务分布式架构丨Spring Cloud)_哔哩哔哩_bilibili

笔记:【SpringCloud】微服务笔记_努力充实,远方可期-CSDN博客

软件和脑图:https://pan.baidu.com/s/1Fxe3db0pTPaorwCd_EE_rA

      c675

      https://gitee.com/lixiaogou/cloud2020

官方代码:https://github.com/zzyybs/atguigu_spirngcloud2020

一、系统架构

  1.1 系统架构

 

  1.2 系统架构分类

      

 

 

   1.3 远程调用方式

 

 

   1.4 模拟微服务

    1.4.1 restTemplate实现工程之间远程调用

       

 

 

       

 

 

         

    1.4.2 nginx实现负载均衡

 

 

 

二、SpringCloud简介

   cloud官网资料:https://cloud.spring.io/spring-cloud-static/Hoxton.SR1/reference/htmlsingle/

  2.1 什么是微服务

    springcloud介绍

      SpringCloud 是微服务一站式服务解决方案,微服务全家桶。它是微服务开发的主流技术栈。它采用了名称,而非数字版本号。

      微服务是一种架构方式,如果你足够了解微服务是什么概念,其实微服务就算不借助任何技术也能实现,只是有很多问题需要我们解决。例如:负载均衡,服务的注册与发现,服务调用,网关...。Spring-Cloud将处理这些问题的的技术全部打包好了,进行统一处理

      SpringCloud 和 springCloud Alibaba 目前是最主流的微服务框架组合。

    springcloud功能

      它将现在非常流行的一些技术整合到一起,实现了诸如:配置管理,服务发现,智能路由,负载均衡,熔断器,控制总线,集群状态等等功能

    springcloud和dubbo对比

   

 

 

      Spring Cloud抛弃了Dubbo的RPC通信,采用的是基于HTTP的REST方式

    springcloud的版本问题

      springBoot使用的是数字作为版本,官网强烈建议升级到2.0以上。springCloud使用的是字母作为版本,伦敦地铁站站名。

    

 

 

     查看版本对应关系:https://start.spring.io/actuator/info

       

 

     教程中使用的版本:

      

 

   2.2 微服务停更说明   

1,Eureka停用,可以使用zk作为服务注册中心

2,服务调用,Ribbon准备停更,代替为LoadBalance

3,Feign改为OpenFeign

4,Hystrix停更,改为resilence4j

​ 或者阿里巴巴的sentienl

5.Zuul改为gateway

6,服务配置Config改为 Nacos

7,服务总线Bus改为Nacos

 

       C:\Users\WeiLi\.m2\repository

 三、父工程构建

  3.1 新疆项目

  

 

 

   

 

 

   3.2 配置环境

    

 

 

     

    

     删除父工程中的src目录

   3.3 添加依赖

<?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>org.example</groupId>
<artifactId>springcloud_2021</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>


<!-- 统一管理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.38</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>
<configuration>
<fork>true</fork>
<addResources>true</addResources>
</configuration>
</plugin>
</plugins>
</build>

</project>

  3.4 复习DependencyManagement和Dependencies

    DependencyManagement:

      Maven使用dependencyManagement元素来提供一种管理依赖版本号的方式

      通常会在一个组织或者项目的最顶层的父POM中看到dependencyManagement元素

      使用pom.xml中的dependencyManagement元素能让所有在子项目中引用个依赖而不用显式的列出版本号。

         Maven会沿着父子层次向上走,直到找到一个拥有dependencyManagement元素的项目,然后它就会使用这个dependencyManagement元素中指定的版本号。

<dependencyManagement>
    <dependencies>
        <dependency>
        <groupId>mysq1</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.2</version>
        </dependency>
    <dependencies>
</dependencyManagement>

    然后在子项目里就可以添加mysql-connector时可以不指定版本号,例如:

<dependencies>
    <dependency>
    <groupId>mysq1</groupId>
    <artifactId>mysql-connector-java</artifactId>
    </dependency>
</dependencies>

    好处:

    这样做的好处就是:如果有多个子项目都引用同一样依赖,则可以避免在每个使用的子项目里都声明一个版本号,这样当想升级或切换到另一个版本时,只需要在顶层父容器里更新,而不需要一个一个子项目的修改;另外如果某个子项目需要另外的一个版本,只需要声明version就可。

  注意点:

    dependencyManagement里只是声明依赖,并不实现引入,因此子项目需要显示的声明需要用的依赖。

    如果不在子项目中声明依赖,是不会从父项目中继承下来的;只有在子项目中写了该依赖项,并且没有指定具体版本,才会从父项目中继承该项,并且version和scope都读取自父pom。

    如果子项目中指定了版本号,那么会使用子项目中指定的jar版本。

    父工程创建完成执行mvn : install将父工程发布到仓库方便子工程继承。

四、支付模块

  流程:

    1. 建Module
    2. 改POM
    3. 写YML
    4. 主启动
    5. 业务类

  4.1 建module

     

     

   4.2 改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">
<parent>
<artifactId>springcloud_2021</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>cloud-provider-payment8001</artifactId>


<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>

<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>

<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-jdbc -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-devtools -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>

<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>

<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>


</dependencies>


</project>

  4.3 写yml

server:
port: 8001

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/springcloud?useUnicode=true&characterEncoding=utf-8&useSSL=false
username: root
password: mysql

devtools:
restart:
enabled: true #是否支持热部署


mybatis:
mapperLocations: classpath:mapper/*.xml
type-aliases-package: com.atguigu.springcloud.entities

  4.4 主启动

package com.lili.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

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

   4.5 构建数据库

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=utf8mb4

  4.6 构建对应的实体类

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Payment implements Serializable {
    private Long id;
    private String serial;
}

  4.7 JSON封装体CommonResult

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class CommonResult<T>{
    private Integer code;
    private String message;
    private T data;

    public CommonResult(Integer code, String message){
        this(code, message, null);
    }
}

  4.8 Dao

  com.lili.springcloud.dao下新建接口PaymentDao:

import com.atguigu.springcloud.entities.Payment;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

/**
 */
@Mapper
//@Repository不用Spring的
public interface PaymentDao
{
    public int create(Payment payment);

    public Payment getPaymentById(@Param("id") Long id);
}

  MyBatis映射文件PaymentMapper.xml,路径:resources/mapper/PaymentMapper.xml

<?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.lili.springcloud.dao.PaymentDao">

    <insert id="create" parameterType="Payment" useGeneratedKeys="true" keyProperty="id">
        insert into payment(serial)  values(#{serial});
    </insert>

    <resultMap id="BaseResultMap" type="com.lili.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>

  @param注解详解:(85条消息) MyBatis-@param注解详解_xuonline4196的博客-CSDN博客_@param

  @Mapper注解详情:

  @Autowired @Resource @Service:Spring5:@Autowired注解、@Resource注解和@Service注解 - IT·达人 - 博客园 (cnblogs.com)

  4.9 service

    PaymentService接口文件,路径:com.lili.springcloud.service

package com.lili.springcloud.service;

import com.lili.springcloud.entities.Payment;
import org.apache.ibatis.annotations.Param;

public interface PaymentService {
    public int create(Payment payment);

    public Payment getPaymentById(@Param("id") Long id);
}

    PaymentServiceImpl实现类,路径:com.lili.springcloud.service.impl

package com.lili.springcloud.service.impl;

import com.lili.springcloud.dao.PaymentDao;
import com.lili.springcloud.entities.Payment;
import com.lili.springcloud.service.PaymentService;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

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


    @Override
    public int create(Payment payment) {
        return paymentDao.create(payment);
    }

    @Override
    public Payment getPaymentById(Long id) {
        return paymentDao.getPaymentById(id);
    }
}

  4.10 controller

package com.lili.springcloud.controller;

import com.lili.springcloud.entities.CommonResult;
import com.lili.springcloud.entities.Payment;
import com.lili.springcloud.service.PaymentService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
public class PaymentController {
    @Resource
    private PaymentService paymentService;


    @PostMapping(value = "/payment/create")
    public CommonResult create(Payment payment){
        int result = paymentService.create(payment);
        if(result>0){
            return  new CommonResult(200,"插入数据成功",result);
        }else{
            return  new CommonResult(444,"插入数据失败",result);
        }
    }

    @GetMapping(value = "/payment/get/{id}")
    public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id)
    {
        Payment payment = paymentService.getPaymentById(id);

        if(payment != null)
        {
            return new CommonResult(200,"查询成功 ",payment);
        }else{
            return new CommonResult(444,"没有对应记录,查询ID: "+id,null);
        }
    }
}

  4.11 测试

   课堂资源 MindManager破解版:(86条消息) SpringCloud 课堂源码以及思维导图_N₂的博客-CSDN博客

   

  4.12 热部署Devtools

    4.12.1 在子模块生产者pom中添加以下热部署依赖

<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-devtools -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
   <scope>runtime</scope>
    <optional>true</optional>
</dependency>

    4.12.2 在父工程中添加插件(上面配置过程中已经添加过了)

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

    4.12.3 配置

 

 

     按住ctrl + shift + Alt+/ 选择Registry,勾选以下两项

  

 

  

 

   注意:compiler.automake.allow.when.app.running选项,在IntelliJ IDEA 2021.2之后的版本迁移到高级设置中

   在这里:

  

  修改Running Application Update Policies

 

  

 

 

   4.12.4 重启idea

  注意:热部署只允许在开发阶段使用

五、消费者

  5.1 新建模块cloud-consumer-order80(和提供者模块狗酱方式相同)

  5.2 修改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">
    <parent>
        <artifactId>springcloud_2021</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-consumer-order80</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web  -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-devtools -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-test -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

</project>

  5.3 修改yml

server:
  port: 80

  5.4 添加主启动类

package com.lili.springcloud;


import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

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

  5.5 添加entities包  复制提供者模块的entities包即可

  5.6 新建配置类(在com.lili.springcloud下建立包config 在包中建立配置类

package com.lili.springcloud.config;

import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class ApplicationContextConfig {

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

  5.7 controller包

package com.lili.springcloud.controller;

import com.lili.springcloud.entities.CommonResult;
import com.lili.springcloud.entities.Payment;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;

@RestController
public class OrderController {
    public static final String PAYMENT_URL = "http://localhost:8001";


    @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);
    }
}

  5.8 启动提供者和消费者两个模块

    测试消费者模块

    测试:

 

 

     

 

 

   5.9 存在的问题

    

 

 

     没有加@RequestBody注解前,进行插入,虽然显示插入成功,但是serial数据并未真正写入数据库中,如下图

    @RequestBody:RequestBody和ResponseBody踩坑 - 知乎 (zhihu.com)

    

 

    没有添加@RequestBody PaymentController类中接收到的Payment如下:

    provider before:Payment(id=null, serial=null)

    添加@RequestBody后接收到的数据是:

    provider before:Payment(id=null, serial=lili0013)

 

    无论是否添加@RequestBody OrderController类中都能收到传过来的Payment的serial属性:

    paymenxxt:Payment(id=null, serial=lili007)

 

六、工程重构

  目的上面 两个子项目,有多次重复的 导入 jar,和重复的 Entity 实体类。可以把 多余的部分,加入到一个独立的模块中,将这个模块打包,并提供给需要使用的 module

    搭建步骤:

    1. 新建一个 cloud-api-commons 子模块(与前面相同 这里不重复)

    2. 在pom中添加依赖

 <dependencies>
        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-devtools -->
        <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>

        <!-- https://mvnrepository.com/artifact/cn.hutool/hutool-all -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.1.0</version>
        </dependency>
    </dependencies>

    3. 将 entities 包里面的实体类放到这个子模块中

    4. maven命令clean install

      跳过测试

      

 

 

       运行clean成功后运行install

    4. 将 提供者 和 消费者 两个项目中的 entities 包删除,并删除掉加入到 cloud-api-commons 模块的 依赖配置

      删除provider和consumer两个模块中的entities后,引入自己生成的公共cloud-api-commons包

<!-- 自己生成的公资源包 -->
        <dependency>
            <groupId>org.example</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

七、Eureka

  7.1 Eureka简介

    7.1.1 什么是服务治理

      SpringCloud封装了Netflix公司开发的Eureka模块来实现服务治理。

      在传统的rpc远程调用框架中,管理每个服务与服务之间依赖关系比较复杂,管理比较复杂,所以需要使用服务治理,管理服务于服务之间依赖关系,可以实现服务调用、负载均衡、容错等,实现服务发现与注册。

     7.1.2 什么是服务注册与发现

    Eureka采用了CS的设计结构,Eureka Server服务注册功能的服务器,它是服务注册中心。而系统中的其他微服务,使用Eureka的客户端连接到Eureka Server并维持心跳连接。这样系统的维护人员就可以通过Eureka Server来监控系统中各个微服务是否正常运行。这点和zookeeper很相似

    在服务注册与发现中,有一个注册中心。当服务器启动时候,会把当前自己服务器的信息 比如服务地址 通讯地址等以别名方式注册到注册中心上。另一方(消费者服务提供者),以该别名的方式去注册中心上获取到实际的服务通讯地址,然后再实现本地RPC调用。RPC远程调用框架核心设计思想:在于注册中心,因为便用注册中心管理每个服务与服务之间的一个依赖关系(服务治理概念)。在任何rpc远程框架中,都会有一个注册中心(存放服务地址相关信息(接口地址))

    

Eureka 官方停更不停用,以后可能用的越来越少。

Eureka 是 Netflix 开发的,一个基于 REST 服务的,服务注册与发现的组件,以实现中间层服务器的负载平衡和故障转移。

Eureka 分为 Eureka Server 和 Eureka Client及服务端和客户端。Eureka Server为注册中心,是服务端,而服务提供者和消费者即为客户端,消费者也可以是服务者,服务者也可以是消费者。同时Eureka Server在启动时默认会注册自己,成为一个服务,所以Eureka Server也是一个客户端,这是搭建Eureka集群的基础。

Eureka Client:一个Java客户端,用于简化与 Eureka Server 的交互(通常就是微服务中的客户端和服务端)。通过注册中心进行访问。是一个Java客户端,用于简化Eureka Server的交互,客户端同时也具备一个内置的、使用轮询(roundrobin)负载算氵去的负载均衡器

在应用启动后,将会向Eureka Server发送心跳(默认周期为30秒)。如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,Eureka Server将会从服务注册表中把这个服务节点移除(默认90秒)

Eureka Server:提供服务注册服务,各个微服务节,通过配置启动后,会在Eureka Serverc中进行注册,这样Eureka Server中的服务注册表中将会存储所有可用服务节点信息,服务节点的信息可以在界面中直观看到。

服务在Eureka上注册,然后每隔30秒发送心跳来更新它们的租约。如果客户端不能多次续订租约,那么它将在大约90秒内从服务器注册表中剔除。注册信息和更新被复制到集群中的所有eureka节点。来自任何区域的客户端都可以查找注册表信息(每30秒发生一次)来定位它们的服务(可能在任何区域)并进行远程调用

服务提供者向注册中心注册服务,并每隔30秒发送一次心跳,就如同人还活着存在的信号一样,如果Eureka在90秒后还未收到服务提供者发来的心跳时,那么它就会认定该服务已经死亡就会注销这个服务。这里注销并不是立即注销,而是会在60秒以后对在这个之间段内“死亡”的服务集中注销,如果立即注销,势必会对Eureka造成极大的负担。这些时间参数都可以人为配置。

Eureka还有自我保护机制,如果在15分钟内超过85%的节点都没有正常的心跳,那么Eureka就认为客户端与注册中心出现了网络故障,所以不会再接收心跳,也不会删除服务。

客户端消费者会向注册中心拉取服务列表,因为一个服务器的承载量是有限的,所以同一个服务会部署在多个服务器上,每个服务器上的服务都会去注册中心注册服务,他们会有相同的服务名称但有不同的实例id,所以拉取的是服务列表。我们最终通过负载均衡来获取一个服务,这样可以均衡各个服务器上的服务。

  7.2 单机版Eureka

    消费者端口80,提供者端口8001,Eureka端口7001

    7.2.1 IDEA生成eurekaServer端服务注册中心类似物业公司

      1. 新建Module  cloud-eureka-server7001

       2. 改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">
    <parent>
        <artifactId>springcloud_2021</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-eureka-server7001</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
    

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

        <!-- 自己生成的公资源包 -->
        <dependency>
            <groupId>org.example</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web  -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-devtools -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-test -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>

    </dependencies>
</project>



</project>

      

 

 

       3. 写yml

server:
  port: 7001

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

       4. 创建主启动类 

package com.lili.springcloud;


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

@SpringBootApplication
@EnableEurekaServer   // 表示它是服务注册中心
public class EurekaServerMain7001 {
    public static void main(String[] args){
        SpringApplication.run(EurekaServerMain7001.class, args);
    }
}

       5. 测试

        http://localhost:7001/

 

 

 

    7.2.2 EurekaClient端cloud-provider-payment8001将注册进EurekaServer成为服务提供者provider

      1.改cloud-provider-payment8001的pom

<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-eureka-server -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

      2. 写yml

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

      3. 主启动类上添加注解

      @EnableEurekaClient

      4.测试

      先要启动EurekaServer

      http://localhost:7001/

      

 

   7.2.3 EurekaClient端cloud-consumer-order80将注册进EurekaServer成为服务消费者consumer

    1. 改pom

    2. 写yml

    3. 主启动加注解

    4.测试

      先要启动EurekaServer,7001服务

      再要启动服务提供者provider,8001服务

      http://localhost/consumer/payment/get/31

 7.3 集群Eureka

    Eureka: (87条消息) spring-cloud(五)微服务调用篇(一) RestTemplate_胖虎儿的博客-CSDN博客_resttemplate微服务调用

    7.3.1 集群搭建

      1. 参考7001搭建7002

      2. 改POM

      3. 修改映射配置

        找到C:\Windows\System32\drivers\etc路径下的hosts文件

        修改映射配置添加进hosts文件:127.0.0.1  eureka7001.com

                      127.0.0.1  eureka7002.com

      4. 写yml

server:
  port: 7001

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

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

    7.3.2 将支付服务8001微服务发布到上面2台Eureka集群配置中

      修改原有的yml文件:

service-url:
  defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka  #集群版
 

    7.3.3 将订单服务80微服务发布到上面2台Eureka集群配置中

service-url:
  defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka  #集群版

    7.3.4 测试01

      先要启动EurekaServer,7001/7002服务

      再要启动服务提供者provider,8001服务

      再要启动消费者,80

      http://localhost/consumer/payment/get/31

      

 

 

       

 

 

       

    7.3.5 支付服务提供者8001集群环境构建

      1. 参考cloud-provider-payment8001新建cloud-provider-payment8002

      2. 将8001的pom复制到8002

      3. 写YML

        将8001的yml文件复制到8002并将端口改为8002

      4. 添加主启动类

      5. 将8001的controller dao service mapper等复制到8002 

      6. 修改8001和8002的controller

        

 

 

       

 

 

    7.3.6 负载均衡

      原始的80消费者的订单服务访问地址写死,改为如下:

        public static final String PAYMENT_URL = "http://CLOUD-PAYMENT-SERVICE";

      在restTemplate上加注解

        使用@LoadBalanced注解赋予RestTemplate负载均衡的能力

        注意:在配置文件中

        

     7.3.7 测试

        先要启动EurekaServer,7001/7002服务

        再要启动服务提供者provider,8001/8002服务

        http://localhost/consumer/payment/get/31

        结果:负载均衡效果达到,8001/8002端口交替出现

        Ribbon和Eureka整合后Consumer可以直接调用服务而不用再关心地址和端口号,且该服务还有负载功能了

    7.4 actuator微服务信息完善(不是必须的步骤 可以不完善 但完善更好)

   7.4.1 存在的问题

      1. eureka注册中心可以看到主机名称,目前的主机名称是localhost,我们需要更改它

      

       更改方式:

        修改8001 8002的yml

        添加:

          instance:   

            instance-id: payment8001          

        

      更改后效果:

        

       2. 没有ip提示,点击下面的服务不会显示ip地址

      

        更改方式:

          修改8001 8002的yml

          添加prefer-ip-address: true

          

 

 

  7.5 服务发现Discovery

    对于注册进eureka里面的微服务,可以通过服务发现来获得该服务的信息

    1. 修改cloud-provider-payment8001的Controller

@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 this.discoveryClient;
}

    2. 8001主启动类 添加注解

      @EnableDiscoveryClient

    3. 自测

      先要启动EurekaServer,7001/7002服务

      再启动8001主启动类,需要稍等一会

      http://localhost:8001/payment/discovery

  7.6 Eureka自我保护

某时刻某一个微服务不可用了,Eureka不会立即清理,依旧会对该微服务的信息进行保存。属于CAP里的AP(高可用)分支

保护模式主要用于一组客户和Eureka Server之间存在网络分区场景下保护。一旦进入保护模式,Eureka Server将会尝试保护其服务注册表中的信息,不再删除服务注册表中固定信息,也就是不会注销任何微服务。

如果在Eureka Server的首页看到以下这段提示,则说明Eureka讲入了保护模式:   

EMERGENCY!EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY’RE NOT.RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEING EXPIRED JUST TO BE SAFE

为什么会产生Eureka自我保护机制?

为了防止Eureka Client可以正常运行但是与Eureka Server网络不通情况下,Eureka Server不会立刻将Eureka Client服务剔除

什么是自我保护模式?

默认情况下,如果Eureka Server在一定时间内没有接收到某个微服务实例的心跳,Eureka Server将会注销该实例(默认90秒)。但是当网络分区故障发生时、卡顿、拥挤)时,微服务与Eureka Server之间无法正常通信,以上行为可能变得非常危险了—因为微服务本身其实是健康的,此时本不应该注销这个微服务。Eureka通过"自我保护模式"来解决这个问题—当Eureka Server节点在短时间内丢失过多客户端时(可能发生了网络分区故障),那么这个节点就会进入自我保护模式。

自我保护机制:默认情况下Eureka CIient定时向Eureka Server端发送心跳包。

如果Eureka在server端在一定时间内(默认90秒)没有收到Eureka Client发送心跳包,便会直接从服务注册列表中剔除该服务,但是在短时间(90秒内)内丢失了大量的服务实例心跳,这时Eureka Server会开启自我保护机制,不会剔除该服务(该现象可能出现如果网络不通但是Eureka Client出现宕机,此时如果别的注册中心如果一定时间内没有收到心跳会将剔除该服务这样就出现了严重失误,因为客户端还能正常发送心跳,只是网络延迟问题,而保护机制是为了解决此问题而产生的

在自我保护模式中,Eureka Server会保护服务注册表中的信息,不再注册任何服务实例。

它的设计哲学就是宁可保留错误的服务注册信息,也不盲目注销任何可能健康的服务实例。一句话讲解:好死不如赖活着

综上,自我保护模式是一种应对网络异常的安全保护措施。它的架构哲学是宁可同时保留所有微服务(健康的微服务和不健康的微服务都会保留)也不盲目注销任何健康的微服务。使用自我保护模式,可以让Eureka集群更加的健壮、稳定。

禁止自我保护:(如果想)

Eureka Server:

server:
port: 7001

eureka:
instance:
 hostname: eureka7001.com

client:
 register-with-eureka: false
 fetch-registry: false
 service-url:
   defaultZone: http://eureka7002.com:7002/eureka/
server: # 与client平行
    # 关闭自我保护机制,保证不可用该服务被及时剔除
    enable-self-preservation: false
    eviction-interval-timer-in-ms: 2000
Eureka Client

# 提供者
server:
  port: 8001  # 端口号不一样

eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url: # 集群
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/
  instance: #重点,和client平行
    instance-id: payment8001 # 每个提供者的id不同,显示的不再是默认的项目名
    prefer-ip-address: true # 可以显示ip地址
    # Eureka客户端像服务端发送心跳的时间间隔,单位s,默认30s
    least-renewal-interval-in-seconds: 1
    # Rureka服务端在收到最后一次心跳后等待时间上线,单位为s,默认90s,超时将剔除服务
    least-expiration-duration-in-seconds: 2

八、Zookeeper

   8.1 启动zookeeper(参照我的zookeeper笔记)

     1. 安装与启动(参照我的zookeeper笔记)

      2. 关闭防火墙

      centos6: chkconfig iptables off
      centos7:systemctl stop firewald            
    3. 测试网络      

   8.2 构建服务提供者

      1.新建cloud-provider-payment8004

      2. 修改pom

      

<dependencies>

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


        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-zookeeper-discovery -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-devtools -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-test -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>


    </dependencies>

    3. yml

server:
  port: 8004

spring:
  application:
    name: cloud-provider-payment
  cloud:
    zookeeper:
      connect-string: 192.168.136.140:2181
 

  4. 主启动类

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

  5. controller

package com.atguigu.springcloud.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.UUID;

@RestController
@Slf4j
public class PaymentController {

    @Value("${server.port}")
    private String serverPort;

    @GetMapping(value = "/payment/zk")
    public String paymentzk(){
        return "springcloud with zookeeper:"+serverPort+"\t"+ UUID.randomUUID().toString();
    }

}
 

  6. 启动8004注册进zookeeper

    

  7.测试

    浏览器测试:http://localhost:8004/payment/zk

    centos服务器测试:

      先启动zookeeper客户端和服务器

      

 

 

   8. 思考

    zookeeper是临时节点

        zookeeper和eureka区别:Eureka和zookeeper的区别 - 经典鸡翅 - 博客园 (cnblogs.com)

  8.3 构建服务消费者

    1.新建cloud-consumerzk-order80

    2. 改pom

    

 <dependencies>

        <!-- 自己生成的公资源包 -->
        <dependency>
            <groupId>org.example</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>


        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-zookeeper-discovery -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-devtools -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-test -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>


    </dependencies>

    3. yml

server:
  port: 80

spring:
  application:
    name: cloud-consumer-order
  cloud:
    zookeeper:
      connect-string: 192.168.255.129:2181

  4. 添加配置文件

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class ApplicationContextConfig {

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

}

  5. controller

import com.lili.springcloud.entities.CommonResult;
import com.lili.springcloud.entities.Payment;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;

@RestController
@Slf4j
public class OrderZKController {

    public static final String INVOME_URL = "http://cloud-provider-payment";

    @Resource
    private RestTemplate restTemplate;

    @GetMapping("/consumer/payment/zk")
    public String payment (){
      String result = restTemplate.getForObject(INVOME_URL+"/payment/zk",String.class);
      return result;
    }
}

  6. 主启动

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

  7. 测试

    http://localhost:8004/payment/zk

    http://localhost/consumer/payment/zk 

    如下 提供模块和消费者都注册进了zookeeper:

 

 

     

九、 Consul服务注册与发现

  9.1 简介    

    consul也是服务注册中心的一个实现,是由go语言写的。

    官网地址:Intro to Consul | Consul by HashiCorp

           https://www.consul.io/intro

    中文地址: https://www.springcloud.cc/spring-cloud-consul.html
    Consul是一套开源的分布式服务发现和配置管理系统。
    提供了微服务系统中的服务治理,配置中心,控制总线等功能。这些功能中的每一个都可以根据需要单独使用,也可以一起使用以构建全方位的服务网络。

     功能:

      • 服务发现:提供HTTP和DNS两种发现方式
      • 健康监测:支持多种方式,HTTP、TCP、Docker、Shell脚本定制化
      • KV存储:Key、Value的存储方式
      • 多数据中心:Consul支持多数据中心
      • 可视化Web界面

  9.2 安装

      下载连接:Downloads | Consul by HashiCorp

      实际上是不用安装的,在exe文件所在目录打开dos窗口使用即可

      使用开发模式启动:consul agent -dev

      访问8500端口,即可访问首页

      

 

 

   9.3 服务提供者

      1. 新建Module支付服务provider8006

      2. 改pom

      

<dependencies>
        <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-consul-discovery -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-consul-discovery</artifactId>
        </dependency>

        <!-- 自己生成的公资源包 -->
        <dependency>
            <groupId>org.example</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>


        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-devtools -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-test -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>



    </dependencies>

    3. YML

server:
  port: 8006


spring:
  application:
    name: consul-provider-payment
  cloud:
    consul:
      host: localhost
      port: 8500
      discovery:
        service-name: ${spring.application.name}
 

    4. z主启动

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

  5. controller

    

@RestController
@Slf4j
public class PaymentController {

    @Value("${server.port}")
    private String serverPort;

    @GetMapping(value = "/payment/consul")
    public String paymentConsul(){
        return "springcloud with consul: "+serverPort+"\t"+ UUID.randomUUID().toString();
    }
}

  6. 测试

    http://localhost:8006/payment/consul

    

 

 

     

 

 

   9.4 消费者

    1. 新建 cloud-consumerconsul-order80

    2. POM

    

 <dependencies>
        <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-consul-discovery -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-consul-discovery</artifactId>
        </dependency>

        <!-- 自己生成的公资源包 -->
        <dependency>
            <groupId>org.example</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>


        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-devtools -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-test -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>



    </dependencies>

    3. YML

server:
  port: 80


spring:
  application:
    name: consul-consumer-order
  cloud:
    consul:
      host: localhost
      port: 8500
      discovery:
        service-name: ${spring.application.name}
 

  4. 主启动类

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

  5. 配置Bean

@Configuration
public class ApplicationContextConfig {

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

}

  6. controller

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;

@RestController
@Slf4j
public class OrderConsulController {

    public static final String INVOME_URL = "http://consul-provider-payment";

    @Resource
    private RestTemplate restTemplate;

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


}

  7. 测试

    http://localhost/consumer/payment/consul

  9.5 CAP理论

    1. CAP

      • C:Consitency 强一致性
      • A:Available 可用性
      • P:Partition tolerance 分区容错性

 

 

         

 

 

         

 

 

 十、Ribbon负载均衡服务调用

    我们这里提前启动好之前在搭建的 eureka Server 集群(5个模块)

  10.1 Ribbon简介

       1. SpringCloud Ribbon是基于NetfIixRibbon实现的一套客户端负载均衡的工具。

2. 简单的说,Ribbon是Neix发布的开源项目,主要功能是提供客户端的软件负载均衡算法和服务调用。Ribbon客户端组件提供一系列完善的配置项如连接超时,重试等。简单的说,就是在配置文件中列出LoadBalancer(简称LB)后面所有的机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随机连接等)去连接这些机器。我们很容易使用Ribbon实现自定义的负载均衡算法。

       3. LB负载均衡(LoadBalance):就是将用户的请求平摊的分配到多个服务上,从而达到系统的HA(高可用)

       4. 常见的负载均衡软件:Nginx,LVS,硬件F5等

       5. Ribbon本地负载均衡客户端 VS Nginx服务端负载均衡区别:  

      • Nginx是服务器负载均衡(集中式LB),客户端所有请求都会交给nginx,然后由nginx实现转发请求。即负载均衡是由服务端实现的。
      • Ribbon是本地负载均衡(进程内LB),在调用微服务接口时候,会在注册中心上获取注册信息服务列表之后缓存到JVM本地,从而在本地实现RPC远程服务调用技术。
      • 集中式LB:即在服务的消费方和提供方之间使用独立的LB设施(可以是硬件,如F5,也可以是软件,如nginx),由该设施负责把访问请求通过某种策略转发至服务的提供方;
      • 进程内LB:将LB逻辑集成到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选择出一个合适的服务器!Ribbon就属于进程内LB,它只是一个类库,集成于消费方进程,消费方通过它来获取到服务提供方的地址。

        

 

 

       6. Ribbon工作步骤:

      • 第一步先选择Eureka Server,它优先选择在同一个区域内负载较少的server
      • 第二步再根据用户指定的策略,在从server取到的服务注册列表中选择一个地址。
        • 其中Ribbon提供了多种策略:比如轮询、随相和根据响应时间加权。

      7. 上面在eureka时,确实实现了负载均衡机制,那是因为 eureka-client包里面自带着ribbon

        一句话,Ribbon 就是 负载均衡 + RestTemplate 调用。实际上不止eureka的jar包有,zookeeper的jar包,还有consul的jar包都包含了他,就是上面使用的服务调用。

        

 

 

   10.2 RestTemplate

      1. 有两种请求方式:post和get ,还有两种返回类型:object 和 Entity

        getForObject()/getForEntity()

          Object:返回对象响应体中数据转化成的对象,基本上可以理解成json

          Entity:返回对象是ResponseEntity对象,包含了响应中的一些重要信息,比如响应头、响应状态码、响应体等返回的entity.getBody()即得到了Object

 @GetMapping("/consumer/payment/getForEntity/{id}")
    public CommonResult<Payment> getPayment2(@PathVariable("id") Long id){
        //   ResponseEntity是spring中的类
        ResponseEntity<CommonResult> entity = restTemplate.getForEntity(PAYMENT_URL+"/payment/get/"+id,CommonResult.class);

        if(entity.getStatusCode().is2xxSuccessful()){
            return entity.getBody();//获取json
        }else{
            return new CommonResult<>(444,"操作失败");
        }
    }

        postForObject()/postForEntity()

 

  10.3 Ribbon核心组件IRule

      IRule:根据特定算法从服务列表中选择一个要访问的服务

      Ribbon 负载均衡规则类型: 

com.netflix.loadbalancer.RoundRobinRule:轮询
com.netflix.loadbalancer.RandomRule:随机
com.netfIix.IoadbaIancer.RetryRuIe:先按照RoundRobinRule的策略获取服务,如果获取服务失败则在指定时间内会进行重试,获取可用的服务
WeightedResponseTimeRule:对RoundRobinRule的扩展,响应速度越快的实例选择权重越大,越容易被选择
BestAvailableRule:会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务
AvailabilityFilteringRule:先过滤掉故障实例,再选择并发较小的实例
ZoneAvoidanceRule:默认规则,复合判断server所在区域的性能和server的可用性选择服务器

    配置负载均衡规则:

    1. 自定义Rule类

      注意:这个自定义配置类不能放在@ComponentScan 所扫描的当前包下以及子包下,否则我们自定义的这个配置类就会被所有的Ribbon客户端所共享,达不到特殊化定制的目的了。

      创建包:  

        • java
          • com.lili
            • myrule
              • MySelfRule.java
            • springcloud
              • 主启动类
package com.lili.myrule;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MySelfRule {
    @Bean
    public IRule myrule(){
        return new RandomRule(); //负载均衡规则定义为随机
    }
}

         2. 然后在主启动类上添加如下注解 @RibbonClient:

@SpringBootApplication
@EnableEurekaClient
@RibbonClient(name="CLOUD-PAYMENT-SERVICE", configuration = MySelfRule.class)//指定该负载均衡规则对哪个提供者服务使用 , 加载自定义规则的配置类
public class OrderMain80 {
    public static void main(String[] args) {
        SpringApplication.run(OrderMain80.class,args);
    }
}

        3. 测试

        

  10.4 Ribbon负载均衡算法

   

十一、OpenFeign

  11.1 概述

简介:

  这里和之前学的dubbo很像,例如消费者的controller 可以调用提供者的 service层方法,但是不一样,它貌似只能调用提供者的 controller,即写一个提供者项目的controller的接口,消费者来调用这个接口方法,就还是相当于是调用提供者的 controller ,和RestTemplate 没有本质区别

 

Feign能干什么:

  Feign旨在使编写JavaHttp客户端变得更容易。

  前面在使用Ribbon+RestTemplate时,利用RestTemplate对http请求的封装处理,形成了一套模版化的调用方法。但是在实际开发中,由于对服务依赖的调用可能不止一处,往往一个接囗会被多处调用,所以通常都会针对每个微服务自行封装些客户端类来包装这些依赖服务的调用。所以,Feign在此基础上做了进一步封装,由他来帮助我们定义和实现依赖服务接口的定义。在Feign的实现下,我们只需创建一个接口并使用注解的方式来配置它(以前是Dao接口上面标注Mapper注解,现在是一个微服务接口上面标注一个Feign注解即可,即可完成对服务提供方的接口绑定,简化了使用Springcloud Ribbon时,自动封装服务调用客户端的开发量。
  

Feign集成了Ribbon

  利用Ribbon维护了Payment的服务列表信息,并目通过轮询实现了客户端的负载均衡。而与Ribbon不同的是,通过feign只需要定义服务绑定接口目以声明式的方法,优雅而简单的实现了服务调用  

   11.2 消费端搭建

      1. 新建module

      2. pom

  

    <!--openfeign-->
    <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.example</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>1.0-SNAPSHOT</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. yml

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

  4. 主启动

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

  5. service

package com.lili.springcloud.service;

@Component
@FeignClient(value = "CLOUD-PAYMENT-SERVICE")
public interface PaymentFeignService {

    @GetMapping(value = "/payment/get/{id}")
    public CommonResult getPaymentById(@PathVariable("id") Long id);
}
 

  6. controller

package com.lili.springcloud.controller;

import com.lili.springcloud.entities.Payment;
import com.lili.springcloud.service.PaymentFeignService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
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);
    }
}

  7. 测试

    先启动2个eureka集群7001/7002

    再启动2个微服务8001/8002

    启动OpenFeign

    http://localhost/consumer/payment/get/31

    Feign自带负载均衡配置项

  11.3 超时控制

    11.3.1 超时设置,故意设置超时演示出错情况

        OpenFeign默认等待一秒钟,超过后报错

        1. 服务提供方8001故意写暂停程序

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

        2. 服务消费方80添加超时方法PaymentFeignService

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

        3. 服务消费方80添加超时方法OrderFeignController

@GetMapping(value = "/consumer/payment/feign/timeout")
public String paymentFeignTimeout(){
   return paymentFeignService.paymentFeignTimeout();
}

        4. 测试

          http://localhost/consumer/payment/feign/timeout

          出现错误页面:OpenFeign默认等待一秒钟,超过后报错

          

 

       5. YML文件里需要开启OpenFeign客户端超时控制 

        cloud-consumer-feign-order80 

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

    

  11.4 开启日志打印

Feign共了日志打印功能,我们可以诵过配置来调整日志级别,从而了解Feign中Tttp请求的细节。

说白了就是对Feign接口的调用情况进行监控和输出。

日志级别:

      • NONE.默认的,不显示任何日志;
      • BASIC,仅记录请求方法、URL、响应状态码及执行时间;
      • HEADERS:除了BASIC中定义的信息之外,还有请求和响应的头信息
      • FULL:除了HEADERS中定义的信息之外,还有请求和响应的正文及元数据。

    配置步骤:

      1. 在cloud-consumer-feign-order80写一个config配置类

package com.lili.springcloud.config;

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

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

      2. 然后在yml文件中开启日志打印配置

server:
  port: 80
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

logging:
  level:
    # feign日志以什么级别监控哪个接口
    com.atguigu.springcloud.service.PaymentFeignService: debug

      3. 后台日志查看

十二、Hystrix断路器 ---中级部分

  12.1 概述

      1. 简介

官方地址:https://github.com/Netflix/Hystrix/wiki/How-To-Use

"断路器“本身是一种开关装置,当某个服务单元发生故障之后,涌过断路器的故障监控(类似熔断保险丝),向调用方返回一个符合预期的、可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方无法处理的异常,这样就保证了服务调用方的线程不会被长时间、不必要地占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。

         2. 服务雪崩

      多个微服务之间调用的时候,假设微服务A调用微服务B和微服务C,微服务B和微服务C又调用其它的微服务,这就是所谓的“扇出"

        A-->B,C BC-->D

      如果扇出的链路上某个微服务的调用响应时间过长或者不可用,对微服务A的调用就会占用越来越多的系统资源,进而引起系统崩溃,即所谓的雪崩效应   

对于高流量的应用来说,单一的后端依赖可能会导致所有服务器上的所有资源都在几秒钟内饱和。比失败更糟糕的是,这些应用程序还可能导致服务之间的延迟增加,备份队列,线程和其他系统资源紧张,导致整个系统发生更多的级联故障。这些都表示需要对故障和延迟进行隔离和管理,以便单个依赖关系的失败,不能取消整个应用程序或系统。

Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时、异常等,Hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性。

  3. 服务降级 fallback

服务器忙碌或者网络拥堵时,不让客户端等待并立刻返回一个友好提示,fallback。

对方系统不可用了,你需要给我一个兜底的方法,不要耗死。
向调用方返回一个符合预期的、可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方无法处理的异常,这样就保证了服务调用方的线程不会被长时间、不必要地占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。

 

降低发生的情况:

  • 程序运行异常
  • 超时
  • 服务熔断触发服务降级
  • 线程池/信号量打满也会导致服务降级

       4. 服务熔断 break

    • 类比保险丝达到最大服务访问后,直接拒绝访问,拉闸限电,然后调用服务降级的方法并返回友好提示
    • 就是保险丝:服务的降级->进熔断->恢复调用链路

      5. 服务限流 flowlimit

        秒杀高并发等操作,严禁一窝蜂的过来拥挤,大家排队,一秒钟几个,有序进行

  12.2 案例

    前提:首先构建一个eureka作为服务中心的单机版微服务架构 ,这里使用之前eureka Server 7001模块,作为服务中心(修改7001的yml文件)

server:
  port: 7001

eureka:
  instance:
    hostname: eureka7001.com    #eureka服务端的实例名字
  client:
    register-with-eureka: false    #表识不向注册中心注册自己
    fetch-registry: false   #表示自己就是注册中心,职责是维护服务实例,并不需要去检索服务
    service-url:
      # 集群方式
      #defaultZone: http://eureka7002.com:7002/eureka/    #设置与eureka server交互的地址查询服务和注册服务都需要依赖这个地址
      # 单机方式
      defaultZone: http://eureka7001.com:7001/eureka/    #设置与eureka server交互的地址查
  server: # 与client平行
    # 关闭自我保护机制,保证不可用该服务被及时剔除
    enable-self-preservation: false
    eviction-interval-timer-in-ms: 2000

    12.2.1 构建cloud-provider-hystrix-payment8001

      1. 构建cloud-provider-hystrix-payment8001

      2. pom

 <dependencies>
        <!--新增hystrix-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>


        <!-- 自己生成的公资源包 -->
        <dependency>
            <groupId>org.example</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>1.0-SNAPSHOT</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. yml

server:
  port: 8001


eureka:
  client:
    register-with-eureka: true    #表识不向注册中心注册自己
    fetch-registry: true   #表示自己就是注册中心,职责是维护服务实例,并不需要去检索服务
    service-url:
      # defaultZone: http://eureka7002.com:7002/eureka/    #设置与eureka server交互的地址查询服务和注册服务都需要依赖这个地址
      defaultZone: http://eureka7001.com:7001/eureka/
#  server:
#    enable-self-preservation: false
spring:
  application:
    name: cloud-provider-hystrix-payment
#    eviction-interval-timer-in-ms: 2000

  4. 主启动

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

  5. service

package com.lili.springcloud.service;

import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

@Service
public class PaymentService {

    //成功
    public String paymentInfo_OK(Integer id){
        return "线程池:"+Thread.currentThread().getName()+"   paymentInfo_OK,id:  "+id+"\t"+"哈哈哈"  ;
    }

    //失败
    public String paymentInfo_TimeOut(Integer id){
        int timeNumber = 3;
        try { TimeUnit.SECONDS.sleep(timeNumber); }catch (Exception e) {e.printStackTrace();}
        return "线程池:"+Thread.currentThread().getName()+"   paymentInfo_TimeOut,id:  "+id+"\t"+"呜呜呜"+" 耗时(秒)"+timeNumber;
    }

}

  6. controller

package com.lili.springcloud.controller;

import com.lili.springcloud.service.PaymentService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
@Slf4j
public class PaymentController {

    @Resource
    private PaymentService paymentService;

    @Value("${server.port}")
    private String serverPort;

    @GetMapping("/payment/hystrix/ok/{id}")
    public String paymentInfo_OK(@PathVariable("id") Integer id){
        String result = paymentService.paymentInfo_OK(id);
        log.info("*******result:"+result);
        return result;
    }
    @GetMapping("/payment/hystrix/timeout/{id}")
    public String paymentInfo_TimeOut(@PathVariable("id") Integer id){
        String result = paymentService.paymentInfo_TimeOut(id);
        log.info("*******result:"+result);
        return result;
    }
}

  7. 测试

    启动eureka7001

    启动cloud-provider-hystrix-payment8001

    访问:http://localhost:8001/payment/hystrix/ok/31  访问成功速度快

       http://localhost:8001/payment/hystrix/timeout/31  访问成功速度慢

    12.2.2 高并发测试

      1. 安装JMeter

        安装教程:JMeter安装图文及入门教程 - 测试开发喵 - 博客园 (cnblogs.com)

        本机环境: jemeter4.0 jdk8

      2. 利用jmeter模拟高并发

        

 

         

 

         

 

       3. 测试

        http://localhost:8001/payment/hystrix/timeout/31

       4.结果

        两个都在自己转圈圈

从测试可以看出,当模拟的超长请求被高并发以后,访问普通的小请求速率也会被拉低。

tomcat的默认工作线程数被打满了,没有多余的线程来分解压力和处理。

上面还是服务8001自己测试,加入此时外部的消费者80页来访问,那消费者只能干等,最终导致消费端80不满意,服务端8001直接被拖死。

测试可见,当启动高并发测试时,消费者访问也会变得很慢,甚至出现超时报错。

演示问题:8001端口自身已经被打满了,80还要访问8001,80也响应慢了。

        • 超时导致服务器变慢(转圈):超时不再等待
        • 出错(宕机或程序运行出错):出错要有兜底

       5. 解决思路

        • 对方服务(8001)超时了,调用者(80)不能一直卡死等待,必须有服务降级
        • 对方服务(8001) down机了,调用者(80)不能一直卡死等待,必须有服务降级
        • 对方服务(8001) OK,调用者(80)自己出故障或有自我要求(自己的等待时间小于服务提供者),自己处理降级

    12.2.3 看热闹不嫌弃事大,80新建加入

       1. 新建cloud-consumer-feign-hystrix-order80

      2. POM

  <dependencies>
        <!--新增hystrix-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
        <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.example</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>1.0-SNAPSHOT</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. YML

server:
  port: 80


eureka:
  client:
    register-with-eureka: false    #表识不向注册中心注册自己
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/

      4. 主启动

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

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

      5. service

package com.lili.springcloud.service;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;

@Component
@FeignClient(value="CLOUD-PROVIDER-HYSTRIX-PAYMENT")
public interface PaymentHystrixService {
    //成功
    @GetMapping("/payment/hystrix/ok/{id}")
    public String paymentInfo_OK(Integer id);

    //失败
    @GetMapping("/payment/hystrix/timeout/{id}")
    public String paymentInfo_TimeOut(Integer id);

}

      6. controller

package com.lili.springcloud.controller;

import com.lili.springcloud.service.PaymentHystrixService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
@Slf4j
public class OrderHystrixController {

    @Resource
    private PaymentHystrixService paymentHystrixService;

    @Value("${server.port}")
    private String serverPort;

    @GetMapping("/payment/hystrix/ok/{id}")
    public String paymentInfo_OK(@PathVariable("id") Integer id){
        String result = paymentHystrixService.paymentInfo_OK(id);
        return result;
    }
    @GetMapping("/payment/hystrix/timeout/{id}")
    public String paymentInfo_TimeOut(@PathVariable("id") Integer id){
        String result = paymentHystrixService.paymentInfo_TimeOut(id);
        return result;
    }
}

      7. 正常测试

        http://localhost/consumer/payment/hystrix/ok/31

      8. 高并发测试

        

  12.2.4 解决方案一   服务降级

    8001fallback测试

    1. 首先 对 8001的service进行配置(对容易超时的方法进行配置) 

      设置自身调用时间为5s

//失败
    public String paymentInfo_TimeOut(Integer id){
        int timeNumber = 5;
        try { TimeUnit.SECONDS.sleep(timeNumber); }catch (Exception e) {e.printStackTrace();}
        return "线程池:"+Thread.currentThread().getName()+"   paymentInfo_TimeOut,id:  "+id+"\t"+"呜呜呜"+" 耗时(秒)"+timeNumber;
    }

    2.  服务端降级@HystrixCommand

首先 对 8001的service进行配置(对容易超时的方法进行配置) :

降级配置:@HystrixCommand,可以在里面指定超时/出错的回调方法,作为兜底方法

提供方 service方法:演示超时

代码说明:    

        设置超时/异常回调方法:  fallbackMethod = "paymentInfo_TimeOutHandler"
        设置超时时间3s:
@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="3000")
  //失败
    @HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler",commandProperties = {
            @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",//时间单位
                    value="3000")})//超时时间
    public String paymentInfo_TimeOut(Integer id){
        int timeNumber = 5;
        try { TimeUnit.SECONDS.sleep(timeNumber); }catch (Exception e) {e.printStackTrace();}
        return "线程池:"+Thread.currentThread().getName()+"   paymentInfo_TimeOut,id:  "+id+"\t"+"呜呜呜"+" 耗时(秒)"+timeNumber;
    }

    public String paymentInfo_TimeOutHandler(Integer id){
        return "线程池:  "+Thread.currentThread().getName()+"  8001系统繁忙或者运行报错,请稍后再试,id:  "+id+"\t"+"o(╥﹏╥)o";
    }

    3. 主启动类激活

    添加新注解@EnableCircuitBreaker

package com.lili.springcloud;

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

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

    4. 测试

    80fallback测试

      上面的案例是服务端降级,现在我们服务端处理3s,然后返回。但是消费端等1s就等不住了,这时候就需要消费端也有降级方法

      80的降级。原理是一样的,上面的@HystrixCommand降级可以放在服务端,也可以放在消费端。但一般放在客户端。

      1. 更改yml

feign:
  hystrix:
    enabled: true #如果处理自身的容错就开启。开启方式与生产端不一样。

      2. 主启动加注解

        @EnableHystrix

      3. controller

        然后对 80 进行服务降级:很明显 service 层是接口,所以我们对消费者,在它的 controller 层进行降级。继续使用@HystrixCommand注解指定方法超时后的回调方法

package com.lili.springcloud.controller;

import com.lili.springcloud.service.PaymentHystrixService;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
@Slf4j
public class OrderHystrixController {

    @Resource
    private PaymentHystrixService paymentHystrixService;

    @Value("${server.port}")
    private String serverPort;

    @GetMapping("/payment/hystrix/ok/{id}")
    public String paymentInfo_OK(@PathVariable("id") Integer id){
        String result = paymentHystrixService.paymentInfo_OK(id);
        return result;
    }


    @GetMapping("/payment/hystrix/timeout/{id}")
    @HystrixCommand(fallbackMethod = "paymentTimeOutFallbackMethod",
            commandProperties = {
                    @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",
                            value="1500") })//只等1.5s
    public String paymentInfo_TimeOut(@PathVariable("id") Integer id){
        String result = paymentHystrixService.paymentInfo_TimeOut(id);
        return result;
    }

    public String paymentTimeOutFallbackMethod(@PathVariable("id") Integer id) {
        return "我是消费者80,对方支付系统繁忙请10秒钟后再试 或者 自己运行出错请检查自己,o(╥﹏╥)o";
    }
}

     4. 测试:

      http://localhost:80/consumer/payment/hystrix/timeout/31 

    目前存在的问题: 

      • 每个业务方法对应一个兜底的方法,代码膨胀
      • 兜底方法和业务方法没有分开

    解决方法:

      针对问题1:每个业务方法对应一个兜底的方法,代码膨胀

        我们定义一个全局的兜底方法,这样就不用每个方法都得写兜底方法了

            全局兜底@DefaultProperties
package com.lili.springcloud.controller;

import com.lili.springcloud.service.PaymentHystrixService;
import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
@Slf4j
@DefaultProperties(defaultFallback = "payment_Global_FallbackMethod") //添加了这个
public class OrderHystrixController {

    @Resource
    private PaymentHystrixService paymentHystrixService;

    @Value("${server.port}")
    private String serverPort;

    @GetMapping("/consumer/payment/hystrix/ok/{id}")
    public String paymentInfo_OK(@PathVariable("id") Integer id){
        String result = paymentHystrixService.paymentInfo_OK(id);
        return result;
    }


    @GetMapping("/consumer/payment/hystrix/timeout/{id}")
    @HystrixCommand(fallbackMethod = "paymentTimeOutFallbackMethod",
            commandProperties = {
                    @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",
                            value="1500") })//只等1.5s
    public String paymentInfo_TimeOut(@PathVariable("id") Integer id){
        String result = paymentHystrixService.paymentInfo_TimeOut(id);
        return result;
    }

    public String paymentTimeOutFallbackMethod(@PathVariable("id") Integer id) {
        return "我是消费者80,对方支付系统繁忙请10秒钟后再试 或者 自己运行出错请检查自己,o(╥﹏╥)o";
    }

    // 下面是全局fallback方法
    public String payment_Global_FallbackMethod(){
        return "Global异常处理信息,请稍后再试,/(ㄒoㄒ)/~~";
    }
}

      针对问题2:兜底方法和业务方法没有分开

        1. 首先上面再controller中添加的 @HystrixCommand 和 @DefaultProperties 两个注解去掉,就是保持原来的controller

package com.lili.springcloud.controller;

import com.lili.springcloud.service.PaymentHystrixService;
import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
@Slf4j
@DefaultProperties(defaultFallback = "payment_Global_FallbackMethod") //添加了这个
public class OrderHystrixController {

    @Resource
    private PaymentHystrixService paymentHystrixService;

    @Value("${server.port}")
    private String serverPort;

    @GetMapping("/consumer/payment/hystrix/ok/{id}")
    public String paymentInfo_OK(@PathVariable("id") Integer id){
        String result = paymentHystrixService.paymentInfo_OK(id);
        return result;
    }


    @GetMapping("/consumer/payment/hystrix/timeout/{id}")
    public String paymentInfo_TimeOut(@PathVariable("id") Integer id){
        String result = paymentHystrixService.paymentInfo_TimeOut(id);
        return result;
    }

    public String paymentTimeOutFallbackMethod(@PathVariable("id") Integer id) {
        return "我是消费者80,对方支付系统繁忙请10秒钟后再试 或者 自己运行出错请检查自己,o(╥﹏╥)o";
    }

    // 下面是全局fallback方法
    public String payment_Global_FallbackMethod(){
        return "Global异常处理信息,请稍后再试,/(ㄒoㄒ)/~~";
    }
}

 

        2. 根据cloud-consumer-feign-hystrix-order80已经有的PaymentHystrixService接口,重新新建一个类(PaymentFallbackService)实现该接口,统一为接口里面的方法进行异常处理

@Component
public class PaymentFallbackService implements PaymentHystrixService {
    @Override
    public String paymentInfo_OK(Integer id) {
        return "-----PaymentFallbackService fall back-paymentInfo_OK , (┬_┬)";
    }

    @Override
    public String paymentInfo_TimeOut(Integer id) {
        return "-----PaymentFallbackService fall back-paymentInfo_TimeOut , (┬_┬)";
    }
}
 

        3. yml

# 用于服务降级 在注解@FeignClient 中添加 fallback 属性值
feign: hystrix: enabled:
true #如果处理自身的容错就开启。开启方式与生产端不一样。

        4.修改service 接口:

package com.lili.springcloud.service;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;

@Component
@FeignClient(value="CLOUD-PROVIDER-HYSTRIX-PAYMENT",fallback = PaymentFallbackService.class)
public interface PaymentHystrixService {
    //成功
    @GetMapping("/payment/hystrix/ok/{id}")
    public String paymentInfo_OK(Integer id);

    //失败
    @GetMapping("/payment/hystrix/timeout/{id}")
    public String paymentInfo_TimeOut(Integer id);

}

      5.  测试

        单个eureka先启动7001

        PaymentHystrixMain8001启动

        正常访问测试:http://localhost/consumer/payment/hystrix/ok/31

        故意关闭微服务8001

        客户端自己调用提升:此时服务端provider已经down了,但是我们做了服务降级处理,让客户端在服务端不可用时也会获得提示信息而不会挂起耗死服务器

 

  12.2.5 解决方案二   服务熔断

    1. 简介

服务雪崩

多个微服务之间调用的时候,假设微服务A调用微服务B和微服务C,微服务B和微服务C有调用其他的微服务,这就是所谓的”扇出”,如扇出的链路上某个微服务的调用响应式过长或者不可用,对微服务A的调用就会占用越来越多的系统资源,进而引起系统雪崩,所谓的”雪崩效应”

Hystrix:

Hystrix是一个用于分布式系统的延迟和容错的开源库。在分布式系统里,许多依赖不可避免的调用失败,比如超时、异常等,Hystrix能够保证在一个依赖出问题的情况下,不会导致整个服务失败,避免级联故障,以提高分布式系统的弹性。

断路器:

“断路器”本身是一种开关装置,当某个服务单元发生故障监控(类似熔断保险丝),向调用方法返回一个符合预期的、可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方法无法处理的异常,这样就保证了服务调用方的线程不会被长时间、不必要地占用,从而避免了故障在分布式系统中的蔓延。乃至雪崩。

服务熔断:

熔断机制是应对雪崩效应的一种微服务链路保护机制,当扇出链路的某个微服务不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回”错误”的响应信息。

当检测到该节点微服务响应正常后恢复调用链路,在SpringCloud框架机制通过Hystrix实现,Hystrix会监控微服务见调用的状况,当失败的调用到一个阈值,默认是5秒内20次调用失败就会启动熔断机制,熔断机制的注解是@HystrixCommand

    2. 实操

      1) 修改cloud-provider-hystrix-payment8001

        修改PaymentService

//服务熔断
@HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback",commandProperties = {
        @HystrixProperty(name = "circuitBreaker.enabled",value = "true"),  //是否开启断路器
        @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"),   //请求次数
        @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000"),  //时间范围
        @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "60"), //失败率达到多少后跳闸
})
public String paymentCircuitBreaker(@PathVariable("id") Integer id){
    if (id < 0){
        throw new RuntimeException("*****id 不能负数");
    }
    String serialNumber = IdUtil.simpleUUID();

    return Thread.currentThread().getName()+"\t"+"调用成功,流水号:"+serialNumber;
}
public String paymentCircuitBreaker_fallback(@PathVariable("id") Integer id){
    return "id 不能负数,请稍候再试,(┬_┬)/~~     id: " +id;
}
 

        修改controller

//===服务熔断
@GetMapping("/payment/circuit/{id}")
public String paymentCircuitBreaker(@PathVariable("id") Integer id){
    String result = paymentService.paymentCircuitBreaker(id);
    log.info("*******result:"+result);
    return result;
}

    3. 详细说明

      涉及到断路器的三个重要参数快照时间窗、请求总数阀值、错误百分比阀值

1:快照时间窗:断路器确定是否打开需要统计一些请求和错误数据而统计的时间范围就是快照时间窗,默认为最近的10秒。
2:请求总数阀值:在快照时间窗内,必须满足请求总数阀值才有资格熔断。默认为20,意味着在10秒内,如果该hystrix命令的调用次数不足20次,即使所有的请求都超时或具他原因失败,断路器都不会打开。
3:错误百分比阀值:当请求总数在快照时间窗内超过了阀值,上日发生了30次调用,如果在这30次调用中,有15次发生了超时异常,也就是超过50%的错误百分比,在默认设定50%阀值情况,这时候就会将断路器打开。

4. 测试

      开启7001和8001

      正常:http://localhost:8001/payment/circuit/31

      错误:http://localhost:8001/payment/circuit/-31

      结果:多次出错调用fallback后,调用正常的也出错调用fallback。过了一会又自己恢复了。

    5. 在yml中进行熔断配置

hystrix:
command:
default:
circuitBreaker:
  enabled: true
  requestVolumeThreshold: 10
  sleepWindowInMilliseconds: 10000
  errorThresholdPercentage: 60

     6. Hystrix DashBoard

      除了隔离依赖服务的调用以外,Hystrix还提供了准实时的调用监控(HystrixDashboard),Hystrix会持续地记录所有通过Hystrix发起的请求的执行信息,并以统计报表和图形的形式展示给用户,包括每秒执行多少请求多少成功,多少失败等。Netflix通过hystrix-metrics-event-stream实现了对以上指标的监控。SpringCloud也提供了HystrixDashboard的整合,对监控内容转化成可视化界面。

      1. 新建模块cloud-consumer-hystrix-dashboard9001

      2.pom

<dependencies>
        <!--新增hystrix dashboard-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</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. YML

server:
  port: 9001

      4. 主启动类添加hystrix dashboard注解

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

        5. 所有Provider微服务提供类(8001/8002/8003)都需要监控依赖配置

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

      6. 测试

      启动9001

      访问:http://localhost:9001/hystrix

      效果

 

 

     7. 断路器演示

      1) 修改cloud-provider-hystrix-payment8001

        新版本Hystrix需要在主启动类MainAppHystrix8001中指定监控路径

package com.lili.springcloud;

import com.netflix.hystrix.contrib.metrics.eventstream.HystrixMetricsStreamServlet;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;

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

@Bean
public ServletRegistrationBean getServlet(){
HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
registrationBean.setLoadOnStartup(1);
registrationBean.addUrlMappings("/hystrix.stream");
registrationBean.setName("HystrixMetricsStreamServlet");
return registrationBean;
}

}

        2)测试

          启动7001

          启动9001

          启动8001

          9001监控8001

 

 

           测试地址:http://localhost:8001/payment/circuit/31

                http://localhost:8001/payment/circuit/-31

                上述测试通过

十三、GateWay

  13.1 简介

      zuul 1.x:  https://github.com/Netflix/zuul/wiki

      当前gateway:https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.1.RELEASE/reference/html/

      概述:

    • SpringCloud Gateway是SpringCloud的一个全新项目,基于Spring5.O+Springboot 2.0和ProjectReactor等技术开发的网关,它旨在为微服务架构提供一种简单有效的统一的API路由管理方式。
    • SpringCloudGateway作为SpringCloud生态系统中的网关,目标是替代Zuul,在SpringCloud2.0以上版本中,没有对新版本的zuul2.0以上最新高性能版本进行集成,仍然还是使用的Zuul 1.x非Reactor模式的老版本。而为了提升网关的性能,SpringCloud Gateway是基于WebFlux框架实现的,而webFlux框架底层则使用了高性能的Reactor模式通信框架Netty
    • springCloudGateway的目标提供统一的路由方式且基于Filter链的方式提供了网关基本的功能,例如:安全,监控/指标,和限流。

    为何选择gateway:

    • 一方面因为Zuul 1.0已经进入了维护阶段,而且Gateway是SpringCloud团队研发的是亲儿子产品,值得信赖。而且很多功能比zull用起来都简单便捷。
    • Gateway是基于异步非阻塞模型上进行开发的,性能方面不需要担心。虽然Netflix早就发布了最新的Zuul2.x,但Spring Cloud貌似没有整合计划。而且Netflix相关组件都宣布进入维护期;不知前景如何?

      

 

 

     SpringCloudGateway与Zuul的区别:

    1、Zuul 1.x是一个基于阻塞I/O的APIGateway

2、Zuul 1.x基于ServIet2.5使用阻塞架构,它不支持任何长连接(如WebSocket),Zuul的设计模式和Nginx较像,每次I/O操作都是从

工作线程中选择一个执行,请求线程阻塞到工作线程完成,但是差别是Nginx用C++实现,Zuul用Java实现,而JVM本身会有第一次加载较慢的情况,使得Zuul的性能相对较差。

3、Zuul 2.x理念更先进想基于Netty非阻塞和支持长连接,但SpringCloud目前还没有整合。Zuul2.x的性能较Zuul1.x有较大提升。在性能方面,根据官方提供的基准测试,SpringCloudGateway的RPS(每秒请求数)是Zuul的1.6倍。

4、SpringCloudGateway建立在SpringFramework5、ProjectReactor和SpringB00t2.之上,使用非阻塞API

5、SpringCloudGateway还支持WebSocket,并且与Spring紧密集成拥有更好的开发体验

Gateway特性:  

  • 基于SpringFramework5,ProjectReactor和SpringBoot 2.0进行构建;
  • 动态路由:能够匹任何请求属性;
  • 可以对路由指定Predicate(断言)和Filter(过滤器)·
  • 集成Hystrix的断路器功能;
  • 集成SpringCloud服务发现功能;
  • 易于编写的Predicate(断言)和Filter(过滤器)·
  • 请求限流功能;
  • 支持路径重写。

GateWay的三大核心概念:

  • Route(路由):路由是构建网关的基本模块,它由ID、目标URI、一系列的断言和过滤器组成,如果断言为true则匹配该路由
  • Predicate(断言):参考的是Java8的java.util.function.predicate。开发人员可以匹配HTTP请求中的所有内容(例如请求头或请求参数),如果请求与断言相匹配则进行路由
    •   匹配条件
  • Filter(过滤):指的是spring框架中GatewayFilter的实例,使用过滤器,可以在请求被路由前或者之后对请求进行修改。
  • 例子:通过断言虽然进来了,但老师要罚站10min(过滤器操作),然后才能正常坐下听课

 

 

       

客户端向Spring Cloud Gateway发出请求。然后在Gateway HandlerMapping中找到与请求相匹配的路由,将其发送到Gateway Web Handler

Handler再通过指定的过滤器链来将请求发送到我们实际的服务执行业务逻辑,然后返回。

过滤器之间用虚线分开是因为过滤器可能会在发送代理请求之前(Pre)或之后(post)执行业务逻辑。

Filter在pre类型的过滤器可以做参数校验,权限校验,流量监听,日志输出,协议转换等,

在post类型的过滤器中可以做响应内容、响应头的修改,日志的输出,流量监控等有着非常重要的作用。

13.2 入门配置

      1. 新建模块 cloud-gateway-gateway9527

      2. pom

 <dependencies>
        <!--新增gateway-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <!-- 自己生成的公资源包 -->
        <dependency>
            <groupId>org.example</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</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. yml

server:
  port: 9527
spring:
  application:
    name: cloud-gateway
  cloud:
    gateway:
      routes:
        - id: payment_routh #路由的ID,没有固定规则但要求唯一,建议配合服务名
          uri: http://localhost:8001   #匹配后提供服务的路由地址
          predicates:
            - Path=/payment/get/**   #断言,路径相匹配的进行路由

        - id: payment_routh2
          uri: http://localhost:8001
          predicates:
            - Path=/payment/lb/**   #断言,路径相匹配的进行路由


eureka:
  instance:
    hostname: cloud-gateway-service
  client:
    service-url:
      register-with-eureka: true
      fetch-registry: true
      defaultZone: http://eureka7001.com:7001/eureka
 

    4. 主启动

package com.lili.springcloud;

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

    5. 测试

      启动7001      启动8001    启动9527网关

      如果启动9527报错,删除pom中的web和actual依赖

      访问:添加网关前  http://localhost:8001/payment/get/31

          添加网关后  http://localhost:9527/payment/get/31

    6. 上面是yml方式配置的路由,也有用config方式配置的

package com.lili.springcloud.config;

import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class GateWayConfig{
    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder){
        RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();

        // 分别是id,本地址,转发到的地址
        routes.route("path_route_atguigu",
                r -> r.path("/guonei").uri("http://news.baidu.com/guonei")
                    ).build();//JDK8新特性

        return routes.build();
    }
}

 13.3 通过微服务名实现动态路由

这里所谓的动态配置就是利用服务注册中心,来实现 负载均衡 的调用 多个微服务。

默认情况下gateway会根据注册中心注册的服务列表,以注册中心上微服务名为路径创建动态路由进行转发,从而实现动态路由的功能

注意,这是GateWay 的负载均衡

    对yml进行配置

server:
  port: 9527

spring:
  application:
    name: cloud-gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true # 开启从注册中心动态创建路由的功能,利用微服务名进行路由
      routes:
        - id: payment_routh  # 路由ID , 没有固定的规则但要求唯一,建议配合服务名
         # uri: http://localhost:8001  # 匹配后提供服务的路由地址 
          uri: lb://CLOUD-PROVIDER-SERVICE # lb 属于GateWay 的关键字,代表是动态uri,即代表使用的是服务注册中心的微服务名,它默认开启使用负载均衡机制 
          predicates:
            - Path=/payment/get/**  # 断言,路径相匹配的进行路由

        - id: payment_routh2  # 路由ID , 没有固定的规则但要求唯一,建议配合服务名
        #  uri: http://localhost:8001  # 匹配后提供服务的路由地址
          uri: lb://CLOUD-PROVIDER-SERVICE
          predicates:
            - Path=/payment/lb/**  # 断言,路径相匹配的进行路由

# uri: lb://CLOUD-PROVIDER-SERVICE  解释:lb 属于GateWay 的关键字,代表是动态uri,即代表使用的是服务注册中心的微服务名,它默认开启使用负载均衡机制 

eureka:
  instance:
    hostname: cloud-gateway-service
  client: #服务提供者provider注册进eureka服务列表内
    service-url:
      register-with-eureka: true
      fetch-registry: true
      defaultZone: http://eureka7001.com:7001/eureka

    测试:

      开启8001 8002 7001 9527进行测试  

  13.4 Predicate的使用

      注意到上面yml配置中,有个predicates 属性值。

1 After Route Predicate
2 Before Route Predicate
3 Between Route Predicate
4 Cookie Route Predicate
5 Header Route Predicate
6 Host Route Predicate
7 Method Route Predicate
8 Path Route Predicate
9 Query Route Predicate

应用:

spring:
  application:
    name: cloud-gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由
      routes:
        - id: payment_routh #payment_route    #路由的ID,没有固定规则但要求唯一,建议配合服务名
          #uri: http://localhost:8001          #匹配后提供服务的路由地址
          uri: lb://cloud-payment-service #匹配后提供服务的路由地址
          predicates:
            - Path=/payment/get/**         # 断言,路径相匹配的进行路由

        - id: payment_routh2 #payment_route    #路由的ID,没有固定规则但要求唯一,建议配合服务名
          #uri: http://localhost:8001          #匹配后提供服务的路由地址
          uri: lb://cloud-payment-service #匹配后提供服务的路由地址
          predicates:
            - Path=/payment/lb/**         # 断言,路径相匹配的进行路由
            #- After=2020-02-21T15:51:37.485+08:00[Asia/Shanghai]
            #- Cookie=username,zzyy
            #- Header=X-Request-Id, \d+  # 请求头要有X-Request-Id属性并且值为整数的正则表达式

      predicates下面可以有多个属性,表示多个属性与操作为true这个路由才生效

        predicates:
            - Path=/payment/lb/** 
            - After=2020-02-21T15:51:37.485+08:00[Asia/Shanghai] # 会在这个时间之后这个路由才生效
predicates:
    - Cookie=username,zzyy,ch.p # 需要有这个Cookie值才生效  最后一个是正则表达式
    # curl 地址 --cookie "a=b"

 

 

 predicates:
        - Host=**.somehost.org,**.anotherhost.org  # Host Route Predicate Factory接收一组参数,一组匹配的域名列表,这个模板是一个ant分隔的模板,用.号作为分隔符。它通过参数中的主机地址作为匹配规则
- Method=GET
- Query=username, \d+ #要有参数名称并且是正整数才能路由
  predicates:
            - Path=/payment/lb/**   #断言,路径相匹配的进行路由
            #- After=2020-03-08T10:59:34.102+08:00[Asia/Shanghai]
            #- Cookie=username,zhangshuai #并且Cookie是username=zhangshuai才能访问
            #- Header=X-Request-Id, \d+ #请求头中要有X-Request-Id属性并且值为整数的正则表达式
            #- Host=**.atguigu.com
            #- Method=GET
            #- Query=username, \d+ #要有参数名称并且是正整数才能路由

 

  1.3 5 Filter的使用

    种类:

      已有的过滤器:

        GatewayFilter:https://docs.spring.io/spring-cloud-gateway/docs/2.2.5.RELEASE/reference/html/#gatewayfilter-factories
        GlobalFilter:https://docs.spring.io/spring-cloud-gateway/docs/2.2.5.RELEASE/reference/html/#global-filters

spring:
  cloud:
    gateway:
      routes:
      - id: add_request_header_route
        uri: https://example.org
        filters:
        - AddRequestHeader=X-Request-red, blue # 添加了个请求头X-Request-red

      自定义过滤器:

        2个主要接口:implements GlobalFilter,Ordered

package com.lili.springcloud.filter;

import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.Date;

@Component
@Slf4j
public class MyLogGateWayFilter implements GlobalFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange,
                             GatewayFilterChain chain) {

        log.info("********** come in MyLogGateWayFilter:  "+new Date());

        String uname = exchange.getRequest().getQueryParams().getFirst("uname");

        //合法性检验
        if(uname == null) {
            log.info("*******用户名为null,非法用户,o(╥﹏╥)o,请求不被接受");
            //设置 response 状态码   因为在请求之前过滤的,so就算是返回NOT_FOUND 也不会返回错误页面
            exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
            //完成请求调用
            return exchange.getResponse().setComplete();
        }

        return chain.filter(exchange);//过滤链放行
    }

    // 返回值是加载顺序,一般全局的都是第一位加载
    @Override
    public int getOrder() {
        return 0;
    }
}

      测试:http://localhost:9527/payment/lb?uname=z3

十四、config分布式配置中心

  14.1 概述

    是什么:

      微服务意味着要将单应用中的业务拆分成一个个子服务,每个服务的粒度相对较小,因此系统中会出现大量的服务。由于每个服务都需要必要的配置信息才能运行,所以一套集中式的、动态的配置管理设施是必不可少的。

      springCloud提供了ConfigServer来解决这个问题,我们每一个微服务自己带着一个application.yml,上百个配置文件的管理。比如数据库的信息,我们可以写到一个统一的地方。

 

 

       SpringCloud Config是什么:Spring Cloud Config为微服务架构中的微服务提供集中化的外部配置支持,配置服务器为各个不同微服务应用的所有环境提供了一个中心化的外部配置

    怎么玩:

      SpringCloud Config分为服务端和客户端两部分。   

  • 服务端也称为分布式配置中心,它是一个独立的微服务应用,用来连接配置服务器并为客户端提供获取配置信息,加密/解密信息等访问接口
  • 客户端则是通过指定的配置中心来管理应用资源,以及与业务相关的配置内容,并在启动的时候从配置中心取和加载配置信息配置服务器默认采用git来存储配置信息,这样就有助于对环境配置进行版本管理,并且可以通过git客户端工具来方便的管理和访问配置内容

     能干嘛: 

  • 集中管理配置文件
  • 不同环境不同配置,动态化的配置更新,分环境部署比如dev/test/prod/beta/release
  • 运行期间动态调整配置,不再需要在每个服务部署的机器上编写配置文件,服务会向配置中心统一拉取配置自己的信息
  • 当配置发生变动时,服务不需要重启即可感知到配置的变化并应用新的配置
  • 将配置信息以REST接囗的形式暴露

14.2 config服务端配置

      1. 用你自己的账号在Github上新建一个名为sprincloud-config的新Repository

      2.在容器中添加文件   

        在git根目录下创建

      • 开发环境:config-dev.yml
      • 生产环境:config-pro.yml
      • 测试环境:config-test.tml

      3. 克隆到本地

        git clone https://github.com/LiLi8080/sprincloud-config.git

      4. 新建Module模块cloud-config-center-3344它既为Cloud的配置中心模块cloudConfig Center

        1. 新建模块 cloud-config-center-3344

        2. pom

<dependencies>


        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-config-server</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!-- 自己生成的公资源包 -->
        <dependency>
            <groupId>org.example</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>1.0-SNAPSHOT</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. yml

server:
  port: 3344
spring:
  application:
    name: cloud-config-center
  cloud:
    config:
      server:
        git:
          uri:  填写你自己的github路径
          search-paths:
            - springcloud-config
      label: master
eureka:
  client:
    service-url:
      defaultZone:  http://localhost:7001/eureka

      4. 主启动 @EnableConfigServer

@SpringBootApplication
@EnableConfigServer   //关键注解
public class ConfigCenterMain3344 { // 注意先去把Eureka启动起来
    public static void main(String[] args) {
        SpringApplication.run(ConfigCenterMain3344.class,args);
    }
}

      5. C:\Windows\System32\drivers\etc\hosts文件添加映射

        127.0.0.1 config-3344.com

      6. 测试

        启动微服务3344,访问 http://config-3344.com:3344/master/config-dev.yml 文件(注意,要提前在git上弄一个这文件)

           7. 文件命名和访问的规则

      

      • label:分支branch
      • name:服务名
      • profiles:环境dev/test/prod

  14.3 客户端测试与配置

      1. 新建模块 cloud-config-client-3355

      2. pom

<dependencies>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!-- 自己生成的公资源包 -->
        <dependency>
            <groupId>org.example</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>1.0-SNAPSHOT</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. yml

      将Client模块下的application.yml文件改为bootstrap.yml,删掉application.yml,将Client模块下的application.yml文件改为bootstrap.yml,删掉application.yml.

      appllication.yml是用户级的资源配置项

      bootstrap.yml 是系统级的,优先级更加高

SpringCloud会创建一个"Bootstrap Context"作为Spring应用的ApplicationContext的父上下文。初始化的时候,Bootstrap Context负责从外部源加载配置属性并解析配置。这两个上下文共享一个从外部获取的Environment

Bootstrap 属性有高优先级,默认情况下,它们不会被本地配置覆盖。BootstrapContext和ApplicationContext、有着不同的约定,所以新增了一个bootstrap.yml文件,保证BootstrapContext和ApplicationContext配置的分离。

 

bootstrap.yml文件:

coding
server:
port: 3355

spring:
application:
name: config-client
cloud:
config:
label: master
name: config
profile: dev
uri: http://localhost:3344
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka

 

 

 

      

 

 

     4. 主启动

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

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

    5. controller

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ConfigClientController {

    @Value("${config.info}")
    private String configInfo;

    @GetMapping("/configInfo")
    public String getConfigInfo(){
        return configInfo;
    }
}

    6. 测试

      启动Config配置中心3344微服务并自测:

        http://config-3344.com:3344/master/config-dev.yml

      启动3355作为Client准备访问

        http://localhost:3355/configInfo

  14.4 动态刷新

    14.4.1 问题

       Linux运维修改GitHub上的配置文件内容做调整:比如修改config-dev.yml提交

         刷新3344,发现ConfigServer服务端配置中心立刻响应,得到最新值了

         刷新3355,发现ConfigClient客户端没有任何响应,拿到的还是旧值

         客户端3355没有变化除非自己重启或者重新加载,才能拿到最新值

         难到每次运维修改配置文件,客户端都需要重启??噩梦       

就是github上面配置更新了,config Server 项目上是动态更新的,但是,client端的项目中的配置,目前还是之前的,它不能动态更新,必须重启才行。

    14.4.2 动态刷新

      1.client3355端一定要有actuator依赖:

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

      2. client 端增加 yml 配置如下,即在 bootstrap.yml 文件中:

server:
  port: 3355

spring:
  application:
    name: config-client
  cloud:
    config:
      label: master
      name: config
      profile: dev
      uri: http://localhost:3344
eureka:
  client:
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka
      
# 暴露监控端点
management:
  endpoints:
    web:
      exposure:
        include: "*"

      3. controller 上加@RefreshScope

package com.lili.springcloud.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RefreshScope
public class ConfigClientController {

    @Value("${config.info}")
    private String configInfo;

    @GetMapping("/configInfo")
    public String getConfigInfo(){
        return configInfo;
    }
}

      4. 到此为止,配置已经完成,但是测试客户端 localhost:3355/configInfo 仍然不能动态刷新,还是旧值(也就是说环境变量里的还是旧值),需要下一步。

        向 client 端发送一个 POST 请求

        如 curl -X POST “http://localhost:3355/actuator/refresh”

          两个必须:1.必须是 POST 请求,2.请求地址:http://localhost:3355/actuator/refresh

      5. 新问题

        但是又有一个问题,就是要向每个微服务客户端发送一次POST请求,当微服务数量庞大,又是一个新的问题。

        能否广播,一次通知,处处生效?(还要求不要全广播,差异化管理,定点清除,20台只有18台更新)

        就有下面的消息总线!

 

十五、SpringCloud Bus 消息总线

  15.1 简介

      Bus:

        spring cloud Bus配置spring cloud Config使用可以实现配置的动态刷新

        spring cloud bus是用来将分布式系统的节点与轻量级消息系统链接起来的框架,它整合了java的事件处理机制和消息中间件的功能。

        spring cloud bus目前支持RabbitMQ和Kafka(因为是主题订阅)

      什么是总线

在微服务架构的系统中,通常会使用轻量级的消息代理来构建一个共用的消息主题,并让系统中所有微服务实例都连接上来。由于该主题中产生的消息会被所有实例监听和消费,所以称它为消息总线。在总线上的各个实例,都可以方便地广播一些需要让貝他连接在该主题上的实例都知道的消息。

       基本原理

ConfigClient实例都监听MQ中同一个topic主题(默认是springCloud Bus)。当一个服务刷新数据的时候,它会把这个信息放入到Topic中,这样其它监听同一topic的服务就能得到通知,然后去更新自身的配置

下图原盈利是就是给其中一台发送我们的刷新POST,他刷新完后给Bus发消息,然后Bus通过消息中间件发送给BC进行更新

      

 

       Bus能管理和传输分布式系统间的消息,就像一个分布式执行器,可用于广播状态更改、事件推送等,也可以当做微服务间的通信通道

  15.2 rabbitmq安装

    本机环境:

      win10 + rabbitmq 3.7.14 +erlang20.3

    1. 进入 rabbitMQ安装目录的sbin目录下,打开cmd窗口,执行 【rabbitmq-plugins enable rabbitmq_management
    2. 访问【http://localhost:15672/】,输入密码和账号:默认为guest

  15.3 SpringCloud Bus动态刷新全局广播

      1. 前提:

        • 必须先具有良好的RabbitMQ环境
        • 演示广播效果,增加复杂度,再以3355为模板再制作一个3366

       2. 新建3366

        1. 新建moduel

        2, pOm

<dependencies>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!-- 自己生成的公资源包 -->
        <dependency>
            <groupId>org.example</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>1.0-SNAPSHOT</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. yml

    

server:
  port: 3366

spring:
  application:
    name: config-client
  cloud:
    config:
      label: master
      name: config
      profile: dev
      uri: http://localhost:3344
eureka:
  client:
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka
management:
  endpoints:
    web:
      exposure:
        include: "*"

    4. 主启动

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

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

    5. controller

import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RefreshScope
public class ConfigClientController {

    @Value("${server.port}")
    private String serverPort;

    @Value("${config.info}")
    private String configInfo;


    @GetMapping("/configInfo")
    public String getConfigInfo(){
        return "serverPort:"+serverPort+"\t\n\n configInfo: "+configInfo;
    }


}

    3. 设计思想

      • 1)利用消息总线触发一个客户端/bus/refresh,而刷新所有客户端的配置
      • 2)利用消息总线触发一个服务端ConfigServer的/bus/refres端点,从而刷新所有客户端的配置

       图二的架构显然更加合适,图一不适合的原因如下

      • 打破了微服务的职责单一性,因为微服务本身是业务模块,它本不应该承担配置刷新的职责
      • 破坏了微服务各节点的对等性。
      • 有一定的局限性“例如,微服务在迁移时,它的网络地址常常会发生变化,此时如果想要做到自动刷新,那就会增加更多的

    4. 给cloud-config-center-3344配置中心服务端添加消息总线支持

      pom:

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

      yml:

server:
  port: 3344
spring:
  application:
    name: cloud-config-center
  cloud:
    config:
      server:
        git:
          uri: https://github.com/LiLi8080/sprincloud-config.git
          search-paths:
            - springcloud-config
      label: master

rabbitmq:
  host: localhost
  port: 5672
  username: guest
  password: guest

eureka:
  client:
    service-url:
      defaultZone:  http://localhost:7001/eureka

management:
  endpoints:
    web:
      exposure:
        include: 'bus-refresh'

    5. 给cloud-config-center-3355客户端添加消息总线支持

    pom:

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

    yml:

server:
  port: 3355

spring:
  application:
    name: config-client
  cloud:
    config:
      label: master
      name: config
      profile: dev
      uri: http://localhost:3344

rabbitmq:
  host: localhost
  port: 5672
  username: guest
  password: guest

eureka:
  client:
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka

# 暴露监控端点
management:
  endpoints:
    web:
      exposure:
        include: "*"

    6. 给cloud-config-center-3366客户端添加消息总线支持

    pom

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

    yml:

server:
  port: 3366

spring:
  application:
    name: config-client
  cloud:
    config:
      label: master
      name: config
      profile: dev
      uri: http://localhost:3344


rabbitmq:
  host: localhost
  port: 5672
  username: guest
  password: guest

eureka:
  client:
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka
management:
  endpoints:
    web:
      exposure:
        include: "*"

  7 测试

  15.4 Bus动态刷新定点通知

新的需求:指定具体某一个实例(的参数)生效而不是全部,一些是最新值,一些是旧值

公式:  http://localhost:配置中心的端口号/actuator/bus-refresh/{destination}

例子:  curl -X POST "http//localhost:3344/actuator/bus-refresh/config-client:3355

     即微服务名称+端囗号

        /bus/refresh请求不再发送到具体的服务实例上,而是发给configserver并通过destination参数类指定需要更新配置的服务或实例

     我们这里以刷新运行在3355端口上的config-client为例

        只通知3355
     不通知3366

十六、SpringCloud Stream消息驱动

  16.1 简介

    Stream

      需求:消息中间件很多,希望向上抽象一个接口,我们不关心底层用的是什么消息中间件

        屏蔽底层消息中间件的差异,降低切换成本,统一消息的编程模型

        就像 JDBC 形成一种规范,统一不同数据库的接口

    

  16.2 案例

    1.  工程中新建三个子模块

      cloud-stream-rabbitmq-provider8801,作为生产者进行发消息模块

        1. 新建模块 cloud-stream-rabbitmq-provider8801

        2. POM

 <dependencies>


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

        <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-eureka-server -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

        <!-- 自己生成的公资源包 -->
        <dependency>
            <groupId>org.example</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>


        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-devtools -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-test -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>



    </dependencies>

        3. yml

server:
  port: 8801

spring:
  application:
    name: cloud-stream-provider
  cloud:
    stream:
      binders: # 在此处配置要绑定的rabbitmq的服务信息;
        defaultRabbit: # 表示定义的名称,用于于binding整合
          type: rabbit # 消息组件类型
          environment: # 设置rabbitmq的相关的环境配置
            spring:
              rabbitmq:
                host: localhost
                port: 5672
                username: guest
                password: guest
      bindings: # 服务的整合处理
        output: # 这个名字是一个通道的名称
          destination: studyExchange # 表示要使用的Exchange名称定义
          content-type: application/json # 设置消息类型,本次为json,文本则设置“text/plain”
          binder: defaultRabbit  # 设置要绑定的消息服务的具体设置

eureka:
  client: # 客户端进行Eureka注册的配置
    service-url:
      defaultZone: http://localhost:7001/eureka
  instance:
    lease-renewal-interval-in-seconds: 2 # 设置心跳的时间间隔(默认是30秒)
    lease-expiration-duration-in-seconds: 5 # 如果现在超过了5秒的间隔(默认是90秒)
    instance-id: send-8801.com  # 在信息列表时显示主机名称
    prefer-ip-address: true     # 访问的路径变为IP地址

        4. 主启动类StreamMQMain8801

package com.lili.springcloud;

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

        5. 业务类

package com.lili.springcloud.service;

public interface IMessageProvider
{
    public String send();
}
 
package com.lili.springcloud.service;

import com.lili.springcloud.service.IMessageProvider;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.integration.support.MessageBuilderFactory;
import org.springframework.messaging.MessageChannel;
import org.springframework.integration.support.MessageBuilder;
import javax.annotation.Resource;
import org.springframework.cloud.stream.messaging.Source;

import javax.annotation.Resource;
import java.util.UUID;


@EnableBinding(Source.class) //定义消息的推送管道
public class MessageProviderImpl implements IMessageProvider
{
    @Resource
    private MessageChannel output; // 消息发送管道

    @Override
    public String send()
    {
        String serial = UUID.randomUUID().toString();
        output.send(MessageBuilder.withPayload(serial).build());
        System.out.println("*****serial: "+serial);
        return null;
    }
}

        6. 测试

          启动7001eureka

          启动rabbitmq   (http://localhost:15672/)

          启动8801

          访问:http://localhost:8801/sendMessage

      cloud-stream-rabbitmq-consumer8802,作为消息接收模块

        1. 新建模块cloud-stream-rabbitmq-consumer8802

        2. pom

 <dependencies>


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

        <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-eureka-server -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

        <!-- 自己生成的公资源包 -->
        <dependency>
            <groupId>org.example</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>


        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-devtools -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-test -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>



    </dependencies>

      3. yml

server:
  port: 8802

spring:
  application:
    name: cloud-stream-provider
  cloud:
    stream:
      binders: # 在此处配置要绑定的rabbitmq的服务信息;
        defaultRabbit: # 表示定义的名称,用于于binding整合
          type: rabbit # 消息组件类型
          environment: # 设置rabbitmq的相关的环境配置
            spring:
              rabbitmq:
                host: localhost
                port: 5672
                username: guest
                password: guest
      bindings: # 服务的整合处理
        input: # 这个名字是一个通道的名称
          destination: studyExchange # 表示要使用的Exchange名称定义
          content-type: application/json # 设置消息类型,本次为json,文本则设置“text/plain”
          binder: defaultRabbit  # 设置要绑定的消息服务的具体设置

eureka:
  client: # 客户端进行Eureka注册的配置
    service-url:
      defaultZone: http://localhost:7001/eureka
  instance:
    lease-renewal-interval-in-seconds: 2 # 设置心跳的时间间隔(默认是30秒)
    lease-expiration-duration-in-seconds: 5 # 如果现在超过了5秒的间隔(默认是90秒)
    instance-id: receive-8802.com  # 在信息列表时显示主机名称
    prefer-ip-address: true     # 访问的路径变为IP地址

      4. 主启动

package com.lili.springcloud;


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

      5. 业务类

package com.lili.springcloud.controller;

import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.annotation.StreamListener;
import org.springframework.cloud.stream.messaging.Sink;
import org.springframework.messaging.Message;

@Component
@EnableBinding(Sink.class)
public class ConsumerController {

    @Value("${server.port}")
    private String serverPort;

    @StreamListener(Sink.INPUT) // 消费者
    public void input(Message<String> message){
        System.out.println("消费者1号,serverport: " + serverPort + ",接受到的消息:" + message.getPayload());
    }
}

      6. 测试测试8801发送8802接收消息

      cloud-stream-rabbitmq-consumer8803,作为消息接收模块

        和8802一样

   2. 配置分组消费

      问题:

        依照8802,clone出来一份运行8803 

        当运行时,会有两个问题。

          第一个问题,两个消费者都接收到了消息,这属于重复消费。例如,消费者进行订单创建,这样就创建了两份订单,会造成系统错误

          第二个问题,消息持久化问题

      解决:

        8002 8003f放置在同一个组中

        微服务应用放置于同一个group中,就能够保证消息只会被其中一个应用消费一次。不同的组是可以消费的,同一个组内会发生竞争关系,只有其中一个可以消费。

      8802/8803都变成不同组,group两个不同

        8002yml

bindings: # 服务的整合处理
        input: # 这个名字是一个通道的名称
          destination: studyExchange # 表示要使用的Exchange名称定义
          content-type: application/json # 设置消息类型,本次为json,文本则设置“text/plain”
          binder: defaultRabbit  # 设置要绑定的消息服务的具体设置
          group:  atguiguA

        8003yml

 bindings: # 服务的整合处理
        input: # 这个名字是一个通道的名称
          destination: studyExchange # 表示要使用的Exchange名称定义
          content-type: application/json # 设置消息类型,本次为json,文本则设置“text/plain”
          binder: defaultRabbit  # 设置要绑定的消息服务的具体设置
          group:  atguiguB

      8802/8803都变成相同组,group两个相同

      8802yml

bindings: # 服务的整合处理
        input: # 这个名字是一个通道的名称
          destination: studyExchange # 表示要使用的Exchange名称定义
          content-type: application/json # 设置消息类型,本次为json,文本则设置“text/plain”
          binder: defaultRabbit  # 设置要绑定的消息服务的具体设置
          group:  atguiguA

      8803yml

bindings: # 服务的整合处理
        input: # 这个名字是一个通道的名称
          destination: studyExchange # 表示要使用的Exchange名称定义
          content-type: application/json # 设置消息类型,本次为json,文本则设置“text/plain”
          binder: defaultRabbit  # 设置要绑定的消息服务的具体设置
          group:  atguiguA

    持久化:

      加上group配置,就已经实现了消息的持久化。

      8802没加group时,8801发送消息后再启动8802,8802不会去消费之前8801发送的消息

      8803加group,8801发送消息后再启动8803,8803会去消费之前8801发送的消息

十七、Sleuth分布式请求链路追踪

  17.1 简介

      问题:

        分布式请求链路跟踪,超大型系统。需要在微服务模块极其多的情况下,比如80调用8001的,8001调用8002的,这样就形成了一个链路,如果链路中某环节出现了故障,我们可以使用Sleuth进行链路跟踪,从而找到出现故障的环节。

        在微服务框架中,一个由客户端发起的请求在后端系统中会经过多个不同的的服务节点调用来协同产生最后的请求结果,每一个前段请求都会形成一条杂的分布式服务调用链路,链路中的亻刊可一环出现高延时或错误都会引起整个请求最后的失败。

sleuth 负责跟踪,而zipkin负责展示。

zipkin 下载地址: http://dl.bintray.com/openzipkin/maven/io/zipkin/java/zipkin-server/2.12.9/zipkin-server-2.12.9-exec.jar

  17.2 环境配置

      1. zipkin下载

        下载:zipkin-server-2.12.9.exec.jar

        运行jar: java -jar zipkin-server-2.12.9-exec.jar

        访问:http://localhost:9411/zipkin/

  17.3 案例

      使用之前的 提供者8001 和 消费者80

      分别给他们引入依赖:

    <!-- 引入sleuth + zipkin -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zipkin</artifactId>
        </dependency>

      yml增加配置:

spring:
  zipkin:
    base-url: http://localhost:9411  # zipkin 地址
  sleuth:
    sampler:
      # 采样率值 介于0-1之间 ,1表示全部采集
      probability: 1

      8001 controller

@GetMapping("/payment/zipkin")
    public String paymentZipkin()
    {
        return "hi ,i'am paymentzipkin server fall back,welcome to atguigu,O(∩_∩)O哈哈~";
    }

    80controller

// ====================> zipkin+sleuth
    @GetMapping("/consumer/payment/zipkin")
    public String paymentZipkin()
    {
        String result = restTemplate.getForObject("http://localhost:8001"+"/payment/zipkin/", String.class);
        return result;
    }

    测试

        4.依次启动eureka7001/8001/80   80调用8001几次测试下

        5.打开浏览器访问:http:localhost:9411

      

 十八、SpringCloud Alibaba -- 高级部分

  18.1 简介

    18.1.1 spring netflix进入维护模式

        什么是维护模式:spring cloud团队将不会再向模块添加新功能,我们将修复block级别的bug以及安全问题,我们也会考虑并审查社区的小型pull request。我们打算继续支持这些模块,知道Greenwich版本被普遍采用至少一年,SpringCloud Netflix将不再开发新的组件

        以下spring cloud netflix模块和响应的starter将进入维护模式

spring-cloud-netflix-archaius
spring-cloud-netflix-hystrix-contract
spring-cloud-netflix-hystrix-dashboard
spring-cloud-netflix-hystrix-stream
spring-cloud-netflix-hystrix
spring-cloud-netflix-ribbon
spring-cloud-netflix-turbine-stream
spring-cloud-netflix-turbine
spring-cloud-netflix-zuul
这不包括Eureka或并发限制模块

我们都知道SpringCloud版本迭代是比较快的,因而出现了很多重大ISSUE都还来不及Flix就又推另一个RELEASE了。进入维护模式意思就是目前以及以后一段时间SpingCloud Netflix提供的报务和功能就这么多了,不在开发新的组件和功能了。以后将以雏护和Merge分支Full Request为主

    18.1.2 历史

      • alibaba出了dubbo,停更
      • spring结合netflix整出spring cloud。又停更了
      • alibaba又出马了得出springcloud alibaba

     18.1.3 spring cloud alibaba带来了什么

        主要功能:

服务限流降级:默认支持 WebServlet、WebFlux, OpenFeign、RestTemplate、Spring Cloud Gateway, Zuul, Dubbo 和 RocketMQ 限流降级功能的接入,可以在运行时通过控制台实时修改限流降级规则,还支持查看限流降级 Metrics 监控。
服务注册与发现:适配 Spring Cloud 服务注册与发现标准,默认集成了 Ribbon 的支持。
分布式配置管理:支持分布式系统中的外部化配置,配置更改时自动刷新。
消息驱动能力:基于 Spring Cloud Stream 为微服务应用构建消息驱动能力。
分布式事务:使用 @GlobalTransactional 注解, 高效并且对业务零侵入地解决分布式事务问题。。
阿里云对象存储:阿里云提供的海量、安全、低成本、高可靠的云存储服务。支持在任何应用、任何时间、任何地点存储和访问任意类型的数据。
分布式任务调度:提供秒级、精准、高可靠、高可用的定时(基于 Cron 表达式)任务调度服务。同时提供分布式的任务执行模型,如网格任务。网格任务支持海量子任务均匀分配到所有 Worker(schedulerx-client)上执行。
阿里云短信服务:覆盖全球的短信服务,友好、高效、智能的互联化通讯能力,帮助企业迅速搭建客户触达通道。

引入依赖:

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

        组件:

Sentinel:把流量作为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。
Nacos:一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。
RocketMQ:一款开源的分布式消息系统,基于高可用分布式集群技术,提供低延时的、高可靠的消息发布与订阅服务。
Dubbo:Apache Dubbo™ 是一款高性能 Java RPC 框架。
Seata:阿里巴巴开源产品,一个易于使用的高性能微服务分布式事务解决方案。
Alibaba Cloud OSS: 阿里云对象存储服务(Object Storage Service,简称 OSS),是阿里云提供的海量、安全、低成本、高可靠的云存储服务。您可以在任何应用、任何时间、任何地点存储和访问任意类型的数据。
Alibaba Cloud SchedulerX: 阿里中间件团队开发的一款分布式任务调度产品,提供秒级、精准、高可靠、高可用的定时(基于 Cron 表达式)任务调度服务。
Alibaba Cloud SMS: 覆盖全球的短信服务,友好、高效、智能的互联化通讯能力,帮助企业迅速搭建客户触达通道。

 

 十九、Nacos

  19.1 简介

        nacos(NAming COnfiguration Service):服务注册和配置中心

Nacos = Eureka + Config + Bus

替代Eureka做服务注册中心

替代Config做服务配置中心

       github地址: https://github.com/alibaba/Nacos

       Nacos 地址: https://nacos.io/zh-cn/

服务注册与服务框架CAP模型控制台管理社区活跃度
Eureka AP高可用 支持 低(2.x版本闭源)
Zookeeper CP一致 支持
Consul CP 支持
Nacos AP(可以切换) 支持

 

       nacos可以切换 AP 和 CP ,可使用如下命令切换成CP模式:

        curl -X PUT '$NACOS_SERVER:8848/nacos/v1/ns/operator/switches?entry=serverMode&value=CP'

   19.2 下载

下载地址: https://github.com/alibaba/nacos/releases/tag/1.1.4

直接下载网址: https://github.com/alibaba/nacos/releases/download/1.1.4/nacos-server-1.1.4.zip

解压安装包,直接运行bin目录下的startup.cmd

命令运行成功后直接访问http://localhost:8848/nacos

可访问 : 【 http://localhost:8848/nacos/index.html】地址,默认账号密码都是nacos

   19.3 Nacos作为服务注册中心演示

    19.3.1 基于Nacos的服务提供者

      1. 新建module cloudalibaba-provider-payment9001

      2. 父pom

<!--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>

      3. 本pom

<dependencies>
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-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>

    4. yml

server:
  port: 9001

spring:
  application:
    name: nacos-payment-provider
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #配置Nacos地址

management:
  endpoints:
    web:
      exposure:
        include: '*'

    5. 主启动

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

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

    6. controller

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;


@RestController
public class PaymentController
{
    @Value("${server.port}")
    private String serverPort;

    @GetMapping(value = "/payment/nacos/{id}")
    public String getPayment(@PathVariable("id") Integer id)
    {
        return "nacos registry, serverPort: "+ serverPort+"\t id"+id;
    }
}
 

    7. 测试

      http://lcoalhost:9001/payment/nacos/1

    8. 为了下一章节演示nacos的负载均衡,参照9001新建9002

   19.3.2 基于Nacos的消费者

    1. 新建module

    2. pom

<dependencies>
        <!--SpringCloud ailibaba nacos -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!-- 自己生成的公资源包 -->
        <dependency>
            <groupId>org.example</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>1.0-SNAPSHOT</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. yml

server:
  port: 83


spring:
  application:
    name: nacos-order-consumer
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848


service-url:
  nacos-user-service: http://nacos-payment-provider

    4,。主启动

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;


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

    5. config

package com.lili.springcloud.config;

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;


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

      6. controller

package com.lili.springcloud.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;


@RestController
@Slf4j
public class OrderNacosController
{
    @Resource
    private RestTemplate restTemplate;

    @Value("${service-url.nacos-user-service}")
    private String serverURL;

    @GetMapping(value = "/consumer/payment/nacos/{id}")
    public String paymentInfo(@PathVariable("id") Long id)
    {
        return restTemplate.getForObject(serverURL+"/payment/nacos/"+id,String.class);
    }

}
 

      7. 测试

        启动9001 查看nacos服务列表,里面有该项服务

        

     

   19.4 Nacos作为服务配置中心演示

      Nacos作为配置中心-基础配置

      1. cloudalibaba-config-nacos-client3377

      2. pom

<dependencies>
    <!--nacos-config-->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
    </dependency>
    <!--nacos-discovery-->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    <!--web + actuator-->
    <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. bootstrap.yml

server:
  port: 3377

spring:
  application:
    name: nacos-config-client
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #服务注册中心地址
      config:
        server-addr: localhost:8848 #配置中心地址
        file-extension: yaml #指定yaml格式的配置
        

    4. application.yml

spring:
  profiles:
    active: dev

    5. 主启动

package com.lili.springcloud.alibaba;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;


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

    6. controller

import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;


@RestController
@RefreshScope
public class ConfigClientController
{
    @Value("${config.info}")
    private String configInfo;

    @GetMapping("/config/info")
    public String getConfigInfo() {
        return configInfo;
    }
}

    7. Nacos中的匹配规则

      在 Nacos Spring Cloud 中,dataId 的完整格式如下:(就是说在nacos端我们怎么命名文件的)

        ${prefix}-${spring.profiles.active}.${file-extension}

       prefix 默认为 spring.application.name 的值,也可以通过配置项 spring.cloud.nacos.config.prefix来配置。

spring.profiles.active 即为当前环境对应的 profile,详情可以参考 Spring Boot文档。 注意:当 spring.profiles.active 为空时,对应的连接符 - 也将不存在,dataId 的拼接格式变成 ${prefix}.${file-extension}

file-exetension 为配置内容的数据格式,可以通过配置项 spring.cloud.nacos.config.file-extension 来配置。目前只支持 properties 和 yaml 类型。(注意nacos里必须使用yaml)

     8. nacos进行修改

       nacos里不要写成yml,要写成yaml

      当修改配置值,会发现 3377 上也已经修改,Nacos自带自动刷新功能!

      

       9. 测试

        启动前需要在nacos客户端-配置管理-配置管理栏目下有没有对应的yaml配置文件

        运行cloud-config-nacos-client3377的主启动类

        http://localhost:3377/config/info

        修改下Nacos中的yaml配置文件,再次调用查看配置的接口,就会发现配置已经刷新

      Nacos作为配置中心-分类配置

        DataID方案:

          指定spring.profile.active和配置文件的DataID来使不同环境下读取不同的配置

          默认空间+默认分组+新建dev和test两个DataID

          通过spring.profile.active属性就能进行多环境下配置文件的读取

        Group方案

        Namespace方案

 

   19.5 Nacos集群和持久化配置(重要)

      官网架构图:

      

       前提:

一台linux虚拟机:nginx服务器(虚拟ip),3个nacos服务,一个mysql数据库。

nginx的安装参考之前学,使用 ContOs7 至少需要安装gcc库,不然无法编译安装【yum install gcc】

nacos下载linux版本的 tar.gz 包:https://github.com/alibaba/nacos/releases/download/1.1.4/nacos-server-1.1.4.tar.gz

       Nacos支持三种部署模式 https://nacos.io/zh-cn/docs/deployment.html

      • 单机模式 - 用于测试和单机试用。
      • 集群模式 - 用于生产环境,确保高可用。
      • 多集群模式 - 用于多数据中心场景。

      单机模式支持mysql:

        在0.7版本之前,在单机模式时nacos使用嵌入式数据库(derby,他的pom里有这个依赖)实现数据的存储,不方便观察数据存储的基本情况。0.7版本增加了支持mysql数据源能力,具体的操作步骤:

        • 1.安装数据库,版本要求:5.6.5+

        • 2.初始化mysql数据库。

            • 创建数据库nacos_config

            • 找到数据库初始化文件:nacos/conf/nacos-mysql.sql

            • 再nacos_config数据库下执行该sql脚本
        • 3.修改IDEA中nacos/conf/application.properties文件(切换数据库),增加支持mysql数据源配置(目前只支持mysql),添加mysql数据源的url、用户名和密码
        •   
          spring.datasource.platform=mysql
           
          db.num=1
          db.url.0=jdbc:mysql://127.0.0.1:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
          db.user=root
          db.password=mysql

          4. 重新启动nacos,可以看到是个全新的空记录界面,以前是记录进derby

      •   Linux版Nacos+MySQL生产环境配置

        •   预计需要,1个nginx+3个nacos注册中心+1个mysql
        •       1. Nacos linux安装
          •   下载nacos-server-1.1.4.tar.gz
          •         解压:tar -zxvf  nacos-server-1.1.4.tar.gz
            •   解压后会生成一个nacos的文件夹
      •    集群配置步骤(重点)

          • 1. 在mysql中新建数据库nacos_config,并执行nacos/conf/下的nacos-mysql.sql
          • 2. 将conf/application.properties进行备份
            •   cp application.properties application.properties.init
          • 3. 修改application.properties内容,添加如下代码
          • spring.datasource.platform=mysql
             
            db.num=1
            db.url.0=jdbc:mysql://127.0.0.1:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
            db.user=root
            db.password=mysql

            4. 拷贝conf 

            • cp cluster.conf.example cluster.conf

          • 5. 修改conf
            •  查看本机ip: hostname -i
                • 我的ip为 192.168.78.12

              

 

 

             6. stratup.sh 备份       

              cp startup.sh startup.sh.bk

            7. 修改startup.sh

 

             8. 执行

 

            9. Nginx的配置,由它作为负载均衡器

            1. 在linux上安装nginx

              安装教程:(491条消息) Centos6.5 安装Nginx_zhouzhiwengang的专栏-CSDN博客

            2. 修改nginx.conf

            

 

             3. 启动nginx

              ./nginx -c ../conf/nginx.conf

 

 

cp startup.sh startup.sh.bk

 

posted @ 2021-12-04 16:18  微~粒  阅读(372)  评论(0)    收藏  举报