springcloud

本文详细介绍了SpringCloud框架中的Eureka注册中心的搭建和配置,包括集群、安全性配置和高可用性。接着讲解了服务调用的两种方式——RestTemplate和Feign,并涉及服务降级的概念。此外,还讨论了Hystrix防止服务雪崩的策略,如服务降级、熔断、线程隔离和请求缓存。最后提到了Config配置服务和GateWay路由组件在微服务架构中的作用。
摘要由CSDN通过智能技术生成

一、springcloud的认知

springcloud 是一个分布式的架构的框架,spring全家桶中的重要一员。

​ Netflix:网飞对springloud架构思想进行了技术实现

​ alibaba: 对springlcoud实现

30多个组件,重要组件如下:

1、注册中心:

​ Eureka:Netflix 目前已经不更新

​ Nacos: alibaba 兼顾注册中心+配置中心的职能

2、服务调用:

​ Feign:服务之间的调用

3、负载均衡

​ Robbin :实现负载均衡

4、防止服务雪崩

​ Hystrix: 服务降级 熔断 线程隔离 请求缓存

5、配置中心:同一管理各个服务的配置文件

​ Config

6、路由服务器:统一对外提供调用接口

GateWay: 提供路由服务

7、sleuth+zipkin 实现链路追踪

8、分布式事务 seata alibaba

二、搭建Eureka注册中心

1、新建maven项目项目的类型是pom类型

pom:父子工程,一个父工程,若干个子工程

父工程:统一控制spring boot springcloud的版本

子工程:就是拆分的各个分布式项目

<?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.qf</groupId>
    <artifactId>pda2205</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>pom</packaging>
    <!--在父工程中统一限定了各个微服务的springboot 的版本-->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.4.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Hoxton.SR4</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

</project>

【注意】

springboot 和 springcloud的兼容问题

2、新建Eureka注册中心子工程

就是一个springboot项目

是一个war工程

添加依赖

<!--添加springboot的依赖-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--添加eureka注册中心服务器的依赖-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>

3、创建启动程序

package com.qf.pda2205eureka;

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

@SpringBootApplication
@EnableEurekaServer  //是eureka注册中心的服务器,可以接收其他服务的注册
public class MyEurekaApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyEurekaApplication.class,args);
    }
}

4、创建配置文件application.yml

server:
  port: 8080 # 端口号
spring:
  application:
    name: EUREKASERVER  # 服务名:组件集群、服务调用都需要服务名
eureka:
  instance:
    hostname: localhost  # 访问注册中心的域名
  client:
    register-with-eureka: false   #  此注册中心不向其它注册中心注册,是单击版本,没有集群
    fetch-registry: false  # 是否向其他注册中心同步数据  当注册中心是集群是,才需要设置为true,注册中心之间相互拉取数据
    service-url:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ # 注册中心的地址

5、启动注册中心,访问:

在这里插入图片描述

2、1 注册中心的作用:

1、接收其它服务的注册,监控服务的运行情况

2、组建集群:当同一个服务,部署到多台服务器上,如果都向注册中心注册,注册的时候只要是同一服务名,那么eureka注册中心就会给该服务自动组件集群。

3、为服务之间的调用提供服务列表

2、2 注册中心的原理

在这里插入图片描述

三、新建入港申请微服务

<packaging>war</packaging>
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!--因为向注册中心注册所以是eureka的客户端-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
</dependencies>

启动程序

package com.qf.pda2205apply;

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

@SpringBootApplication
@EnableEurekaClient //向注册中心注册
public class ApplyApplication {
    public static void main(String[] args) {
        SpringApplication.run(ApplyApplication.class,args);
    }
}

application.yml

server:
  port: 8081
spring:
  application:
    name: APPLYSERVER
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8080/eureka/   # 注册中心的地址
  instance:
    lease-renewal-interval-in-seconds: 35  # 每间隔多少秒向eureka发送一次心跳监测
    lease-expiration-duration-in-seconds: 90 # 过多少秒,不向注册中心发送心跳监测,注册中心就认为宕机,从服务列表中删除掉我

四、eureka注册中心的详细配置

4、1:安全性配置

在eureka服务端配置账号、密码,防止服务随意的注册到注册中心来。

添加安全性的jar依赖

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

编写配置类,拦截对eureka的注册请求

package com.qf.pda2205eureka.util;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;

