SpringCloud文档

springcloud 框架的集合

微服务架构集大成者,云计算最佳业务实践。

1.单体应用架构存在的问题

一个归档包(例如war)包含所有功能的应用程序,通常称为单体应用。

在这里插入图片描述

  • 复杂性高:定时炸弹
  • 技术债务:no broken don’t fix
  • 可靠性差:单个bug,导致整个系统瘫痪
  • 阻碍技术创新

2.架构的演变

在这里插入图片描述

3.什么是微服务

​ 微服务架构就是将单一程序开发成一系列微小服务,每个微服务运行在自己的进程中,并使用轻量级的机制通信,通常是HTTP RESTFUL API。这些服务围绕业务能力来划分,并通过自动化部署机制来独立部署。这些服务集中管理,可以使用不同的编程语言,不同数据库,以保证最低限度的集中式管理。

  • 每个服务可独立运行在自己的进程里
  • 一系列独立运行的微服务共同构建起整个系统
  • 每个服务为独立的业务开发,一个微服务只关注某个特定的功能,例如订单管理,用户管理
  • 微服务之间通过一些轻量的通信机制进行通信,例如Restful API进行调用
  • 可以使用不同的语言与数据存储

官网链接:https://www.martinfowler.com/articles/microservices.html

4.微服务的解决方案

在这里插入图片描述

5.什么是springcloud

​ Spring Cloud是一个含概多个子项目的开发工具集,集合了众多的开源框架,他利用了Spring Boot开发的便利性实现了很多功能,如服务注册,服务注册发现,负载均衡等.Spring Cloud在整合过程中主要是针对Netflix(耐非)开源组件的封装.

​ NetFlix 是美国的一个在线视频网站,微服务业的翘楚,他是公认的大规模生产级微服务的杰出实践者,NetFlix的开源组件已经在他大规模分布式微服务环境中经过多年的生产实战验证,因此spring cloud中很多组件都是基于NetFlix组件的封装

​ Spring Cloud的出现真正的简化了分布式架构的开发

6.springcloud的特点

  • 服务注册和发现

  • 路由

  • service - to - service调用

  • 负载均衡

  • 断路器

7.Spring Cloud 的服务架构图

在这里插入图片描述

8.Eureka组件

Eureka是Netfilx开源的服务发现组件,本身是一个基于rest的服务,它包含client和server两部分。

Spirng Cloud将它集成在子项目Spirng Cloud Netfilx中,从而实现服务的注册和发现

8.1.eureka中的server和client的介绍及特点

  • Eureka Server:提供服务发现的能力,各个微服务启动时,会向Eureka Server注册自己的信息例如(IP,端口号,服务名称等),Eureka会存储这些信息
  • Eureka Client:是一个java的客户端用来简化Eureka Server的交互
  • 微服务启动后会周期性的(默认30秒)向Eureka Server发送心跳,如果Eureka在规定的时间没有收到心跳,则会注销该实例(默认90秒)
  • Eureka Client会缓存服务注册表中的信息。这种方式有一定的优势首先可以降低Eureka Server的压力,其次当所有的Eureka Server宕机服务调用方依然可以完成调用

8.2.服务注册与服务发现

  • 服务注册:当微服务client启动时,向Eureka Server发起注册,并上报该节点的相关信息
  • 服务发现:client从Eureka Server获取注册信息,然后发起调用

8.3.Eureka Server开发

1.引入springcloud的相关依赖
<!--仓库的位置-->
<repositories>
<repository>
   <id>spring-milestones</id>
   <name>Spring Milestones</name>
   <url>https://repo.spring.io/milestone</url>
   <snapshots>
      <enabled>false</enabled>
   </snapshots>
</repository>
</repositories>

<!--引入版本号-->
<dependencyManagement>
  <dependencies>
      <dependency>
          <groupId>org.springframework.cloud</groupId>
          <artifactId>spring-cloud-dependencies</artifactId>
          <version>Dalston.RC1</version>
          <type>pom</type>
          <scope>import</scope>
      </dependency>
  </dependencies>
</dependencyManagement>

<!--eureka server-->
<dependency>
 <groupId>org.springframework.cloud</groupId>
 <artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
