学习springcloud

文章目录

第一章 认识Spring Cloud

1.1 Spring Cloud是什么

Spring Cloud为开发人员提供了在分布式系统中快速构建一些常见模式的工具(例如配置管理,服务发现,断路器,智能路由,微代理,控制总线,一次性令牌,全局锁定,领导选举,分布式会话,集群状态)。分布式系统的协调导致样板模式,使用Spring Cloud开发人员可以快速建立实现这些模式的服务和应用程序。它们可以在任何分布式环境中很好地工作,包括开发人员自己的笔记本电脑,裸机数据中心和托管平台(如Cloud Foundry)。

1.2 特征

Spring Cloud专注于为典型用例提供良好的开箱即用体验,并为其他用例提供扩展性机制。

  • 分布式/版本化配置

  • 服务注册和发现

  • 路由

  • 服务到服务呼叫

  • 负载平衡

  • 断路 器

  • 全局锁

  • 领导层选举和集群状态

  • 分布式消息传递

第二章 搭建Spring Cloud

2.1搭建和配置一个服务提供者

1.创建一个SpringBoot工程,并且添加SpringBoot的相关依赖
2.创建服务提供者的访问方法,也就是后续消费者如何访问提供者
Spring Cloud是基于rest的访问,所以我们添加一个Controller,在该Controller中提供一个访问入口:

@RestController
public class HelloController {
    @RequestMapping("/service/hello")
    public String hello(){
        //进行业务处理
        System.out.println("被执行了");
        return "hello,Spring Cloud";
    }
}

3.启动运行改SpringBoot程序,访问该controller

2.2 搭建和配置一个服务者

1.创建一个SpringBoot工程,并且添加SpringBoot的相关依赖
2.开发一个消费者方法,去消费服务提供者的服务,这个消费者方法也是一个Controller

@Configuration
public class BeanConfig {
    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}
@RestController
public class WebController {
    @Autowired
    private RestTemplate restTemplate;
    @RequestMapping("/web/hello")
    public String hello(){
        System.out.println("我被执行了");
       return restTemplate.getForEntity("http://localhost:8080/service/hello",String.class).getBody();
    }
}

2.3 服务注册中心Eureka

一定要对准版本号
Spring Cloud提供了多种服务注册与发现的实现方。
什么是服务注册?
服务注册:服务的消费者所在主机,端口,版本号,通信协议等信息登记到注册中心上;
什么服务发现?
服务发现:服务消费者向注册中心请求已经登记的服务列表,然后得到某个服务的主机,端口,版本号,通信协议等信息,从而实现对具体服务的调用;
Eureka是什么?
Eureka是一个服务治理组件,它主要包括服务注册和服务发现,主要用来搭建服务注册中心
Eureka是一个基于REST的服务,用来定位服务,进行中间层服务器的负载均衡和故障转移

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

EurekaClient通过注册中心进行访问,是一个java客户端,用于简化Eureka Server的交互,客户端同时也具备一个内置的、使用轮询(round-robin)负载算法的负载均衡器。在应用启动后,将会向Eureka Server发送心跳(默认周期为30秒),如果Eureka Server多个心跳周期内没有接收到某个节点的心跳,Eureka Server将会从服务注册表中吧这个服务节点移除(默认90秒)

2.4 搭建与配置Eureka服务注册中心

1.创建一个SpringBoot项目,并且添加SpringBoot的相关依赖
2.添加eureka的依赖

<dependency>
     <groupId>org.springframework.cloud</groupId>
       <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId
 </dependency>
    <dependencies>
           <dependency>
               <groupId>org.springframework.cloud</groupId>
               <artifactId>spring-cloud-dependencies</artifactId>
               <version>Finchley.SR2</version>
               <type>pom</type>
               <scope>import</scope>
           </dependency>
       </dependencies>
   </dependencyManagement>

3.在SpringBoot的入口类上添加一个@EnbleEurekaServer注解,用于开启Eureka注册中心服务端
4.在application.properties文件中配置Eureka服务注册中心信息

#内嵌的Tomcat的端口
server.port=8761
#设置该服务注册中心的hostname
eureka.instance.hostname=localhost
#由于我们目前创建的应用是一个服务注册中心,而不是普通的应用,默认情况下,这个应用会向注册中心(也是他自己)组测他自己,设置为false表示禁止这种自己想自己注册默认行为
eureka.client.register-with-eureka=false
#表示不去检索其他的服务,因为服务注册中心本身的职责就是维护服务实例,它不需要去检索其他服务
eureka.client.fetch-registry=false
#指定服务注册中心的位置
eureka.client.service-url.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka/
<repositories>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/libs-milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>

2.5 向Eureka服务注册中心注册服务