@EnableWebSecurity  //创建当前类的对象
public class MyWebConfig extends WebSecurityConfigurerAdapter {
    protected void configure(HttpSecurity http) throws Exception {
        //只拦截对/eureka/的请求 ,判断是否提供正确的账号、密码  对/eureka/**忽略
        http.csrf().ignoringAntMatchers("/eureka/**");
        //调用父类的配置方法
        super.configure(http);
    }
}

在eureka服务端配置账号秘密

在这里插入图片描述

4、2注册中的高可用

通过搭建eureka注册中心的集群,能够有效的提高注册中心的高可用,防止单点故障

伪集群:IP相同、端口号不同

真集群:IP不同

1、再新建一个项目

2、创建父包

3、pom的jar依赖 、 启动类、配置文件、子包及内容,复制即可


在第一个注册中心中,向其它注册中心注册、拉取配置文件的配置,都设置为false ,因为是第一个,在启动时,没有其它注册中心可供注册及拉取数据

第二个注册中心,按集群方式配置

在这里插入图片描述
eureka的客户端向注册中心注册的时候,需要提供注册中心的所有服务列表,但是,只需注册到注册中心列表中的任意一台即可,注册中心之间会同步数据


eureka的缺点:当注册到eureka集群中的某一个服务器后,如果没有同步到集群中的其他服务器,此时,这台eureka服务器宕机了,那么服务相当于没有注册上。


eureka的客户端如何向集群进行注册?

在这里插入图片描述

4、3 注册中心的其他配置

更新本地服务列表的时间间隔

在这里插入图片描述
在eureka的服务端,开启自我保护机制

eureka的自我保护机制:在15分钟内,当一个eureka的客户端向注册中心发送的心跳监测低于85%的时候,eureka服务器就会开启自我保护机制,不会删除长时间没向注册中心发送心跳监测的服务。防止由于网络延迟,没有及时发送心跳监测,而被服务器给删除这种情况。当在15分钟内,发送的心跳监测符合要求时,自动关闭掉自我保护机制。

在这里插入图片描述

五、服务调用

在分布式是框架,服务的调用有两种类别:

1、RPC 远程过程调用 Dubbo+Zookeeper

​ 远程的服务,相当于本地的类一样进行调用

2、HTTP调用 在HTTP协议的基础上进行的钓鱼用

在SpringCloud框架中的RestTemplate Feigin都是基于HTTP进行的服务调用

5、1:使用RestTemplate进行调用

1、新建一个共通的jar程序 pda2205-common

各个微服务的共通资源,存于此服务

实体类、工具类等资源

【微服务】能够独立部署、独立运行的服务

2、在入港申请微服务里,编写入港申请的添加信息

使用的数据库是java2205

jar依赖

<packaging>war</packaging>
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!--因为向注册中心注册所以是eureka的客户端-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    <!--添加pda2205-common的jar依赖-->
    <dependency>
        <groupId>com.qf</groupId>
        <artifactId>pda2205-common</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
</dependencies>
package com.qf.pda2205apply.controller;

import com.qf.pda2205common.pojo.Apply;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/apply")
public class ApplyController {

    //入港申请添加
    @GetMapping("/add")
    public String addApply(@RequestBody Apply apply){
        //调用本服务的service--->dao
        System.out.println("接收到用户微服务传递的入港申请信息:"+apply);
        return "apply success!";
    }
}

3、新建客户微服务,使用客户数据库 pdauser

添加jar

创建启动程序

创建配置文件

server:
  port: 8083
spring:
  application:
    name: USERSERVER
eureka:
  client:
    service-url: 
      defaultZone: http://root:root@localhost:8080/eureka/

编写controller,获取用户的申请信息

调用入港申请微服务 pda2205-apply


4、RestTemplate服务调用

4、1在启动类中通过@Bean注解创建RestTemplate对象

在这里插入图片描述
4、2在controller中注入 RestTemplate

服务之间之间传输的复杂的数据一定是JSON格式

4、3在controller的方法里通过RestTemplate调用入港申请微服务

package com.qf.pda2205user.controller;

import com.qf.pda2205common.pojo.Apply;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
@RequestMapping("/user")
public class UserController {
    //注入RestTemplate对象
    @Autowired
    private RestTemplate restTemplate;
    @GetMapping("/addApply")
    public String addApply(Apply apply){
        //调用入港申请微服务pda2205-apply,完成入港申请
        String url="http://APPLYSERVER/apply/add";
        //参数1:调用的服务的网址
        //参数2:传递的数据,把对象   apply  转成JSON
        //参数3:调用的服务的返回值类型
        String result= restTemplate.postForObject(url,apply,String.class);
        return result;
    }
}

get方式发出请求,查询

在服务提供者 pda2205-applly

//查询
@GetMapping("/find")
public List<Apply> findApplyByCondition(String userName){
    //userName作为查询条件
    System.out.println("参数userName:"+userName);
    List<Apply> list=new ArrayList<Apply>();
    Apply apply=new Apply(1,"2023-05-16","韩晓晨","hxc","2023-05-20","服装","集装箱",10,1001);
    Apply apply2=new Apply(2,"2023-05-16","小韩","xiaohan","2023-05-20","鞋","集装箱",10,1001);
    list.add(apply);
    list.add(apply2);
    return list;
}

在服务消费者 pda2205-user

以get方式调用

@GetMapping("/find")
public List<Apply> findApply(String userName){
    String url="http://APPLYSERVER/apply/find?userName="+userName;
    //参数1:请求路径
    //参数2:返回值的类型,当是集合的时候,直接指定集合即可,无需指定泛型
    List<Apply> list= restTemplate.getForObject(url,List.class);
    return list;
}

5、2 使用Feign组件进行调用

1、在服务消费者端,引入Feign的jar依赖

<!--添加feign的依赖-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

2、在启动类中添加注解

在这里插入图片描述
3、创建接口,在接口中编写调用方法

package com.qf.pda2205user.feigns;

import com.qf.pda2205common.pojo.Apply;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

import java.util.List;

@FeignClient(value = "APPLYSERVER") //value就是要调用的微服务的名字
public interface ApplyFeign {
    //接口中的方法,和要调用的服务的方法的返回值类型、参数要相同,方法名可以不同
    //参数的名字要和被调用服务的名字要一致
    //此处必须使用RequestMapping,不能使用对应的GetMapping  或PostMapping
    //value:要访问的服务的请求路径
    //method:请求方式
    @RequestMapping(value = "/apply/find2",method = RequestMethod.GET)
    public List<Apply> myFindApply(@RequestParam(value = "userName") String userName);
}

服务提供者对应的方法

@GetMapping("/find2")
//通过Feign调用,参数定义时,可以使用注解
public List<Apply> find2(@RequestParam(value="userName")String userName){
    //userName作为查询条件
    System.out.println("参数userName:"+userName);
    List<Apply> list=new ArrayList<Apply>();
    Apply apply=new Apply(1,"2023-05-16","韩晓晨","hxc","2023-05-20","服装","集装箱",10,1001);
    Apply apply2=new Apply(2,"2023-05-16","小韩","xiaohan","2023-05-20","鞋","集装箱",10,1001);
    list.add(apply);
    list.add(apply2);
    return list;
}

Feign调用时的传参方式有三种:

第一种:使用@RequestParam

​见以上代码

第二种:传递JSON, 服务提供者的方法,Feign接口的方法,都需要使用@RequestBody注解

服务提供者的方法

//Feign调用
@PostMapping("/add2")
public String add2(@RequestBody Apply apply){
    System.out.println("接收到参数apply对象:"+apply);
    return "add2 success!";
}

服务消费者的Feign接口

//post调用,传递JSON
@RequestMapping(value = "/apply/add2",method = RequestMethod.POST)
public String addApply2(@RequestBody Apply apply);

controller调用

@GetMapping("/add2")
public String addApply2(Apply apply){
    return applyFeign.addApply2(apply);
}

第三种:路径传参

服务提供者里的方法

//Feign调用   路径传参
@GetMapping("/find3/{userName}/{applyTime}")
public List<Apply> findApply3(@PathVariable String userName,@PathVariable String applyTime){
    System.out.println("收参userName:"+userName);
    System.out.println("收参applyTime:"+applyTime);
    List<Apply> list=new ArrayList<Apply>();
    Apply apply=new Apply(1,"2023-05-16","韩晓晨","hxc","2023-05-20","服装","集装箱",10,1001);
    Apply apply2=new Apply(2,"2023-05-16","小韩","xiaohan","2023-05-20","鞋","集装箱",10,1001);
    list.add(apply);
    list.add(apply2);
    return list;
}

服务消费者的Feign接口中

//路径传参
@RequestMapping(value = "/apply/find3/{userName}/{applyTime}",method = RequestMethod.GET)
public List<Apply> findApply3(@PathVariable(value = "userName")String userName,
                              @PathVariable(value = "applyTime")String applyTime);

消费者的Controller

@GetMapping("/find3/{userName}/{applyTime}")
public List<Apply> findApply3(@PathVariable String userName,@PathVariable String applyTime){
    return applyFeign.findApply3(userName,applyTime);
}

六、Feign 服务降级

Feign的作用:服务调用

服务降级:Feign 是通过Hystrix组件进行的服务降级

服务降级:A服务调用B服务时,B服务由于各种原因:发生异常、负载过大、宕机等原因,不能正常响应给A服务结果,当B不能给A正常结果是,不希望A 也发生状况,而是A要返回一些 示例性质、demo、提示性质的数据

把示例性质、demo、提示性质的数据 称为托底数据

【说明】Feign的服务降级依赖Feign接口的

1、新建Feign接口的实现类

在接口的实现类方法中,编写托底数据

package com.qf.pda2205user.feigns;

import com.qf.pda2205common.pojo.Apply;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
@Component
public class ApplyFeignImpl implements ApplyFeign {
    @Override
    public List<Apply> myFindApply(String userName) {
        return null;
    }

    @Override
    public String addApply2(Apply apply) {
        return null;
    }

    @Override
    public List<Apply> findApply3(String userName, String applyTime) {
        Apply apply=new Apply();
        apply.setApplyUserName("服务暂时无法调用,请稍后重试");
        List<Apply> list=new ArrayList<Apply>();
        list.add(apply);
        //此处的list就是托底数据
        return list;
    }
}

2、在Feign接口的@FeignClient注解进行设置

在这里插入图片描述3、在服务消费者的配置文件中,开启服务降级

在这里插入图片描述

七、服务降级

Feign是通过Hystrix实现的服务降级,有两种降级方式:

1、FallBack降级

​ 创建Feign接口的实现类,在实现类方法中编写托底数据。

2、FallBackFactory降级

​ 在FallBack的基础上实现的降级

​ 能够获取服务提供者的异常信息

实现过程如下:

1、新建一个类,实现FallbackFactory接口

package com.qf.pda2205user.feigns;

import feign.hystrix.FallbackFactory;
import org.springframework.beans.factory.annotation.Autowired;

public class ApplyFeignFactory implements FallbackFactory<ApplyFeign> {
    //注入Feign接口的实现类对象
    @Autowired
    private ApplyFeignImpl applyFeign;
    @Override
    public ApplyFeign create(Throwable throwable) {
        //当服务提供者有异常时,就会回到此方法,在此方法中就可以获取服务提供者的异常信息
        System.out.println("服务提供者发生异常,异常信息如下:"+throwable.getMessage());
        //向下继续执行,返回托底数据
        return applyFeign;
    }
}

2、在Feign接口的@FeignClient注解出进行设置

在这里插入图片描述1、给Apply服务搭建集群—新建pda2205-apply2程序

新建父包

复制:pom 子包 启动类等

负载均衡:当某个服务是集群的时候,需要让集群中的服务器处理网络请求相对平均。

负载:就是要处理的网络请求

均衡:集群中的服务器平均分担、处理网络请求

2、在消费者端添加Ribbon的jar依赖

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

3、配置负载均衡算法

方式1:在启动类中

在这里插入图片描述方式2:在application.yml文件中配置

在这里插入图片描述
【总结】
Ribbon被称为基于客户端的负载均衡,在Eureka的客户端设置负载均衡策略

​ Ribbon的负载均衡策略:

​ 1、RandomRule:随机策略

​ 2、RoundRobbinRule:轮询策略

​ 3、WeightedResponseTimeRule:默认会采用轮询的策略,后续会根据服务的响应时间,自动给你分配权重- - -推荐

​ 4、BestAvailableRule:根据被调用方并发数最小的去分配

八、Hystrix防止服务雪崩

服务雪崩:服务的调用会形成一个调用链,如果调用链中的某个服务发生问题,可能导致整个调用链全部瘫痪,称为服务雪崩。

在这里插入图片描述

分析服务雪崩产生的原因:

1、网络延迟、故障,导致处理请求过慢,造成请求的积压

2、web服务器的线程都被某个服务锁占用,当某个服务出现状况后,把web服务器的所有线程都占用、耽搁,导致web服务器不能调用其它可用的服务

3、宕机

4、请求不合理: C在调用一次D之后,C又进行了一次相同的对D的调用

后果:
1、增加了D服务器的处理请求访问量

2、延长了C获取数据的时间

Hystrix 从4个角度防止服务雪崩:

1、服务降级:当异常、宕机、超时 返回托底数据

2、熔断:优化了服务降级

3、线程隔离: 不让web服务器的所有线程都去访问同一个服务

4、请求缓存:第一次请求某个服务后,把结果进行缓存,第二次相同的请求时,直接返回请求结果,无需访问另一个服务

8、1 服务降级

Feign通过集成Hystrix,自行能够实现服务降级

RestTemplate如何服务降级呢?

引入Hystrix的jar依赖

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

在启动类通过注解启用hystrix

在这里插入图片描述
在controller中编写某个handler的降级方法

//编写findApply请求的降级方法
public List<Apply> findApplyFallBack(String userName){
    Apply apply=new Apply();
    apply.setApplyUserName("Hystrix服务降级,服务暂时无法调用,请稍后重试");
    List<Apply> list=new ArrayList<Apply>();
    list.add(apply);
    //此处的list就是托底数据
    return list;
}

通过注解在controller方法上设置服务降级

在这里插入图片描述
在服务提供者的apply/find请求对应的方法中引发一个异常

测试结果:

在这里插入图片描述

8、2 线程隔离

解决的是,防止一个服务把所有线程都 用在某一个服务上,都某个服务出问题好,这个服务的所有线程都被占用,没有可用的线程,执行其它任务。

通过两个方面解决:

8、 2、1:Hystrix线程池

服务C的线程不直接去请求服务D,而是C把请求转移给Hystrix线程池里的线程,有Hystrix线程池里的线程去请求D服务

@GetMapping("/find")
@HystrixCommand(fallbackMethod = "findApplyFallBack",commandProperties = {
        @HystrixProperty(name = "execution.isolation.strateg",value = "THREAD"),//使用hystrx线程池
        @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "1000"),//hystrix线程池里的线程请求服务的超时时间
        @HystrixProperty(name = "execution.timeout.enabled",value = "true"),//true:开启超时时间限制
        @HystrixProperty(name = "execution.isolation.thread.interruptOnTimeout",value = "true"),//超时后,Hystrix线程池里的线程立刻中断
        @HystrixProperty(name = "execution.isolation.thread.interruptOnCancel",value = "false")//中断后是否取消该请求任务  false:不取消,重新请求
})
public List<Apply> findApply(String userName){
    String url="http://APPLYSERVER/apply/find?userName="+userName;
    //参数1:请求路径
    //参数2:返回值的类型,当是集合的时候,直接指定集合即可,无需指定泛型
    List<Apply> list= restTemplate.getForObject(url,List.class);
    return list;
}

Hystrix线程池的配置信息:

在这里插入图片描述

8、 2、2:信号量

不允许某个服务的所有线程都去请求另一个服务,只允许一部分线程去请求另一个服务,剩余的线程还可以去请求其他服务。

在这里插入图片描述

@GetMapping("/find2")
@HystrixCommand(commandProperties = {
        @HystrixProperty(name = "execution.isolation.strateg",value = "SEMAPHORE"),// 信号量
        @HystrixProperty(name = "execution.isolation.semaphore.maxConcurrentRequests",value = "10")//线程的舒亮亮
})
public List<Apply> find2(String userName){
    return applyFeign.myFindApply(userName);
}

8、 3 熔断、断路器

服务降级:A服务的请求,到达了B服务,B服务由于各种原因,没有处理这个请求,导致A对B的请求进行服务降级

断路器、熔断:直接拦截请求,不让A服务的请求到达B服务器,而直接降级,从而提升响应效率。

断路器有3种状态:

1、全开: 拦截请求,直接返回 进行服务降级

2、半开状态:放一部分请求 ,去请求B服务器

3、关闭状态:不拦截请求

工作原理:

1、当在一个时间段内,默认为10S,如果一个服务的请求的失败率 或失败数量达到阈值,则断路器打开

2、断路器打开后,直接拦截请求,进行服务降级

3、过一段时间后,可配置,断路器转为半开状态,放行少量请求,去请求B服务器

4、如果请求失败,则断路器转为全开状态,继续拦截所有请求,艰难估计

​ 请求成功,则断路器关闭,放行所有请求