2.入口类的开发
//@EnableDiscoveryClient  //不仅支持Eureka作为注册中心还支持zookeeper
@EnableEurekaServer  //只支持Eureka
@SpringBootApplication
public class EurekaServer {
    
    
    public static void main(String[] args) {
    
    
        SpringApplication.run(EurekaServer.class,args);
    }
}
3.配置文件
#设置eureka server 的交互地址,之后的服务获取和服务注册都需要依赖于这个地址
eureka.client.service-url.defaultZone=http://localhost:8761/eureka

#表示是否将自己注册到eureka上
eureka.client.register-with-eureka=false

#表示是否从eureka server上获取注册信息
eureka.client.fetch-registry=false

#应用服务名   微服务服务名
spring.application.name=eureka-server

#端口号
server.port=8761

8.4.Eureka Client的开发

1.jar包和Server相同
 将spring-cloud-starter-eureka-server改为spring-cloud-starter-eureka
2.入口类
@EnableEurekaClient
@SpringBootApplication
public class EurekaServerApplication {
    
    

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

3.配置文件

#注册中心的地址   声明
eureka.client.service-url.defaultZone=http://localhost:8761/eureka

#微服务实例名
spring.application.name=eureka-client1
#端口号
server.port=8762

8.5.Eureka client之间的相互调用

1.导入相关的jar
和eureka server 一致
2.java配置RestTemplate
@Configuration
public class RestConf {
    
    
    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate(){
    
    
        RestTemplate restTemplate = new RestTemplate();
        return restTemplate;
    }
}
3.调用
@RestController
@RequestMapping("test")
public class TestController {
    
    

    @Autowired
    private RestTemplate restTemplate;