1.在该服务提供者中添加eureka的依赖,因为服务提供者向注册中心注册服务,需要连接eureka,所以需要eureka客户端支持;

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-service</artifactId>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Finchley.SR2</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

指定springcloud下载仓库。

<repositories>
     <repository>
         <id>spring-milestones</id>
         <name>Spring Milestones</name>
         <url>https://repo.spring.io/libs-milestone</url>
         <snapshots>
             <enabled>false</enabled>
         </snapshots>
     </repository>
 </repositories>     

2.激活Eureka中的EnableEurekaClient功能
在Spring Boot的入口函数处,通过添加@EnableEurekaClient注册来表明自己是一个eureka客户端,让我的服务提供者可以连接eureka注册中心;
3.配置服务名称和注册中心地址

#服务提供者的内嵌tomcat端口
server.port=9100
#配置服务的名称
spring.application.name=_02-springcloud-service-provider
#eureka的链接地址
eureka.client.service-url.defaultZone=http://localhost:8761/eureka

4.启动服务提供者SpringBoot程序的main方法运行

2.6 从Eureka服务注册中心发现与消费服务

由eureka客户端实现,而服务的消费有Ribbon实现,也就是说服务的调用需要eureka客户端和Ribbon两个配合起来才能实现
Eureka客户端是什么?
Eureka客户端就是一个Java客户端,用来连接Eureka服务端,与服务端进行交互,负载均衡,服务的故障切换等;
Ribbon是什么?
Ribbon是一个基于HTTP和TCP的客户端负载均衡器,当使用Ribbon对服务进行访问的时候,它会扩展Eureka客户端的服务发现功能,实现从Eureka注册中心中获取服务端列表,并通过Eureka客户端来确定服务就是已经启动,Ribbon在Eureka客户端发现的基础上,实现了对服务实例的选择策略,从而实现对服务的负载消费。
步骤
1.在该消费者项目中添加eureka的依赖,因为服务消费者从注册中心获取服务,需要连接eureka,所以需要eureka的支持
2.激活Eureka中的EnableEurekaClient功能
在SpringBoot的入口函数处,通过添加@EnableEurekaClient注解来表明自己是一个eureka客户端,让我的服务消费者可以使用eureka注册中心
3.配置服务中心的地址:

server.port=8081
#配置服务的名称
spring-application.name=_02-springcloud-service-consumer
#配置eureka注册中心地址
eureka.client.service-url.defaultZone=http://localhost:8761/eureka

4.前面我介绍了服务的发现由eureka客户端实现,而服务的真正调用由ribbon实现,所以我们要在调用服务提供者时使用ribbon来调用

  • 1.添加@LoadBalanced进行负载均衡
@Configuration
public class BeanConfig {
    @LoadBalanced
    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}
  • 2.加入了ribbon的支持,那么在调用时,即可使用服务名称来访问
@RestController
public class WebController {
    @Autowired
    private RestTemplate restTemplate;
    @RequestMapping("/web/hello")
    public String hello(){
        System.out.println("我被执行了web");
        return restTemplate.getForEntity("http://_01-SPRINGCLOUD-SERVICE-PROVIDER/service/hello",String.class).getBody();
    }
}

5.完成步骤后,就可以启动消费者的SpringBoot程序,main方法运行;
6.启动运行成功之后,通过在浏览器地址栏访问我们的消费者,看是否可以正常调用完成服务提供者提供的服务;

第三章 服务注册中心Eureka

3.1 Eureka注册中心高可用集群概述

为了避免只存在一个eureka注册中心节点,而导致的发生故障,因此需要通过注册中心集群来解决

eureka服务中心本身也是一个服务,他也可以看作是一个提供者,又可以看作是一个消费者,我们之前通过配置:
eureka.client.register-with-eureka=false让注册中心不注册自己,但是我们可以像其他注册中心注册自己;

Eureka Serve的高可用性就是通过相互注册,进而实现服务清单的互相同步,却到每一个注册中心都可查询到已注册的效果,从而达到高可用性。
在这里插入图片描述

3.2 Eureka注册中心高可用集群搭建

1.在8761的配置文件中,让它的service-url指向8762,在8762的配置文件中让它的service-uel指向8761
2.由于8761和8762互相只想对方,实际上我们构建了一个双节点的服务注册中心集群
eureka.client.service-url.defaultZone=http://eureka8761:8761/eureka
eureka.client.service-url.defaultZone=http://eureka8761:8762/eureka
然后在本地hosts文件配置:C:\Windows\System32\divers\etc\hosts
127.0.0.1 eureka8761
127.0.0.1 eureka8762
运行时,在运行配置项目中Program Arguments中配置:
–spring.profiles.active=eureka8761
–spring.profiles.active=eureka8762
分别启动两个注册中心,访问两个注册中心页面,观察注册中心页面是否正常;