在这里插入图片描述

@GetMapping("/add2")
public String addApply2(Apply apply){
    return applyFeign.addApply2(apply);
}
@HystrixCommand(commandProperties = {
        @HystrixProperty(name = "circuitBreaker.enabled",value = "true"),//开启断路器  开始熔断,默认在10S秒
        @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "2"),//请求失败的数量的阈值
        @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "50"),//请求失败的百分比的阈值
        @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000"),//拒绝请求的时间,也就是由全开,转为半开的时间

})
@GetMapping("/find3/{userName}/{applyTime}")
public List<Apply> findApply3(@PathVariable String userName,@PathVariable String applyTime){
    return applyFeign.findApply3(userName,applyTime);
}

9、Hystrix的请求缓存

A服务请求一次B服务获取数据,又请求一次B服务,还是获取相同的数据

带来的问题:
1、增加了B服务的访问量
2、又一次获取B服务的数据,也需要时间

优化: A对B的第一次请求后, 开启请求缓存,缓存查询结果,再一次请求时,直接从缓存得到数据即可

好处:减少了对B服务的访问次数,提升了获取数据的效率

【强调】

在这里插入图片描述
请求缓存的原理:

1、类Map集合进行缓存,使用方法的参数值做为key,使用查询结果作为value

2、序列化的方式进行缓存

请求缓存的实现:

1、在业务逻辑层进行请求缓存的实现

package com.qf.pda2205user.service.impl;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.cache.annotation.CacheKey;
import com.netflix.hystrix.contrib.javanica.cache.annotation.CacheRemove;
import com.netflix.hystrix.contrib.javanica.cache.annotation.CacheResult;
import com.qf.pda2205common.pojo.Apply;
import com.qf.pda2205user.feigns.ApplyFeign;
import com.qf.pda2205user.service.ApplyService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
@Service
public class ApplyServiceImpl implements ApplyService {
    //注入Feign对象
    @Autowired
    private ApplyFeign applyFeign;
    @Override
    @CacheResult  //对此方法的查询结果进行缓存
    @HystrixCommand(commandKey = "findRequestCache") // 对哪一个方法的返回结果进行缓存
    //@CacheKey 使用哪个参数的值作为缓存的key
    public List<Apply> findRequestCache(@CacheKey String userName) {
        //不需要在controller中发出对apply的请求,而在此处发出
        return applyFeign.myFindApply(userName);
    }

    @Override
    @CacheRemove(commandKey = "findRequestCache")  //清除commandKey设置的查询的请求缓存
    @HystrixCommand
    public void clearApplyResultCache(@CacheKey String userName) {
        System.out.println("已经清除请求缓存");
    }
}

报错分析:

在这里插入图片描述
加上过滤器,在过滤器中进行初始化

package com.qf.pda2205user.filters;

import com.netflix.hystrix.strategy.concurrency.HystrixRequestContext;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

@WebFilter(filterName = "HystrixRequestCacheFilter",value = "/*")
public class HystrixRequestCacheFilter implements Filter {
    public void destroy() {
    }

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        //执行初始化
        HystrixRequestContext.initializeContext();
        chain.doFilter(req, resp);
    }

    public void init(FilterConfig config) throws ServletException {

    }
}

在过滤器中初始化后,依然报错,因为springboot默认不加载、扫描 servlet, filter等

需要通过注解的设置,让springboot扫描、加载过滤器,这样才会生效

在这里插入图片描述
controller中调用开启请求缓存的服务调用

@GetMapping("/findCache")
public List<Apply> findCache(String userName){
    //第一次调用
    //对查询结果进行请求缓存
    List<Apply> list1=applyService.findRequestCache(userName);

    //清除请求缓存
    //applyService.clearApplyResultCache(userName);

    //再调用一次,无需调用apply服务里的find2方法,直接从请求缓存来拿数据
    List<Apply> list2=applyService.findRequestCache(userName);

    return list2;
}

10、Config配置服务

每个服务都自己的配置文件,各个服务都会相互调用

1、配置文件分散在各个服务中,查找比较困难

2、任何人都可以看到、修改配置文件,造成安全隐患

使用Config组件,对各个服务的配置文件进行统一管理

Config服务的工作原理:

1、向注册中心注册

2、可以使用本地、远程仓库等方式存储配置文件

3、其它各服务启动后,优先到Config服务拉取配置文件,然后根据配置文件启动程序。