    @RequestMapping("test")
    public String test(String name){
    
    

        //调用其他微服务实例   http请求   httpClint
        String forObject = restTemplate.getForObject("http://localhost:8762/product/product?name=" + name, String.class);
        return forObject;
    }
}

8.6.client的高可用

保证端口号不一致(测试环境)
保证实例名一致
1.配置如下
  • Client3
eureka.client.service-url.defaultZone=http://peer:8761/eureka
spring.application.name=eureka-producter
server.port=8763        
  • Client4
eureka.client.service-url.defaultZone=http://peer:8761/eureka
spring.application.name=eureka-producter
server.port=8764        
2.导入ribbon的jar包
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-ribbon -->
<!-- ribbon 组件主要做负载均衡 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
3.调用方配置加入注解
@LoadBalanced   //负载均衡
4.修改请求
    //调用其他微服务实例   http请求   httpClint
    String forObject = restTemplate.getForObject("http://EUREKA-CLIENT2/product/product?name=" + name, String.class);

8.7.Eureka的自我保护机制

Eureka进入自我保护机制的最直接体现,是Eureka首页输出警告如图:

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

紧急! 如果不是,EUREKA可能会不正确地提出索赔要求。 续订比阈值要少,因此仅出于安全性考虑,实例不会过期。

在这里插入图片描述

默认情况下,如果Eureka Server在一定时间内没有接收到服务实例的心跳,Eureka将会注销该实例(默认90秒).但是当网络分区发生故障时,微服务和Eureka Server 无法正常通信.以上行为可能变得特别危险了-因为微服务本身是健康的,此时不能注销该服务实例.

Eureka通过自我保护机制来解决这个问题,当Eureka Server在短时间丢失过多的服务实例(可能发生了网络分区的故障,那么这个节点进入自我保护模式,一旦进入此模式,Eureka Server将会保护服务注册表中的信息,不再删除服务注册表中的数据(也就是不再注销任何的服务实例),当网络故障恢复后,Eureka会自动退出自我保护模式。

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

Eureka在运行期间会统计心跳失败的比例,在15分钟内是否低于85%,如果出现了低于的情况,Eureka Server会将当前的实例注册信息保护起来,同时提示一个警告,一旦进入保护模式,Eureka Server将会尝试保护其服务注册表中的信息,不再删除服务注册表中的数据。也就是不会注销任何微服务。

自我保护模式被激活的条件是:在 1 分钟后,Renews (last min) < Renews threshold

这两个参数的意思:

  • Renews thresholdEureka Server 期望每分钟收到客户端实例续约的总数
  • Renews (last min)Eureka Server 最后 1 分钟收到客户端实例续约的总数

具体的值,我们可以在 Eureka Server 界面可以看到:

在springcloud中可以在eureka-server中关闭自我保护机制

#关闭自我保护机制  默认开启
eureka.server.enable-self-preservation=false

如果想及时剔除eureka的服务除了关闭自我保护机制外,可以调低eureka的心跳值

eureka-server服务端
配置文件中我们添加如下配置

#关闭保护机制,以确保注册中心将不可用的实例正确剔除
eureka.server.enable-self-preservation=false
#(代表是5秒,单位是毫秒,清理失效服务的间隔 )
eureka.server.eviction-interval-timer-in-ms=5000
客户端
配置文件中我们添加如下配置

# 心跳检测检测与续约时间
# 测试时将值设置设置小些,保证服务关闭后注册中心能及时踢出服务
# 配置说明
#  lease-renewal-interval-in-seconds 每间隔10s,向服务端发送一次心跳,证明自己依然”存活“
#  lease-expiration-duration-in-seconds  告诉服务端,如果我20s之内没有给你发心跳,就代表我“死”了,将我踢出掉。
eureka.instance.lease-renewal-interval-in-seconds=10   
eureka.instance.lease-expiration-duration-in-seconds=20

8.8.Eureka Server的高可用

单节点的Eureka Server 不适合线上的生产环境,Eureka Client会定时连接Eureka Server,获取服务注册表中的信息并缓存在本地,微服务消费远程API总是使用本地缓存的数据,因此一般来说即使Eureka Server发生宕机,也不会影响到服务的调用,但是如果Eureka Server宕机时某些微服务也出现了不可用的情况,Eurek Client中的缓存若不被更新,可能会影响到服务的调用,甚至影响到整个系统的高可用性,因此在生产环境会部署一个高可用的Eureka Server集群。

Eureka可以通过运行多个实例并互相注册实现高可用部署,Eureka Server实例会彼此同步信息。

server.port=8761
eureka.client.service-url.defaultZone=http://peer2:8762/eureka
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
eureka.instance.hostname=peer1
server.port=8762
eureka.client.service-url.defaultZone=http://peer1:8761/eureka
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
eureka.instance.hostname=peer2

注意:在client注册eureka server时 需要填写所有eureka的地址

eureka.client.service-url.defaultZone=http://peer:8761/eureka,http://peer1:8765/eureka

8.9.Eureka的健康监测

人会生病,就像人一样服务有时候也会出现异常情况,我们也需要知道某个服务的健康状况。我们可以通过添加如下依赖,开启某个服务的健康检查。

<!--健康检测-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

我们来访问一下这个接口http://localhost:8761/health,看到了一个很简短的健康报告:

{
    
    "description":"Spring Cloud Eureka Discovery Client","status":"UP"}

类似的还有

  • info 显示任意的应用信息
  • metrics 展示当前应用的指标信息 true
  • mappings 显示所有@RequestMapping路径的整理列表
  • trace 显示trace信息(默认为最新的一些HTTP请求)
  • health 展示应用的健康信息
  • beans 显示一个应用中所有Spring Beans的完整列表

这其中有一些是敏感信息,出于安全考虑,一般用户无法访问,如果内网情况下可以

#开启健康检查
eureka.client.healthcheck.enabled=true
#关掉认证(公网下的生产环境不建议,内网部署可以)
management.security.enabled=false

9.使用Ribbon完成负载均衡

在这里插入图片描述

9.1.Ribbon简介

ribbin是Netflix发布的负载均衡器,有助于控制http和tcp客户端的行为,为ribbon配置服务提供者列表后,ribbon就可以基于某种负载均衡算法,自动的帮助服务消费者去请求。ribbon提供了很多的负载均衡算法例如

Ribbon 的负载均衡策略

  • RoundRobinRule 轮训策略 按顺序循环选择 Server
    RandomRule 随机策略 随机选择 Server
  • AvailabilityFilteringRule 可用过滤策略 会先过滤由于多次访问故障而处于断路器跳闸状态的服务,还有并发的连接数量超过阈值的服务,然后对剩余的服务列表按照轮询策略进行访问
  • WeightedResponseTimeRule 响应时间加权策略 根据平均响应的时间计算所有服务的权重,响应时间越快服务权重越大被选中的概率越高,刚启动时如果统计信息不足,则使用RoundRobinRule策略,等统计信息足够会切换到
  • RetryRule 重试策略 先按照RoundRobinRule的策略获取服务,如果获取失败则在指定时间内进行重试,获取可用的服务。
  • BestAviableRule 最低并发策略 会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务
默认为轮询策略

在springCloud中,当ribbon和Eureka配和使用时ribbon可以自动获取服务注册列表,并基于负载均衡算法,请求其中的一个服务提供实例
在这里插入图片描述

9.2.微服务消费者整合ribbon

1.ribbon和springcloud的启动器

**注意:**如果已经引入了spring-cloud-starter-eureka-server该依赖已经包含了spring-cloud-starter-ribbon,所以不需要再次引入

<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-ribbon -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
2.ribbon整合RestTemplate
   @Bean
   @LoadBalanced
    RestTemplate getRestTemplate(){
    
    
        return new RestTemplate();
    }
3.使用
    @Autowired
    RestTemplate restTemplate;

    @RequestMapping("/hi")
    public String hi(String name) {
    
    
        String restTemplateForObject = restTemplate.getForObject("http://HI-SERVICE/test/test?name=" + name, String.class);
        return restTemplateForObject;
    }
4.验证负载均衡
@Resource
LoadBalancerClient loadBalancerClient;

@RequestMapping("/test")
public void test() {
    
    

    ServiceInstance choose = loadBalancerClient.choose("EUREKA-CLIENT2");
    
    //  ===http://SKY-20181016DKS:8763  计算机名+端口号
    System.out.println("==="+choose.getUri());
    //  ===SKY-20181016DKS    计算机名
    System.out.println("==="+choose.getHost());
    //  ===8763     端口号
    System.out.println("==="+choose.getPort());
    //  ===EUREKA-CLIENT2    服务实例名
    System.out.println("==="+choose.getServiceId());
}

9.3.自定义ribbon配置

修改负载均衡策略

#调整负载均衡策略   把轮循修改为随机
EUREKA-CLIENT2.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule

10.使用feign实现声明式的调用

使用RestTemplate+ribbon已经可以完成对服务端的调用,为什么还要使用feign?

   @RequestMapping("/test")
    public String test(String name) {
    
    
        String restTemplateForObject = restTemplate.getForObject("http://EUREKA-CLIENT2/product/product?name=" + name, String.class);
        return restTemplateForObject;
    }

上述代码采用url拼接参数的形式发送请求,如果要发送多个请求参数那么代码就会变得很低效并且难以维护。例如

http://localhost:8762/test/test?name=zhangcn&password=111111&age=18

如果使用字符串拼接的方式,那么代码可以编排为:

  @RequestMapping("/test")
    public String test(String name, String password, Integer age) {
    
    
		HashMap<String, Object> map = new HashMap<>();
        map.put("name",name);
        map.put("password",password);
        map.put("age",age);

        //调用其他的微服务实例   http请求  httpClient
        String restTemplateForObject = restTemplate.getForObject("http://EUREKA-CLIENT2/product/product?name={name}&password={password}&age={age}", String.class,map);
        return restTemplateForObject;
    }

在这里url仅仅包含三个参数,如果url为10个参数那么代码会变的更加难以维护。

10.1.feign简介

Feign是一个声明式的伪Http客户端,它使得写Http客户端变得更简单。使用Feign,只需要创建一个接口并加入注解。它具有可插拔的注解特性(可以使用springmvc的注解),可使用Feign 注解和JAX-RS注解。Feign支持可插拔的编码器和解码器。Feign默认集成了Ribbon,并和Eureka结合,默认实现了负载均衡的效果并且springcloud为feign添加了springmvc注解的支持。

10.2.为服务消费者整合feign

1.引入feign的相关依赖
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
2.入口类开启feign支持
/*开启feign的支持*/
@EnableFeignClients
@EnableEurekaClient  
@SpringBootApplication
public class EurekaServerApplication {
    
    
    public static void main(String[] args) {
    
    
        SpringApplication.run(EurekaServerApplication.class, args);
    }
}
3.创建feign接口配置
//name=所调用的微服务实例名
@FeignClient(name = "EUREKA-CLIENT2")
public interface TestFeign {
    
    

    //生声明式    伪客户端
    @RequestMapping("/feign/test")
    public String testFeign(@RequestParam("name") String name);

    //多个参数    一定要使用 @RequestParam
    @RequestMapping("/feign/tests")
    public String testFeigns(@RequestParam("name")String name,@RequestParam("password")String password,@RequestParam("age")Integer age);
    
    //对象类型参数 提交方式要是POST
    @RequestMapping(value = "/feign/testUser",method = RequestMethod.POST)
    public String testFeignUser(User user);
}
4.Feign服务提供方
@RestController
@RequestMapping("feign")
public class FeignController {
    
    
    //单个参数
    @RequestMapping("test")
    public String test(String name){
    
    
        return "hello Client2 8763:"+name;
    }
    //多个参数
    @RequestMapping("tests")
    public String tests(String name,String password,Integer age){
    
    
        return "hello Client2 8763: "+ name+"-"+password+"-"+age;
    }
    //接收对象类型的参数  要加@RequestBody
    @RequestMapping("testUser")
    public String testUser(@RequestBody User user){
    
    
        user.setAge(-1);
        return "hello Client2 8763: "+user;
    }
}
5.测试
@RestController
@RequestMapping("testFeign")
public class TestFeignController {
    
    

    @Resource
    TestFeign testFeign;
    
    //单个参数
    @RequestMapping("testFeign")
    public String testFeign(String name){
    
    
        String s = testFeign.testFeign(name);
        System.out.println("===feign: "+s);
        return "hello Feign 8765: "+s;
    }
    //多个参数
    @RequestMapping("testFeigns")
    public String testFeigns(String name,String password,Integer age){
    
    
        String s = testFeign.testFeigns(name,password,age);
        System.out.println("===feign: "+s);
        return "hello Feign 8765: "+s;
    }
    //接收对象
    @RequestMapping("feignTestUser")
    public String feignTestUser(UserDto userDto){
    
    

        System.out.println(userDto.getSex());
        System.out.println(userDto.getStatus());

        User user = new User(userDto.getName(), userDto.getPassword(), userDto.getAge());
        String feigns = testFeign.feignsUser(user);
        return "client-feign 8765== "+feigns;
    }
}

10.3.feign日志

很多的场景下,需要了解feign处理请求的具体细节,如何满足这种需求呢?

feign对日志的处理非常灵活可为每个feign客户端指定日志记录策略,每个客户端都会创建一个logger默认情况下logger的名称是feign的全限定名需要注意的是,feign日志的打印只会DEBUG级别做出响应。

我们可以为feign客户端配置各自的logger.lever对象,告诉feign记录那些日志logger.lever有以下的几种值

  • NONE 不记录任何日志
  • BASIC 仅仅记录请求方法,url,响应状态代码及执行时间
  • HEADERS 记录Basic级别的基础上,记录请求和响应的header
  • FULL 记录请求和响应的header,body和元数据
1.java配置核心日志类
@Configuration
public class feignConf {
    
    
    @Bean
    public Logger.Level feignConfiguration() {
    
    
        return Logger.Level.FULL;
    }
}
2.配置feign客户端的日志级别
#日志    ERROR、WARN、INFO、DEBUG  高-低
logging.level.com.baizhi.feign.TestFeign=debug

10.4.feign构造多参数请求

1.错误方式

feign的注意事项:

//1.多参数传输的时候 必须要在feign接口上进行参数的绑定 
    public String testFeign(@RequestParam("name") String name, @RequestParam("password") String password, @RequestParam("age") Integer age);
//2.以对象格式为参数进行数据传输时 必须设置feign的请求形式为post
//3.在服务方接收对象参数时需在形参上加入@RequestBody的注解
public interface feignPost {
    
    
    @RequestMapping(value = "/test/test1", method = RequestMethod.GET)
    public User sayHi(User user);
}

错误日志信息:

feign.FeignException: status 405 reading feignPost#sayHi(User); content:
{
    
    "timestamp":1546852876890,"status":405,"error":"Method Not Allowed","exception":"org.springframework.web.HttpRequestMethodNotSupportedException","message":"Request method 'POST' not supported","path":"/test/test1"}

由异常信息可知,尽管指定了get的方法,feign依然会使用post发送请求(对象传输时)。正确的方式如下:

2.get
@FeignClient(name = "EUREKA-CLIENT2")
public interface TestFeign {
    
    
    //对象类型参数 提交方式要是POST
    @RequestMapping(value = "/feign/testUser",method = RequestMethod.GET)
    public String testFeignUser(User user);
}
3.post

feign配置

@FeignClient(name = "EUREKA-CLIENT2")
public interface TestFeign {
    
    
    //对象类型参数 提交方式要是POST
    @RequestMapping(value = "/feign/testUser",method = RequestMethod.POST)
    public String testFeignUser(User user);
}

服务提供方

@RestController
@RequestMapping("feign")
public class FeignController {
    
    	
	@RequestMapping("/testUser")
	public User testUser(@RequestBody User user) {
    
    
    	return user;
	}
}

11.使用Hystrix实现微服务的容错处理

11.1.实现容错的手段

如果服务提供者响应的速度特别慢,那么消费者对提供者的请求就会强制等待,直到提供者响应或者超时。在高负载的情况下,如果不做任何处理,此类问题可能会导致服务消费者的资源耗尽甚至整个系统的崩溃。例如曾经发生的一个案例,某个电子商务网站在某个星期五发生过载,过多的并发请求,导致用户支付请求延迟很久没有响应,在等待很长时间后最终失败,支付失败又导致用户重新刷新页面再次尝试支付,进一步增加了服务器的负载,最终整个系统崩溃了。

1.1雪崩效应

我们常把基础服务故障,导致级联服务故障的现象,称为雪崩效应。雪崩效应描述的是提供方不可用,导致消费方不可用并将不可用逐渐放大的过程。

1.2.如何容错

想要避免雪崩效应,必须有强大的容错机制,该容错机制需要实现以下两点

  • 为网络请求设置超时

  • 使用断路器模式

    正常情况下断路器关闭可正常请求服务,当一定时间内请求达到了一定的阈(yu)值(错误率达到百分之50或者100次/分钟),断路器就会打开此时不会再去请求依赖服务,断路器打开一段时间之后,会自动进入半开的状态。此时断路器允许一个请求请求服务实例,如果该服务可以调用成功,则关闭断路器,否则继续保持打开状态。

在这里插入图片描述

断路器开启或者关闭的条件:

1、 当满足一定的阀值的时候(默认10秒内超过20个请求次数)

2、 当失败率达到一定的时候(默认10秒内超过50%的请求失败)

3、 到达以上阀值,断路器将会开启

4、 当开启的时候,所有请求都不会进行转发

5、 一段时间之后(默认是5秒),这个时候断路器是半开状态,会让其中一个请求进行转发。如果成功,断路器会关闭,若失败,继续开启。重复4和5。

11.2.使用hystrix实现容错

hystrix是Netfilx开源的延迟和容错库,用于隔离访问远程系统,服务或者第三方库,防止级联失败,从而提升系统的可用性和容错性

11.3.通用方式整合Hystrix

1.为项目添加依赖
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-hystrix -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
2.在消费者的入口类上添加注解
@EnableHystrix
@EnableEurekaClient
@SpringBootApplication
public class EurekaServerApplication {
    
    
    public static void main(String[] args) {
    
    
        SpringApplication.run(EurekaServerApplication.class, args);
    }
}
3.让当前方法具备容错的能力
@RestController
@RequestMapping("test")
public class TestController {
    
    

    @Autowired
    private RestTemplate restTemplate;

    @Resource
    LoadBalancerClient loadBalancerClient;

    @RequestMapping("test")
    @HystrixCommand(fallbackMethod = "testFallback")  //指定容错方法  方法名参数表一致
    public String test(String name,String password,Integer age){
    
    

        HashMap<String, Object> map = new HashMap<>();
        map.put("name",name);
        map.put("password",password);
        map.put("age",age);

        //调用其他微服务实例   http请求   httpClint
        String forObject = restTemplate.getForObject("http://EUREKA-CLIENT2/product/product?name={name}&password={password}&age={age}", String.class, map);
        return forObject;

    }

    //容错方法   与原方法参数表一致
    public String testFallback(String name,String password,Integer age){
    
    
        return "method invoke fail "+name;
    }
}

注意:默认Fallback 和设置超时时间 默认是一秒 在HystrixCommandProperties类中找超时的配置 如果请求超过一秒则不管是否异常就触发降级(进入容错方法) 所以此时就需要把超时设置得长一些

@HystrixCommand(fallbackMethod = "testFallBack",commandProperties = {
    
    @HystrixProperty(name ="execution.isolation.thread.timeoutInMilliseconds" ,value ="3000" )})
4.测试
1.启动eureka服务
2.启动生产者
3.启动消费者
4.访问消费者
结果
{
    
    "name":"xiaohei","password":null,"age":null}
5.关闭生产者
6.访问消费者
结果
{
    
    "name":"xiaohei","password":null,"age":-1}

我们知道,当请求失败,被拒绝超时或者短路器打开时都会进入到回退方法,但是进入回退方法并不是意味着断路器被打开,那怎样才算断路器打开呢?

5.实验 进行健康监测实时查看断路器是否打开
1.启动eureka服务
2.启动生产者
3.启动消费者
4.访问消费者
结果
{
    
    "name":"xiaohei","password":null,"age":null}
5.访问 http://ip:port/health
{
    
    
    status:"up",
    hystrix:{
    
    
        status:"up"
    }
}
6.关闭生产者
7.访问消费者
结果
{
    
    "name":"xiaohei","password":null,"age":-1}
8.访问 http://ip:port/health
{
    
    
    status:"up",
    hystrix:{
    
    
        status:"up"
    }
}
通过这里我们可以发现,尽管执行了fallback方法,但此时hystrix状态依然是up,这是因为咱们的失败率还没有达到阀值(默认520次失败),这里再次强调,执行回退的逻辑并不代表断路器已经打开,请求超时,失败,被拒绝以及断路器打开都会执行回退逻辑
9.5秒请求20{
    
    
    status:"up",
    hystrix:{
    
    
        status:"CIRCUIT_OPEN"(断路器打开的标识)
    }
}

11.4.feign整合Hystrix

很显然@HystrixCommand是添加到方法上的,那么对于feign来说一定不适用,如何让Hystrix整合feign

1.feign方法上添加相关注解
@FeignClient(name = "EUREKA-CLIENT2",fallback = FeignClientFallBack.class)
public interface TestFeign {
    
    
    //对象类型参数 提交方式要是POST
    @RequestMapping(value = "/feign/testUser",method = RequestMethod.POST)
    public String testFeignUser(User user);
}
2.实现Feign接口
@Component
public class FeignClientFallBack implements TestFeign{
    
    
	@Override
    public String testFeignUser(User user) {
    
    
        return "method invoke fail ";
    }
}
3.添加feign的配置文件
#为feign开启断路器
feign.hystrix.enabled=true

12.统一配置中心

为什么需要统一配置中心?

统一配置中心顾名思义,就是将配置统一管理,配置统一管理的好处是在日后大规模集群部署服务应用时相同的服务配置一致,日后再修改配置只需要统一修改全部同步,不需要一个一个服务手动维护

统一配置中心的架构图:

在这里插入图片描述

12.1.配置中心服务器的开发

1.添加依赖
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-config-server</artifactId>
</dependency>
2.添加注解支持
@EnableEurekaClient
@EnableConfigServer
@SpringBootApplication
public class EurekaApplication {
    
    

	public static void main(String[] args) {
    
    
		SpringApplication.run(EurekaApplication.class, args);
	}
}
3.在远程的仓库创建配置文件(yml,properties,yaml)
4.配置相关的配置文件
#注册到注册中心
eureka.client.service-url.defaultZone=http://localhost:8761/eureka
#默认端口号 8888
server.port=8888
# 实例名  
spring.application.name=config
#配置git的远程仓库   https 暂时不支持ssh
spring.cloud.config.server.git.uri=https://gitee.com/my_os_do_you_know/springcloud-config.git
5.启动配置中心服务

http://localhost:8888/order-a.yml

http://localhost:8888/order-a.yaml

http://localhost:8888/order-a.properties

http://localhost:8888/order-a.json

以上四种访问方式都可以
在这里插入图片描述

6.服务的配置的访问规则

{name}-{profiles}.yml

{name}/{profiles:.[^-].}

{name}-{profiles}.json

{label}/{name}-{profiles}.yml

{name}/{profiles}/{label:.*}

{label}/{name}-{profiles}.properties

{label}/{name}-{profiles}.json

{name}/{profile}/{label}/**

{name}/{profile}/{label}/**

说明:

label: 分支名称 默认是master分支

name:文件的服务的名称(自定义的名称)

profiles:不同环境

12.2.创建配置中心客户端

1.导入相关依赖
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-config-client</artifactId>
</dependency>
2.添加配置
#开启配置中心
spring.cloud.config.enabled=true
#找到配置中心实例
spring.cloud.config.discovery.service-id=CONFIG
#指定名字
spring.cloud.config.name=eureka
#指定环境
spring.cloud.config.profile=client1
#指定分支
spring.cloud.config.label=master
#指定配置中心的uri
spring.cloud.config.uri=http://localhost:8888
3.注意将配置文件名修改为 bootstrap.yml

在这里插入图片描述

注意:如果不改成bootstrap.properties 会报地址被占用的错

bootstrap.yml(bootstrap.properties)用来程序引导时执行,应用于更加早期配置信息读取,如可以使用来配置application.yml中使用到参数等

application.yml(application.properties) 应用程序特有配置信息,可以用来配置后续各个模块中需使用的公共参数等。

bootstrap.yml 先于 application.yml 加载

典型的应用场景如下:

  • 当使用 Spring Cloud Config Server 的时候,你应该在 bootstrap.yml 里面指定 spring.application.name 和 spring.cloud.config.server.git.uri
  • 和一些加密/解密的信息

技术上,bootstrap.yml 是被一个父级的 Spring ApplicationContext 加载的。这个父级的 Spring ApplicationContext是先加载的,在加载application.yml 的 ApplicationContext之前。

为何需要把 config server 的信息放在 bootstrap.yml 里?

当使用 Spring Cloud 的时候,配置信息一般是从 config server 加载的,为了取得配置信息(比如密码等),你需要一些提早的或引导配置。因此,把 config server 信息放在 bootstrap.yml,用来加载真正需要的配置信息

13.路由网关(zuul)

在微服务架构中,需要多个基础的服务治理组件,包括服务注册与发现、服务消费、负载均衡、断路器、智能
路由、配置管理等,由这些基础组件相互协作,共同组建了一个简单的微服务系统。一个简单的微服务系统如下图
在这里插入图片描述

注意:A服务和B服务是可以相互调用的,并且配置服务也是注册到服务注册中心的。
总结:在Spring Cloud微服务系统中,一种常见的负载均衡方式是,客户端的请求先先经过负载均衡(
Ngnix),再到达服务网关(zuul集群),然后再到具体的服务。服务统一注册到高可用的服务注册中心集群,服务的所有的配置文件由配置服务管理,配置服务的配置文件仓库,方便开发人员随时改配置。

13.1.Zuul 简介

Zuul的主要功能是路由转发和过滤器。路由功能是微服务的一部分,比如/api/user转发到到user服
务,/api/shop转发到到shop服务。zuul默认和Ribbon结合实现了负载均衡的功能

1.搭建Zull
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-zuul</artifactId>
 </dependency>
2.编写Zuul的入口类
 @EnableZuulProxy
 @EnableEurekaClient
 @SpringBootApplication
 public class ServiceZuulApplication {
    
    

 public static void main(String[] args) {
    
    
 SpringApplication.run(ServiceZuulApplication.class, args);
 }
}
3.编写application.yml
eureka:
	client:
		serviceUrl:
 			defaultZone: http://localhost:8080/eureka/
server:
	port: 8769
spring:
	application:
		name: service-zuul
zuul:
 	routes:
 		api-a:
 			path: /api-a/**
 			serviceId: service-ribbon
 		api-b:
 			path: /api-b/**
			serviceId: service-feign
#关闭安全认证		
management.security.enabled=false
#设置断路时间
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=5000			 

猜你喜欢

转载自blog.csdn.net/weixin_46055113/article/details/106782693