周陽 SpringCloud


SpringCloud介紹

img

image-20210407170707704

SpringClound

就是分布式微服務架構的一站式解決方案,是多種微服務架構落地技術的集合體,俗稱微服務全家桶。

功能:

服務注冊與發現、服務調用、服務熔斷、負載均衡、服務降級、服務消息隊列、配置中心管理、服務網關、

服務監控、全鏈路追蹤、自動化構建部署、服務定時任務調度操作

版本對應https://start.spring.io/actuator/info

Cloud升級與技術選型:

Cloud升級

父工程的創建

路線:

父工程的創建

創建

image-20210408193923573

編輯器編碼

image-20210408193812398

開啟注解

image-20210408193825474

選擇java版本

image-20210408194328379

在編輯器中隱藏不想顯示的文件后綴類型

image-20210408193906937

pom.xml

<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">  <modelVersion>4.0.0</modelVersion>  <groupId>com.atguigu.springcloud</groupId>  <artifactId>cloud2020</artifactId>  <version>1.0-SNAPSHOT</version>  <packaging>pom</packaging><!--1-->  <name>Maven</name>  <!-- FIXME change it to the project's website -->  <url>http://maven.apache.org/</url>  <inceptionYear>2001</inceptionYear>  <distributionManagement>    <site>      <id>website</id>      <url>scp://webhost.company.com/www/website</url>    </site>  </distributionManagement>  <!--統一管理jar包版本-->  <properties>    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>    <maven.compiler.source>1.8</maven.compiler.source>    <maven.compiler.target>1.8</maven.compiler.target>    <junit.version>4.12</junit.version>    <log4j.version>1.2.17</log4j.version>    <lombok.version>1.16.18</lombok.version>    <mysql.version>5.1.47</mysql.version>    <druid.version>1.1.16</druid.version>    <mybatis.spring.boot.version>1.3.0</mybatis.spring.boot.version>  </properties>  <!--子模塊繼承之后,提供作用:鎖定版本+子module不用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>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>

最終父工程:

image-20210408203151556

Model 創建與步驟

image-20210409214641100

建model

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>cloud2020</artifactId>        <groupId>com.atguigu.springcloud</groupId>        <version>1.0-SNAPSHOT</version>    </parent>    <modelVersion>4.0.0</modelVersion>    <artifactId>cloud-provider-payment8001</artifactId>    <dependencies>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-web</artifactId>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-actuator</artifactId>        </dependency>        <dependency>            <groupId>org.mybatis.spring.boot</groupId>            <artifactId>mybatis-spring-boot-starter</artifactId>        </dependency>        <dependency>            <groupId>com.alibaba</groupId>            <artifactId>druid-spring-boot-starter</artifactId>            <version>1.1.10</version>        </dependency>        <!--mysql-connector-java-->        <dependency>            <groupId>mysql</groupId>            <artifactId>mysql-connector-java</artifactId>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-jdbc</artifactId>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-devtools</artifactId>            <scope>runtime</scope>            <optional>true</optional>        </dependency>        <dependency>            <groupId>org.projectlombok</groupId>            <artifactId>lombok</artifactId>            <optional>true</optional>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-test</artifactId>            <scope>test</scope>        </dependency>    </dependencies></project>

寫yml

server:  port: 8001  #服務端口號spring:  application:    name: cloud-provider-service  #服務名  datasource:    type: com.alibaba.druid.pool.DruidDataSource      #當前數據源操作類型    driver-class-name: org.gjt.mm.mysql.Driver        #mysql驅動包    url: jdbc:mysql://localhost:3306/db2019?useUnicode=true&characterEncoding-utr-8&useSSL=false    username: root    password: 3333mybatis:  mapper-locations: classpath:mapper/*.xml  type-aliases-package: com.atguigu.springcloud.entities       #所有Entity別名類所在包

主啟動類

package com.atguigu.springcloud;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplicationpublic class PaymentMain8001 {    public static void main(String[] args) {        SpringApplication.run(PaymentMain8001.class,args);    }}

業務類