3.3 Eureka注册中心高可用集群测试

在要进行注册的服务中配置:

eureka.client.service-url.defaultZone=http://eureka8761:8761/eureka/,http://eureka8762:8762/eureka/

会在两个注册中心发现生产者,消费者共享

3.4 Eureka服务注册中心自我保护机制

当自我保护机制是Eureka注册中心的重要特性,当Eureka注册中心进行自我保护模式时,在Eureka Server首页会输出如下警告信息:
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 Server将会注销实例,但是当发生网略分区故障时,那么微服务与Eureka Server之间将无法正常通信,以上行为可能变得非常危险了——因为微服务本身是正常的,此时不应该注销这个微服务,如果没有自我保护机制,那么Eureka Server就会将此服务注销掉。

Eureka通过”自我保护模式“来解决这个问题——当Eureka Server节点在短时间内丢失过多客户端时(可能发生了网络分区故障),他就会把这个微服务节点进行保护,一旦进入自我保护模式,Eure Server就会保护服务注册表中的信息,不删除服务注册表中的数据(也就不会注销任何微服务),当网络故障恢复后,该Eureka Server节点会再自动退出自我保护模式。

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

当然也可以使用配置项:eureka.server.enable-self-preservation=false禁用自我保护模式

但是Eureka Server自我保护模式也会给我们带来一些困扰,如果在保护期内某个服务提供者刚好非正常下线了,此时服务消费者就会把不到一个无效的服务实例,此时会调用失败,对于这个问题需要服务消费者端具有一些容错机制,如重试,断路器等。

Eureka 的自我保护模式是有意义的,被模式激活后,它不会从注册列表中剔除因长时间没收到心跳导致注册过期的服务,而是等待修复,知道心跳恢复正常之后,它自动退出自我保护模式,这种模式旨在避免网络分区导致服务不可用的问题。
关闭后
THE SELF PRESERVATION MODE IS TURNED OFF.THIS MAY NOT PROTECT INSTANCE EXPIRY IN CASE OF NETWORK/OTHER PROBLEMS.

#每间隔2秒,向服务器发送一次心跳,证明自己依然”存活“
eureka.instance.lease-renewal-interval-in-seconds=2
#告诉服务端,如果我10s内没有给你发心跳,就代表我故障了,将我提出掉
eureka.instance.lease-expiration-duration-in-seconds=10

第四章 客户端负载均衡Ribbon

4.1 Spring Cloud中的Ribbon是什么?

负载均衡是指将一个请求均匀地分摊到不同的节点单元上执行,负载均衡和分为硬件负载均衡和软件负载均衡;
硬件负载均衡: F5,深信服,Array等
软件负载均衡: Nginx,LVS,HAProxy等
软硬件负载均衡,他们都会维护一个可用地服务端清单,通过心跳检测来剔除故障的服务端节点以保证清单中都是可以正常访问地服务端节点。当客户端发送请求到负载均衡设备的时候,该设备按某种算法(比如轮询,权重,最小连接数等)从维护的可用服务端清单中取出一台服务端的地址,然后进行转发。

Ribbon是Netfix发布的开源项目,主要功能是提供客户端的软件负载均衡算法,是一个基于HTTP和TCP的客户端负载均衡工具。
Ribbon与Nginx的区别
Ribbon是客户端的负载均衡工具,而客户端负载均衡和服务端负载均衡最大的区别在于服务清单所存储的位置不同,在客户端负载均衡中,所有客户端节点下的服务端清单,需要自己从服务注册中心上获取,比如Eureka服务注册中心。同服务端负载均衡的框架类似,在客户端负载均衡中也需要心跳去维护服务端清单的健康性,只是这个步骤需要与服务注册中心配合完成。在Spring Cloud中,由于SpringCloud对Ribbon做了二次分装,所以默认会创建针对Ribbon的自动化整合配置。

4.2 Ribbon实现客户负载均衡

由于Spring Cloud Ribbon的封装,我们在微服务架构中使用客户端负载均衡调用非常简单,只需要如下两步:
1.启动多个服务提供者示例并注册到一个服务注册中心或是服务注册中心集群。
2.服务消费者通过被@LoadBalanced注解修饰过的RestTemplate来调用服务提供者。

4.3Ribbon负载均衡策略

负载均衡
随机 (Random)
轮询 (RoundRobin)
一致性哈希 (ConsistentHash)
哈希 (Hash)
加权(Weighted)

@Bean
    public IRule iRule(){
        return new RandomRule();//默认策略
    }

4.4 Rest请求模板类解读

当我们从服务消费端去调用服务提供者的服务的时候,使用RestTemplate
常见的Rest的方式常见4种:
GET请求–查询数据
POST请求–添加数据
PUT请求–修改数据
DELETE请求–删除数据

4.5 RestTemplate的GET请求

Get请求有两种方式:
第一种:getForEntity()
该方法返回一个ResponseEntity<T>对象,ResponseEntity<T>是Spring对HTTP请求响应的封装,包括了几个重要的元素,比如响应码,contentType,contentLength,响应消息体等;

//调用SpringCloud服务提供者提供的服务
//参数一:要调用的服务的地址
//参数二:表示要反悔的body对象是String类型
        ResponseEntity<String> responseEntity=restTemplate.getForEntity("http://01-SPRINGCLOUD-SERVICE-PROVIDER/service/hello",String.class);
        int statusCodeValue=responseEntity.getStatusCodeValue();
        HttpStatus httpStatus=responseEntity.getStatusCode();
        HttpHeaders httpHeaders=responseEntity.getHeaders();
        String body=responseEntity.getBody();
        System.out.println(statusCodeValue);
        System.out.println(httpHeaders);
        System.out.println(httpStatus);
        System.out.println(body);

重载方法,传参

 public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables) throws RestClientException {
        RequestCallback requestCallback = this.acceptHeaderRequestCallback(responseType);
        ResponseExtractor<ResponseEntity<T>> responseExtractor = this.responseEntityExtractor(responseType);
        return (ResponseEntity)nonNull(this.execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables));
    }

 public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException {
        RequestCallback requestCallback = this.acceptHeaderRequestCallback(responseType);
        ResponseExtractor<ResponseEntity<T>> responseExtractor = this.responseEntityExtractor(responseType);
        return (ResponseEntity)nonNull(this.execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables));
    }

第一个重写方法

 @RequestMapping("/service/getUser")
   public User getuser(@RequestParam("id") Integer id,
                       @RequestParam("name") String name,
                       @RequestParam("phone") String phone){
       User user=new User();
       user.setId(id);
       user.setName(name);
       user.setPhone(phone);

       return user;
   }
   
@RequestMapping("/web/getUser")
   public User getUser(){
       System.out.println("我被执行了呀--------------");
   String [] strArray={"105","张无忌","13546788"};
       //调用SpringCloud服务提供者提供的服务
       ResponseEntity<User> responseEntity=restTemplate.getForEntity("http://01-SPRINGCLOUD-SERVICE-PROVIDER/service/getUser?id={0}&name={1}&phone={2}",User.class,strArray);
       int statusCodeValue=responseEntity.getStatusCodeValue();
       HttpStatus httpStatus=responseEntity.getStatusCode();
       HttpHeaders httpHeaders=responseEntity.getHeaders();
       User body=responseEntity.getBody();
       System.out.println(statusCodeValue);
       System.out.println(httpHeaders);
       System.out.println(httpStatus);
       System.out.println(body.getId()+""+body.getName()+""+body.getPhone());
       System.out.println("我被执行了web");
       return restTemplate.getForEntity("http://01-SPRINGCLOUD-SERVICE-PROVIDER/service/getUser?id={0}&name={1}&phone={2}",User.class,strArray).getBody();
   }

第二个重写方法

 String [] strArray={"105","张无忌","13546788"};
        Map<String,Object> paraMap=new ConcurrentHashMap<String, Object>();
        paraMap.put("id",1078);
        paraMap.put("name","张景山");
        paraMap.put("phone","123542341");
        //调用SpringCloud服务提供者提供的服务
        ResponseEntity<User> responseEntity=restTemplate.getForEntity("http://01-SPRINGCLOUD-SERVICE-PROVIDER/service/getUser?id={id}&name={name}&phone={phone}",User.class,paraMap);
        int statusCodeValue=responseEntity.getStatusCodeValue();
        HttpStatus httpStatus=responseEntity.getStatusCode();
        HttpHeaders httpHeaders=responseEntity.getHeaders();
        User body=responseEntity.getBody();
        System.out.println(statusCodeValue);
        System.out.println(httpHeaders);
        System.out.println(httpStatus);
        System.out.println(body.getId()+""+body.getName()+""+body.getPhone());
        System.out.println("我被执行了web");
        return restTemplate.getForEntity("http://01-SPRINGCLOUD-SERVICE-PROVIDER/service/getUser?id={id}&name={name}&phone={phone}",User.class,paraMap).getBody(); 

第二种:getForObject()
该类与getForEntity使用类似,只不过该类是基于getForEntity的再次封装,可以将http的响应体body信息进行转化成指定的对象,方便我们的代码开发

当不需要返回响应中的其他信息,只需要body体信息的时候,可以使用这个更方便
三种重写方法

@Nullable
    <T> T getForObject(String var1, Class<T> var2, Object... var3) throws RestClientException;
    //例如
	User responseEntity=restTemplate.getForObject("http://01-SPRINGCLOUD-SERVICE-PROVIDER/service/getUser?id={id}&name={name}&phone={phone}",User.class,paraMap);
      
    @Nullable
    <T> T getForObject(String var1, Class<T> var2, Map<String, ?> var3) throws RestClientException;

    @Nullable
    <T> T getForObject(URI var1, Class<T> var2) throws RestClientException;  

4.6 RestTemplate的POST请求

Post与Get请求非常类似

restTemplate.postForObject()
restTemplate.postForEntity()
restTemplate.postForLocation()

4.7 RestTemplate的PUT请求

restTemplate.put()

4.8 RestTEmplate的DELETE请求

restTemplate.delete()

第五章 服务熔断Hystrix

5.1 Hystrix是什么

在微服务框架中,我们将系统拆分成了很多服务单元,各个服务单元的应用之间通过服务注册与订阅的方式相互依赖。由于每一个服务单元都在不同的进程中运行,依赖通过远程调用的方式执行,这样就有可能因为网络原因或者是依赖服务自身问题出现调用故障或延迟,而这些问题会直接导致调用方的对外服务也出现延迟,若此时调用方法请求不断增加,最后就会形成任务积压,最终导致服务的瘫痪。
断路器
在微服务架构中,存在着那么多的服务单元,若一个单元出现故障,就很容易因依赖关系而引发故障的蔓延,最终导致整个系统的瘫痪,这样的架构相较传统架构更加不稳定。为了解决这样的问题,产生了断路器等一系列的服务保护机制。

断路器模式源于 Martin Fowler 的 Circuit Breaker 一文。“断路器” 本身是一种开关装置,用于在电路上保护线路过载,当线路中有电器发生短路时,“断路器” 能够及时切断故障电路,防止发生过载、发热甚至起火等严重后果。
Hystrix 是一个库,通过添加延迟容忍和容错逻辑,帮助你控制这些分布式服务之间的交互。Hystrix 通过隔离服务之间的访问点、停止级联失败和提供回退选项来实现这一点,所有这些都可以提高系统的整体弹性。

Hystrix 被设计的目标是:

  • 对通过第三方客户端库访问的依赖项(通常是通过网络)的延迟和故障进行保护和控制。

  • 在复杂的分布式系统中阻止级联故障。

  • 快速失败,快速恢复。

  • 回退,尽可能优雅地降级。

  • 启用近实时监控、警报和操作控制。

Hystrix 能做什么

  • 延迟和容错
    停止级联故障。Fallbacks 和优雅的降级。Fail fast 和快速恢复。通过中断来隔离线程和信号。

  • 实时监控
    实时监控和修改配置。当系统中的服务和属性发生改变时立即更新。在数秒内察觉改变、做出决策、响应变化并看到调整后的结果。

  • 并发性
    并发并行执行。并发感知请求缓存。通过请求合并自动批处理。

5.2 Hystrix快速入门

1.添加依赖

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-hystrix</artifactId>
            <version>1.4.4.RELEASE</version>
        </dependency>

2.在入口类中使用@EnableCircuitBreaker注解开启断路器功能
在调用远程服务的方法上添加注解
@HystrixCommand(fallbackMethod = "error")

@RequestMapping("/web/hystrix")
    @HystrixCommand(fallbackMethod = "error")
    public String hystrix(){
        return restTemplate.getForEntity("http://01-SPRINGCLOUD-SERVICE-PROVIDER/service/hello",String.class).getBody();
    }
    public String error(){
        return "error";
    }  

也可以使用一个名为@SpringCloudApplication的注解代替main方法上的三个注解

5.3 服务消费者Hystrix测试

hystrix默认超时时间1000毫秒,如果你后端的响应超过此时间,就会触发断路器
修改hystrix的默认超时时间

@RequestMapping("/web/hystrix")
    @HystrixCommand(fallbackMethod = "error",commandProperties={@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value = "3500")})
    public String hystrix(){
        return restTemplate.getForEntity("http://01-SPRINGCLOUD-SERVICE-PROVIDER/service/hello",String.class).getBody();
    }
    public String error(){
        return "error";
    }

5.4 Hystrix的服务降级

服务降级(fallback):就是某个服务出现故障了,不让调用方一直等待,返回一个友好的提示(fallback)。下面情况会发出降级:程序运行异常、超时、服务熔断触发服务降级、线程池\信号量打满也会导致服务降级。

5.5 Hystrix的异常处理

5.5.1只需要在服务器降级方法中添加一个Throwable类型的参数就能够获取到抛出的异常类型

@RequestMapping("/web/hystrix")
    @HystrixCommand(fallbackMethod = "error",commandProperties={@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value = "3500")})
    public String hystrix(){
        return restTemplate.getForEntity("http://01-SPRINGCLOUD-SERVICE-PROVIDER/service/hello",String.class).getBody();
    }
    public String error(Throwable throwable){
        System.out.println("异常:"+throwable.getMessage());
        return "error";
    }

5.5.2 如果远程服务有一个异常抛出后我们不希望进行降级处理,而是直接抛给用户。使用@HystrixCommand的注解进行忽略异常

@HystrixCommand(fallbackMethod = "error",ignoreExceptions = RuntimeException.class)

5.5.3 自定义Hystrix请求的服务器异常熔断处理

也可以自定义继承自HystrixCommand来实现自定义的Hystrix请求,在getFallback方法中调用getExecutionException方法来获取服务抛出的异常;

public class MyHystrixCommand extends HystrixCommand<String> {
    private RestTemplate restTemplate;
    @Override
    public String getFallback() {
        return "error";
    }

    public MyHystrixCommand (Setter setter,RestTemplate restTemplate){
        super(setter);
        this.restTemplate=restTemplate;
    }

    @Override
    protected String run() throws Exception {
        return restTemplate.getForEntity("http://01-SPRINGCLOUD-SERVICE-PROVIDER/service/hello",String.class).getBody();
    }
}
@RequestMapping("/web/hystrix2")
     public String hystrix2(){
        MyHystrixCommand myHystrix=new MyHystrixCommand(com.netflix.hystrix.HystrixCommand.Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("")),restTemplate);
        String str=myHystrix.execute();
        return str;
    }

5.5.4 Hystrix的自定义同步与异步调用

@RequestMapping("/web/hystrix2")
     public String hystrix2() throws ExecutionException, InterruptedException {
        MyHystrixCommand myHystrix=new MyHystrixCommand(com.netflix.hystrix.HystrixCommand.Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("")),restTemplate);
       //同步调用(该方法执行后,会等待远程的返回结果,拿到了远程的返回结果,该方法才返回,然后代码继续往下执行)
        //String str=myHystrix.execute();
        //异步调用(该方法不会马上有远程的返回结果,将来会有结果)
        Future<String> future=myHystrix.queue();
        String str=future.get();
        return str;
    }

5.5.5 自定义Hystrix请求的服务异常熔断处理

在自定义类的getFallback方法中调用getExecutionException方法来获取服务抛出的异常

@Override
    public String getFallback() {
        Throwable throwable=super.getExecutionException();
        System.out.println(throwable.getMessage());
        return "error";
    }

5.6 Hystrix仪表盘监控

5.6.1 环境搭建

1.创建一个普通的Spring Boot工程
2.添加依赖
3.入口类上添加注解@EnableHystrixDashboard
4.属性配置:比如端口号设置为3721.
页面访问
http://localhost:3721/hystrix
在这里插入图片描述

5.6.2 搭建监控端点

1.消费者项目要有hystrix的依赖

<dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-hystrix</artifactId>
      <version>1.4.4.RELEASE</version>
 </dependency>

2.需要有一个springboot的服务监控依赖

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

3.配置文件需要配置springboot监控端点的访问权限
management.endpoints.web.exposure.include=*
用来暴露endpoints的
4.访问入口
http://localhost:8081/actuator/hystrix.stream
注:先要访问一次工程中的其他接口。
在这里插入图片描述

使用仪表盘监控
在这里插入图片描述

在这里插入图片描述

第六章 声明式服务消费Feign

6.1 Feign是什么

Feign一个声明式REST调用客户端。
Ribbon负载均衡,Hystrix服务熔断都被整合到Feign中,简化了开发工作,提供了一种声明式的Web服务客户端定义方式。

6.2 使用Feign实现消费者

1.创建普通SpringBoot工程

2.添加依赖

		<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-feign</artifactId>
            <version>1.4.5.RELEASE</version>
        </dependency>

3.添加注释
在构建主类中添加@EnableFeignClients

4.声明服务
定义一个HelloService接口,通过@FeignClient注解来指定服务名称,进而绑定服务,然后再通过SpringMVC中提供的注解来绑定服务提供者提供的接口

@FeignClient("01-springcloud-service-provider")
public interface HelloService {
    /**
     * 声明一个方法,这个方法就是远程的服务提供者提供的哪个方法
     * */
    @RequestMapping("/service/hello")
    public String hello();
}

对应绑定的如下:

 @RequestMapping("/service/hello")
    public String hello(){
        try {
            Thread.sleep(4000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //进行业务处理
        System.out.println("被执行了--------------------------------------------------------------------------");
        return "hello,Spring Cloud,,,,,,,1";
    }

5.使用Controller中调用服务

@RestController
public class FeignController {
    @Autowired
    private HelloService helloService;
    
    @RequestMapping("/web/hello")
    public String hello(){
        //调用声明式接口方法,实现对远程服务的调用
        return helloService.hello();
    }
}

6.属性配置
在application.properties中指定服务注册中心,端口号等信息,如下:

server.port=8082
#配置服务的名称
spring.application.name=05-springcloud-service-feign
#配置eureka注册中心地址
eureka.client.service-url.defaultZone=http://eureka8761:8761/eureka/,http://eureka8762:8762/eureka/

7.测试

6.3使用Feign实现负载均衡的服务消费

使用Feign实现消费就本身实现负载均衡
服务熔断
1.在application.properties文件开启hystrix功能
feign.hystrix.enabled=true
2.指定熔断回调逻辑
@FeignClient(value = "01-springcloud-service-provider",fallback = MyFallback.class)

*/public class MyFallback implements HelloService {
    @Override
    public String hello() {
        return "远程服务不可用,暂时采用本地逻辑代替。。。";
    }
}

3.服务熔断获取异常信息
使用fallbackFactory

@FeignClient(value = "01-springcloud-service-provider",fallbackFactory = MyFallbackFactory.class)
@Component
public class MyFallbackFactory implements FallbackFactory<HelloService> {
    @Override
    public HelloService create(Throwable throwable) {
        return new HelloService() {
            @Override
            public String hello() {
                return throwable.getMessage();
            }
        };
    }
}

第七章 API网关Zuul

7.1 Spring Cloud的Zuul是什么

Zuul作为微服务系统的网关组件,是从设备和网站到Netflix流应用程序后端的所有请求的前门。作为边缘服务应用程序,Zuul旨在实现动态路由,监控,弹性和安全性。

7.2 使用Zuul构建API网关

1.创建普通项目,加入依赖

<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
            <version>2.2.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            <version>2.0.2.RELEASE</version>
        </dependency>

2.在入口类上添加@EnableZuulProxy注解,开启Zuul的API网关服务功能
3.在application.properties文件中配置路由规则

#配置服务内嵌的Tomcat窗口
server.port=8080
#配置服务的名称
spring.application.name=_06-springcloud-api-geteway
#配置路由规则
zuul.routes.api-wkcto.path=/api-wkcto/**
zuul.routes.api-wkcto.serviceId=05-springcloud-service-feign
#配置API网关到注册中心上,API网关也将作为一个服务注册到eureka-server上
eureka.client.service-url.defaultZone=http://eureka8761:8761/eureka/,http"//eureka8762:8762/eureka/

7.3使用Zuul进行请求过滤

public class AuthFilter extends ZuulFilter {
    //filterType方法的返回值为过滤的类型,过滤器的类型决定了过滤器在哪个生命周期执行,
    // pre表示在路由之前执行过滤器,其他值还有post,error,route和static,当然也可以自定义
    @Override
    public String filterType() {
        return "pre";
    }
    //filterOrder方法表示过滤器的执行顺序,当过滤器很多时,我们可以通过该方法的返回值来指定过滤器的执行顺序
    @Override
    public int filterOrder() {
        return 0;
    }
    //shouldFilter方法用来判断过滤器是否执行,true表示执行,false表示不执行
    @Override
    public boolean shouldFilter() {
        return false;
    }
    //run方法则表示过滤的具体逻辑,如果请求地址中携带了token参数的话,
    // 则认为是合法请求,否则为非法请求,如果是非法请求的话,首先设置ctx.setSendZuulResponse(false);
    // 表示不对请求进行路由,然后设置响应码和响应值。可返回任何值,但目前没有意义
   @Override
    public Object run() throws ZuulException {
       RequestContext ctx= RequestContext.getCurrentContext();
        HttpServletRequest request=ctx.getRequest();
        String token=request.getParameter("token");
        if(token==null){
            ctx.setSendZuulResponse(false);
            ctx.setResponseStatusCode(401);
            ctx.addZuulResponseHeader("content-type","text/html;charset=utf-8");
            ctx.setResponseBody("非法访问");
        }
        return null;
    }
}

7.4 Zuul的路由规则

7.4.1 简化配置

zuul.routes.api-wkcto.path=/api-wkcto/**
zuul.routes.api-wkcto.serviceId=05-springcloud-service-feign
上述可以简化为下面
zuul.routes.05-springcloud-service-feign=/api-wkcto/**

7.4.2 默认配置

如果映射规则我们什么都不写,系统也给我们提供了一套默认的配置规则

7.4.2 默认配置的跳过

默认情况下,Eureka上所有注册的服务都会被Zuul创建映射关系来进行路由。
可以采用如下配置进行路由忽略

#进行路由忽略
zuul.ignored-services=01-springcloud-service-provider
#忽略部分接口
zuul.ignored-patterns=/**/hello/**

7.4.3 路由通配符

在这里插入图片描述

7.4.4 Zuul的自我业务处理

API网关只是作为各个微服务的统一入口,但有时候也需要做相应的逻辑处理,如下操作

@RestController
public class GateWayController {
    @RequestMapping("/api/local")
    public String local(){
        System.out.println("在api getway中执行业务逻辑判断。。。");
        return "exec the api gateway";
    }
}
zuul.routes.gateway.path=/gateway/**
zuul.routes.geteway.url=forward:/api/local

7.5 Zuu的异常处理

1.正常情况下所有的请求都是按照pre,route,post的顺序来执行,然后由post返回responce
2.在pre阶段,如果有自定义的过滤器则执行自定义的过滤器
3.pre,routing,post的任意一个阶段如果抛异常了,则执行error过滤器我们可以有两种方式统一处理异常:
方式一:禁用zuul默认的异常处理SendErrorFilter过滤器,然后自定义自己的ErrorFilter过滤器
zuul.SendErrorFilter.error.disable=true

@Component
public class ErrorFilter extends ZuulFilter {
    private static  final Logger logger=LoggerFactory.getLogger(ErrorFilter.class);
    @Override
    public String filterType() {
        return "error";
    }

    @Override
    public int filterOrder() {
        return 1;
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() throws ZuulException {
       try {
           RequestContext context = RequestContext.getCurrentContext();
           ZuulException exception = (ZuulException) context.getThrowable();
           logger.error("进入系统异常拦截:", exception);

           HttpServletResponse response = context.getResponse();
           response.setContentType("application/json;charset=utf8");
           response.setStatus(exception.nStatusCode);
           PrintWriter writer = null;

           try {
               writer = response.getWriter();
               writer.print("{code:" + exception.nStatusCode + ",message:\"" + exception.getMessage() + "\"}");
           } catch (IOException e) {
               e.printStackTrace();
           } finally {
               if (writer != null) {
                   writer.close();
               }
           }
       }catch (Exception e){
           ReflectionUtils.rethrowRuntimeException(e);
       }
        return null;
    }
}

方式二:自定义全局error错误页面

@RestController
public class ErrorHandlerController implements ErrorController {
    @Override
    public String getErrorPath() {
        return "/error";
    }
    @RequestMapping("/error")
    public Object error(){
        RequestContext ctx=RequestContext.getCurrentContext();
        ZuulException exception=(ZuulException) ctx.getThrowable();
        return exception.nStatusCode+"--"+exception.getMessage();
    }
}

第八章 Spring Cloud Config

8.1 Spring Cloud Config是什么

Spring Cloud Config为分布式系统中的外部配置提供服务器和客户端支持,可以方便的对微服务各个环境下的配置进行集中式管理。Spring Cloud Config分为Config Server和Config Client两部分。Config Server负责读取配置文件,并且暴露Http API接口,Config Client通过调用Config Server的接口来读取配置文件。

8.2 构建Springcloud config配置中心

步骤:
1.创建一个spring boot 项目
2.在pom.xml文件中添加依赖

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

3.在入口类,也就是main方法的类上添加注解@EnableConfigServer
4.在application.properties中配置一下git仓库信息,此外我们使用GitHub,首先在我的Github上创建一个名为spring-cloud-config的项目,创建之后,在做如下配置

server.port=3721
spring.application.name=07-springcloud-config-server
spring.cloud.config.server.git.uri=https://github.com/myspring/spring-cloud-config.git
spring.cloud.config.server.git.search-paths=config-center
spring.cloud.config.server.git.username=xxx@163.com
spring.cloud.config.server.git.password=xxxx123456

8.2 构建SpringCloud config配置中心仓库

1.创建一个普通的Spring Boot工程,添加依赖

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

2.创建bootststrap.properties文件,用于获取配置信息

server.port=37222
spring.application.name=application
spring.cloud.config.profile=dev
spring.cloud.config.label=master
spring.cloud.config.url=http://localhost:3721/

3.创建controller进行测试

@RestController
public class ConfigController {
    @Autowired
    private Environment env;
    
    @Value("${url}")
    private String url;

    @RequestMapping("/cloud/url")
    public String url(){
        return url;
    }
    @RequestMapping("/cloud/url")
    public String url2(){
        return env.getProperty("url");
    }
}

8.3 Springcloud config配置中心客户端

1.创建工程,添加依赖

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

2.创建bootstrap.properties文件,用于获取配置信息

server.port=3722
spring.application.name=application
spring.cloud.config.profile=dev
spring.cloud.config.label=master
spring.cloud.config.url=http://localhost:3721/

8.3 Springcloud config的安全保护

整合SpringSecurity
1.在springcloud config server项目添加依赖

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

2.在springcloud config server项目中application.properties中配置用户名密码

spring.security.user.name=wkcto
spring-security.user.password=123456

3.在springcloud config client上配置用户名和密码

spring.security.user.name=wkcto
spring-security.user.password=123456
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值