Config配置服务的搭建

1、新建maven项目

2、添加依赖

<packaging>war</packaging>
<dependencies>
    <!-- configserver已经添加了springboot的依赖,无需重复添加 -->
    <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>
</dependencies>

3、创建启动程序

package com.qf.pda2205config;

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

@SpringBootApplication
@EnableConfigServer //Config的服务端,接收客户端拉取配置文件的操作
@EnableEurekaClient
public class MyConfigApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyConfigApplication.class,args);
    }
}

4、创建并编写配置文件

server:
  port: 8085
spring:
  application:
    name: configserver
  profiles:
    active: native  # 各个服务的配置文件存储于Config服务器的本地,而不是远程仓库
  cloud:
    config:
      server:
        native:
          search-locations: classpath:configs  #配置文件在Config服务器的存储路径
eureka:
  client:
    service-url:
      defaultZone: http://root:root@localhost:8080/eureka/

5、把各个服务的配置文件,移植到config服务中

名字 规范: XXX-local.yml来命名

6、在各个服务中,新建bootstrap.yml的文件

在此文件中配置:
1、config服务的请求路径
2、拉取哪一个配置文件

spring:
  cloud:
    config:
      uri: http://localhost:8085  #配置服务器的地址
      name: user   # 配置文件的名字  -之前的名字
      profile: local # 配置文件-之后的名字

7、各个服务添加config客户端的依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-config-client</artifactId>
</dependency>

在启动注册中心后,优先启动Config配置服务

在这里插入图片描述

11、GateWay路由

架构图:业务架构图(对应的都有哪些功能服务,调用关系)

技术架构图(都使用哪些技术)

在这里插入图片描述路由组件:Zuul Netflix 公司的 太烂了

GateWay: springcloud自身的组件

GateWay路由的搭建

1、新建maven项目

2、添加jar依赖

<packaging>war</packaging>
<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
    </dependency>
</dependencies>

【注意】spring-cloud-starter-gateway已经依赖了springboot,无需额外添加springboot的依赖

3、创建启动类

4、创建配置文件

server:
  port: 8086
spring:
  application:
    name: GATEWAYSERVER
  cloud:
    gateway:
      routes: # 配置路由   请求的路径  对应的  服务
        - id: applyroute  #路由的唯一标识
          predicates:
            - Path=/apply/*   # 没有服务名的请求路径
          uri: lb://APPLYSERVER # 强求的服务命名   http://APPLYSERVER/apply/add
        - id: userroute
          predicates:
            - Path=/user/*
          uri: lb://USERSERVER
eureka:
  client:
    service-url:
      defaultZone: http://root:root@localhost:8080/eureka/

5、当所调用到的服务是个集群的时候,需要路由服务器配置负载均衡策略

针对不同的服务,配置不同的负载均衡算法

server:
  port: 8086
spring:
  application:
    name: GATEWAYSERVER
  cloud:
    gateway:
      routes: # 配置路由   请求的路径  对应的  服务
        - id: applyroute  #路由的唯一标识
          predicates:
            - Path=/apply/*   # 没有服务名的请求路径
          uri: lb://APPLYSERVER # 强求的服务命名   http://APPLYSERVER/apply/add
        - id: userroute
          predicates:
            - Path=/user/*
          uri: lb://USERSERVER
eureka:
  client:
    service-url:
      defaultZone: http://root:root@localhost:8080/eureka/
APPLYSERVER:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.WeightedResponseTimeRule
USERSERVER:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.WeightedResponseTimeRule

12、链路追踪

在分布式的架构中,服务器的调用链比较复杂,当发生问题后,如何定位追踪到发生问题的服务,可以使用链路追踪

zipkin: 专门用来接收各个服务的运行信息,称为采样

sleuth: 各个服务通过sleuth把运行信息写入到zipkin ,配置写入的信息

使用过程:

1、到网关上下载zipkin

2、放在非中文、非桌面的路径下

3、启动
.
在这里插入图片描述
4、访问网址

在这里插入图片描述5、哪个服务需要链路追踪,需要在此服务中引入zipkin sleuth的jar依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>

6、在需要链路追踪的服务里,添加配置信息

​ 1、zipkin服务的地址

​ 2、配置采样率:把运行信息的多少写入到zipkin附中
.在这里插入图片描述
7、各个服务启动运行后,就会将运行信息通过sleuth写入到zipkin服务中

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值