建表sql
CREATE TABLE `payment`(	`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID',	`serial` varchar(200) DEFAULT '',	PRIMARY KEY (`id`)) ENGING=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8
entities
package com.atguigu.springcloud.entities;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;import java.io.Serializable;@Data@AllArgsConstructor@NoArgsConstructorpublic class Payment implements Serializable { /*繼承用於自動化部署*/    private Long id;    private String serial;}
//Json封裝體CommentResult,傳給前端,判斷編碼是否成功,成功才顯示package com.atguigu.springcloud.entities;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;@Data@AllArgsConstructor@NoArgsConstructorpublic class CommonResult<T> {    private Integer code;    private String message;    private T data;    public CommonResult(Integer code,String message) { //泛型:如果裝的payment 返回payment,裝的order 返回order        this(code,message,null);    }    //new CommonResult(200,'成功')。。?}
dao
package com.atguigu.springcloud.dao;import com.atguigu.springcloud.entities.Payment;import org.apache.ibatis.annotations.Mapper;import org.apache.ibatis.annotations.Param;@Mapperpublic interface PaymentDao {    int create(Payment payment);    Payment getPaymentById(@Param("id") Long id);}
<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapper        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"        "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.atguigu.springcloud.dao.PaymentDao">    <insert id="create" parameterType="Payment" useGeneratedKeys="true" keyProperty="id" >        insert into payment(serial) values (#{serial})    </insert>    <resultMap id="BaseResultMap" type="com.atguigu.springcloud.entities.Payment">        <id column="id" property="id" jdbcType="BIGINT" />        <result column="serial" property="serial" jdbcType="VARCHAR" />    </resultMap>    <select id="getPaymentById" parameterType="Long" resultMap="BaseResultMap" >        select * from payment where id=#{id}    </select></mapper>
service
package com.atguigu.springcloud.service;import com.atguigu.springcloud.entities.Payment;import org.apache.ibatis.annotations.Param;public interface PaymentService {    int create(Payment payment);    Payment getPaymentById(@Param("id") Long id);}
package com.atguigu.springcloud.service.impl;import com.atguigu.springcloud.dao.PaymentDao;import com.atguigu.springcloud.entities.Payment;import com.atguigu.springcloud.service.PaymentService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import javax.annotation.Resource;@Servicepublic class PaymentServiceImpl implements PaymentService {    @Resource    private PaymentDao paymentDao;    public int create(Payment payment) {        return paymentDao.create(payment);    }    public Payment getPaymentById(Long id) {        return paymentDao.getPaymentById(id);    }}
controller
package com.atguigu.springcloud.controller;import com.atguigu.springcloud.entities.CommonResult;import com.atguigu.springcloud.entities.Payment;import com.atguigu.springcloud.service.PaymentService;import lombok.extern.slf4j.Slf4j;import org.springframework.web.bind.annotation.*;import javax.annotation.Resource;@RestController@RequestMapping("/payment")@Slf4jpublic class PaymentController {    @Resource    private PaymentService paymentService;    //只傳給前端CommonResult,不需要前端了解其他的組件    @PostMapping(value = "/create")    public CommonResult create(Payment payment) {        int result = paymentService.create(payment);        log.info("*****插入結果:"+result);        if(result > 0) {            return new CommonResult(200,"插入數據成功",result);        }else {            return new CommonResult(444,"插入數據失敗",null);        }    }    @GetMapping(value = "/get/{id}")    public CommonResult getPaymentById(@PathVariable("id") Long id) {        Payment payment = paymentService.getPaymentById(id);        log.info("*****查詢結果:"+payment);        if(payment != null) {            return new CommonResult(200,"查詢成功",payment);        }else {            return new CommonResult(444,"沒有對應記錄,查詢ID:"+id,null);        }    }}

devtools熱部署

image-20210409214726625

模塊

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

父工程

  <!--熱部署-->  <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>

IDEA設置

image-20210409214329373

ctrl+shift+alt+/

image-20210409214433230

image-20210409214413380

重啟IDEA

消費者模塊

image-20210410180546928

創建model

普通Maven

改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>cloud2020</artifactId>        <groupId>com.atguigu.springcloud</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>        <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></project>

寫yml

server:  port: 86

主啟動類

package com.atguigu.springcloud;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplicationpublic class OrderMain80 {    public static void main(String[] args) {        SpringApplication.run(OrderMain80.class,args);    }}

entities

package com.atguigu.springcloud.entities;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;@Data@AllArgsConstructor@NoArgsConstructorpublic class CommonResult<T>{    private Integer code;    private String message;    private T data;    public CommonResult(Integer code,String message) {        this(code,message,null);    }}
package com.atguigu.springcloud.entities;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;import sun.rmi.runtime.Log;@Data@AllArgsConstructor@NoArgsConstructorpublic class Payment {    private Log id;    private String serial;}

config

package com.atguigu.springcloud.config;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.web.client.RestTemplate;@Configurationpublic class ApplicationContextConfig {    @Bean    public RestTemplate getRestTemplate() {        return new RestTemplate();    }}

controller

package com.atguigu.springcloud.controller;import com.atguigu.springcloud.entities.CommonResult;import com.atguigu.springcloud.entities.Payment;import lombok.extern.slf4j.Slf4j;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@Slf4jpublic class OrderController {    private static final String PAYMENT_URL="http://localhost:8001";    @Resource    private RestTemplate restTemplate;    @GetMapping("/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);    }}

image-20210410181056211

重構:

common模塊

package com.atguigu.springcloud.entities;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;@Data@AllArgsConstructor@NoArgsConstructorpublic class Payment {    private Long id;    private String serial;}
package com.atguigu.springcloud.entities;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;@Data@AllArgsConstructor@NoArgsConstructorpublic class CommonResult<T> {  //泛型:如果裝的payment 返回payment,裝的order 返回order    private Integer code;    private String message;    private T data;    public CommonResult(Integer code,String message){        this(code,message,null);    }}
<?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>springcloud2021</artifactId>        <groupId>com.atguigu</groupId>        <version>1.0-SNAPSHOT</version>    </parent>    <modelVersion>4.0.0</modelVersion>    <artifactId>cloud-common</artifactId>    <dependencies>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-devtools</artifactId>            <scope>runtime</scope>            <optional>true</optional>        </dependency>        <dependency>            <groupId>org.projectlombok</groupId>            <artifactId>lombok</artifactId>            <optional>true</optional>        </dependency>        <dependency>            <groupId>cn.hutool</groupId>            <artifactId>hutool-all</artifactId>            <version>5.1.0</version>        </dependency>    </dependencies></project>

核心知識學習

Eureka

注冊中心 Eureka

img

image-20210417182416549

改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>springcloud2021</artifactId>        <groupId>com.atguigu</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>com.atguigu</groupId>            <artifactId>cloud-common</artifactId>            <version>1.0-SNAPSHOT</version>        </dependency>        <!--eureka-server-->        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>        </dependency>        <!--boot 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>        </dependency>        <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>

寫yml

server:  port: 7001eureka:  instance:    hostname: eureka7001.com #eureka服務端的實例名稱  client:    #false表示不向注冊中心注冊自己    register-with-eureka: false    #false表示自己端就是注冊中心,我的職責就是維護服務實例,並不需要去檢索服務    fetch-registry: false    service-url:      #設置與Eureka Server交互的地址查詢服務和注冊服務都需要依賴這個地址,相互注冊      defaultZone: http://7002.cn.utools.club/eureka/

主啟動

package com.atguigu.springcloud;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;@SpringBootApplication//Eureka服務端@EnableEurekaServerpublic class EurekaMain7001 {    public static void main(String[] args) {        SpringApplication.run(EurekaMain7001.class,args);    }}

入住服務

        <!--eureka-client client入駐server -->        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>        </dependency>
server:  port: 8002spring:  application:    name: cloud-provider-service  datasource:    type: com.alibaba.druid.pool.DruidDataSource      #當前數據源操作類型    driver-class-name: org.gjt.mm.mysql.Driver        #mysql驅動包    url: jdbc:mysql://localhost:3306/db2019?useUnicode=true&characterEncoding=utf-8&useSSL=false    username: root    password: 3333mybatis:  mapper-locations: classpath:mapper/*.xml  type-aliases-package: com.atguigu.springcloud.entities       #所有Entity別名類所在包#服務入駐eureka:  client:    #表示是否將自己注冊進EurekaServer默認為true    register-with-eureka: true    fetch-registry: true    service-url:      defaultZone: https://7001.cn.utools.club/eureka,https://7002.cn.utools.club/eureka  instance:    instance-id: payment-8002    prefer-ip-address: true  #mouse hover時顯示IP地址
@SpringBootApplication@EnableEurekaClientpublic class PaymentMain8002 {    public static void main(String[] args) {        SpringApplication.run(PaymentMain8002.class,args);    }}

集群

就是多注冊幾個。

RestTemplate負載均衡

package com.atguigu.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;@Configurationpublic class ApplicationContextConfig {    @Bean    @LoadBalanced  //賦於 負載均衡的能力    public RestTemplate getRestTemplate(){        return new RestTemplate();    }}
package com.atguigu.springcloud.controller;import com.atguigu.springcloud.entities.CommonResult;import com.atguigu.springcloud.entities.Payment;import lombok.extern.slf4j.Slf4j;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@Slf4jpublic class OrderController {    //    private static final String PAYMENT_URL="http://localhost:8001";    //通過在eureka上注冊過的微服務名稱調用    private static final String PAYMENT_URL = "http://CLOUD-PROVIDER-SERVICE";    @Resource    private RestTemplate restTemplate;    @GetMapping("/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);    }}

可選: mouse hover 顯示ip

#服務入駐eureka:  client:    #表示是否將自己注冊進EurekaServer默認為true    register-with-eureka: true    fetch-registry: true    service-url:      defaultZone: https://7001.cn.utools.club/eureka,https://7002.cn.utools.club/eureka  instance:    instance-id: payment-8002    prefer-ip-address: true  #mouse hover時顯示IP地址

服務發現 Discovery

image-20210417182416549

注入與使用Discovery

//PaymentControllerpackage com.atguigu.springcloud.controller;import com.atguigu.springcloud.entities.CommonResult;import com.atguigu.springcloud.entities.Payment;import com.atguigu.springcloud.service.PaymentService;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Value;import org.springframework.cloud.client.ServiceInstance;import org.springframework.cloud.client.discovery.DiscoveryClient;import org.springframework.web.bind.annotation.*;import javax.annotation.Resource;import java.util.List;@RestController@Slf4jpublic class PaymentController {    @Resource    private PaymentService paymentService;    @Value("${server.port}")    private String serverPort;    @Resource    private DiscoveryClient discoveryClient;    //只傳給前端CommonResult,不需要前端了解其他的組件    @PostMapping(value = "/payment/create")    public CommonResult create(@RequestBody Payment payment){        int result = paymentService.create(payment);        log.info("*****插入結果:"+result);        if(result > 0){            return new CommonResult(200,"插入數據成功,serverPort="+serverPort,result);        }else{            return new CommonResult(444,"插入數據失敗",null);        }    }    @GetMapping(value = "/payment/get/{id}")    public CommonResult getPaymentById(@PathVariable("id") Long id){        Payment payment = paymentService.getPaymentById(id);        log.info("*****查詢結果:"+payment);        if(payment != null){            return new CommonResult(200,"查詢成功,serverPort="+serverPort,payment);        }else{            return new CommonResult(444,"沒有對應記錄,查詢ID:"+id,null);        }    }    @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-PROVIDER-SERVICE");        for (ServiceInstance instance : instances) {            log.info(instance.getServiceId()+"\t"+instance.getHost()+"\t"+instance.getPort()+"\t"+instance.getUri());        }        return this.discoveryClient;    }}

效果

image-20210417175651591

image-20210417175612544

禁止自我保護

--服務端

server:  port: 7001eureka:  instance:    hostname: eureka7001.com #eureka服務端的實例名稱  client:    #false表示不向注冊中心注冊自己    register-with-eureka: false    #false表示自己端就是注冊中心,我的職責就是維護服務實例,並不需要去檢索服務    fetch-registry: false    service-url:      #設置與Eureka Server交互的地址查詢服務和注冊服務都需要依賴這個地址      defaultZone: http://7002.cn.utools.club/eureka/  # ******下面的代碼用來關閉Eureka服務端的自我保護機制******  server:    # 關閉自我保護機制,保證不可用的服務被及時剔除    enable-self-preservation: false    # 如果2秒內沒有收到某個微服務的心跳,那就剔除該微服務,單位為毫秒    eviction-interval-timer-in-ms: 2000

客戶端

server:  port: 8002spring:  application:    name: cloud-provider-service  datasource:    type: com.alibaba.druid.pool.DruidDataSource      #當前數據源操作類型    driver-class-name: org.gjt.mm.mysql.Driver        #mysql驅動包    url: jdbc:mysql://localhost:3306/db2019?useUnicode=true&characterEncoding=utf-8&useSSL=false    username: root    password: 3333mybatis:  mapper-locations: classpath:mapper/*.xml  type-aliases-package: com.atguigu.springcloud.entities       #所有Entity別名類所在包#服務入駐eureka:  client:    #表示是否將自己注冊進EurekaServer默認為true    register-with-eureka: true    fetch-registry: true    service-url:      defaultZone: https://7001.cn.utools.club/eureka,https://7002.cn.utools.club/eureka  instance:    instance-id: payment-8002    prefer-ip-address: true  #mouse hover時顯示IP地址    #Eureka客戶端向服務器端發送心跳的時間間隔 ,單位為秒(默認是30秒)    lease-renewal-interval-in-seconds: 1    #Eureka 服務端在收到最后一次心跳后等待時間上限,單位為秒(默認是90秒),超時剔除服務    lease-expiration-duration-in-seconds: 2

效果

zookeeper

zookeeper注冊中心

概覽:

Linux端環境:就是在Linux上啟用zookeeper,與查看上zookeeper注冊的服務。

步驟說明:因為zookeeper要用到jdk,所以我們需要在linux上安裝jdk,然后再配置使用zookeeper.也就是先配置jdk再配置zookeeper,才能使用zookeeper。

效果:我們使用Linux的ip地址與一般固定的2181端口用於注冊中心給模塊注冊。然后我們用命令來查看在上面的服務列表可以看到我們注冊的服務-> 模塊上的具體信息, 這不懂??


全局使用:解壓命令:tar -zxvf 壓縮包.tar.gz

**1、java-jdk **
【下載jdk1.8】((我選擇的是jdk-8u281-linux-x64.tar.gz)):
https://www.oracle.com/cn/java/technologies/javase/javase-jdk8-downloads.html
【配置環境變量】
export JAVA_HOME=/opt/jdk1.8.0_281
export CLASSPATH=.:$JAVA_HOME/lib/tools.jar:$JAVA_HOME/lib/dt.jar
export PATH=$JAVA_HOME/bin:$HOME/bin:$HOME/.local/bin:$PATH

2、zookeeper

(下載: http://archive.apache.org/dist/zookeeper/zookeeper-3.4.9/?C=S;O=A)

【修改zookeeper包配置】

進入包根目錄,mkdir data

進入包下的conf ,將 zoo_sample.cfg 復制一份並命名為 zoo.cfg

​ cp zoo_sample.cfg zoo.cfg

編輯 vi zoo.cfg 修改dataDir為你的包路徑下的data,這不會??

【環境變量】
打開vim /etc/profile,在最后加入
export ZOOKEEPER_HOME=/opt/zookeeper-3.4.9
export PATH=.:$HADOOP_HOME/bin:$ZOOKEEPER_HOME/bin:$JAVA_HOME/bin:$PATH

【設置zookeeper開機自啟】
1)進入/etc/init.d 然后 vi zookeeper,內容是:

#!/bin/sh#chkconfig: 2345 80 90#description:auto_run#description:zookeeper#processname:zookeeperZK_PATH=/opt/zookeeper-3.4.9export JAVA_HOME=/opt/jdk1.8.0_281case $1 in         start) sh  $ZK_PATH/bin/zkServer.sh start;;         stop)  sh  $ZK_PATH/bin/zkServer.sh stop;;         status) sh  $ZK_PATH/bin/zkServer.sh status;;         restart) sh $ZK_PATH/bin/zkServer.sh restart;;         *)  echo "require start|stop|status|restart"  ;;esac

​ 2)注冊為服務(就可以使用service來操作zookeeper了)
chkconfig --add zookeeper

​ 3)添加操作zookeeper文件權限,否則4會提示沒有權限
chmod a+xwr zookeeper #如果沒有權限

​ 4)然后以服務的方式啟動zookeeper
service zookeeper start

【關閉防火牆 !!】
[rooMode: standalone
t@localhost bin]# systemctl stop firewalld #關閉防火牆
[root@localhost bin]# systemctl status firewalld #查看防火牆是否關閉
● firewalld.service - firewalld - dynamic firewall daemon
Loaded: loaded (/usr/lib/systemd/system/firewalld.service; enabled; vendor preset: enabled)
Active: inactive (dead) since 一 2021-04-19 21:01:41 PDT; 13s ago
...

【開啟zookeeper注冊中心功能】
service zookeeper start #啟動服務
ZooKeeper JMX enabled by default
Using config: /opt/zookeeper-3.4.9/bin/../conf/zoo.cfg
Starting zookeeper ... already running as process 2765.

service zookeeper status #查看狀態
ZooKeeper JMX enabled by default
Using config: /opt/zookeeper-3.4.9/bin/../conf/zoo.cfg
Mode: standalone

【查看注冊情況】
zookeeper>bin執行命令:
[root@localhost opt]# cd zookeeper-3.4.9/bin #1、進入zookeeper的bin目錄下
[root@localhost bin]# ./zk
zkCleanup.sh zkCli.cmd zkCli.sh zkEnv.cmd zkEnv.sh zkServer.cmd zkServer.sh
[root@localhost bin]# ./zkCli.sh #2、執行zkCli.sh
Connecting to localhost:2181
2021-04-19 21:39:27,413 [myid:] - INFO [main:Environment@100] - Client environment:zookeeper.version=3.4.9-1757313, built on 08/23/2016 06:50 GMT
2021-04-19 21:39:27,423 [myid:] - INFO [main:Environment@100] - Client environment:host.name=localhost
2021-04-19 21:39:27,423 [myid:] - INFO [main:Environment@100] - Client environment:java.version=1.8.0_282
....
WatchedEvent state:SyncConnected type:None path:null
[zk: localhost:2181(CONNECTED) 0] #3、說明代表成功

[zk: localhost:2181(CONNECTED) 9] ls / #查看

[services, zookeeper]
[zk: localhost:2181(CONNECTED) 10] ls /services #查看服務
[cloud-provider-payment, cloud-provider-payment-test] #哪些服務注冊在zookeeper上了
[zk: localhost:2181(CONNECTED) 11] ls /services/cloud-provider-payment #查看服務名下有哪些模塊
[ec071f77-3477-4be1-a625-ab5a015f6719]
[zk: localhost:2181(CONNECTED) 13] get /services/cloud-provider-payment/ec071f77-3477-4be1-a625-ab5a015f6719 #查看模塊具體信息(下面json字符串)
{"name":"cloud-provider-payment","id":"ec071f77-3477-4be1-a625-ab5a015f6719","address":"DESKTOP-HPKMNTJ","port":8004,"sslPort":null,"payload":{"@class":"org.springframework.cloud.zookeeper.discovery.ZookeeperInstance","id":"application-1","name":"cloud-provider-payment","metadata":{}},"registrationTimeUTC":1618903366771,"serviceType":"DYNAMIC","uriSpec":{"parts":[{"value":"scheme","variable":true},{"value":"😕/","variable":false},{"value":"address","variable":true},{"value":":","variable":false},{"value":"port","variable":true}]}}

...

服務模塊: zookeeper注冊中心在Linux中部署好后,我們可以讓服務模塊注冊上傳,那怎么寫這樣的模塊呢?也就是創建一個可以注冊到時注冊中心上的服務模塊。

效果:注冊上去后,我們在Linux中使用命令可以看到服務名下的節點(服務模塊)

創建模塊

改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>springcloud2021</artifactId>        <groupId>com.atguigu</groupId>        <version>1.0-SNAPSHOT</version>    </parent>    <modelVersion>4.0.0</modelVersion>    <artifactId>cloud-provider-payment8004</artifactId>    <properties>        <maven.compiler.source>8</maven.compiler.source>        <maven.compiler.target>8</maven.compiler.target>    </properties>    <dependencies>        <!--springboot整合zookeeper客戶端-->        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>            <!--先排除自帶的zookeeper3.5.3-->            <exclusions>                <exclusion>                    <groupId>org.apache.zookeeper</groupId>                    <artifactId>zookeeper</artifactId>                </exclusion>            </exclusions>        </dependency>        <!--添加zookeeper3.4.14版本-->        <dependency>            <groupId>org.apache.zookeeper</groupId>            <artifactId>zookeeper</artifactId>            <version>3.4.14</version>            <exclusions>                <exclusion>                    <groupId>log4j</groupId>                    <artifactId>log4j</artifactId>                </exclusion>                <exclusion>                    <groupId>org.slf4j</groupId>                    <artifactId>slf4j-log4j12</artifactId>                </exclusion>            </exclusions>        </dependency>        <!--Springboot整合web組件-->        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-web</artifactId>        </dependency>        <!--引入自定義的api通用包,可以使用payment支付Entity-->        <dependency>            <groupId>com.atguigu</groupId>            <artifactId>cloud-common</artifactId>            <version>1.0-SNAPSHOT</version>        </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></project>

寫yml

#8004表示注冊到zookeeper服務器的支付服務提供者端口號server:  port: 8004#服務別名----注冊zookeeper到注冊中心名稱spring:  application:    name: cloud-provider-payment  cloud:    zookeeper:      connect-string: 192.168.44.130:2181

主啟動類

package com.atguigu.springcloud;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.client.discovery.EnableDiscoveryClient;@SpringBootApplication@EnableDiscoveryClient  //該注解用於想使用consul或者zookeeper作為注冊中心時注冊服務public class PaymentMain8004 {    public static void main(String[] args) {        SpringApplication.run(PaymentMain8004.class,args);    }}

業務

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@Slf4jpublic class PaymentController {    @Value("${server.port}")    private String serverPort;    @GetMapping(value = "/payment/zk")    public String paymentzk(){        return "springcloud with zookeeper:"+serverPort+"\t"+ UUID.randomUUID().toString();    }}

在linux上查看服務 按住ctr去看

3、zookeeper是臨時還是持久節點

簡要: 當我們注冊上去后,是可以在zookeeper注冊中心看到某服務名的節點的,但如果該節點對應的服務關閉了,也就是zookeeper接收不到該服務的心跳了,等下就會該節點主會被刪除,下次恢復后,節點值不是之前的節點值了,也就是zookeeper一定時間接收不到服務的心跳后一定時間后服務對應的節點會被刪除,下次注冊進來與前一次無關系。

zookeeper服務發現

概述: 當我們的生產都注冊zookeeper后,我們注冊上面的消費都需要調用zookeeper上的生產者。也就是生產者與消費者都注冊進zookeeper后,我們消費都用RestTemplate來調用生產都接口。

步驟說明: 創建消費者服務模塊 ->RestTemplate通過服務發現來調用生產者中的接口。

開始:

建model

創建名為 cloud-consumerzk-order8080 的maven模塊。

改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>springcloud2021</artifactId>        <groupId>com.atguigu</groupId>        <version>1.0-SNAPSHOT</version>    </parent>    <modelVersion>4.0.0</modelVersion>    <artifactId>cloud-consumerzk-order8080</artifactId>    <properties>        <maven.compiler.source>8</maven.compiler.source>        <maven.compiler.target>8</maven.compiler.target>    </properties>    <dependencies>        <!--springboot整合zookeeper客戶端-->        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>            <!--先排除自帶的zookeeper3.5.3-->            <exclusions>                <exclusion>                    <groupId>org.apache.zookeeper</groupId>                    <artifactId>zookeeper</artifactId>                </exclusion>            </exclusions>        </dependency>        <!--添加zookeeper3.4.14版本-->        <dependency>            <groupId>org.apache.zookeeper</groupId>            <artifactId>zookeeper</artifactId>            <version>3.4.14</version>            <exclusions>                <exclusion>                    <groupId>log4j</groupId>                    <artifactId>log4j</artifactId>                </exclusion>                <exclusion>                    <groupId>org.slf4j</groupId>                    <artifactId>slf4j-log4j12</artifactId>                </exclusion>            </exclusions>        </dependency>        <!--Springboot整合web組件-->        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-web</artifactId>        </dependency>        <!--引入自定義的api通用包,可以使用payment支付Entity-->        <dependency>            <groupId>com.atguigu</groupId>            <artifactId>cloud-common</artifactId>            <version>1.0-SNAPSHOT</version>        </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></project>

寫yml

server:  port: 8080spring:  application:    name: cloud-consumer-order  cloud:    #注冊到zookeeper地址    zookeeper:      connect-string: 192.168.44.130:2181

主啟動類

package com.atguigu.springcloud;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.client.discovery.EnableDiscoveryClient;@SpringBootApplication@EnableDiscoveryClientpublic class OrderZKMain8080 {    public static void main(String[] args) {        SpringApplication.run(OrderZKMain8080.class,args);    }}

業務

將RestTemplate注入Springboot作為組件

package com.atguigu.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;@Configurationpublic class ApplicationContextConfig {    @Bean    @LoadBalanced    public RestTemplate getRestTemplate() {        return new RestTemplate();    }}

controller

package com.atguigu.springcloud.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@Slf4jpublic class OrderZKController {    public static final String INVOKE_URL = "http://cloud-provider-payment";    @Resource    private RestTemplate restTemplate;    @GetMapping(value = "/consumer/payment/zk")    public String paymentInfo() {        String result = restTemplate.getForObject(INVOKE_URL + "/payment/zk", String.class);        return result;    }}

測試

http://127.0.0.1:8080/consumer/payment/zk

Consul

Consul 注冊中心

Consul簡介: 提供服務發現,健康監測、KV存儲、多數據中心、可視化Web界面,它是用Go語言編寫的。

官網:https://learn.hashicorp.com/consul

下載:https://www.consul.io/downloads / https://releases.hashicorp.com/consul/

使用:

它是一個可直接命令運行程序,在consul所在目錄下查看版本 consul --version,運行 consul agent -dev

瀏覽器打開web界面:http://127.0.0.1:8500/ ,到此Consul 注冊中心完成了,就這不懂??

消費者模塊:創建一個浪費者模塊與上面zookeeper一樣,只是換了注冊中心的yml配置與pom的依賴。

也就是想要加入在Consul注冊中心中,需要加入Consul的依賴與對應的yml配置。這不懂??

步驟說明:創建一個模塊,改pom,寫yml, 主啟動, 業務controll

消費模塊開始:

創建一個模塊 :創建 cloud-providerconsul-payment8006 Maven模塊

改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>springcloud2021</artifactId>        <groupId>com.atguigu</groupId>        <version>1.0-SNAPSHOT</version>    </parent>    <modelVersion>4.0.0</modelVersion>    <artifactId>cloud-providerconsul-payment8006</artifactId>    <properties>        <maven.compiler.source>8</maven.compiler.source>        <maven.compiler.target>8</maven.compiler.target>    </properties>    <dependencies>        <!--springboot整合consul客戶端-->        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-starter-consul-discovery</artifactId>        </dependency>        <!--consul 建康檢測報錯用到,如果不報錯可不用-->        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-actuator</artifactId>        </dependency>        <!--Springboot整合web組件-->        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-web</artifactId>        </dependency>        <!--引入自定義的api通用包,可以使用payment支付Entity-->        <dependency>            <groupId>com.atguigu</groupId>            <artifactId>cloud-common</artifactId>            <version>1.0-SNAPSHOT</version>        </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></project>

寫yml

server:  port: 8006spring:  application:    name: consul-provider-payment#####consul 注冊中心地址  cloud:    consul:      host: localhost      port: 8500      discovery:        #hostname: 127.0.0.1        service-name: ${spring.application.name}

主啟動類

package com.atguigu.springcloud;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.client.discovery.EnableDiscoveryClient;@SpringBootApplication@EnableDiscoveryClientpublic class PaymentMain8006 {    public static void main(String[] args) {        SpringApplication.run(PaymentMain8006.class,args);    }}

業務

package com.atguigu.springcloud.controller;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Value;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import java.util.UUID;@RestController@Slf4jpublic class ConsulPaymentController {    @Value("${server.port}")    private String serverPort;    @RequestMapping(value = "/payment/consul")    public String paymentConsul() {        return "springCloud with consul: "+serverPort+"\t   "+ UUID.randomUUID().toString();    }}

Consul 服務發現

消費者模塊: Consul 注冊中心及消費者注冊到Consul上去后,下面我們需要創建一個消費者模塊,像zookeeper與Eureka一樣進行服務發現,也就是創建一個消費模塊並將之注冊到Consul注冊中心上,然后再進行調用消費者接口,這不懂??

步驟:創建模塊 ->改pom ->寫yml ->主啟動類 ->業務

創建模塊 創建 cloud-consumerconsul-order8080

改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>springcloud2021</artifactId>        <groupId>com.atguigu</groupId>        <version>1.0-SNAPSHOT</version>    </parent>    <modelVersion>4.0.0</modelVersion>    <artifactId>cloud-consumerconsul-order8080</artifactId>    <properties>        <maven.compiler.source>8</maven.compiler.source>        <maven.compiler.target>8</maven.compiler.target>    </properties>    <dependencies>        <!--springboot整合consul客戶端-->        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-starter-consul-discovery</artifactId>        </dependency>        <!--consul 建康檢測報錯用到-->        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-actuator</artifactId>        </dependency>        <!--Springboot整合web組件-->        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-web</artifactId>        </dependency>        <!--引入自定義的api通用包,可以使用payment支付Entity-->        <dependency>            <groupId>com.atguigu</groupId>            <artifactId>cloud-common</artifactId>            <version>1.0-SNAPSHOT</version>        </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></project>

寫yml

server:  port: 8080spring:  application:    name: consul-consumer-payment  #####consul 注冊中心地址  cloud:    consul:      host: localhost      port: 8500      discovery:        #hostname: 127.0.0.1        service-name: ${spring.application.name}

主啟動類

package com.atguigu.springcloud;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.client.discovery.EnableDiscoveryClient;@SpringBootApplication@EnableDiscoveryClientpublic class ConsulPaymentMain8080 {    public static void main(String[] args) {        SpringApplication.run(ConsulPaymentMain8080.class,args);    }}

業務

注冊RestTemplate組件到springboot

package com.atguigu.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;@Configurationpublic class ApplicationContextConfig {    @Bean    @LoadBalanced    public RestTemplate getRestTemplate() {        return new RestTemplate();    }}

controller

package com.atguigu.springcloud.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@Slf4jpublic class ConsulPaymentController {    public static final String INVOKE_URL = "http://consul-provider-payment";    @Resource    private RestTemplate restTemplate;    @GetMapping(value = "/consumer/payment/consul")    public String paymentInfo() {        String result = restTemplate.getForObject(INVOKE_URL + "/payment/consul", String.class);        return result;    }}

CAP 說明

分布式系統(distributed system)正變得越來越重要,大型網站幾乎都是分布式的。分布式系統的最大難點,就是各個節點的狀態如何同步。CAP 定理是這方面的基本定理,也是理解分布式系統的起點。

分區:一台服務器放在中國,另一台服務器放在美國,這就是兩個區;

一致性(C):寫操作之后的讀操作,是立即返回,還是等分區數據同步保證數據一致性再可讀操作。

可用性(A):Availability 中文叫做"可用性",意思是只要收到用戶的請求,服務器就必須給出回應。

分區容錯性(p):一台服務器放在中國,另一台服務器放在美國,這就是兩個區,它們之間可能無法通信。

相互關系

img

一般來說,分區容錯無法避免,因此可以認為 CAP 的 P 總是成立。CAP 定理告訴我們,剩下的 C 和 A 無法同時做到。

只能CP與AP,因為CA是相互矛盾的,如果想要保存數據一致性就不能保證可用性,可用性又要求請求即返回,所以不能保證數據一致性。

Eureka(AP),而zookeeper與consul是CP.

還不懂?強烈推薦閱讀:https://www.ruanyifeng.com/blog/2018/07/cap.html

Ribbon 服務調用(負載均衡)

Ribbon是進程內的LB,還有一種是集中式的LB (nginx) ,LB是負均衡,比如一個人去醫院,人家一看你口腔腫了,好那你去口腔科,這是集中式的LB,而口腔科有很多醫生,誰給你看這時就是進程內的LB。這不懂??也就是都是屬性LB負載均衡,只不過集中式在進程內的前面。

具體:集中式負責把訪問請求通過某種策略發至服務提供方;進程內的集成於消費方進程。且RestTemplate + 負載均衡 = Ribbon

RestTemplate學習

RestTemplate: 上面也用到了RestTemplate,因為Ribbon是是RestTemplate的包裝,所以我們需要再認識認識RestTemplate.

RestTemplate注冊到SpringBoot組件后,我們在使用RestTemplate進行負載均衡訪問API時有兩種,分別get/postForObject返回的是json,與get/postForEntity它返回的是更詳細的內容entity.getBody也是json。也是一個直接json,另一種是更詳細的信息,不僅包含json還是狀態碼等。不懂??

以下步驟: 是get/postForObject與get/postForEntity 的示例

package com.atguigu.springcloud.controller;import com.atguigu.springcloud.entities.CommonResult;import com.atguigu.springcloud.entities.Payment;import lombok.extern.slf4j.Slf4j;import org.springframework.http.ResponseEntity;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 org.springframework.web.client.RestTemplate;import javax.annotation.Resource;@RestController@Slf4jpublic class OrderController {    //    private static final String PAYMENT_URL="http://localhost:8001";    //通過在eureka上注冊過的微服務名稱調用    private static final String PAYMENT_URL = "http://CLOUD-PROVIDER-SERVICE";    @Resource    private RestTemplate restTemplate;    //get/postForObject --!!!----------    @GetMapping("/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);    }        //get/postForEntity --!!!----------    @GetMapping("/consumer/payment/getForEntity/{id}")    public CommonResult<Payment> getPayment2(@PathVariable("id") Long id){        ResponseEntity<CommonResult> entity = restTemplate.getForEntity(PAYMENT_URL + "/payment/get/" + id, CommonResult.class);        if(entity.getStatusCode().is2xxSuccessful()) {            return entity.getBody();        }else {            return new CommonResult<>(444,"操作失敗");        }    }    @GetMapping("/consumer/payment/createForEntity")    public CommonResult<Payment> create2(Payment payment){        ResponseEntity<CommonResult> entity = restTemplate.postForEntity(PAYMENT_URL + "/payment/create", payment, CommonResult.class);        if(entity.getStatusCode().is2xxSuccessful()) {            return entity.getBody();        }else {            return new CommonResult<>(444,"操作失敗");        }    }}

Ribbon默認負載均衡輪詢算法

簡介:Ribbon有很多負載均衡的算法。下面演示的是隨機的。不懂?也就是使用Ribbon進行服務調用,負載均衡演示的是隨機。

以下步驟:引入pom,因為Eureka已經依賴了 spring-cloud-starter-netflix-ribbon ,不信?你點擊Eureka的依賴,搜索一下ribbon ——>

注意: 官方文檔明確給出了警告:

在這里插入圖片描述

這個自定義配置類不能放在 @ComponentScan 所掃描的當前包下以及子包下,否則自定義的配置類就會被所有的 Ribbon 客戶端所共享,達不到特殊化定制的目的了。也就是我們在創建與啟動類不同目錄下。我們選擇在com.atguigu.myRulecom.atguigu.springcloud同級,即myRule下創建自定義的配置類。——>

再在主啟動類將服務與策略綁定。

具體:

依賴

寫Eureka依賴后,無須再寫Ribbon的依賴 spring-cloud-starter-netflix-ribbon

自定義配置類

package com.atguigu.myRule;import com.netflix.loadbalancer.IRule;import com.netflix.loadbalancer.RandomRule;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;/** * 自定義負載均衡規則類 */@Configurationpublic class MySelfRule {    @Bean    public IRule myRule(){        return new RandomRule();    }}

主啟動類

package com.atguigu.springcloud;import com.atguigu.myRule.MySelfRule;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.netflix.eureka.EnableEurekaClient;import org.springframework.cloud.netflix.ribbon.RibbonClient;@SpringBootApplication@EnableEurekaClient//服務與策略綁定@RibbonClient(name = "CLOUD-PROVIDER-SERVICE",configuration = MySelfRule.class)public class OrderMain8080 {    public static void main(String[] args) {        SpringApplication.run(OrderMain8080.class,args);    }}

使用

RestTemplate就是使用。不知道?? 學習 使用過程就是將RestTemplate注入到Springboot組件中,然后在Controller使用RestTemplate進行服務調用即可。

手寫輪詢算法

一以下步驟:不用原來的算法,即注釋掉RestTemplate注冊到Springboot組件上的@LoadBalanced,沒有該注釋當不具備負載均衡。我們寫輪詢,該算法的使用是,輸入該服務下的所有節點List ,輸出其中某個節點,即讓我們選擇出一個節點。返回給調用者(Controller中的API方法),獲取調用節點的getUri方法獲取uri,然后api方法再具體訪問某個請求。 不懂??也就是幫人家在指定服務中選一個節點。

——注釋——

package com.atguigu.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;@Configurationpublic class ApplicationContextConfig {    @Bean    //@LoadBalanced  //賦於 負載均衡的能力  輪詢算法第一步    public RestTemplate getRestTemplate(){        return new RestTemplate();    }}

——開始我們的輪詢算法:創建一個接口——

package com.atguigu.springcloud.lb;import org.springframework.cloud.client.ServiceInstance;import java.util.List;//第二步public interface LoadBalancer {    ServiceInstance instances(List<ServiceInstance> serviceInstances);}

——實現類——

package com.atguigu.springcloud.lb;import org.springframework.cloud.client.ServiceInstance;import org.springframework.stereotype.Component;import java.util.List;import java.util.concurrent.atomic.AtomicInteger;@Component //第三步public class MyLB implements LoadBalancer{    private AtomicInteger atomicInteger = new AtomicInteger(0);    //作用: 記錄訪問次數    public  int getAndIncrement() {        int current;//0/1        int next;//1/2        do {            current = this.atomicInteger.get(); //0-1/1-2            next = current >= 2147483647 ? 0 : current+1;        }while (! this.atomicInteger.compareAndSet(current,next));        System.out.println("*****next: "+next);        return next;//1/2/3/4/5    }    @Override    public ServiceInstance instances(List<ServiceInstance> serviceInstances) {        int index = getAndIncrement() % serviceInstances.size();        //返回要訪問的具體server        return serviceInstances.get(index);    }}

——使用——

package com.atguigu.springcloud.controller;import com.atguigu.springcloud.entities.CommonResult;import com.atguigu.springcloud.entities.Payment;import com.atguigu.springcloud.lb.LoadBalancer;import lombok.extern.slf4j.Slf4j;import org.springframework.cloud.client.ServiceInstance;import org.springframework.cloud.client.discovery.DiscoveryClient;import org.springframework.http.ResponseEntity;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 org.springframework.web.client.RestTemplate;import javax.annotation.Resource;import java.net.URI;import java.util.List;@RestController@Slf4jpublic class OrderController {    //通過在eureka上注冊過的微服務名稱調用    private static final String PAYMENT_URL = "http://CLOUD-PROVIDER-SERVICE";    //1、依賴注入    @Resource    private RestTemplate restTemplate;    @Resource    private LoadBalancer loadBalancer;	    @Resource    private DiscoveryClient discoveryClient;        @GetMapping(value = "/consumer/payment/lb")    public String getPaymentLB() {        List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PROVIDER-SERVICE");        if(instances == null || instances.size() <= 0) {            return  null;        }        //使用!!        ServiceInstance serviceInstance = loadBalancer.instances(instances);        URI uri = serviceInstance.getUri();        System.out.println("****選中server的uri:"+uri);        return restTemplate.getForObject(uri+"/payment/lb",String.class);    }}

OpenFeign 服務調用(負載均衡)

在這里插入圖片描述

它的使用方式非常簡單,我們概述一下,Feign是Springcloud組件中的一個輕量級Restful的HTTP服務客戶端,Feign內置了Ribbon,用來做客戶端負載均衡,去調用服務注冊中心的服務。Feign的使用方式是:使用Feign的注解定義接口,調用這個接口,就可以調用服務注冊中心的服務
OpenFeign是springcloud在Feign的基礎上支持了SpringMVC的注解,如@RequestMapping等等。OpenFeign的@FeignClient可以解析SpringMVC的@RequestMapping注解下的接口,並通過動態代理的方式產生實現類,實現類中做負載均衡並調用其他服務。

使用說明:創建一個module,引入OpenFeign的依賴,就一個 spring-cloud-starter-openfeign, 且它

也像spring-cloud-starter-netflix-eureka-client依賴一樣引入了ribbon,因為openfeign是內置了ribbon的。yml就eureka的注冊中心與服務端口配置,主啟動類加一個注解激活OpenFeign, 主要是業務類,openfeign使我們像調用service一樣使用生產者的API,但我們需要寫配配置這個”浮在表面“的service,也就是注解+方法,這個方法就是消費端Controller方法上的注解與方法聲明(復制過來即可)。不懂??也就是配置好模塊的必須環境后,如依賴,激活OpenFeign后,余下工作就是service了,而service就是注解+復制過來的方法頭。

OpenFeign做負載均衡步驟: 創建module,改pom,寫yml,主啟動類,業務

創建模塊

cloud-consumer-feign-order8080

改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>springcloud2021</artifactId>        <groupId>com.atguigu</groupId>        <version>1.0-SNAPSHOT</version>    </parent>    <modelVersion>4.0.0</modelVersion>    <artifactId>cloud-consumer-feign-order8080</artifactId>    <properties>        <maven.compiler.source>8</maven.compiler.source>        <maven.compiler.target>8</maven.compiler.target>    </properties>    <dependencies>        <!--openfeign-->        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-starter-openfeign</artifactId>        </dependency>        <!--eureka client-->        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>        </dependency>        <!--引入自定義的api通用包,可以使用Payment支付Entity-->        <dependency>            <groupId>com.atguigu</groupId>            <artifactId>cloud-common</artifactId>            <version>1.0-SNAPSHOT</version>        </dependency>        <!--web-->        <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></project>

寫yml

server:  port: 8080eureka:  client:    register-with-eureka: false    service-url:      defaultZone: https://7001.cn.utools.club/eureka,https://7002.cn.utools.club/eureka

主啟動類

package com.atguigu.springcloud;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.openfeign.EnableFeignClients;@SpringBootApplication@EnableFeignClients  //激活OpenFeignpublic class OrderFeignMain8080 {    public static void main(String[] args) {        SpringApplication.run(OrderFeignMain8080.class,args);    }}

業務

service

package com.atguigu.springcloud.service;import com.atguigu.springcloud.entities.CommonResult;import org.springframework.cloud.openfeign.FeignClient;import org.springframework.stereotype.Component;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.PathVariable;@Component@FeignClient(value = "http://CLOUD-PROVIDER-SERVICE")  ////指定調用哪個微服務public interface PaymentFeignService {    @GetMapping(value = "/payment/get/{id}")    public CommonResult getPaymentById(@PathVariable("id") Long id);}

controller調用service

package com.atguigu.springcloud.controller;import com.atguigu.springcloud.entities.CommonResult;import com.atguigu.springcloud.entities.Payment;import com.atguigu.springcloud.service.PaymentFeignService;import lombok.extern.slf4j.Slf4j;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@Slf4jpublic class OrderFeignController {    @Resource    private PaymentFeignService paymentFeignService;    @GetMapping("/consumer/payment/get/{id}")    public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id) {        return paymentFeignService.getPaymentById(id);    }}

OpenFeign超時控制 : 兩端,消費者調用生產者超時控制,有兩種,即兩種建立連接的超時控制與建立連接到返回資源給消費者的超時控制。不懂??就是生產者可能獲取資源需要3秒,但消費端它只能等1秒。且OpenFeign 默認超時時候是1秒,這里我們可能需要修改超時時間,這就是超時控制。

以下測試步驟:在消費者端創建一個耗時的API -> 消費者OpenFeign寫入到Service接口 -> Controller調用service -> 訪問出現500超時錯誤 -> 進行超時控制 -> 訪問正常

消費端寫一個耗時的API

...@GetMapping(value = "/payment/timeout")    public String getPaymentTimeout() {        try {            Thread.sleep(3000);        } catch (InterruptedException e) {            e.printStackTrace();        }        return serverPort;    }...

消費者OpenFeign寫入到Service接口

package com.atguigu.springcloud.service;import com.atguigu.springcloud.entities.CommonResult;import org.springframework.cloud.openfeign.FeignClient;import org.springframework.stereotype.Component;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.PathVariable;@Component@FeignClient(value = "http://CLOUD-PROVIDER-SERVICE")  ////指定調用哪個微服務public interface PaymentFeignService {    @GetMapping(value = "/payment/get/{id}")    public CommonResult getPaymentById(@PathVariable("id") Long id);    //”浮在表面“的耗時API    @GetMapping(value = "/payment/timeout")    public String getPaymentTimeout();}

OpenFeign消費端 Controller調用service

package com.atguigu.springcloud.controller;import com.atguigu.springcloud.entities.CommonResult;import com.atguigu.springcloud.entities.Payment;import com.atguigu.springcloud.service.PaymentFeignService;import lombok.extern.slf4j.Slf4j;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@Slf4jpublic class OrderFeignController {    @Resource    private PaymentFeignService paymentFeignService;    @GetMapping("/consumer/payment/get/{id}")    public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id) {        return paymentFeignService.getPaymentById(id);    }    @GetMapping(value = "/consumer/payment/timeout")    public String getPaymentTimeout() {        return paymentFeignService.getPaymentTimeout();    }}

訪問測試

http://127.0.0.1:8080/consumer/payment/timeout

image-20210424002504424

超時控制

server:  port: 8080eureka:  client:    register-with-eureka: false    service-url:      defaultZone: https://7001.cn.utools.club/eureka,https://7002.cn.utools.club/eurekaribbon:  ReadTimeout: 5000  #建立連接超時時間  ConnectTimeout: 5000 #建立連接到服務器讀取到杉資源所用的時間

結果

訪問正常

步驟:對fs 模塊的方法進行逐一講解

OpenFeign日志增強: 就是對OpenFeign負載均衡的API進行日志增強。我們訪問被日志增強的API,在控制台就可以看到詳細的日志輸出。

以下步驟:向SpringBoot注冊加入Logger.Level 為組件。 -> 然后進行日志配置,即哪些Service進行日志增強。-> 測試 -> 查看控制台

注冊組件

package com.atguigu.springcloud.config;import feign.Logger;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;@Configurationpublic class FeignConfig {    @Bean    Logger.Level feignLoggerLevel() {        return Logger.Level.FULL;    }}

yml配置

server:  port: 8080eureka:  client:    register-with-eureka: false    service-url:      defaultZone: https://7001.cn.utools.club/eureka,https://7002.cn.utools.club/eurekaribbon:  ReadTimeout: 5000  #建立連接超時時間  ConnectTimeout: 5000 #建立連接到服務器讀取到杉資源所用的時間logging:  level:    #feign日志以什么級別監控哪個接口    com.atguigu.springcloud.service.PaymentFeignService: debug

測試

啟動注冊中心與生產者端,再啟動我們消費端,訪問調用了 PaymentFeignService 接口API的Controller方法。

查看控制台

image-20210424200356472

Hystrix

Hystrix :為解決服務雪崩而生,形成原因分為三個階段,服務提供者不可用,重試加大請求流量,服務調用者不可用。

Hystrix是一個用於處理分布式系統的延遲和容錯的開源庫,在分布式系統里,許多依賴不可避免的會調用失敗,比如超時,異常等,Hystrix能夠保證在一個依賴出問題的情況下,不會導致整體服務失敗,避免級聯故障,以提高分布式系統的彈性。

”斷路器“本身是一種開關裝置,當某個服務單元發生故障之后,通過斷路器的故障監控(類似熔斷保險絲),向調用方返回一個符合預期,可處理的備選響應(FallBack) ,而不是長時間的等待或者拋出調用方無法處理的異常,這樣就保證了服務調用方的線程不會地被長時間,不必要地占用,從而避免了故障在分布式系統中蔓延,乃至雪崩。

這里寫圖片描述

這里寫圖片描述

Hystrix 重要概念

服務降級(fallback):不讓客戶端等待並立刻返回一個友好提示。導致服務降級的原因有以下幾點,程序運行異常,超時,服務熔斷觸發服務降級,線程池/信號量打滿也會導致服務降級

服務熔斷(break):類比保險絲達到最大服務訪問后,直接拒絕訪問,拉閘限電,然后調用服務降級的方法並返回友好提示。

服務限流(flowlimit): 都給我一個一個來。

開始:我們先開始創建基本的模塊cloud-provider-hystrix-payment8001,有兩個方法一個正常的API一個延時的API,然后進行高並發測試,用的是JMeter,使用方式是先添加線程組,再配置並發量,線程組上加http請求。然后查看我們正常API的訪問速度,結果由秒開變為要等待。不懂??也就是創建一個用於高並發測試的模塊。

以下步驟:創建模塊 cloud-provider-hystrix-payment8001 -> 改pom -> 寫yml -> 業務 -> JMeter高並發測試

創建模塊

cloud-provider-hystrix-payment8001

改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>springcloud2021</artifactId>        <groupId>com.atguigu</groupId>        <version>1.0-SNAPSHOT</version>    </parent>    <modelVersion>4.0.0</modelVersion>    <artifactId>cloud-provider-hystrix-payment8001</artifactId>    <properties>        <maven.compiler.source>8</maven.compiler.source>        <maven.compiler.target>8</maven.compiler.target>    </properties>    <dependencies>        <!--hystrix-->        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>        </dependency>        <!--Eureka依賴-->        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>        </dependency>        <!--引入公共模塊坐標-->        <dependency>            <groupId>com.atguigu</groupId>            <artifactId>cloud-common</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></project>

寫yml

server:  port: 8001#微服務名稱spring:  application:    #入駐進Eureka服務器的名稱    name: cloud-provider-hystrix-payment#Eurekaeureka:  client:    #表示是否將自己注冊進EurekaServer,默認為true    register-with-eureka: true    #表示是否從EurekaServer中抓取已有的注冊信息,默認為true,單節點無所謂,集群必須設置為true才能默認ribbon使用負載均衡    fetch-registry: true    service-url:      #注冊地址      defaultZone: https://7001.cn.utools.club/eureka,https://7002.cn.utools.club/eureka

業務

service

package com.atguigu.springcloud.service;import org.springframework.stereotype.Service;import java.util.concurrent.TimeUnit;@Servicepublic class PaymentHystrixService {    /**     * 正常訪問     */    public String paymentInfo_OK(Long id) {        return "線程池: "+Thread.currentThread().getName()+"   paymentInfo_OK id: "+id+"\t哈哈";    }    /**     * 模擬出錯     */    public String paymentInfo_TimeOut(Long id){        int timeNum = 3;        try {            TimeUnit.SECONDS.sleep(timeNum);        }catch (InterruptedException e) {            e.printStackTrace();        }        return "線程池:   "+Thread.currentThread().getName()+"   paymentInfo_Timeout id: "+id+"\t耗時"+timeNum+"秒鍾";    }}

controller

package com.atguigu.springcloud.controller;import com.atguigu.springcloud.service.PaymentHystrixService;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;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@Slf4jpublic class PaymentController {    @Resource    private PaymentHystrixService paymentHystrixService;    @Value("${server.port}")    private String serverPost;    //正常訪問    @GetMapping("/payment/hystrix/OK/{id}")    public String paymentInfo_OK(@PathVariable("id") Long id){        return paymentHystrixService.paymentInfo_OK(id);    }    //延時訪問    @GetMapping("/payment/hystrix/Timeout/{id}")    public String paymentInfo_TimeOut(@PathVariable("id") Long id){        return paymentHystrixService.paymentInfo_TimeOut(id);    }}

高並發測試

image-20210424222721020

image-20210424222733151

image-20210424222755162

image-20210424222809043

Hystrix 加入8080高並發測試 : 就是完成了8001即消費端API的高壓測試后,再加入8001的消費端8080,再調用其API進行高並發測試。

以下步驟: 創建模塊 cloud-consumer-feign-hystrix-order-8080 -> 改pom -> 寫yml -> 主啟動類 -> 業務類 -> 測試

創建模塊

模塊名:cloud-consumer-feign-hystrix-order-8080

改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>springcloud2021</artifactId>        <groupId>com.atguigu</groupId>        <version>1.0-SNAPSHOT</version>    </parent>    <modelVersion>4.0.0</modelVersion>    <artifactId>cloud-consumer-feign-hystrix-order-8080</artifactId>    <properties>        <maven.compiler.source>8</maven.compiler.source>        <maven.compiler.target>8</maven.compiler.target>    </properties>    <dependencies>        <!--openfeign-->        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-starter-openfeign</artifactId>        </dependency>                <!--hystrix-->        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>        </dependency>        <!--Eureka依賴-->        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>        </dependency>        <!--引入公共模塊坐標-->        <dependency>            <groupId>com.atguigu</groupId>            <artifactId>cloud-common</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></project>

寫yaml

server:  port: 8080eureka:  client:    register-with-eureka: false    service-url:      defaultZone: https://7001.cn.utools.club/eureka,https://7002.cn.utools.club/eurekaribbon:  ReadTimeout: 5000  #建立連接超時時間  ConnectTimeout: 5000 #建立連接到服務器讀取到杉資源所用的時間

主啟動類

package com.atguigu.springcloud;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@EnableFeignClientspublic class OrderHystrixMain8080 {    public static void main(String[] args) {        SpringApplication.run(OrderHystrixMain8080.class,args);    }}

業務類

service

package com.atguigu.springcloud.service;import org.springframework.cloud.openfeign.FeignClient;import org.springframework.stereotype.Component;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.PathVariable;@Component@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT")public interface PaymentHystrixService {    //正常訪問    @GetMapping("/payment/hystrix/OK/{id}")    public String paymentInfo_OK(@PathVariable("id") Long id);    //延時訪問    @GetMapping("/payment/hystrix/Timeout/{id}")``    public String paymentInfo_TimeOut(@PathVariable("id") Long id);}

controller

package com.atguigu.springcloud.controller;import com.atguigu.springcloud.service.PaymentHystrixService;import lombok.extern.slf4j.Slf4j;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@Slf4jpublic class OrderHystrixController {    @Resource    private PaymentHystrixService paymentHystrixService;    //正常訪問    @GetMapping("/consumer/payment/hystrix/OK/{id}")    public String paymentInfo_OK(@PathVariable("id") Long id) {        String info_ok = paymentHystrixService.paymentInfo_OK(id);        return info_ok;    }    //延時訪問    @GetMapping("/consumer/payment/hystrix/Timeout/{id}")    public String paymentInfo_TimeOut(@PathVariable("id") Long id) {        String timeOut = paymentHystrixService.paymentInfo_TimeOut(id);        return timeOut;    }}

測試

測試對象就是8001 還不會?

結果:訪問秒開變轉圈,甚至超時

服務降級(FallBack): 即當我們方法比如8001服務提供者,服務超時、程序出錯、消費端宕機與服務熔斷時,就進行服務降級,返回給用戶一個友好的提示。不懂?? 即訪問的API 出現問題了,轉調用其它API,該API可以返回友好提示。

以下步驟:在可能會出錯訪問超時的模塊的主啟動類加入服務降級的激活注解 @EnableCircuitBreaker ,然后在可能會服務超時或出錯的API加入注解 -> 然后進行方法超時(注解指定超時觸發降級時間)與設置API出錯進行測試 -> 瀏覽器查看是否觸發服務降級返回的內容 -> 消費端也進行服務降級 -> 瀏覽器測試是否觸發消費端服務降級 -> 全局默認FallBack -> 測試消費方調用生產方,服務端宕機,進行服務降級

主啟動類加注解

package com.atguigu.springcloud;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;import org.springframework.cloud.netflix.eureka.EnableEurekaClient;@SpringBootApplication@EnableEurekaClient@EnableCircuitBreaker  //服務降級public class PaymentHystrixMain8001 {    public static void main(String[] args) {        SpringApplication.run(PaymentHystrixMain8001.class,args);    }}

APIl加服務降級注解

package com.atguigu.springcloud.service;import com.netflix.hystrix.contrib.javanica.annotation.HystrixCollapser;import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;import org.springframework.stereotype.Service;import java.util.concurrent.TimeUnit;@Servicepublic class PaymentHystrixService {    /**     * 正常訪問     */    public String paymentInfo_OK(Long id) {        return "線程池: "+Thread.currentThread().getName()+"   paymentInfo_OK id: "+id+"\t哈哈";    }    /**     * 模擬出錯     */    @HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler", commandProperties = {            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "3000")}    )    public String paymentInfo_TimeOut(Long id){        int timeNum = 5000;//超時        //int timeNum = 5000;//程序出錯        try {            TimeUnit.MILLISECONDS.sleep(timeNum);        }catch (InterruptedException e) {            e.printStackTrace();        }        return "線程池:   "+Thread.currentThread().getName()+"   paymentInfo_Timeout id: "+id+"\t耗時(毫秒)"+timeNum;    }    //服務降級FallBack    public String paymentInfo_TimeOutHandler(Long id){        return "線程池:   "+Thread.currentThread().getName()+"   paymentInfo_TimeOutHandler id: "+id+"現訪問人數過多或服務出錯,請悄后再試 /(ㄒoㄒ)/~~";    }}

測試

分別測試服務超時與程序出錯測試

image-20210425181002396

消費端8080服務降級

在8080模塊,yml中加入配置 用於Feign開啟hystrix -> 主啟動類加入 @EnableCircuitBreaker -> controller中使用服務降級,具體是寫FallBack,FallBack方法參數可以與原API可相同,然后在調用的API上加入注解

yml : feign.hystrix.enabled=true

server:  port: 8080eureka:  client:    register-with-eureka: false    service-url:      defaultZone: https://7001.cn.utools.club/eureka,https://7002.cn.utools.club/eurekaribbon:  ReadTimeout: 5000  #建立連接超時時間  ConnectTimeout: 5000 #建立連接到服務器讀取到杉資源所用的時間#開啟feign:hystrix屬性 feign:  hystrix:    enabled: true

主啟動類: 加入 @EnableCircuitBreaker //激活Hystrix

package com.atguigu.springcloud;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;import org.springframework.cloud.netflix.eureka.EnableEurekaClient;import org.springframework.cloud.openfeign.EnableFeignClients;@SpringBootApplication@EnableFeignClients@EnableCircuitBreaker  //激活Hystrixpublic class OrderHystrixMain8080 {    public static void main(String[] args) {        SpringApplication.run(OrderHystrixMain8080.class,args);    }}

業務 Controller: 即寫FallBack 方法,然后配置注解,不清楚? 即在可能出現服務調用與程序出錯的API上加入注解,

@HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler",commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "1500")
})

且依賴FallBack 方法:

//FallBack
public String paymentInfo_TimeOutHandler (@PathVariable("id") Long id) {
return "我是消費者8080,對方制度系統繁忙,請10秒后再試,參數是"+id+"或者自己運行出錯請自行檢查,/(ㄒoㄒ)/~~";
}

package com.atguigu.springcloud.controller;import com.atguigu.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.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.PathVariable;import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;@RestController@Slf4jpublic class OrderHystrixController {    @Resource    private PaymentHystrixService paymentHystrixService;    //正常訪問    @GetMapping("/consumer/payment/hystrix/OK/{id}")    public String paymentInfo_OK(@PathVariable("id") Long id) {        String info_ok = paymentHystrixService.paymentInfo_OK(id);        return info_ok;    }    //延時訪問    @HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler",commandProperties = {            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "1500")    })    @GetMapping("/consumer/payment/hystrix/Timeout/{id}")    public String paymentInfo_TimeOut(@PathVariable("id") Long id) {        int i = 1/0;        String timeOut = paymentHystrixService.paymentInfo_TimeOut(id);        return timeOut;    }    //FallBack    public String paymentInfo_TimeOutHandler (@PathVariable("id") Long id) {        return "我是消費者8080,對方制度系統繁忙,請10秒后再試,參數是"+id+"或者自己運行出錯請自行檢查,/(ㄒoㄒ)/~~";    }}

全局默認BackFall :如果第個API都進行服務降級FallBack的話,按上面都要寫一個FallBack方法,那就非常傻X的,於是我們要寫一個全局的FallBack 再在Controll類上加一個注解。不懂??即創建一個全局的FallBack再API在類上加注解@DefaultProperties(defaultFallback = "payment_Global_FallbackMethod"),然后原來的API只需加@HystrixCommand 即可 注解中指定了全局的FallBack方法。

package com.atguigu.springcloud.controller;import com.atguigu.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.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;    //正常訪問    @GetMapping("/consumer/payment/hystrix/OK/{id}")    public String paymentInfo_OK(@PathVariable("id") Long id) {        String info_ok = paymentHystrixService.paymentInfo_OK(id);        return info_ok;    }    //延時訪問    @HystrixCommand     @GetMapping("/consumer/payment/hystrix/Timeout/{id}")    public String paymentInfo_TimeOut(@PathVariable("id") Long id) {        int i = 1/0;        String timeOut = paymentHystrixService.paymentInfo_TimeOut(id);        return timeOut;    }    public String paymentInfo_TimeOutHandler (@PathVariable("id") Long id) {        return "我是消費者8080,對方制度系統繁忙,請10秒后再試,參數是"+id+"或者自己運行出錯請自行檢查,/(ㄒoㄒ)/~~";    }    public String payment_Global_FallbackMethod() {        return "Global異常處理信息,請稍后再試,/(ㄒoㄒ)/~~";    }}

生產端宕機Feign服務端服務降級

以下步驟:在yml中加入配置,因為我們要在Feign接口上使用hystrix的注解 -> 繼承消費端Feign接口 -> 在消費端Feign接口上加上@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT",fallback = OrderHystrixFallback.class) 表示當調用對應CLOUD-PROVIDER-HYSTRIX-PAYMENT 服務上的API失敗,即宕機時,用哪個繼承類當作Fallback

開始:

yml配置

#開啟feign:hystrix屬性feign:  hystrix:    enabled: true

業務

Feign service接口

package com.atguigu.springcloud.service;import com.atguigu.springcloud.service.impl.OrderHystrixFallback;import org.springframework.cloud.openfeign.FeignClient;import org.springframework.stereotype.Component;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.PathVariable;@Component@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT",fallback = OrderHystrixFallback.class)public interface OrderHystrixService {    //正常訪問    @GetMapping("/payment/hystrix/OK/{id}")    public String paymentInfo_OK(@PathVariable("id") Long id);    //延時訪問    @GetMapping("/payment/hystrix/Timeout/{id}")    public String paymentInfo_TimeOut(@PathVariable("id") Long id);}

Feign 服務接口的impl (宕機FeignBack)

package com.atguigu.springcloud.service.impl;import com.atguigu.springcloud.service.OrderHystrixService;import org.springframework.stereotype.Component;@Componentpublic class OrderHystrixFallback implements OrderHystrixService {    @Override    public String paymentInfo_OK(Long id) {        return "---OrderHystrixFallback fall back-paymentInfo_OK,/(ㄒoㄒ)/~~";    }    @Override    public String paymentInfo_TimeOut(Long id) {        return "---OrderHystrixFallback fall back-paymentInfo_TimeOut,/(ㄒoㄒ)/~~";    }}

測試

正常運行注冊中心與生產端消費端,消費端調用生產端正常,然后關閉生產端(宕機,啟用后備),再測試服務降級:

image-20210427091904198

服務熔斷: 熔斷機制是應對雪崩的一種微服務鏈路保護機制,當扇出鏈路的某個微服務出錯不可用或者 響應時間太長時,會進行服務降級,進而熔斷該節點微服務的調用,快速返回錯誤的響應 信息。

當檢測到該節點微服務調用響應正常,恢復調用鏈路。

不懂??服務熔斷會觸發服務降級,准確地來說,服務熔斷也是服務降級的一種,但又不完全是。

ps: 馬丁福勒關於服務熔斷的論文:https://martinfowler.com/bliki/CircuitBreaker.html

服務熔斷

以下步驟(服務熔斷):

cloud-provider-hystrix-payment8001 PaymentHystrixService類寫方法(id是正數訪問正常,負數訪問出錯),該方法具有服務熔斷(具體怎么會熔斷,請看下面PaymentHystrixService 類 41行開始的代碼注釋)說的是當時間窗內,訪問次數達到閥值,並失敗率也達到閥值,斷路器就會打開 ->並在該類下寫服務熔斷后服務降級的方法FallBack -> 測試 -> HystrixCommandPropertiesHystrix配置類

開始:

服務熔斷方法 服務熔斷后服務降級的方法FallBack

package com.atguigu.springcloud.service;import cn.hutool.core.util.IdUtil;import com.netflix.hystrix.contrib.javanica.annotation.HystrixCollapser;import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;import org.springframework.stereotype.Service;import org.springframework.web.bind.annotation.PathVariable;import java.util.concurrent.TimeUnit;@Servicepublic class PaymentHystrixService {    /**     * 正常訪問     */    public String paymentInfo_OK(Long id) {        return "線程池: "+Thread.currentThread().getName()+"   paymentInfo_OK id: "+id+"\t哈哈";    }    /**     * 模擬出錯     */    @HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler", commandProperties = {            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "3000")}    )    public String paymentInfo_TimeOut(Long id){        int timeNum = 5000;        try {            TimeUnit.MILLISECONDS.sleep(timeNum);        }catch (InterruptedException e) {            e.printStackTrace();        }        return "線程池:   "+Thread.currentThread().getName()+"   paymentInfo_Timeout id: "+id+"\t耗時(毫秒)"+timeNum;    }    //服務降級FallBack    public String paymentInfo_TimeOutHandler(Long id){        return "線程池:   "+Thread.currentThread().getName()+"   paymentInfo_TimeOutHandler id: "+id+"現訪問人數過多或服務出錯,請悄后再試 /(ㄒoㄒ)/~~";    }    //==============================服務熔斷================================    @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 serialNnmber = IdUtil.simpleUUID();        return Thread.currentThread().getName()+"\t調用成功,流水號:"+serialNnmber;    }	//服務熔斷后服務降級的方法FallBack    public String paymentCircuitBreaker_fallback(@PathVariable("id") Integer id){        return "id不能為復數,請稍后再試,/(ㄒoㄒ)/~~    id:"+id;    }}

測試

開啟 cloud-provider-hystrix-payment8001 ,訪問服務熔斷的方法

測試過程:

訪問 http://localhost:8001/payment/circuit/1 正數,測試訪問正常

訪問 http://localhost:8001/payment/circuit/-1 負數訪問異常,同時Ctrl+R不斷刷新,提高失敗率以觸發服務熔斷

image-20210427111021863

訪問 http://localhost:8001/payment/circuit/1 正數,發現會觸發FallBack,因為這時已經不是閉合狀態了,不斷刷新,提高成功率,熔斷器閉合,訪問正常

image-20210427111030944

all配置 All全部配置 : 探尋HystrixCommandPropertiesHystrix配置類, 部分如下:

//如果 滾動時間窗(默認10秒) 內僅收到19個請求,即使這19個請求都失敗了,斷路器也不會打開 (75行)@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "20")//該屬性用來設置滾動時間窗中,表示在滾動時間窗中,在請求數量超時,circuitBreaker.requestVolumeThreshold 的情況下,如果錯誤請求數的百分比超過50//就把斷路器設置為“打開”狀態,否則就設置為“關閉”狀態@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "50")//該屬性用來設置當斷路器打開之后的休眠時間窗,休眠時間窗結束之后,會將斷路器設置為“半開” 狀態,嘗試熔斷的請求命令,如果依然失敗就將斷路器設置為“打開”狀態@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "5000")//斷路器強制打開@HystrixProperty(name = "circuitBreaker.forceOpen",value = "false")//斷路器強制關閉@HystrixProperty(name = "circuitBreaker.forceClosed",value = "false")//滾動時間窗設置,該時間用於斷路器判斷健康度時需要收集信息的持續時間@HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds", value = "10000")//該屬性用來設置滾動時間窗統計指標信息時划分“桶”的數量,斷路器在收集指標信息的時候會根據設置的時間長度拆分成多個“桶”來累計各度量值,每個“桶”記錄了一段時間內的采集指標。比如10秒內拆分成10個“桶”收集這樣,所以timeinMilliseconds,必須能被NumBuckets整除,否則會拋出異常@HystrixProperty(name = "metrics.rollingStats.numBuckets",value = "10")//該屬性用來設置對命令執行的延遲是否使用百分位數來跟蹤和計算,如果設置為false ,那么所有的概要統計都將返回-1@HystrixProperty(name = "metrics.rollingPercentile.enabled",value = "false")

Hystrix服務限流: 不展開講,需要了解:https://www.cnblogs.com/huanchupkblog/p/10772274.html

img

Hystrix圖形化Dashboard搭建:

概述:
除了隔離依賴服務的調用外,Hystrix還提供了准實時的調用監控(Hystrix Dashboard),Hystrix會持續地記錄所有通過Hystrix發起的請求執行信息,並以統計報表和圖形展示給用戶,包括每秒執行多少請求多少成功,多少失敗等等,Netflix 通過hystrix-metrics-event-stream項目,實現了對以上指標的監控,SpringCloud也提供了HystrixDashboard的整合,對監控內容轉化成可視化界面;

搭建:

搭建一個模塊,是圖形化Dashboard,其它被監視模塊,需要加一個依賴,不懂??就是塔建一個模塊,啟動后,其它有指定依賴的模塊將被監視

以下步驟 (搭建圖形化DashBoard):創建一個模塊,hystrix-dashboard-consumer9001 -> 改pom ->寫yml -> 主啟動類 -> 啟動服務測試 -> 實時監控實戰

開始:

創建一個模塊

hystrix-dashboard-consumer9001

改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>springcloud2021</artifactId>        <groupId>com.atguigu</groupId>        <version>1.0-SNAPSHOT</version>    </parent>    <modelVersion>4.0.0</modelVersion>    <artifactId>hystrix-dashboard-consumer9001</artifactId>    <properties>        <maven.compiler.source>8</maven.compiler.source>        <maven.compiler.target>8</maven.compiler.target>    </properties>    <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></project>

寫yml

server:  port: 9001

主啟動類 : 主要是@EnableHystrixDashboard注解

package com.atguigu.springcloud;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;@SpringBootApplication@EnableHystrixDashboardpublic class HystrixDashboard9001 {    public static void main(String[] args) {        SpringApplication.run(HystrixDashboard9001.class,args);    }}

啟動服務測試

http://localhost:9001/hystrix

實時監控實戰

監控端 cloud-provider-hystrix-payment8001

確保pom中存在以下依賴:

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

主啟動添加方法getServlet

package com.atguigu.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;    }}

啟動服務 8001:cloud-provider-hystrix-payment8001

9001監控8001:填寫監控地址,http://localhost:8001/hystrix.strea

9001會監控8001的服務熔斷

Zuul路由網關

待掛

https://www.cnblogs.com/itzlg/p/10699575.html

zuul官網:https://github.com/Netflix/zuul/wiki

Gateway新一代網關

Gateway簡介: 是springcloud官方出的

SpringCloud Gateway與Zuul的區別: Geteway是非阻塞異步的,而Zool 1.x是阻塞不支持長連接,Zool 2.x 支持非阻塞和支持長連接

Gateway核心部分:Router(路由)、Predicate(斷言)、Filter(過濾器)

工作流程:以下更多查看

Gateway官網:https://spring.io/projects/spring-cloud-gateway#learn

更多參考 :https://blog.csdn.net/weixin_44449838/article/details/110847357

img

Gateway網關的搭建: 它是服務的擋風口,搭建中Cateway需要注冊在注冊中心中,且在yml配置好路由后,我們就可以進行測試了。

以下步驟:創建Gatway網關模塊 -> 改pom -> 寫yml -> 主啟動類 -> 測試 -> 路由書寫的第二種方式

開始:

創建Gatway網關模塊

創建 cloud-gateway-9527 模塊為Gatway網關模塊

改pom

注意不要再加 spring-boot-starter-webspring-boot-starter-actuator 依賴。否則報錯

<?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>springcloud2021</artifactId>        <groupId>com.atguigu</groupId>        <version>1.0-SNAPSHOT</version>    </parent>    <modelVersion>4.0.0</modelVersion>    <artifactId>cloud-gateway-9527</artifactId>    <properties>        <maven.compiler.source>8</maven.compiler.source>        <maven.compiler.target>8</maven.compiler.target>    </properties>    <dependencies>        <!--新增gateway-->        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-starter-gateway</artifactId>        </dependency>        <dependency>            <groupId>com.atguigu</groupId>            <artifactId>cloud-common</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></project>

寫yml

里面包含路由信息注冊中心的信息

server:  port: 9527spring:  application:    name: cloud-gateway  cloud:    gateway:      routes:        # 路由 1        - id: payment_routh #路由的ID,沒有固定規則但要求唯一,建議配合服務名          uri: http://localhost:8001   #匹配后提供服務的路由地址          predicates:            - Path=/provider/payment/get/**   #斷言,路徑相匹配的進行路由        # 路由 2        - id: payment_routh2          uri: http://localhost:8001          predicates:            - Path=/provider/payment/lb/**   #斷言,路徑相匹配的進行路由eureka:  instance:    hostname: cloud-gateway-service  client:    register-with-eureka: true    fetch-registry: true    service-url:      defaultZone: https://7001.cn.utools.club/eureka,https://7002.cn.utools.club/eureka

主啟動類

注冊中心的客戶端注解

package com.atguigu.springcloud;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.netflix.eureka.EnableEurekaClient;@SpringBootApplication@EnableEurekaClientpublic class GatewayMain9527 {    public static void main(String[] args) {        SpringApplication.run(GatewayMain9527.class, args);    }}

測試

啟動 Eureka-7001、Provider-8001、Gateway-9527

訪問:上面的yml中配置了兩個路由信息,我們先用原服務url進行測試,即 http://localhost:8001/provider/payment/get/1 再修改端口為網關的 url http://localhost:9527/provider/payment/get/1 ,發現訪問結果一致。

路由書寫的第二種方式

在Gateway網關中com.atguigu.springcloud下創建config包,配置書寫方式如下:

package com.atguigu.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;@Configurationpublic class GateWayConfig {    @Bean    public RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder) {        RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();        routes.route("my_router", r -> r.path("/provider/payment/timeout").uri("http://localhost:8001")).build();        return routes.build();    }}

uri有path與無path區別,有path使用該path,無path,將自動匹配,即斷言的path。

Gateway 動態路由配置: 默認情況下Gateway 會根據注冊中心注冊的服務列表以注冊中心上微服務名為路徑創建動態路由進行轉發,從而實現同動態路由的功能。不懂??也就是根據注冊中心來實現動態路由

以下步驟:在 cloud-gateway-9527 (基本Gateway網關功能) -> 改pom -> 測試

開始:

在 cloud-gateway-9527(基本Gateway網關功能)

即 cloud-gateway-9527 官網注冊在注冊中心中

改pom

添加了開啟從注冊中心主動態創建路由的功能 配置與修改路由的 uri

server:  port: 9527spring:  application:    name: cloud-gateway  cloud:    gateway:      # 開啟從注冊中心動態創建路由的功能,利用微服務名進行路由      discovery:        locator:          enabled: true      routes:        # 路由 1        - id: payment_routh #路由的ID,沒有固定規則但要求唯一,建議配合服務名          #uri: http://localhost:8001   #匹配后提供服務的路由地址          uri: lb://CLOUD-PROVIDER-SERVICE # 修改為服務名稱          predicates:            - Path=/provider/payment/get/**   #斷言,路徑相匹配的進行路由        # 路由 2        - id: payment_routh2          #uri: http://localhost:8001          uri: lb://CLOUD-PROVIDER-SERVICE # 修改為服務名稱          predicates:            - Path=/provider/payment/lb/**   #斷言,路徑相匹配的進行路由eureka:  instance:    hostname: cloud-gateway-service  client:    register-with-eureka: true    fetch-registry: true    service-url:      defaultZone: https://7001.cn.utools.club/eureka,https://7002.cn.utools.club/eureka

測試

開啟 cloud-provider-payment8001 cloud-provider-payment8002 cloud-eureka-server7001 cloud-eureka-server7002 與網關cloud-gateway-9527

訪問:http://localhost:9527/provider/payment/lb

結果:在8001與8002之間進行輪詢,成功~

常用的Router Predicate :Predicate就是斷言, 我們使用斷言就可以進行路由匹配了。如 path 也屬於斷言。

下面我們就逐一演示!

以下步驟(斷言演示):After、Before、Between -> Cookie -> Header -> Host -> Method -> Path -> Query

1) After、Before、Between

拿After,配置好后效果是在某時間后該路由才生效,不符合將報404, 演示:

配置:在-Path**下加-After=2021-05-04T10:29:24.264+08:00[Asia/Shanghai] 即:

存在的問題:如何獲取上面的時間格式,創建一個測試類返回當前的上面的時間格式:

import com.thoughtworks.xstream.converters.time.ZonedDateTimeConverter;import java.time.ZonedDateTime;public class T2 {    public static void main(String[] args) {        ZonedDateTime zdt = ZonedDateTime.now();        System.out.println(zdt);    }}

測試:當前是在上面時間之后,通過Gateway 訪問 http://localhost:9527/provider/payment/lb 正常,將時間配置成當前時間之后時(不符合時),再訪問:

Before、Between自行演示~

2) Cookie

配置好后效果是只有攜帶指定Cookie與對應value時才能正常訪問,否則將報404, 演示:

配置:配置:在-Path**下加- Cookie=username,zs 即:

測試:使用curl進行測試,下面演示的是cookie的value是否正確

結果:cookie的value值不符合上面的配置時,報404找不到的錯誤

存在的問題:curl返回中文亂碼解決 https://blog.csdn.net/leedee/article/details/82685636

curl http://127.0.0.1:9527/provider/payment/lb --cookie "username=zs"

3) Header

配置好后效果是只有攜帶指定Header與符合配置好正則的value時才能正常訪問,否則將報404, 演示:

配置:配置:在-Path**下加- Header=X-Request-Id, \d+ 即:

測試:使用curl進行測試,下面演示的是請求頭X-Request-Id的value是否正確

結果:X-Request-Id的value值不符合上面的配置正則時,報404找不到的錯誤

image-20210504110505330

curl http://127.0.0.1:9527/provider/payment/lb -H "X-Request-Id:1234"

4) Host

配置好后效果是只有符合配置好的域名才能正常訪問,否則將報404, 演示:

配置:配置:在-Path**下加- Host=**.cn.utools.club 即:

存在的問題:如何進行內網映射來進行測試,請使用uTools軟件里面的 “內網穿透” 插件,特別簡單。

測試:使用curl進行測試,下面演示域名是否正確

結果:域名不符合上面的配置時,報404找不到的錯誤

curl http://localhost:9527/provider/payment/lb 與 curl http://gateway9527.cn.utools.club/provider/payment/lb

5)Path

最基本的Predicate (斷言) ,上面的演示都有,作用是 請求的Path是否存在符合的路由Path,有可訪問,無報404找不到

6) Query

配置好后效果是只有Path存在符合的Query時才能正常訪問,否則將報404, 演示:

配置:配置:在-Path**下加- Query=username, \d+ 即:

測試:使用curl進行測試,下面演示的是Query 中username的value是否符合配置

結果:Query 中username的value不符合上面的配置正則時,報404找不到的錯誤

curl http://localhost:9527/provider/payment/lb?username=1234

與 curl http://localhost:9527/provider/payment/lb?username=-1234

Gateway 過濾器:

路由過濾器可用於修改進入的HTTP請求和返回的HTTP響應,路由過濾器只能指定路由進行使用。
Spring Cloud Gateway內置了多種路由過濾器,他們都由GatewayFilter的工廠 類來產生。但我們用的是自定義的過濾器。

以下步驟:內置Filter -> 自定義Filter

開始:

內置Filter

請查看官網:

https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gatewayfilter-factories

自定義Filter

在com.atguigu.springcloud 下創建一個 filter包 寫如下自定義Filter類:

(只有當Query存在uname時才能訪問成功)

package com.atguigu.springcloud.filter;import lombok.extern.slf4j.Slf4j;import org.apache.commons.lang.StringUtils;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@Slf4jpublic class MyLogGateWayFilter implements GlobalFilter, Ordered {    @Override    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {        log.info("******come in MyLogGateWayFilter: "+new Date());        if(StringUtils.isEmpty(exchange.getRequest().getQueryParams().getFirst("uname"))) {            log.info("*****用戶名為Null 非法用戶,(┬_┬)");            exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);//給人家一個回應            return exchange.getResponse().setComplete();        }        return chain.filter(exchange);    }    /** 加載過濾器順序,返回值越小,優先級越高 */    @Override    public int getOrder() {        return 0;    }}

測試: 存在uname 訪問成功,Query不存在uname 訪問失敗

Config 分布式配置中心

SpringCloud Config為微服務架構中的微服提供集中化的外部配置支持,配置服務器為各個不同微服務應用的所有環境提供了一個中心化的外部配置。

Config服務端

以下步驟:搭建Git環境 -> 創建Config模塊 cloud-config-center-3344 來讀取git倉庫的配置文件 -> 配置文件讀取規則

搭建Git環境

在github賬號下創建一個springcloud-config 的git倉庫,然后git clone XXX 到本地,創建文件 config-dev.yml config-test.yml config-prod.yml 內容這里寫的是

config:  info: "config-dev.yml"

...

然后git push origin master 到github遠程倉庫上。

創建Config模塊來讀取git倉庫的配置文件

模塊名為 cloud-config-center-3344

改pom

引入的是:spring-cloud-config-server

<?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>springcloud2021</artifactId>        <groupId>com.atguigu</groupId>        <version>1.0-SNAPSHOT</version>    </parent>    <modelVersion>4.0.0</modelVersion>    <artifactId>cloud-config-center-3344</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-config-server</artifactId>        </dependency>        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>        </dependency>        <dependency>            <groupId>com.atguigu</groupId>            <artifactId>cloud-common</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></project>

寫yml

server:  port: 3344spring:  application:    name: cloud-config-center  cloud:    config:      server:        git:          # 填寫自己的 github 路徑,注意如果用的是ssh鏈接,就算你電腦存在ssh並在github賬號加入了,也可能會認證失敗,因此在這里用的是http鏈接          uri: https://github.com/18476305640/springcloud-config.git            search-paths:            - springcloud-config  #倉庫名      label: master   #分支名eureka:  client:    service-url:      defaultZone: https://7001.cn.utools.club/eureka,https://7002.cn.utools.club/eureka

主啟動類

package com.atguigu.springcloud;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.config.server.EnableConfigServer;@SpringBootApplication@EnableConfigServerpublic class ConfigCenterMain3344 {    public static void main(String[] args) {        SpringApplication.run(ConfigCenterMain3344 .class,args);    }}

測試

讀取遠程倉庫 springcloud-config 下的config-dev.yml文件

訪問:http://localhost:3344/master/config-dev.yml

結果(如下成功):

配置文件讀取規則

示例:http://localhost:3344/master/config-dev.yml

規則:label是上面是分支(master),application-profile 就是上面的config-dev.yml中的config-dev

Config客戶端: 即從Config服務端獲取yml配置作為自己的配置

以下步驟:創建Config客戶端模塊cloud-config-client-3355 -> 改pom -> 寫yml -> 主啟動類 -> 業務類 -> 測試(存在的問題) -> 動態刷新(解決問題)

1) 創建Config客戶端模塊

創建模塊 cloud-config-client-3355

2)改pom

引入的是:spring-cloud-starter-config

<?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>springcloud2021</artifactId>        <groupId>com.atguigu</groupId>        <version>1.0-SNAPSHOT</version>    </parent>    <modelVersion>4.0.0</modelVersion>    <artifactId>cloud-config-client-3355</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-config</artifactId>        </dependency>        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>        </dependency>        <dependency>            <groupId>com.atguigu</groupId>            <artifactId>cloud-common</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></project>

3)寫yml:

bootstrap.yml

server:  port: 3355spring:  application:    name: config-client  cloud:    config:      # 分支名稱      label: master      # 配置文件名      name: config      # 環境      profile: dev      # 配置中心的地址      uri: http://localhost:3344eureka:  client:    service-url:      defaultZone: https://7001.cn.utools.club/eureka,https://7002.cn.utools.club/eureka

4)主啟動類

package com.atguigu.springcloud;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplicationpublic class ConfigClientMain3355 {    public static void main(String[] args) {        SpringApplication.run(ConfigClientMain3355.class,args);    }}

5)業務類

controller層

package com.atguigu.springcloud.controller;import org.springframework.beans.factory.annotation.Value;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RestController;@RestControllerpublic class ConfigClientController {    @Value("${config.info}")    private String configInfo;    @GetMapping("/configInfo")    public String getConfigInfo(){        return configInfo;    }}

6)測試(存在問題)

啟動注冊中心7001、7002與Config 服務端3344再啟動Config客戶端3355 ,注意如果3344無法獲取到github上的yml文件,Config客戶端3355 將失敗!

訪問:http://localhost:3355/configInfo

結果:輸出 config-dev.yml ,成功!

存在的問題:Config客戶端3355只在啟動時從Config服務端3344獲取yml,而無法實時更新yml

7)動態刷新(解決問題)

問題:Config客戶端3355只在啟動時從Config服務端3344獲取yml,而無法實時更新yml

最終效果:github上yml配置文件修改后,Config客戶端想要從3355獲取最新的配置文件,不用重啟,運維人員只需向Config客戶端3355發一個post請求即可通知刷新。

以下步驟:改pom -> 改yml -> 改業務 -> 測試

改pom

確保存在以下依賴

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

改yml

management:  endpoints:    web:      exposure:        include: "*"

改業務

在Config類上加入 @RefreshScope 注解

測試

啟動注冊中心7001、7002與Config 服務端3344再啟動Config客戶端3355 ,注意如果3344無法獲取到github上的yml文件,Config客戶端3355 將失敗!

訪問: http://localhost:3355/configInfo

改github上的yml

再次訪問,結果:與之前一樣

發送post請求通知Config客戶端刷新配置文件yml : curl -X POST "http://localhost:3355/actuator/refresh"

再次訪問,結果:配置文件已經刷新!測試成功

Bus 消息總線

簡介

是什么:Spring Cloud Bus是用來將分布式系統的節點與輕量級消息系統鏈接起來的框架,它整合了Java的事件處理機制和消息中間件的功能
目前支持RabbitMQ和Kafka

能做什么:

Spring Cloud Bus能管理和傳播分布式系統間的消息,就像一個分布式執行器,可用於廣播狀態的更改、事件推送等,也可以當做微服務間的通訊通道

消息總線

在微服務架構的系統中,通常會使用輕量級的消息代理來構架一個共用的消息主題,並讓系統中所有微服務實例都連接上來。由於該主題中產生的消息會被所有實例監聽和消費,所以稱它為消息總線。在總線的各個實例,都可以方便地廣播一些需要讓連接在該主題上的實例都知道的消息
基本原理:ConfigClient實例都監聽MQ中同一個topic(默認是springCloudBus)。當一個服務刷新數據的時候,它會把這個信息放到Topic中,這樣其他監聽同一Topic的服務就能得到通知,然后去更新自身的配置

Bus動態刷新全局廣播的設計思想:

方案1 :利用消息總線觸發一個 客戶端 /bus/refresh ,而刷新所有客戶端的配置

方案2 :利用消息總線觸發一個服務端ConfigServer 的 /bus/refresh 端點,而刷新所有客戶端的配置(更加推薦)

**推薦:方案2,方案1客戶端不適合的原因是: **

​ 微服務本身是業務模塊,它本不應該承擔配置刷新職責、

​ 破壞了微服務各節點的對等性、

​ 有一定的局限性。例如,微服務在遷移時,它的網絡地址常常會發生變化,此時如果想要做到自動刷新,那就會增加更多的修改

RibbitMQ環境:安裝RabbitMQ,因為RabbitMQ依賴Erlang所以也需要安裝Erlang安裝完成后,就啟動RabbitMQ服務了

以下步驟:安裝Erlang -> 安裝RabbitMQ -> 啟動RabbitMQ服務

開始:

1) Erlang的安裝:

下載:直鏈 官網 博主版本

安裝過程:可一路回車

2) RabbitMQ 的安裝

下載: 直鏈 官網 博主版本

安裝過程:可一路回車

3) 啟動RabbitMQ服務

請保證設備名稱為英文

1)進入RabbitMQ安裝目錄的sbin目錄下,cmd執行 (先不要關掉):

rabbitmq-plugins enable rabbitmq_management

  1. 在win右下角菜單中選擇點擊 RabbitMQ Service - start

3)再在sbin cmd命令中執行:rabbitmqctl start_app

  1. 訪問地址,查看是否成功: http://localhost:15672/

5)登錄

賬號 :guest
密碼 :guest

利用消息總線解決Config存在的問題:

在分布式配置中心中,存在的問題是我們就能通知一個客戶端模塊一個客戶端模塊地通知刷新,做不到一次修改,廣播通知,處處生效。在下面我們就是圍繞着解決這個問題來做的。在上面我們已經創建了3344服務端模塊與3355客戶端模塊,我們還需要創建一個客戶端模塊3366。且向將處於消息總線系統的模塊即3344(服務端)與33344、3355 (客戶端) 加入配置,即加入消息總線系統中。然后再測試

以下步驟:克隆3355客戶端模塊即3366客戶端模塊(建module -> 改pom -> 寫yml -> 主啟動類 -> 業務類) -> 將3344、3355、3366模塊加入消息總線系統 -> 測試(廣播通知) -> 定點通知

1) 克隆3355客戶端模塊即3366客戶端模塊

建module

cloud-config-client-3366

改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>springcloud2021</artifactId>        <groupId>com.atguigu</groupId>        <version>1.0-SNAPSHOT</version>    </parent>    <modelVersion>4.0.0</modelVersion>    <artifactId>cloud-config-client-3366</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-config</artifactId>        </dependency>        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>        </dependency>        <dependency>            <groupId>com.atguigu</groupId>            <artifactId>cloud-common</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></project>

寫yml

bootstrap.yml

server:  port: 3366spring:  application:    name: config-client  cloud:    config:      # 分支名稱      label: master      # 配置文件名      name: config      # 環境      profile: dev      # 配置中心的地址      uri: http://localhost:3344eureka:  client:    service-url:      defaultZone: https://7001.cn.utools.club/eureka,https://7002.cn.utools.club/eurekamanagement:  endpoints:    web:      exposure:        include: "*"

主啟動類

package com.atguigu.springcloud;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.netflix.eureka.EnableEurekaClient;@EnableEurekaClient@SpringBootApplicationpublic class ConfigClientMain3366 {    public static void main(String[] args) {        SpringApplication.run( ConfigClientMain3366.class,args);    }}

業務類

com.atguigu.springcloud -> controller

package com.atguigu.springcloud.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@RefreshScopepublic 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;    }}

2) 將3344、3355、3366模塊加入消息總線系統

3344模塊:

改pom

加入:

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

改yml:按注釋的“位置”,加入

spring:  # RabbitMQ 相關配置,“位置”:spring的子屬性  rabbitmq:    host: localhost    port: 5672    username: guest    password: guest
management:   # 暴露 Bus 刷新配置的端點,“位置”:與spring同層次  endpoints:    web:      exposure:        include: 'bus-refresh'

Config-Client-3355、3366模塊

改pom

加入:

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

改yml

加入:

# RabbitMQ 相關的配置rabbitmq:	host: localhost	port: 5672	username: guest	password: guest

3) 測試(廣播通知)

**啟動: **

注冊中心:7001與7002

消息總線系統/Config服務端:3344

消息總線系統/Config客戶端:3355、3366

測試步驟:修改遠程倉庫yml(config-dev.yml)配置文件, 訪問:

http://localhost:3344/config-dev.yml
http://localhost:3355/configInfo
http://localhost:3366/configInfo

發現3344yml始終保持最新yml配置文件,而客戶端3355,3366仍是啟動時的yml配置文件信息,未更新。

向服務端3344發送post請求,讓消息總線發送配置更新通知,讓客戶端配置文件更新:

打開 CMD 發送 POST 請求 curl -X POST "http://localhost:3344/actuator/bus-refresh"

再次刷新上面的3個地址,發現服務端配置文件已經是最新! 測試成功

打開RabbitMQ web:發現存在默認的springCloudBus訂閱主題

4) 定點通知

**啟動: **

​ 注冊中心:7001與7002

​ 消息總線系統/Config服務端:3344

​ 消息總線系統/Config客戶端:3355、3366

訪問:

http://localhost:3344/config-dev.yml
http://localhost:3355/configInfo
http://localhost:3366/configInfo

測試:

​ 修改github上的配置文件yml,向注冊中心即服務端3344發現post請求,要求定時通知

​ 格式:http://localhost:配置中心的端口號/actuator/bus-refresh/{destination}

​ 發出定點刷新的請求 curl -X POST "http://localhost:3344/actuator/bus-refresh/config-client:3355"

​ 結果:3355客戶端配置文件刷新配置文件信息最新,3366配置不變。測試成功

總結:

SpringCloud Stream 消息驅動

springcloud stream是一個構建”消息驅動“微服務的框架。能屏蔽底層消息中間件的差異,降低切換版本,統一消息的編程模型

在這里插入圖片描述

這些中間件的差異性導致我們實際項目開發給我們造成了一定的困擾,我們如果用了兩個消息隊列的其中一種,后面的業務需求,我想往另外一種消息隊列進行遷移,這時候無疑就是一個災難性的,一大堆東西都要重新推倒重新做,因為它跟我們的系統耦合了,這時候 springcloud Stream 給我們提供了—種解耦合的方式。

官網地址 :https://spring.io/projects/spring-cloud-stream#overview

參考詳細說明: https://blog.csdn.net/weixin_44449838/article/details/111300096

Binder :很方便的連接中間件,屏蔽差異
Channel :通道,是隊列 Queue 的一種抽象,在消息通訊系統中就是實現存儲和轉發的媒介,通過對 Channel 對隊列進行配置
Source和Sink :簡單的可理解為參照對象是 Spring Cloud Stream 自身,從 Stream 發布消息就是輸出,接受消息就是輸入

Stream消息又要去之生產者:作為生產者,完成 消息的發送,想要完成下面的功能,我們需要搭建RabbitMQ的環境進行搭建。在RabbitMQ確認搭建成功后,開始以下,下面就是創建一個Stream消息生產者8801模塊的創建。完成后進行測試,調用接口 發送信息。在RibbitMQ中可以監控到”流量“。

以下步驟:創建Stream消息生產者cloud-stream-rabbitmq-provider-8801模塊(建module-> 改pom -> 寫yml -> 主啟動類 -> 業務類 ) -> 測試

建module

cloud-stream-rabbitmq-provider-8801

改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>com.atguigu</groupId>            <artifactId>cloud-common</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>

寫yml

server:  port: 8801spring:  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: https://7001.cn.utools.club/eureka,https://7002.cn.utools.club/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地址

主啟動類

com.atguigu.springcloud -> controller

package com.atguigu.springcloud;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplicationpublic class StreamMQMain8801 {    public static void main(String[] args) {        SpringApplication.run(StreamMQMain8801.class, args);    }}

業務類

service接口

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

impl

package com.atguigu.springcloud.service.impl;import com.atguigu.springcloud.service.IMessageProvider;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.cloud.stream.annotation.EnableBinding;import org.springframework.cloud.stream.messaging.Source;import org.springframework.messaging.MessageChannel;import org.springframework.messaging.support.MessageBuilder;import java.util.UUID;@EnableBinding(Source.class)   //定義消息的推送管道, 綁定 信道(Channel)和 Exchangepublic class MessageProviderImpl implements IMessageProvider {    @Autowired    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;    }}

controller

package com.atguigu.springcloud.controller;import com.atguigu.springcloud.service.IMessageProvider;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;@RestControllerpublic class SendMessageController {    @Resource    private IMessageProvider messageProvider;    @GetMapping(value = "/sendMessage")    public String sendMessage() {        return messageProvider.send();    }}

測試

1)啟動 :

image-20210509213130588

注冊中心:7001、7002

Stream消息生產者 cloud-stream-rabbitmq-provider-8801

2)測試

訪問發送消息: http://localhost:8801/sendMessage

打開RabbitMQ web http://localhost:15672/ ,查看以下數據(即消息通道名studyExchange是否存在,studyExchange是我們創建的8801模塊的application.yml中配置的,除此還需要查看是否存在數據波動)

image-20210509212027311

image-20210509212021028

Stream消息消費者:可以監聽獲取指定生產者發送的消息,創建一個8802模塊,它是Stream消息消費者模塊。創建來后,調用方法讓生產者發送消息,然后查看消費者模塊控制台的輸出情況。

以下步驟:創建Stream消費者8002模塊(創建module -> 改pom -> 寫yml -> 主啟動類 -> 業務類) -> 測試

創建Stream消費者8002模塊

1) 創建module cloud-stream-rabbitmq-consumer-8802

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>com.atguigu</groupId>            <artifactId>cloud-common</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: 8802spring:  application:    name: cloud-stream-consumer  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: https://7001.cn.utools.club/eureka,https://7002.cn.utools.club/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地址
  1. 主啟動類
package com.atguigu.springcloud;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplicationpublic class StreamMQMain8802 {    public static void main(String[] args) {        SpringApplication.run(StreamMQMain8802.class, args);    }}
  1. 業務類

com.atguigu.springcloud ->controller

package com.atguigu.springcloud.controller;import org.springframework.beans.factory.annotation.Value;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;import org.springframework.stereotype.Component;@Component@EnableBinding(Sink.class)public class ReceiveMessageListenerController {    @Value("${server.port}")    private String serverPort;    @StreamListener(Sink.INPUT)    public void input(Message<String> message) {        System.out.println("消費者1號,接收:"+message.getPayload()+"\t port: "+serverPort);    }}

測試

1)啟動: 注冊中心7001、7002; Stream消息生產者8801; Stream消息消費者8802,打開RabbitMQ web http://localhost:15672/ 查看指定生產下有哪些監聽者(消費者)

image-20210509221006575

2)測試:調用生產者8801的方法發送消息 http://localhost:8801/sendMessage,查看消費者模塊控制台輸出

image-20210509221952531

image-20210509222156989

消息消費者與生產者存在的問題解決: 將8802消費者克隆一個新的8803模塊,然后再說明並解決他們之間存在的問題。

以下步驟: 8802消費者克隆一個新的8803模塊消費模塊 -> 解決它們之間的問題

8802消費者克隆一個新的8803模塊消費模塊

前提,我們創建了8801生產者模塊並創建一個8802消費者模塊,這時我們進行克隆。

image-20210509230008534

解決它們之間的問題

1) 重復消費問題: 默認情況下,消費者是不在同一個組的,不在同一個組時,在生產者發送消息時,它們都會接收並消費。這是我們開發中必須要解決的問題,即進行分組,分組后同一組內是競爭關系且只消費一次。

未自定義分組前: 自動分配組流水號

image-20210509233554102

分組:在消費者(8801、8802)yml中加入以下配置,group: 組名

image-20210509233746025

異組:即消費者(8801、8802)配置的 group不同 ,調用生產者(http://localhost:8801/sendMessage)發送兩次消息。測試結果:兩個消費者(8801、8802)都消費了這再次消息。

image-20210509234233129

image-20210509234520590

同組:即消費者(8801、8802)配置的 group相同 ,調用生產者(http://localhost:8801/sendMessage)發送兩次消息。測試結果:兩個消費者(8801、8802)輪詢的方式消費了消息。

image-20210509235052684

image-20210509234847845

2)持久化: 當我們配置了 group 分組后,自動支持了持久化,什么是持久化即不在線時人家給你發信息,你在線了,就會收到信息(撿起消費)。

測試:

1)去掉8802分組,啟動,發送消息測試,關閉8802,發送消息測試(此時8802已經下線) ,啟動8802,查看控制台:

發現:無撿起消費!

image-20210510001723854

2) 以異組身份,即8802與8803不同group不同,同組的話另一個”下線“ 會全部被另一消費者消費。所以測試用異組,異組啟動8802,發送消息測試,關閉8802,發送消息(此時8802已經下線),啟動8802,查看控制台:

發現:被撿起消費!!

image-20210510002117799

SpringCloud Sleuth

分布式請求鏈路跟蹤:為什么會出現這個技術,要解決哪些問題。在微服務框架中,一個客戶端發起的請求在后端系統中會經過多次不同的服務節點調用來協同產生最后的請求結果,每一個前段請求都會形成一條復雜的分布式服務調用鏈路,鏈路中的任何一環出現高延時或錯誤都會引起整個請求最后的失敗。SpringCloudSleuth提供了一套完整的服務跟蹤的解決方案,在分布式系統中提供樂追蹤解決方案並且兼容支持了zipkin。

Sleuth :來負責跟蹤整理

zipkin:負責展現

以下步驟:啟動Zipkin -> 向8001、8080加入zipkin依賴,創建測試方法 -> 測試

啟動Zipkin

下載: 點擊下載Zipkin-server

啟動: java -jar zipkin-server-2.12.9-exec.jar

image-20210510153902560

打開Web: http://localhost:9411/zipkin/ 因為我服務器啟動失敗(報錯),所以在虛擬機上啟動:http://192.168.44.130:9411/zipkin/

image-20210510153931062

向8001、8080加入zipkin依賴,創建測試方法

cloud-provider-payment8001

改pom: 加入

        <!-- 包含了sleuth zipkin 數據鏈路追蹤-->        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-starter-zipkin</artifactId>        </dependency>

寫yml

spring:  application:    name: cloud-provider-service  datasource:    type: com.alibaba.druid.pool.DruidDataSource      #當前數據源操作類型    driver-class-name: org.gjt.mm.mysql.Driver        #mysql驅動包    url: jdbc:mysql://localhost:3306/db2019?useUnicode=true&characterEncoding=utf-8&useSSL=false    username: root    password: 3333  #zipkin配置  zipkin:    base-url: http://192.168.44.130:9411 # 指定zipkin地址    sleuth:      sampler:        # 采樣率介於0~1之間,1則表示全部采集        probability: 1

業務類

/** * 測試鏈路監控 */@GetMapping("/payment/zipkin")public String paymentZipkin(){    return "hi, i'am paymentZipkin server fall back, welcome to here, O(∩_∩)O哈哈~";}

cloud-provider-payment8080

改pom: 加入

        <!-- 包含了sleuth zipkin 數據鏈路追蹤-->        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-starter-zipkin</artifactId>        </dependency>

寫yml

server:  port: 8080spring:  application:    name: cloud-consumer-order  #zipkin配置  zipkin:    base-url: http://192.168.44.130:9411 # 指定zipkin地址    sleuth:      sampler:        # 采樣率介於0~1之間,1則表示全部采集        probability: 1

業務類

controller

/** * 測試鏈路監控 */@GetMapping("/consumer/payment/zipkin")public String paymentZipkin(){    String result = restTemplate.getForObject(PAYMENT_URL+"/provider/payment/zipkin", String.class);    return result;}

取消@LoadBalanced注釋!!!!

image-20210510171627265

測試

訪問:打開zipkin Web: http://localhost:8080/consumer/payment/zipkin

說明:訪問后,zipkin就會追蹤到鏈路。我們可以查詢服務名,剛才就是cloud-consumer-order 我們選擇查看就然后點擊“查找” ,就會查詢到追蹤的信息,點入”一項” 追蹤信息,我們可以查詢依賴,就可以查找調用信息(即誰調用誰)。

image-20210510171920407

image-20210510180122123

Alibaba Nacos

簡介:

  • Spring Cloud Alibaba 致力於提供微服務開發的一站式解決方案。此項目包含開發分布式應用微服務的必需組件,方便開發者通過 Spring Cloud 編程模型輕松使用這些組件來開發分布式應用服務。
  • 依托 Spring Cloud Alibaba,您只需要添加一些注解和少量配置,就可以將 Spring Cloud 應用接入阿里微服務解決方案,通過阿里中間件來迅速搭建分布式應用系統。
  • Nacos = Eureka+Config+Bus
  • 前4個字母分別為Naming和Configuration的前兩個字母,最后的s為service

能做什么:

  • 服務限流降級:默認支持 WebServlet、WebFlux, OpenFeign、RestTemplate、Spring Cloud Gateway, Zuul, Dubbo 和 RocketMQ 限流降級功能的接入,可以在運行時通過控制台實時修改限流降級規則,還支持查看限流降級 Metrics 監控。
  • 服務注冊與發現:適配 Spring Cloud 服務注冊與發現標准,默認集成了 Ribbon 的支持。
  • 分布式配置管理:支持分布式系統中的外部化配置,配置更改時自動刷新。
  • 消息驅動能力:基於 Spring Cloud Stream 為微服務應用構建消息驅動能力。
  • 分布式事務:使用 @GlobalTransactional 注解, 高效並且對業務零侵入地解決分布式事務問題。
  • 阿里雲對象存儲:阿里雲提供的海量、安全、低成本、高可靠的雲存儲服務。支持在任何應用、任何時間、任何地點存儲和訪問任意類型的數據。
  • 分布式任務調度:提供秒級、精准、高可靠、高可用的定時(基於 Cron 表達式)任務調度服務。同時提供分布式的任務執行模型,如網格任務。網格任務支持海量子任務均勻分配到所有 Worker(schedulerx-client)上執行。
  • 阿里雲短信服務:覆蓋全球的短信服務,友好、高效、智能的互聯化通訊能力,幫助企業迅速搭建客戶觸達通道。

去哪下:

怎么玩:

具體:

環境的搭建:

以下步驟: 下載 -> 啟動

下載https://github.com/alibaba/nacos/tags

啟動: bin目錄下執行 startup.cmd 啟動,報錯請更換jdk(博主使用:jdk11_jb51

image-20210510221828525

訪問:http://192.168.44.1:8848/nacos/index.html#/login 登錄:nacos nacos

Alibaba注冊中心

AlibabaNacos的注冊中心替代了Eureka。下面就演示,如果將服務注冊到Nacos注冊中心上。

以下步驟:創建module(9001) -> 改pom -> 寫yml -> 主啟動 -> 業務 -> 根據9001克隆9002 -> 創建消費者83 -> 測試

創建module

模塊名:cloudalibaba-provider-payment9001

改pom

    <dependencies>        <!--spring cloud alibaba nacos-->        <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>

寫yml

server:  port: 9001spring:  application:    name: nacos-payment-provider  cloud:    nacos:      discovery:        server-addr: localhost:8848 #配置Nacos地址management:  endpoints:    web:      exposure:        include: '*'  #監控

主啟動類

package com.atguigu.springcloud;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.client.discovery.EnableDiscoveryClient;@SpringBootApplication@EnableDiscoveryClientpublic class PaymentMain9001 {    public static void main(String[] args) {        SpringApplication.run(PaymentMain9001.class,args);    }}

業務

controller

package com.atguigu.springcloud.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;@RestControllerpublic 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;    }}

根據9001克隆9002

不同點:模塊名 cloudalibaba-provider-payment9002 端口:9002

測試

啟動 cloudalibaba-provider-payment9001與 cloudalibaba-provider-payment9002

發現:服務名下存在兩個節點,即9001、9002

image-20210510231537352

創建消費者83

1)創建Module

cloudalibaba-consumer-nacos-order83

2)改pom

<dependencies>        <!--spring cloud alibaba nacos-->        <dependency>            <groupId>com.alibaba.cloud</groupId>            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>        </dependency>        <!--引入我們自定義的公共api jar包-->        <dependency>            <groupId>com.atguigu</groupId>            <artifactId>cloud-common</artifactId>            <version>1.0-SNAPSHOT</version>        </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>
  1. 寫yml
server:  port: 83spring:  application:    name: naocs-order-consumer  cloud:    nacos:      discovery:        server-addr: localhost:8848 # 配置nacos注冊中心地址# 消費者將要去訪問的微服務名稱(成功注冊進nacos的微服務提供者)server-url:  nacos-user-service: http://nacos-payment-provider
  1. 主啟動類
package com.atguigu.springcloud;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.client.discovery.EnableDiscoveryClient;@SpringBootApplication@EnableDiscoveryClientpublic class PaymentMain83 {    public static void main(String[] args) {        SpringApplication.run(PaymentMain83.class,args);    }}
  1. 業務類

config -> ApplicationContextConfig

package com.atguigu.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;@Configurationpublic class ApplicationContextConfig {    @Bean    @LoadBalanced    public RestTemplate getRestTemplate() {        return new RestTemplate();    }}

controller -> OrderNacosController

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.PathVariable;import org.springframework.web.bind.annotation.RestController;import org.springframework.web.client.RestTemplate;import javax.annotation.Resource;@RestController@Slf4jpublic class OrderNacosController {    @Value("${server-url.nacos-user-service}")    private String serverURL;    @Resource    private RestTemplate restTemplate;    @GetMapping("/consumer/payment/nacos/{id}")    public String paymentInfo(@PathVariable("id") Long id) {        return restTemplate.getForObject(serverURL+"/payment/nacos/"+id,String.class);    }}

測試:

啟動生產者9001、9002 與消費者83

訪問:http://localhost:83/consumer/payment/nacos/1

發現:以輪詢的方式進行調用, Nacos天生就支持負載均衡。

image-20210513004842226

注冊中心的比較

Nacos支持AP與CP的切換

image-20210513010004421

Nacos與其他注冊中心特性對比:

image-20210513005508838

Nacos配置中心

Nacos配置中心: 完美解決了Spring Config無法自動解決配置的問題。只要我們修改Nacos中的配置文件,就會立即生效!

以下步驟: 創建3377模塊使用Nacos中的配置文件 -> 在Nacos上創建具有規則的配置文件 -> 測試 -> DataID的切換 演示-> 自定義分組group與獲取指定分組中的指定配置 -> 自定義nameSpace的演示

1) 創建3377模塊使用Nacos中的配置文件

  1. 創建模塊

​ cloudalibaba-config-nacos-client3377

2)改pom

新加入的依賴是:

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

完整依賴:

    <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)寫yml

bootstrap.yml

server:  port: 3377spring:  application:    name: nacos-config-client  cloud:    nacos:      discovery:        server-addr: localhost:8848 # Nacos服務注冊中心地址      config:        server-addr: localhost:8848 # Nacos作為配置中心地址        file-extension: yaml  # 指定yaml格式的配置

application.yml

spring:  profiles:    active: dev # 表示找一個開發環境的配置文件# ${spring.application.name}-${spring.cloud.config.file-extension}.${spring.profiles.active}  配置文件名的規則
  1. 主啟動類
package com.atguigu.com;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.client.discovery.EnableDiscoveryClient;@SpringBootApplication@EnableDiscoveryClientpublic class NacosConfigClientMain3377 {    public static void main(String[] args) {        SpringApplication.run(NacosConfigClientMain3377.class,args);    }}

5)業務類

controller ->

package com.atguigu.com.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   //支持Nacos的動態刷新功能public class ControllerClientController {    @Value("${config.info}")    private String configInfo;    @GetMapping("/config/info")    public String getConfigInfo(){        return configInfo;    }}

2) 在Nacos上創建具有規則的配置文件

${spring.application.name}-${spring.cloud.config.file-extension}.${spring.profiles.active} 配置文件名的規則

即在Nacos上創建: nacos-config-client-dev.yaml 文件

image-20210513014434913

image-20210513014230985

3)測試

啟動 3377 模塊

訪問:http://localhost:3377//config/info

發現:啟動訪問后 是version:1 ,然后修改Nacos 中的nacos-config-client-dev.yaml配置文件后,再刷新發現是version:2 ,所以Nacos的Config 解決了動態刷新問題,這主要依賴於@RefreshScope注解;

image-20210513014530299

4)DataID的切換 演示

有三個需要進行演示說明,分別是DataID、Group、NameSpace,現在演示的是DataID !!

20210129223217

以下步驟:創建一個新的配置文件 -> 在微服務3377中將application.yml中的配置spring.active 修改為test -> 測試

在Nacos上創建一個新的配置文件:nacos-config-client-test.yaml,內容是

image-20210513212143837

在微服務3377中將application.yml中的配置spring.active 修改為test

image-20210513212219434

測試

啟動:cloudalibaba-config-nacos-client3377

訪問:http://localhost:3377/config/info

結果:獲取到了配置test的配置文件。

image-20210513212330968

5)自定義分組與獲取指定分組中的指定配置

以下步驟:自定義一個分組 -> 配置從指定分組中獲取配置 -> 測試

1)自定義一個分組

image-20210513214308367

創建的分組

image-20210513214317935

2) 配置從指定分組中獲取配置

在 spring.cloud.config中加入group ,即:

image-20210513214733384

修改prifiles.active中的值,因為我們在分組中創建的文件是 nacos-config-client-info.yaml

image-20210513214534337

3)測試

開啟3377,訪問:http://localhost:3377/config/info

結果:獲取到了DEV_GROUP里面的 nacos-config-client-info.yaml 配置文件。

image-20210513214821445

切換分組獲取配置:略

6) 自定義nameSpace的演示

說明:加入nameSpace后,就完整了namespace -> group -> dataid

以下步驟:創建兩個命名空間devtest 分別在這二個命名空間內書寫兩個相同分組相同Data ID 的 配置文件-> 修改配置 -> 測試

  1. 創建兩個命名空間devtest 分別在這二個命名空間內書寫兩個相同分組相同Data ID 的 配置文件

    image-20210513222453873

    配置配置分組是DEV_GROUP DataID是info ,配置文件名:nacos-config-client-info.yaml 進行創建

  2. 修改配置:在spring.nacos.config下加入namespace,值是命名空間對應的命名空間ID

    命令空間ID:

    image-20210513222920064

    修改配置:

    image-20210513222742496

  3. 測試:啟動3377,訪問:http://localhost:3377/config/info

    發現:獲取了指定namespace-> group -> dataid下的指定的配置文件

    image-20210513223144698

    切換命名空間測試:略

Nacos在Linux上的集群配置

之前用的老師一個Nacos配置中心,但在生產環境中是不可能一台的,肯定是集群。因為如果不是集群,那么如果一個炸了,注冊到Nacos的服務集群將全部炸。所以我們必須要搞集群。

有沒有這樣的疑惑,之前我們寫的配置文件,都是什么存的?其實, 默認Nacos使用嵌入式數據庫(derby數據庫),實現數據存儲。但是如果啟動多個默認配置下的Nacos節點,每個nacos都有自己獨立呃嵌入式數據庫,存放的數據不一致。為了解決這個問題,Nacos采用了集中式存儲的方式來支持集群化部署,目前只支持MySQL存儲。

以下步驟:window下配置 -> Linux生產級別使用

window下配置

即不使用Nacos,而使用Mysql數據庫代替。需要有數據庫,第二將數據庫配置進去。

1)創建數據庫:在nacos -> conf -> nacos-mysql.sql 執行生成。

2)nacos配置mysql數據庫:在nacos -> conf -> application.properties 追加:

spring.datasource.platform=mysqldb.num=1db.url.0=jdbc:mysql://127.0.0.1:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=truedb.user=rootdb.password=3333

3)啟動nacos,訪問:http://127.0.0.1:8848/nacos/index.html 登錄(nacos、nacos)后,發現原來的數據全部消失了。

加入一條配置文件后,查看數據庫中config_info表,發現數據錄入了我們配置的mysql數據庫中了。

image-20210514003830209

集群配置環境:我們在Linux上配置好Nacos后,需要在Nacos上配置Mysql,然后再進行集群配置,也就是都是修改Nacos的配置文件。

以下步驟:在Linux上搭建Nacos -> 將Mysql配置進Nacos -> Nacos的集群環境的配置

1)在Linux上搭建Nacos

選擇下載第一個:

  • 下載好后,移動到目標目錄進行解壓。

2)將Mysql配置到 Nacos:

  • 創建數據庫:nacos_config 執行根目錄下conf > nacos-mysql.sql

  • 開始配置:將以下配置修改成你的,然后配置進Nacos根目錄下的conf > application.properties“追加” :

    #mysqlspring.datasource.platform=mysqldb.num=1db.url.0=jdbc:mysql://127.0.0.1:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=truedb.user=rootdb.password=3333
    

3)Nacos的集群配置

  1. 說明:其它就只需要配置cluster.conf與startup.sh

  2. cluster.conf:

    • 獲取自身ip地址

      vim cluster.conf

  3. 編輯啟動文件 startup.sh

    • 加入以下內容:

      b

    4.啟動:

    bash ./startup.sh  -p 3333 bash ./startup.sh  -p 4444 bash ./startup.sh  -p 5555#查看啟動個數,在根目錄下ps -ef|grep nacos|grep -v grep|wc -l
    

4)nginx的配置到集群系統

​ nginx的安裝:在Linux上:

  1. 下載:http://nginx.org/en/download.html

  2. 安裝環境:

    Ubuntu:

    apt-get install gccapt-get install libpcre3 libpcre3-devapt-get install zlib1g zlib1g-dev# Ubuntu14.04的倉庫中沒有發現openssl-dev,由下面openssl和libssl-dev替代#apt-get install openssl openssl-devsudo apt-get install openssl sudo apt-get install libssl-dev
    

    CentOS:

    yum -y install gcc pcre-devel zlib-devel openssl openssl-devel
    
  3. 安裝

    需要說明一下,我們一共需要執行三個命令,執行位置是根目錄, 我們安裝分別是:

    解壓后,將目錄修改為別的目錄,然后再創建一個目錄nginx-1.20.0作為我們安裝的目錄。

    #指定安裝目錄./configure --prefix=/usr/local/nginx/nginx-1.20.0#安裝makemake install
    
  4. 啟動

    進行安裝目錄,即/usr/local/nginx/nginx-1.20.0下的sbin/下,執行:

    ./nginx#nginx的關閉:./nginx -s stop./nginx -s quit
    

    測試命令:

    ./nginx -t
    

    顯示以下,則代表成功:

    END

5)測試

如果上面已經按步驟啟動Nacos,nginx.則可以直接進行以下測試:

訪問nginx:http://127.0.0.1:8888/nacos/#/login

發現能可以進行nacos界面,登錄進去,指向的是mysql.即實現了:

img

Alibaba Sentinel

是什么?

隨着微服務的流行,服務和服務之間的穩定性變得越來越重要。Sentinel 以流量為切入點,從流量控制、熔斷降級、系統負載保護等多個維度保護服務的穩定性。

官方中文文檔

Sentinel環境的搭建:

以下步驟: 下載 -> 移動 -> 啟動 -> 訪問測試 -> 服務監控測試

1)下載

https://github.com/alibaba/Sentinel/releases/tag/1.7.0

2)移動

3)啟動

8080端口不能被占用。

java -jar XXX.jar

4)訪問

http://127.0.0.1:8080

流控規則

我們需要創建一個服務,即8401,它測試進了8848,它注冊到了nacos注冊中心中,同時被8848被Sentinal監控。

以下步驟: 流控測試准備 -> QPS模式流控測試 -> 線程數模式流控 -> 關聯流控 -> 預熱 Warm up 流控 -> 排隊等待

開始:

1) 流控測試准備

創建模塊:cloudalibaba-sentinel-service8401

改pom:

    <dependencies>        <!--springcloud alibaba nacos-->        <dependency>            <groupId>com.alibaba.cloud</groupId>            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>        </dependency>        <!--springcloud alibaba sentinel-datasource-nacos 后續做持久化用到-->        <dependency>            <groupId>com.alibaba.csp</groupId>            <artifactId>sentinel-datasource-nacos</artifactId>        </dependency>        <!--springcloud alibaba sentinel-->        <dependency>            <groupId>com.alibaba.cloud</groupId>            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>        </dependency>        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-starter-openfeign</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.projectlombok</groupId>            <artifactId>lombok</artifactId>            <optional>true</optional>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-test</artifactId>            <scope>test</scope>        </dependency>        <dependency>            <groupId>com.atguigu</groupId>            <artifactId>cloud-common</artifactId>            <version>1.0-SNAPSHOT</version>        </dependency>    </dependencies>

寫yml

server:  port: 8401spring:  application:    name: cloudalibaba-sentinel-service  cloud:    nacos:      discovery:        # Nacos服務注冊中心地址        server-addr: localhost:8848    sentinel:      transport:        # 配置Sentinel dashboard地址        dashboard: localhost:8080        # 默認8719端口,假如被占用會自動從8719開始一次+1掃描,直至找到被占用的端口。        port: 8719management:  endpoints:    web:      exposure:        include: "*"

主啟動類:

package com.atguigu.springcloud;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.client.discovery.EnableDiscoveryClient;@SpringBootApplication@EnableDiscoveryClientpublic class SentinelMainApp8401 {    public static void main(String[] args) {        SpringApplication.run(SentinelMainApp8401.class,args);    }}

業務類:

package com.atguigu.springcloud.controller;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RestController;@RestControllerpublic class FlowLimitController {    @GetMapping("/testA")    public String testA(){        return "-------testA";    }    @GetMapping("/testB")    public String testB(){        return "--------testB";    }}

可能存在一個啟動失敗問題:logs/csp/sentinel-record.log.2021-05-18.0.lck

測試

  • 關於nacos

經過上面,現在的nacos是依賴mysql數據庫存儲數據且配置了多端口啟動。

所以我們在保證mysql對應數據庫存在下,與nacos的集群端口

在本次測試時,不需要nacos集群,我們只需要使用之前的單機模式即可。即

http://127.0.0.1:8848/nacos/

  • 關於Sentinel

正常啟動即可

Sentinel顯示如下,則代表成功!

2 ) QPS模式流控測試

下面是對/testA進行流控,即當在1秒時,不能超過1次請求。

否則,默認如下:

JMeter

3 )線程數模式流控

修改/testB ,延長請求處理時間。

    @GetMapping("/testB")    public String testB(){        try{            TimeUnit.MILLISECONDS.sleep(3000);        }catch (Exception e) {            System.out.println(e.getMessage());        }        return "--------testB";    }

設置線程數流控模式

添加到流控列表

測試:使用不同瀏覽器測試, 訪問/testB ,同時去請求/testA,發現訪問/testA直接失敗!

4) 關聯流控

/testBQPS不符合時,即1秒,單機發送超過一個請求時,會導致/testA 不可用。

測試:使用postman 工具去請求/textB, 我們再訪問/testA

與此同時,我們去訪問 http://127.0.0.1:8401/testA ,發現無法訪問,被限流了。

5) 預熱 Warm up 流控

默認3秒(冷加載因子),下面:5秒內單機閾值由(10/3)到10進行過渡, 如果中途超過,則會快速失敗。

6) 排隊等待

請求都能滿足,但需要排隊,而多少處理一個請求,由如下配置決定。

修改/testB :

    @GetMapping("/testB")    public String testB(){        log.info(new Date().toString());        return "--------testB";    }

用postman 線程發送 /testB 請求,在控制台發現: 一秒只收到了一個請求。

服務熔斷

RT

RT模式下,當1秒內超過5個線程,且這些線程1秒打進來的請求它們的平均處理時間超過預定的平均處理時,斷路器將打開,未來1秒后關閉。

為/testB 加入服務熔斷RT,如下:RT代表請求平均請求時間,當一秒內請求的線程數超過5個,且平均請求時間超過500ms時,觸發服務熔斷。

測試:使用JMeter,將10個線程固定打入/testB上,線程這點超過5,並且,我們在/testB設置請求時間為>=1000ms ,超過我們設置的500ms,可以預定將會熔斷。

    @GetMapping("/testB")    public String testB(){        try{            TimeUnit.MILLISECONDS.sleep(1000);        }catch (Exception e) {            System.out.println(e.getMessage());        }        log.info("*************testB");        return "--------testB";    }

JMeter :

結果:/testB一直處於熔斷狀態。

異常比例

當線程數超過5,且請求失敗率大於0.2時,當進行服務熔斷。

以下步驟:修改測試的API方法 -> 使用jmeter進行測試 -> 測試 -> 結果

修改測試的controller項:

    @GetMapping("/testB")    public String testB(){        log.info("*************testB測試異常比例");        int i = 1/0;        return "--------testB";    }

測試:基於上面的服務降級配置,我們用jmeter進行測試。請求的線程數為10,大於5,但每個線程持續以1秒一次發送。可以預想,將會進行服務熔斷。

結果: 測試進行時,我們訪問測試的/testB,會被“flow limiing” 。

停止后,訪問直接報錯。測試成功!

異常數

當在默認1分鍾內,異常數超過5個,就會進入70s的時間窗口期,時間窗口期結束后,斷路器才關閉。

測試:

    @GetMapping("/testB")    public String testB(){        log.info("*************testB測試異常比例");        int i = 1/0;        return "--------testB";    }

訪問:http://127.0.0.1:8401/testB

刷新5次,都會報錯,這時已經超過我們指定的異常數,此時已進入了時間窗口期,當我們第六次刷新時就會看到是報錯的。

熱點規則

標准熱點規則

被@SentinelResource標記,且value值與API的索引為0的參數的值相同時(鍵名可不同),總記,1秒內系統最多處理5個這樣的請求,超過將進入2秒的時間窗口中(統計窗口時長)。

api方法:

在這里我們在@SentinelResource注解中加入blockHandler = "del_testHotKey",來使用我們自定義的兜底的方法del_testHotKey。

    @GetMapping("/testHotKeyabcA")    @SentinelResource(value = "testHotKeyabcA",blockHandler = "del_testHotKey")    public String testHotKey(@RequestParam(value = "p1", required = false) String p1,                             @RequestParam(value = "p2", required = false) String p2) {        return "----testHotKey";    }    public String del_testHotKey(String p1, String p2, BlockException e) {        return "這次不用默認的兜底提示Blocked by Sentinel(flow limiting),自定義提示:del_testHotKeyo(╥﹏╥)o...";    }

測試:

訪問:http://127.0.0.1:8401/testHotKeyabcA?p1=2

不停地刷新窗口,讓其超過單機閾值,當超過后,全部的滿足的請求將執行del_testHotKey。

例外熱點規則

就是在標准熱點規則下,當存在索引為0的參數且相同參數請求的QPS超過1時,當進入窗口時長,如果想有例外,比如參數值為6的,它可以指定其它的閾值,我們則進行參數例外項配置。上面是配置參數值為6時,它的限流閾值是200而不是1。

測試:

分別請求:

http://127.0.0.1:8401/testHotKeyabcA?p1=5

http://127.0.0.1:8401/testHotKeyabcA?p1=6 (例外項)

發現p1=5的,一下子就超過了閾值,而p1=6的我們不停地刷新,而沒有超過我們設置的限流閾值200!

系統規則

這個設置是比較危險的,用的相對較少。它是API容器的入口規則。

上面是設置了入口QPS,即不管你走哪個API,總的QPS是不能超過設定的閾值的。

測試:

使用jmeter,讓它1秒請求一次/testA這個API,來占滿入口QPS,我們再去訪問/testB,發現,直接:

blockHandler與fallback

bolockHandler: 是自定義的界面,如果不自定義,就會顯示Sentinel默認的 Blocked by Sendtinel (flow limiting)

fallback: 當我們執行的API有錯誤時 ,就會跳到我們 @SentinelResource 注解 fallback屬性指定的方法:

blockHandler

@SentinelResource(value = "fallback",blockHandler = "myblockHandler") ,界面指定了blockHandler是本類的 myblockHandler 方法。

package com.atguigu.springcloud.controller;import com.alibaba.csp.sentinel.annotation.SentinelResource;import com.alibaba.csp.sentinel.slots.block.BlockException;import com.atguigu.springcloud.entities.CommonResult;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RestController;@RestControllerpublic class CircuitBreakerController {    @GetMapping("/fallback")    @SentinelResource(value = "fallback",blockHandler = "myblockHandler")    public CommonResult fallback() {        return new CommonResult(200,"ok",null);    }    public CommonResult myblockHandler(BlockException e) {        return new CommonResult(400,"float limit to blockHeadler!",null);    } }

fallback

@SentinelResource(value = "fallback" fallback = "myfallback") 指定了,但本api執行出錯時,交由 本類的myfallback 方法處理。

package com.atguigu.springcloud.controller;import com.alibaba.csp.sentinel.annotation.SentinelResource;import com.alibaba.csp.sentinel.slots.block.BlockException;import com.atguigu.springcloud.entities.CommonResult;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RestController;@RestControllerpublic class CircuitBreakerController {    @GetMapping("/fallback")    @SentinelResource(value = "fallback" fallback = "myfallback")    public CommonResult fallback() {        int i = 1/0;        return new CommonResult(200,"ok",null);    }    public CommonResult myfallback() {        return new CommonResult(444,"error to fallback!",null);    } }

當我訪問/fallback api接口時將執行myfallback 方法,即顯示:

{"code":444,"message":"error to fallback!","data":null}

與業務解構的BlockHandler方法

也就是將之前與api同級的block方法,抽取出來作為一個類,然后再在@SentinelResource 注解中去聲明使用這個類中的指定block方法。

  1. 創建與業務解構的BlockHandler方法:

創建一個與controller同級的handler 目錄,再創建一個名為CutomerBlockHandler的類。

package com.atguigu.springcloud.handler;import com.alibaba.csp.sentinel.slots.block.BlockException;import com.atguigu.springcloud.entities.CommonResult;public class CutomerBlockHandler {    public static CommonResult blockHandlerA(BlockException e) {        return new CommonResult(4444,"與業務解構ockHandlerA的方法");    }    public static CommonResult blockHandlerB(BlockException e) {        return new CommonResult(4444,"與業務解構ockHandlerB的方法");    }}
  1. 使用 與業務解構的BlockHandler方法

在CircuitBreakerController類中加入一個api:

其中 blockHandlerClass 指定的就是 CutomerBlockHandler,而blockHandler就是blockHandlerClass聲明的類所在的方法。

    //測試與代碼解構的Block方法    @GetMapping("/globalBlockHandler")    @SentinelResource(value = "globalBlockHandler",            blockHandlerClass = CutomerBlockHandler.class,            blockHandler = "blockHandlerA")    public CommonResult globalBlockHandler() {        return new CommonResult(200,"ok~");    }

sentinel整合ribbon+openFeign+fallback

Ribbon 系列

我們在整合時,需要先創建兩個服務模塊(9003、9004),與一個消費模塊(8484),在linux下,如果使用1024以下的端口則需要root權限,所以因為我當前使用的不是root權限,所以我們不寫84,而改為8484。下面是Sentinel整合ribbon+fallback

以下步驟:創建生產者9003、9004 -> 消費者8484 -> 基本測試 -> fallback與blockhandler

創建生產者9003、9004

創建模塊:cloudalibaba-provider-payment9003、cloudalibaba-provider-payment9004

改pom:

<dependencies>        <!--spring cloud alibaba nacos-->        <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>        <dependency>            <groupId>com.atguigu</groupId>            <artifactId>cloud-common</artifactId>            <version>1.0-SNAPSHOT</version>        </dependency>    </dependencies>

寫yml:

server:  port: 9003spring:  application:    name: nacos-payment-provider  cloud:    nacos:      discovery:        # Nacos服務注冊中心地址        server-addr: localhost:8848    sentinel:      transport:        # 配置Sentinel dashboard地址        dashboard: localhost:8080        # 默認8719端口,假如被占用會自動從8719開始一次+1掃描,直至找到被占用的端口。        port: 8719management:  endpoints:    web:      exposure:        include: "*"

主啟動類:

package com.atguigu.springcloud;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.client.discovery.EnableDiscoveryClient;@SpringBootApplication@EnableDiscoveryClientpublic class PaymentMain9003 {    public static void main(String[] args) {        SpringApplication.run(PaymentMain9003.class,args);    }}

業務類:

package com.atguigu.springcloud.controller;import com.atguigu.springcloud.entities.CommonResult;import com.atguigu.springcloud.entities.Payment;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 java.util.HashMap;@RestControllerpublic class PaymentController {    @Value("${server.port}")    private String serverPort;    public static HashMap<Long, Payment> hashMap = new HashMap<>();    static {        hashMap.put(1L,new Payment(1L,"大聰明"));        hashMap.put(2L,new Payment(2L,"豬豬"));        hashMap.put(3L,new Payment(3L,"小豬豬"));    }    //模擬查表    @GetMapping(value = "/paymentSQL/{id}")    public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id) {        Payment payment = hashMap.get(id);        return new CommonResult<>(200, "from mysql, serverPORT: "+serverPort, payment);    }}

創建消費者8484

創建模塊:cloudalibaba-consumer-nacos-order8484

改pom :

<dependencies>        <!--spring cloud alibaba nacos-->        <dependency>            <groupId>com.alibaba.cloud</groupId>            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>        </dependency>        <!--引入我們自定義的公共api jar包-->        <dependency>            <groupId>com.atguigu</groupId>            <artifactId>cloud-common</artifactId>            <version>1.0-SNAPSHOT</version>        </dependency>        <!--SpringCloud alibaba sentinel-datasource-nacos:后續做持久化用到-->        <dependency>            <groupId>com.alibaba.csp</groupId>            <artifactId>sentinel-datasource-nacos</artifactId>        </dependency>        <!--SpringCloud alibaba Sentinel-->        <dependency>            <groupId>com.alibaba.cloud</groupId>            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>        </dependency>        <!--openFeign-->        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-starter-openfeign</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>

寫yml:

server:  port: 8484spring:  application:    name: nacos-order-consumer  cloud:    nacos:      discovery:        server-addr: localhost:8848    sentinel:      transport:        dashboard: localhost:8080 # 配置Sentinel dashboard地址        port: 8719 #sentinel后台端口# 消費者將要去訪問的微服務名冊:方便controller的@value獲取server-url:  nacos-user-service: http://nacos-payment-provider

主啟動:

package com.atguigu.springcloud;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.client.discovery.EnableDiscoveryClient;@SpringBootApplication@EnableDiscoveryClientpublic class OrderNacosMain8484 {    public static void main(String[] args) {        SpringApplication.run(OrderNacosMain8484.class,args);    }}

業務類:

config:

package com.atguigu.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;@Configurationpublic class ApplicationContextConfig {    @Bean    @LoadBalanced    public RestTemplate getRestTemplate() {        return new RestTemplate();    }}

controller:

package com.atguigu.springcloud.controller;import com.alibaba.csp.sentinel.annotation.SentinelResource;import com.atguigu.springcloud.entities.CommonResult;import com.atguigu.springcloud.entities.Payment;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Value;import org.springframework.web.bind.annotation.PathVariable;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import org.springframework.web.client.RestTemplate;import javax.annotation.Resource;@RestController@Slf4jpublic class CircuitBreakerController {    @Value("${server-url.nacos-user-service}")    private String SERVER_URL;    @Resource    private RestTemplate restTemplate;    @RequestMapping("/consumer/fallback/{id}")    @SentinelResource(value = "fallback")    public CommonResult<Payment> fallback(@PathVariable("id") Long id) {        CommonResult result = restTemplate.getForObject(SERVER_URL + "/paymentSQL/" + id, CommonResult.class, id);        if(id==4){            throw new IllegalArgumentException("IllegalArgumentException,非法參數異常....");        }else if(result.getData() == null) {            throw new NullPointerException("NullPointerException,該ID沒有對應記錄,空指針異常");        }        return result;    }}

測試

訪問:http://localhost:8484/consumer/fallback/1

發現:在9003、9004之間進行輪詢

faallback與blockhandler

說明:fallback作用只是,當我們的java程序異常時的友好提示或說界面,不至於直接報Error Page。而如果我們違背了Sentinel的規則,但會出現blockhandler的提示或說界面。那就出現一個問題了,如果既違背了Sentinel的規則且java程序出錯。那么是fallback還是blockHandler呢?結果當然是blockHandler啦,下面我們就來驗證一下!

以下步驟:修改controller(加入blockHandler與fallback) -> 加入Sentinel規則 -> 測試 -> exceptionToIgnor的說明

開始:

1)修改controller, 也就是在@SentinelResource 加入blockHandler與fallback ,與對應的方法。

package com.atguigu.springcloud.controller;import com.alibaba.csp.sentinel.annotation.SentinelResource;import com.alibaba.csp.sentinel.slots.block.BlockException;import com.atguigu.springcloud.entities.CommonResult;import com.atguigu.springcloud.entities.Payment;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Value;import org.springframework.web.bind.annotation.PathVariable;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import org.springframework.web.client.RestTemplate;import javax.annotation.Resource;@RestController@Slf4jpublic class CircuitBreakerController {    @Value("${server-url.nacos-user-service}")    private String SERVER_URL;    @Resource    private RestTemplate restTemplate;    @RequestMapping("/consumer/fallback/{id}")    //@SentinelResource(value = "fallback")    @SentinelResource(value = "fallback",            blockHandler = "blockHandler",fallback = "handlerFallback")    public CommonResult<Payment> fallback(@PathVariable("id") Long id) {        CommonResult result = restTemplate.getForObject(SERVER_URL + "/paymentSQL/" + id, CommonResult.class, id);        if(id==4){            throw new IllegalArgumentException("IllegalArgumentException,非法參數異常....");        }else if(result.getData() == null) {            throw new NullPointerException("NullPointerException,該ID沒有對應記錄,空指針異常");        }        return result;    }    //blockHandler:只負責sentinel控制台的違規配置    public CommonResult blockHandler(@PathVariable Long id, BlockException e){        Payment payment = new Payment(id, "null");        return new CommonResult<>(445, "blockHandler-sentinel限流,無此流水:blockException " + e.getMessage());    }    //handlerFallback:兜底處理異常方法    public CommonResult handlerFallback(Long id, Throwable e){        Payment payment = new Payment(id, "null");        //可以把異常帶過來        return new CommonResult<>(444, "兜底異常handlerFallback,exception內容 "+e.getMessage(), payment);    }}

2) 加入規則,流控

3)測試

訪問:http://localhost:8484/consumer/fallback/4 ,參數是4時,程序會拋出異常的

發現:初次訪問時會到我們設置的fallback方法中的提示或說界面,觸發流控后,是blockHandler中方法的提示或說界面。

exceptionToIgnor的說明

我們想要某些異常正常的顯示出來,好排錯。不讓我們自定義的異常處理方法處理。也就是不用exceptionToIgnor屬性取消掉 fallback 屬性的功能。

openFeign 系列

想要通過OpenFeign來實現服務的調用,首先是需要加入Open信賴的,且確保生產者與消費者都已經注冊了注冊中心了。這時我們只需要知道服務名即可開始使用OpenFeign來調用服務了。

以下步驟: 加入OpenFeign的信賴 -> 配置yml激活OpenFeign -> 在主啟動類加入注解開啟OpenFeign -> 開始使用 -> 測試

加入OpenFeign依賴

確保有:

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

開始yml配置激活OpenFeign

feign.sentinel.enabled: true

server:  port: 8484spring:  application:    name: nacos-order-consumer  cloud:    nacos:      discovery:        server-addr: localhost:8848    sentinel:      transport:        dashboard: localhost:8080 # 配置Sentinel dashboard地址        port: 8719 #sentinel后台端口# 消費者將要去訪問的微服務名冊:方便controller的@value獲取server-url:  nacos-user-service: http://nacos-payment-provider  # 激活Sentinel對Feign的支持feign:  sentinel:    enabled: true

在主啟動類加入注解開啟OpenFeign

@EnableFeignClients

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

開始使用

使用消費者8484使用openFeign調用9003、9004。

需要創建對應的service接口來對應9003(9004)對應的api與服務降級后的Fallback即他們的實現類。 也就是創建與調用端相同的api接口與fallback實現類。

service接口(對應生產端的api):

package com.atguigu.springcloud.service;import com.atguigu.springcloud.entities.CommonResult;import com.atguigu.springcloud.entities.Payment;import com.atguigu.springcloud.service.impl.PaymentFallbackService;import org.springframework.cloud.openfeign.FeignClient;import org.springframework.stereotype.Component;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.PathVariable;@Component@FeignClient(value = "nacos-payment-provider",fallback = PaymentFallbackService.class)public interface PaymentService {    @GetMapping(value = "/paymentSQL/{id}") //去找nacos-payment-consumer服務中的相應接口    public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id);}

實現類(當無法調用成功api時的fallback方法):

package com.atguigu.springcloud.service.impl;import com.atguigu.springcloud.entities.CommonResult;import com.atguigu.springcloud.entities.Payment;import com.atguigu.springcloud.service.PaymentService;import org.springframework.stereotype.Component;@Componentpublic class PaymentFallbackService implements PaymentService {    //如果nacos-payment-consumer服務中的相應接口出事了,我來兜底    @Override    public CommonResult<Payment> paymentSQL(Long id) {        return new CommonResult(444,"服務降級返回---PaymentFallbackService", new Payment(id, "errorSerial...."));    }}

controller(在controller中調用)

    //===========openFeign    @Resource    private PaymentService paymentService;    @GetMapping(value = "/consumer/paymentSQL/{id}")    public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id){        return paymentService.paymentSQL(id);    }

測試

訪問:http://127.0.0.1:8484/consumer/paymentSQL/1

發現與輪詢的方式調用生產者9003、9004 ,成功!

當我們關閉9003、9004時,無法調用服務,則會執行service接口的實現類對應的方法。

配置的持久化

如果我們要對一個服務的進行Sentinel的配置進行持久化,比如流控等。我們要做的是寫yml的配置來讀取nacos上我們的配置好的Sentinel的規則。

以下步驟:加入依賴支持 -> 寫yml規則文件在nacos上查找的配置 -> 在nacos上配置規則 -> 測試

加入依賴支持

確保存在

<!--springcloud alibaba sentinel-datasource-nacos 后續做持久化用到--><dependency>    <groupId>com.alibaba.csp</groupId>    <artifactId>sentinel-datasource-nacos</artifactId></dependency>

寫yml規則文件在nacos上查找的配置

與之前我們寫的是不同的。

server:  port: 8401spring:  application:    name: cloudalibaba-sentinel-service  cloud:    nacos:      discovery:        # Nacos服務注冊中心地址        server-addr: localhost:8848    sentinel:      transport:        # 配置Sentinel dashboard地址        dashboard: localhost:8080        # 默認8719端口,假如被占用會自動從8719開始一次+1掃描,直至找到被占用的端口。        port: 8719      #添加Nacos數據源配置      datasource:        ds1: # 數據源1          nacos:            server-addr: localhost:8848            dataId: ${spring.application.name}            groupId: DEFAULT_GROUP            data-type: json            rule-type: flow # 流控規則management:  endpoints:    web:      exposure:        include: "*"

加入的是:

  #添加Nacos數據源配置  datasource:    ds1: # 數據源1      nacos:        server-addr: localhost:8848        dataId: ${spring.application.name}        groupId: DEFAULT_GROUP        data-type: json        rule-type: flow # 流控規則

在nacos上配置規則

如果在nacos上寫配置?

我們nacos上的配置,它是對資源名/testA進行流控:

[    {        "resource": "/testA",        "limitApp": "default",        "grade": 1,        "count": 1,        "strategy": 0,        "controlBehavior": 0,        "clusterMode":  false    }]

測試

訪問:http://127.0.0.1:8401/testA

發現:在流控上有我們配置的持久化Sentinel配置。它與上面配置在nacos的json配置對應:

且能進行使用。

seata 解決分布式問題

Seata 是一款開源的分布式事務解決方案,致力於提供高性能和簡單易用的分布式事務服務。Seata 將為用戶提供了 AT、TCC、SAGA 和 XA 事務模式,為用戶打造一站式的分布式解決方案。

能解決分布式數據一致性的問題。

Seata 一個典型的分布式控制事務的流程:

下載:https://github.com/seata/seata

官網:https://seata.io/zh-cn/docs/overview/what-is-seata.html

1) Seata 環境的搭建

以下步驟 :下載后,移動到你認為合適的位置,然后修改兩個配置文件與創建數據庫

開始:

修改 conf > file.conf :

修改 bin > registry.conf :

創建 "seata" 數據庫

sql來源:conf > db.store.sql 、版本源碼查看(這里是v1.00)

最終seata數據庫下的表:

啟動:

先啟動naocs (8848), 然后再去bin目錄下啟動seata 。

2) 使用案例

整體的,我們需要搭建+配置好Seata環境后。我們需要創建業務要使用的數據 庫,即seata_account、seata_order、seata_storage三個數據庫。

而業務內容是,我們調用下訂單會調用2001(訂單處理模塊)的API進行處理,而2001服務調用2002(庫存處理模塊)進行減庫存,再調用2003(余額表)支付。加入seata后,不管哪里出現了異常,數據都會進行回滾。如果沒有Seata,在支付超時了,但最終還是扣了錢,但訂單狀態沒改變。等等問題。

以下步驟 :數據庫的准備 -> 業務代碼說明

開始:

數據庫的准備

  1. 訂單數據庫准備:seata_order

    CREATE TABLE t_order(    `id` BIGINT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,    `user_id` BIGINT(11) DEFAULT NULL COMMENT '用戶id',    `product_id` BIGINT(11) DEFAULT NULL COMMENT '產品id',    `count` INT(11) DEFAULT NULL COMMENT '數量',    `money` DECIMAL(11,0) DEFAULT NULL COMMENT '金額',    `status` INT(1) DEFAULT NULL COMMENT '訂單狀態:0:創建中; 1:已完結') ENGINE=INNODB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;SELECT * FROM t_order;
    
  2. 庫存數據准備:seata_storage

    CREATE TABLE t_storage(    `id` BIGINT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,    `product_id` BIGINT(11) DEFAULT NULL COMMENT '產品id',   `total` INT(11) DEFAULT NULL COMMENT '總庫存',    `used` INT(11) DEFAULT NULL COMMENT '已用庫存',    `residue` INT(11) DEFAULT NULL COMMENT '剩余庫存') ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;INSERT INTO seata_storage.t_storage(`id`,`product_id`,`total`,`used`,`residue`)VALUES('1','1','100','0','100');SELECT * FROM t_storage;
    
  3. 賬戶業務數據庫准備:seata_account

    CREATE TABLE t_account(    `id` BIGINT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT 'id',    `user_id` BIGINT(11) DEFAULT NULL COMMENT '用戶id',    `total` DECIMAL(10,0) DEFAULT NULL COMMENT '總額度',    `used` DECIMAL(10,0) DEFAULT NULL COMMENT '已用余額',    `residue` DECIMAL(10,0) DEFAULT '0' COMMENT '剩余可用額度') ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;INSERT INTO seata_account.t_account(`id`,`user_id`,`total`,`used`,`residue`) VALUES('1','1','1000','0','1000')SELECT * FROM t_account;
    
    4.  以上三個數據庫的共同表`undo_log`,sql :<a href="https://github.com/seata/seata/tree/develop/script/client/at/db">查看(v1.00)</a> (小於1.00 ,conf  > db_undo_log.sql)
    

    業務代碼說明

    業務代碼:請前往githu 2001、2002、2003模塊

    Seata在業務中的使用是1+1+2,分別是注解配置ymll配置,而resource下的file.conf與registry.conf

    測試

    分別模塊在有無

    @GlobalTransactional(name = "fsp-create-order",rollbackFor = Exception.class)
    

    下測試,數據 是否回滾,最終結果 是,當我們不加入時,模擬失敗,數據不回滾,加入后一旦失敗,數據全部回滾。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM