# springcloud-provider **Repository Path**: wen_biao/springcloud-provider ## Basic Information - **Project Name**: springcloud-provider - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2020-12-17 - **Last Updated**: 2020-12-28 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README **SpringCloud工程创建步骤** **一、搭建和配置一个服务提供者** springcloud 版本:Hoxton.SR9 SpringBoot 版本:2.3.7 1、创建SpringBoot的工程 01-springcloud-service-provider 添加SpringBoot相关依赖; 2、创建服务提供者的访问方法、也就是后续消费者如何访问提供者; (springcloud是基于rest的访问,所以 添加一个Controller在该Controller中提供一个访问入口) `@RestController public class HelloController{ @RequestMapping(value= "/hello", method = RequestMethod.GET) public String hello(){ return "Hello Spring Cloud"; } }` 3、启动SpringBoot 访问该Controller; **二、搭建和配置一个服务消费者** 服务消费者也是一个SpringBoot项目,服务消费者主要用来消费服务提供者提供的服务 1、创建一个SpringBoot工程 02-springcloud-service-consumer,并添加SpringBoot相关依赖; 2、开发一个消费者方法,去消费服务提供者提供的服务,这个消费者方法也是一个Controller `@RestController public class WebController { @Autowired private RestTemplate restTemplate; @RequestMapping(value = "/web/hello") public String hello(){ //逻辑判断省略 //调用springcloud服务提供的服务 return restTemplate.getForEntity("http://localhost/service/hello",String.class).getBody(); } }` 3、启动该SpringBoot程序,测试服务消费者调用服务提供者 **三、集成服务注册中心Eureka** 在微服务中,服务注册与发现是核心组件之一 `SpringCloud支持最好的是Eureka,其次是Consul,再其次是Zookeeper。 著名的CAP理论指出,一个分布式系统不可能同时满足 C(一致性)A(可用性)P(分区容错性) 由于分区容错性在是分布式系统中必须要保证的, 因此我们只能在A和C之间进行权衡,在此Zookeeper保证的是CP, 而Eureka 则是AP。` `在ZooKeeper中,当master节点因为网络故障与其他节点失去联系时, 剩余节点会重新进行leader选举,但是问题在于, 选举leader需要一定时间,且选举期间整个ZooKeeper集群都是不可用的, 这就导致在选举期间注册服务瘫痪。在云部署的环境下, 因网络问题使得ZooKeeper集群失去 master节点是大概率事件, 虽然服务最终能够恢复,但是在选举时间内导致服务注册长期 不可用是难以容忍的。` Eureka保证AP `Eureka优先保证可用性,Eureka各个节点是平等的, 某几个节点挂掉不会影响正常节点的工作, 剩余的节点依然可以提供注册和查询服务。 而Eureka的客户端在向某个Eureka注册或时如果发现连接失败, 则会自动切换至其它节点,只要有一台Eureka还在, 就能保证注册服务可用(保证可用性), 只不过查到的信息可能不是最新的(不保证强—致性)。` **四、搭建配置Eureka服务注册中心** Spring Cloud要使用Eureka注册中心非常简单和方便, Sspring Cloud中的Eureka 服务注册中心实际上也是一个Spring Boot工程, 我们只需通过引入相关依赖和注解配置就能让Spring Boot构建的微服务应用 轻松地与Eureka进行整合。 1、创建一个SpringBoot项目,并且添加SpringBoot的相关依赖; 03-springcloud-eureka-server 2、添加Eureka的依赖 ` org.springframework.cloud spring-cloud-starter-netflix-eureka-server org.springframework.cloud spring-cloud-dependencies Hoxton.SR9 pom import spring-milestones Spring Milestones https://repo.spring.io/libs-milestone false ` 3、在SpringBoot的入口类上添加一个@EnableEurekaServer注解, 用于开启Eureka注册中心服务端 4、在application.properties文件中配置Eureka服务注册中心信息 `server.port=8081 #设置该服务注册中心的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/ ` **五、启动测试Eureka服务注册中心** 1、启动SpringBoot程序; 2、启动成功之后通过浏览器地址栏访问我们的注册中心; **六、向Eureka服务注册中心注册服务** 1、在该服务提供者添加eureka的依赖,因为服务提供者想注册中心注册服务需要 连接eureka,所以需要eureka客户端支持; ` org.springframework.cloud spring-cloud-starter-netflix-eureka-client org.springframework.cloud spring-cloud-dependencies Hoxton.SR9 pom import spring-milestones Spring Milestones https://repo.spring.io/libs-milestone false ` 2、激活Eureka 中的EnableEurekaClient功能: 在SpringBoot的入口函数,通过@EnableEurekaClient注解来表明自己是一个 eureka客户端,让我们的服务提供者可以连接到eureka注册中心; 3、配置服务名称和注册中心地址 `#配置服务的名称 spring.application.name=01-springcloud-service-provider #eureka连接地址 eureka.client.service-url.defaultZone=http://localhost8761/eureka` 4、启动服务提供者SpringBoot程序的main方法运行; 5、启动之后,通过在浏览器访问我们之前搭建 好的eureka注册中心,就可以看到 已经有一个服务注册成功了; **七、从Eureka服务中心发现与消费服务** 我们已经搭建一个服务注册中心,同时也向这个服务注册中心注册了服务,接下来 我们就可以发现和消费服务了,这其中服务的发现由eureka客户端实现,而服务 的消费由Ribbon实现,也就是说服务的调用需要eureka客户端和Ribbon两者组合使用 Eureka是一个Java客户端,用来连接Eureka服务端,与服务端进行交互、负载均衡 ,服务故障切换等; Ribbon是一个基于HTTP和TCP的客户端负载均衡器,当使用Ribbon对服务进行访问 的时候,它会扩展Eureka 客户端的服务发现功能,实现从Eureka注册中心中 获取服务端列表,并通过Eureka客户端来确定服务端是否己经启动。 Ribbon在 Eureka客户端服务发现的基础上,实现了对服务实例的选择策略, 从而实现对服务的负载均衡消费。 用服务消费者通过注册中心去调用服务提供者如下: 1、在该消费者项目中添加eureka的依赖,因为服务消费者从注册中心获取服务 需要连接eureka,所以需要eureka的支持; ` org.springframework.cloud spring-cloud-starter-netflix-eureka-client org.springframework.cloud spring-cloud-dependencies Hoxton.SR9 pom import spring-milestones Spring Milestones https://repo.spring.io/libs-milestone false ` 2、激活Eureka中的EnableEurekaClient功能: 在Spring Boot的入口函数处,通过添加@EnableEurekaClient注解 来表明自己是一个eureka客户端,让我的服务消费者可以使用eureka注册中心; 3、配置服务注册中心地址: `#配置服务的名称 spring.application.name=02-springcloud-service-consumer #eureka连接地址 eureka.client.service-url.defaultZone=http://localhost8761/eureka ` 4、需要在调用服务提供者时使用ribbon来调用: `@LoadBalanced @Bean public RestTemplate restTemplate(){ return new RestTemplate(); }` 在加入ribbon的支持,在调用时候,就可以改为服务名称来访问: `restTemplate.getForEntity("http://01-SPRINGCLOUD-SERVICE-PROVIDER/service/hello",String.class).getBody(); `5、启动消费者的SpringBoot方法,访问之前的地址看是否可以正常调用 远程服务者提供的服务 **八、服务注册中心Eureka** 1、Eureka注册中心高可用集群概述 在微服务架构的这种分布式系统中,我们要充分考虑各个微服务组件的 高可用性问题,不能有单点故障,由于注册中心eureka本身也是一个服务, 如果它只有一个节点,那么它有可能发生故障,这样我们就不能注册与查询服务了, 所以我们需要一个高可用的服务注册中心,这就需要通过注册中心集群来解决。 eureka服务注册中心它本身也是一个服务,它也可以看做是一个提供者, 又可以看做是一个消费者,我们之前通过配置:, eureka.client.register-with-eureka=false让注册中心不注册自己, 但是我们可以向其他注册中心注册自己; Eureka Server 的高可用实际上就是将自己作为服务向其他服务注册中心注册自己, 这样就会形成一组互相注册的服务注册中心,进而实现服务清单的互相同步, 往注册中心A上注册的服务,可以被复制同步到注册中心B上, 所以从任何一台注册中心上都能查询到已经注册的服务, 从而达到高可用的效果。 **九、Eureka注册中心高可用集群搭建** Eureka注册中心高可用集群就是各个注册中心相互注册,所以: 1、在8761的配置文件中,让它的service-url指向8762, 在8762的配置文件中让它的service-url指向8761. 2、由于8761和8762互相指向对方, 实际上我们构建了一个双节点的服务注册中心集群。 `eureka.client.service-url.defaultZone=http://eureka8762:8762/eureka/ eureka.client.service-ur1.defaultZone=http://eureka8761:8761/eureka/` 然后在本地hosts文件配置:C:\Windows\System32\driversletc\hosts 127.0.0.1 eureka8761 127.0.0.1 eureka8762 运行时,在运行配置项目Program Arguments中配置: (右键Application8761->Creat Application8761 在Program arguments中添加对应选择激活文件) --spring.profiles.active=eureka8761 --spring.profiles.active=eureka8762 **十、测试Eureka注册中心高可用集群** 在要进行测试的服务配置中 `eureka.client.service-url.defaultZone=http://eureka8761:8761/eureka,http://eureka8762:8762/eureka ` **十一、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节点在短时间内丢失过多客户端时(可能发生了网络分区故障), 那么就会把这个微服务节点进行保护。一旦进入自我保护模式, Eureka Server就会保护服务注册表中的信息, 不删除服务注册表中的数据(也就是不会注销任何微服务)。 当网络故障恢复后,该Eureka Server节点会再自动退出自我保护模式。 所以,自我保护模式是一种应对网络异常的安全保护措施, 它的架构哲学是宁可同时保留所有微服务 (健康的微服务和不健康的微服务都会保留),也不盲目注销任何健康的微服务, 使用自我保护模式,可以让Eureka集群更加的健壮、稳定。 当然也可以使用配置项禁用自我保护模式 `eureka.server.enable-self-preservation=false` Eureka Server自我保护模式也会给我们带来—些困扰, 如果在保护期内某个服务提供者刚好非正常下线了, 此时服务消费者就会拿到一个无效的服务实例,此时会调用失败, 对于这个问题需要服务消费者端具有一些容错机制,如重试,断路器等。 Eureka 的自我保护模式是有意义的,该模式被激活后, 它不会从注册列表中剔除因长时间没收到心跳导致注册过期的服务, 而是等待修复,直到心跳恢复正常之后,它自动退出自我保护模式。 这种模式旨在避免因网络分区故障导致服务不可用的问题。 例如,两个微服务客户端实例A和B之间有调用的关系,A是消费者,B是提供者, 但是由于网络故障,B未能及时向Eureka发送心跳续约, 这时候Eureka不能简单的将B从注册表中剔除,因为如果剔除了, A就无法从Eureka 服务器中获取B注册的服务,但是这时候B服务是可用的; 所以,Eureka的自我保护模式最好还是开启它。 关于自我保护常用几个配置如下: 服务器端配置: ` #测试时关闭自我保护机制,保证不可用服务及时踢出 eureka.server.enable-self-preservation=false ` 客户配置: ` #每间隔2s,向服务端发送一次心跳.证明自己依然"存活" eureka.instance.lease-renewal-interval-in-seconds=2 #告诉服务端,如果我10s之内没有给你发心跳,就代表我故障了,将我踢出掉 eureka.instance.lease-expiration-duration-in-seconds=10 ` **十二、客户端负载均衡Ribbon** SpringCloud中的Ribbon是什么: 通常说的负载均衡是指将一个请求均匀地分摊到不同的节点单元上执行, 负载均和分为硬件负载均衡和软件负载均衡; 硬件负载均衡:比如F5、深信服、Array等; 软件负载均衡:比如Nginx、LVS、HAProxy 等; 硬件负载均衡或是软件负载均衡,他们都会维护一个可用的服务端清单, 通过心跳检测来剔除故障的服务端节点以保证清单中都是可以正常访问的 服务端节点。当客户端发送请求到负载均衡设备的时候, 该设备按某种算法(比如轮询、权重、最小连接数等)从维护的可用 服务端清单中取出一台服务端的地址,然后进行转发。 Ribbon是 Netflix 发布的开源项目,主要功能是提供客户端的软件负载均衡算法, 是一个基于HTTP和TCP的客户端负载均衡工具。 Spring Cloud对Ribbon做了二次封装,可以让我们使用RestTemplate的服务请求, 自动转换成客户端负载均衡的服务调用。 Ribbon支持多种负载均衡算法,还支持自定义的负载均衡算法。 Ribbon只是一个工具类框架,比较小巧,Spring Cloud对它封装后使用也非常方便, 它不像服务注册中心、配置中心、API网关那样需要独立部署, Ribbon只需要在代码直接使用即可; Ribbon 与Nginx的区别: Ribbon是客户端的负载均衡工具,而客户端负载均衡和服务端负载均衡最大的 区别在于服务清单所存储的位置不同,在客户端负载均衡中,所有客户端节点下 的服务端清单,需要自己从服务注册中心上获取,比如Eureka服务注册中心。 同服务端负载均衡的架构类似,在客户端负载均衡中也需要心跳去维护服务端清 单的健康性,只是这个步骤需要与服务注册中心配合完成。在Spring Cloud 中, 由于Spring Cloud对Ribbon做了二次封装,所以默认会创建针对Ribbon的 自动化整合配置; 在Spring Cloud中,Ribbon主要与RestTemplate对象配合起来使用, Ribbon会自动化配置RestTemplate对象,通过@LoadBalanced 开启RestTemplate对象调用时的负载均衡。I **十三、Ribbon实现客户端负载均衡** 1、启动多个服务者实例并注册到服务中心或者服务注册中心集群。 2、服务消费者通过被@LoadBalanced注解修饰过的RestTemplate来调用服务提供者 **十四、Ribbon负载均衡策略** Ribbon的负载均衡策略是由IRule接口定义,该接口由如下实现: 1、RandomRule 随机 2、RoundRobinRule(默认策略) 轮询 3、AvailabilityFilteringRule 先过滤掉由于多次访问故障的服务,以及主发连接数超过阈值的服务, 然后对剩下的服务按照轮询策略进行访问; 4、WeightedResponseTimeRule 根据平均响应时间计算所有服务的权重,响应时间越快服务权重就越大被 选中的概率即越高,如果服务刚启动时统计信息不足, 则使用RoundRobinRule策略,待统计信息足够会切换到 该WeightedResponseTimeRule策略; 5、RetryRule 先按照RoundRobinRule策略分发,如果分发到的服务不能访问, 则在指定时间内进行重试,分发其他可用的服务; 6、BestAvailableRule 先过滤掉由于多次访问故障的服务,然后选择一个并发量最小的服务: 7、ZoneAvoidanceRule 综合判断服务节点所在区域的性能和服务节点的可用性,来决定选择哪个服务; **十五、Rest请求模板类解读** 在日常操作中(RestTemplate),基于Rest的方式通常是四种情况,它们分表是: 1、GET请求(查询数据) Get请求可以有2种方式: 第一种:getForEntity 该方法返回一个ResponseEntity对象,ResponseEntity 是Spring对HTTP请求响应的封装,包括了几个重要的元素, 比如响应码、IebntentType、contentLength、响应消息体等; `ResponseEntity responseEntity = restTemplate.getForEntity("http://01-SPRINGCLOUD-SERVICE-PROVIDER/service/hello",String.class); ` getForEntity方法第一个参数为要调用的服务的地址, 即服务提供者提供的http://01-SPRINGCLOUD-SERVICE-PROVIDER/service/hello接口地址, 注意这里是通过服务名调用而不是服务地址,如果改为服务地址就无法使用 Ribbon实现客户端负载均衡了。 getForEntity方法第二个参数String.class表示希望返回的body 类型是String-类型,如果希望返回—个对象,也是可以的,比如User对象; 另外2个重载方法 `public ResponseEntity getForEntity(String url, Class responseType,object...uriVariables) throws RestClientException ` `restTemplate.getForEntity( "http://01-SPRINGCLOUD-SERVICE-PROVIDER/service/hello?id={1}&name={2}", String.class, "1,"张三'}").getBody(); ` `public ResponseEntity getForEntity(String url,Class responseType,MapuriVariables) throws RestClientException ` 比如: Map paramMap = new ConcurrentHashMap<>(); paramMap.put("id",1); paramMap.put( "name","张三"); restTemplate.getForEntity("http://01-SPRINGCLOUD-SERVICE-PROVIDER/service/getUser?id={id}&name={name}", User.class, paramMap); 第二种getForObject() 与getForEntity使用类似,只不过getForobject是在 getForEntity基础上进行了再次封装,可以将http的响应体 body信息转化成指定的对象; T getForObject(URI url,Class responseType) throws RestClientException; T getForObject(String url,Class responseType,Object... uriVariables) throwsRestClientException; T getForObject(String url,Class responseType,Map uriVariables)throws RestClientException; 2、POST请求(添加数据) Post 与Get请求非常类似: restTemplate.postForObject() restTemplate.postForEntity() restTemplate.postForLocation() `//要传的表单信息,参数数据 MultiValueMap dataMap = new LinkedMultiValueMap<>(); dataMap.add("id",1); dataMap.add("name","张三"); dataMap.add("phone","1781738613"); dataMap.add("age","23"); ResponseEntity responseEntity = restTemplate.postForEntity("http://01-SPRINGCLOUD-SERVICE-PROVIDER/service/addUser",dataMap, User.class); User user = restTemplate.postForObject("http://01-SPRINGCLOUD-SERVICE-PROVIDER/service/addUser",dataMap, User.class); ` 3、PUT请求(修改数据) restTemplate.put(); 4、DELETE请求(删除数据) restTemplate.delete(); **十六、服务熔断Hystrix** 在微服务架构中,我们是将一个单体应用拆分成多个服务单元, 各个服务单元之间通过注册中心彼此发现和消费对方提供的服务, 每个服务单元都是单独部署,在各自的服务进程中运行, 服务之间通过远程调用实现信息交互,那么当某个服 务的响应太慢或者故障,又或者因为网络波动或故障, 则会造成调用者延迟或调用失败,当大量请求到达,则会造成请求的堆积, 导致调用者的线程挂起,从而引发调用者也无法响应,调用者也发生故障。 比如电商中的用户下订单,我们有两个服务,一个下订单服务, 一个减库存服务,当用户下订单时调用下订单服务, 然后下订单服务又调用减库存服务,如果减库存服务响应延迟或者没有响应, 则会造成下订单服务的线程挂起等待,如果大量的用户请求下订单, 或导致大量的请求堆积,引起下订单服务也不可用, 如果还有另外一个服务依赖于订单服务,比如用户服务,它需要查询用户订单, 那么用户服务查询订单也会引起大量的延迟和请求堆积,导致用户服务也不可用。 所以在微服务架构中,很容易造成服务故障的蔓延,引发整个微服务系统瘫痪不可用 为了解决此问题,微服务架构中引入了一种叫熔断器的服务保护机制。 熔断器也有叫断路器,他们表示同一个意思, 最早来源于微服务之父MartinFowler的论文CircuitBreaker—文。 “熔断器”本身是一种开关装置,用于在电路上保护线路过载, 当线路中有电器发生短路时,能够及时切断故障电路, 防止发生过载、发热甚至起火等严重后果。 微服务架构中的熔断器,就是当被调用方没有响应, 向调用方直接返回一个错误响应即可,而不是长时间的等待, 这样避免调用时因为等待而线程一直得不到释放,避免故障在分布式系统蔓延。 SpringSloud Hystrix实现了熔断器、线程隔离等—系列服务保护功能。 该功能也是基于Netflix的开源框架 Hystrix实现的, 该框架的目标在于通过控制那些访问远程系统、服务和第三方库的节点, 从而对延迟和故障提供更强大的容错能力。Hystrix具备服务降级、服务熔断、 线程和信号隔离、请求缓存、请求合并以及服务监控等强大功能。 1、添加依赖 ` org.springframework.cloud spring-cloud-starter-netflix-hystrix 2.1.5.RELEASE ` 2、在入口类中使用@EnableCircuitBreaker注解开启断路器功能: (也可以使用一个名为@SpringCloudApplication的注解代替 主类方法上的三个注解) 3、在方法上使用@HystrixCommand注解 **十七、服务消费者Hystrix测试** hystrix默认超时时间是1000毫秒,如果你后端的响应超过此时间,就会触发断路器; 修改hystrix的默认超时时间: `@HystrixCommand(fallbackMethod = "error" ,commandProperties = { @HystrixProperty(name= "execution.isolation.thread.timeoutInMilliseconds" , value = "1500") })//熔断器,调用不通,回调error()方法 public String webHello ()` Hystrix服务降级 有了服务的熔断,随之就会有服务的降级,所谓服务降级, 就是当某个服务熔断之后,服务端提供的服务将不再被调用, 此时由客户端自己准备一个本地的fallback回调, 返回一个默认值来代表服务端的返回; 这种做法,虽然不能得到正确的返回结果, 但至少保证了服务的可用,比直接抛出错误或服务不可用要好很多, 当然这需要根据具体的业务场景来选择; Hystrix的异常处理 我们在调用服务提供者时,我们自己也有可能会抛异常,I默认情况下方法抛了异 常会自动进行服务降级,交给服务降级中的方法去处理; 当我们自己发生异常后,只需要在服务降级方法中添加一个Throwable类型的 参数就能够获取到抛出的异常的类型,如下: `public String error( Throwable throwable) { system.out.println(throwable.getMessage()); return "error" ; }` 此时我们可以在控制台看到异常的类型; 如果远程服务有一个异常抛出后我们不希望进入到服务降级方法中去处理,而是 直接将异常抛给用户,那么我们可以在@HystrixCommand注解中添加忽略异 常,如下:(ignoreExceptions = Exception.class) @HystrixCommand(fallbackMethod="error", ignoreExceptions = Exception.class) 自定义Hystrix请求的服务异常熔断处理 我们也可以自定义类继承自HystrixCommand来实现自定义的Hystrix请求, 在getFallback方法中调用getExecutionException方法来获取服务抛出的异常; `com.netflix.hystrix.HystrixCommand.Setter.withGroupKey(HystrixCommandGroupKey .Factory .asKey("")) ` **Hystrix仪表盘监控** Hystrix仪表盘(Hystrix Dashboard),就像汽车的仪表盘实时显示汽车的 各项数据一样,Hystrix仪表盘主要用来监控Hystrix的实时运行状态,通过它我 们可以看到Hystrix的各项指标信息,从而快速发现系统中存在的问题进而解决它; 要使用Hystrix仪表盘功能,我们首先需要有一个Hystrix Dashboard,这个功 能我们可以在原来的消费者应用上添加,让原来的消费者应用具备 Hystrix仪表盘功能,但一般地,微服务架构思想是推崇服务的拆分, Hystrix Dashboard也是一个服务,所以通常会单独创建一个新的工程专门 用做 Hystrix Dashboard服务 **搭建一个Hystrix Dashboard服务步骤** 1、创建一个SpringBoot工程 比如创建一个名为springcloud-hystrix-dashboard 的 Spring Boot工程,建立好基本的结构和配置; 2、添加相关依赖 ` org.springframework.cloud spring-cloud-starter-netflix-hystrix-dashboard 2.1.5.RELEASE ` 3、入口类加上注解@EnableHystrixDashboard开启仪表盘 4、属性配置application.properties `server.port=3721` 访问输入ip+:端口+/hystrix Hystrix仪表盘工程已经创建好了,现在我们需要有一个服务, 让这个服务提供一个路径为/actuator/hystrix.stream接口, 然后就可以使用 Hystrix仪表盘来对该服务进行监控了; 我们改造消费者服务,让其能提供/actuator/hystrix.stream接口,步骤如下: 1、消费者项目需要有hystrix的依赖: ` org.springframework.cloud spring-cloud-starter-hystrix ` 2、需要一个SpringBoot的服务监控依赖 ` org.springframework.boot spring-boot-starter-actuator ` 3、配置文件中需要配置SpringBoot监控端点的访问权限: `management.endpoints.web.exposure.include=*` 这个是用来暴露endpoints的,由于endpoints中会包含很多敏感信息, 除了 health 和info 两个支持直接访问外,其他的默认不能直接访问, 所以我们让它都能访问,或者指定: `management.endpoints.web.exposure.inc1ude=hystrix.stream ` 4、访问入口[http://localhost:8080/actuator/hystrix.stream] 注意:这里有一个细节需要注意,要访问/hystrix.stream接口,首先得访问 consumer工程中的任意一个其他接口,否则直接访问/hystrix.stream接口时 会输出出一连串的ping: ping:….先访问consumer 中的任意一个熔断器接口, 然后再访问/hystrix.stream接口即可; **十七、声明式服务消费Feign** Feign是Netflix公司开发的一个声明式的REST调用客户端; Ribbon负载均衡、Hystrix服务熔断是我们Spring Cloud中进行微服务开发 非常基础的组件,在使用的过程中我们也发现它们一般都是同时出现的, 而且配置也都非常相似,每次开发都有很多相同的代码,因此 Spring Cloud 基于NetflixFeign整合了Ribbon和 Hystrix两个组件, 让我们的开发工作变得更加简单,就像Spring Boot是对Spring+SpringMVC的简化 一样,Spring Cloud Feign对Ribbon负载均衡、Hystrix服务熔断进行简化, 在其基础上进行了进一步的封装,不仅在配置上大大简化了开发工作, 同时还提供了一种声明式的 Web服务客户端定义方式. **使用Feign实现消费者** 1、创建SpringBoot工程(05-springcloud-service-feign) 2、添加依赖: 主要是:spring-cloud-starter-netflix-eureka-client和 spring-cloud-starter-feign 如下: 3、添加注解在项目入口类上添加@EnableFeignClients注解表示开启Spring Cloud Feign 的支持功能; 4、声明服务 定义一个HelloService接口,通过@FeignClient注解来指定服务名称,进而绑 定服务,然后再通过SpringMVC中提供的注解来绑定服务提供者提供的接口 `@FeignClient("01-springcloud-service-provider") public interface HelloService { /** * 生命一个方法,这个方法就是远程服务提供者提供的额那个方法 * @return */ @RequestMapping(value = "/service/hello") String hello(); }` 这相当于绑定了一个名叫e1-springcloud-service-provider(这里 e1-springcloud-service-provider大小写01-SPRINGCLOUD-SERVICE-PROVIDER都可以)的服务提供者提供的/service/hello接口E 我们服务提供者提供的接口如下: ` @RequestMapping(value = "/service/hello") public String hello(){ //进行业务处理 System.out.println("服务提供1"); return "hello spring cloud.Provider1"; }` 5、使用Controller来调用服务 `@RestController public class FeignController { @Autowired private HelloService helloService; @RequestMapping(value = "/web/hello") public String hello(){ //调用声明式的接口方法,实现对远程服务的调用 return helloService.hello(); } }` 6、属性配置 在application.properties 中指定服务注册中心、端口号等信息,如下: server.port=8081 #配置服务的名称 spring.application.name=05-springcloud-service-feign #配置eureka 注册中心地址 eureka.client.service-ur1.defaultZone=http://eureka8761:8761/eureka/, http://eureka8762:8762/eureka/ **使用Feign实现消费者的测试** 负载均衡: 我们知道,Spring Cloud提供了Ribbon来实现负载均衡, 使用Ribbo直接注入一个RestTemplate对象即可, RestTemplate已经做好了负载均衡的配置;在Spring Cloud 下, 使用Feign 也是直接可以实现负载均衡的, 定义一个注解有@FeignClient注解的接口, 然后使用@RequestMapping 注解到方法上映射远程的REST服务, 此方法也是做好负责均衡配置的。 服务熔断: 1、在application.properties文件开启hystrix功能 `feign.hystrix.enabled=true ` 2、指定熔断回调逻辑 需要在service加上这个类(fallback =MyFallback.class) `@FeignClient(name="01-springcloud-service-provider", fallback =MyFallback.class)` `@Component public class MyFallback implements HelloService { @Override public string hello() { return "发生异常了"; } }` 3、服务熔断获取异常信息 为@FeignClient修饰的接口加上fallback方法可以实现远程服务发生异常后 进行服务的熔断,但是不能获取到远程服务的异常信息, 如果要获取远程服务的异常信息,此时可以使用fallbackFactory: ` @Component public class MyFallbackFactory implements FallbackFactory { @Override public HelloService create(Throwable throwable) { return new HelloService() { @Override public String hello() { return throwable.getMessage(); } }; } }` **十八、API 网关Zuul** 1、创建一个普通的Spring Boot工程名为06-springcloud-api-gateway, 然后添加相关依赖,这里我们主要添加两个依赖zuul和eureka依赖 添加 spring cloud 的zuuL的起步依赖 ` org.springframework.cloud spring-cloud-starter-netflix-zuul ` 添加spring cLoud 的eureka的客户端依赖 ` org.springframework.cloud spring-cloud-starter-netflix-eureka-client ` 2、在入口类上添加@EnableZuulProxy注解,开启Zuul的API网关服务功能: 3、在application.properties 文件中配置路由规则: 配置服务内嵌的Tomcat端口 `server.port=8082` 配置服务的名称 `spring.application.name=06-springcloud-api-gateway ` 配置路由规则 `zuul.routes.api-wenbiao.path=/api-wenbiao/** zuul.routes.api-wenbiao.service-id=05-springcloud-service-feign` 配置AP工 网关到注册中心上,AP工 网关也将作为一个服务注册到eureka-server 上 `eureka.client.service-url.defaultZone=http://eureka8761:8761/eureka/,http://eureka8762:8762/eureka/ ` 以上配置,我们的路由规则就是匹配所有符合/api-wenbiao/**的请求, 只要路径中带有/api-wenbiao/都将被转发到 05-springcloud-service-feign服务上,至于05-springcloud-service-feign 服务的地址到底是什么则由eureka-server注册中心去分析, 我们只需要写上服务名即可。 以我们目前搭建的项目为例,请求http://localhost:8082/api-wenbiao/web/hello 接口则相当于请求http://localhost:8081/web/hello (05-springcloud-service-feign服务的地址为http://localhost:8081/web/hello), 路由规则中配置的api-wenbiao是路由的名字,可以任意定义, 但是一组path和servceld映射关系的路由名要相同。 如果以上测试成功,则表示们的API网关服务已经构建成功了,我们发送的符 合路由规则的请求将自动转发到相应的服务去处理。 **使用Zuul进行请求过滤** 1、我们定义一个过滤器并继承ZuulFilter,并将Filter作为一个Bean: ` @Component public class AuthFilter extends ZuulFilter { @Override public String filterType() { return "pre"; } @Override public int filterOrder() { return 0; } @Override public boolean shouldFilter() { return true; } @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; } }` ①filterType方法的返回值为挝滤器的类型,过滤器的类型决定了过滤器在 哪个生命周期执行,pre表示在路由之前执行过滤器, 其他值还有post、error、route和static,当然也可以自定义。 ② filterOrder方法表示过滤器的执行顺序,当过滤器很多时, 我们可以通过该方法的返回值来指定过滤器的执行顺序。 ③shouldFilter方法用来判断过滤器是否执行,true表示执行,false表示不执行。 ④run方法则表示过滤的具体逻辑,如果请求地址中携带了token参数的话, 则认为是合法请求,否则为非法请求,如果是非法请求的话,首先设置 ctx.setSendZuulResponse(false);表示不对该请求进行路由, 然后设置响应码和响应值。这个run方法的返回值目前暂时没有任何意义, 可以返回任意值。 2、[http://localhost:8082/api-wenbiao/web/hello]地址访问就会被过滤器过滤 **Zuul的路由规则** 1、 `zuul.routes.api-wenbiao.path=/api-wenbiao/** zuul.routes.api-wenbiao.service-id=05-springcloud-service-feign` 上面这2行等价于这一行配置 `zuul.routes.05-springcloud-service-feign=/api-wenbiao/** ` 2、如果映射规则我们什么都不写,系统也给我们提供了一套默认的 配置规则默认的配置规则如下: #默认的规则 zuul.routes.05-springcloud-service-feign.path=/05-springcloud-service-feign/** zuul.routes.05-springcloud-service-feign.service-id=05-springcloud-service-feign 3、默认情况下,Eureka上所有注册的服务都会被Zuul创建映射关系来进行路由。 05-springcloud-service-feign提供服务; 而 01-springcloud-service-provider作为服务提供者只对服务消费者提供服务, 不对外提供服务。如果使用默认的路由规则,则Zuul也会自动为 01-springcloud-service-provider创建映射规则, 这个时候我们可以采用如下方式来让Zuul跳过01-springcloud-service-provider 服务,不为其创建路由规则: `#忽略掉服务提供者的默认规则 zuul.ignored-services=01-springcloud-service-provider` 不给某个服务设置映射规则,这个配置我们可以进一步细化,比如说我不想给 /hello接口路由,那我们可以按如下方式配置: `#忽略掉某一些接口路径 zuul.ignored-patterns=/**/he1lo/**` 此外,我们也可以统一的为路由规则增加前缀,设置方式如下: `#配置网关路由的前缀 zuul.prefix=/myapi` 4、路由规则通配符含义: ? 含义:匹配任意单个字符 比如/05-springcloud-feign/? 匹配 /05-springcloud-feign/a,/05-springcloud-feign/b,/05-springcloud-feign/c等 * 含义:匹配任意数量的字符 比如:/05-springcloud-feign/* 匹配 /05-springcloud-feign/aaa,/05-springcloud-feign/bbb,/05-springcloud-feign/ccc 无法匹配 /05-springcloud-feign/a/b/c ** 含义:匹配任意数量的字符 比如:/05-springcloud-feign/** 匹配 /05-springcloud-feign/aaa,/05-springcloud-feign/bbb,/05-springcloud-feign/ccc 也可以匹配 /05-springcloud-feign/a/b/c 5、一般情况下API网关只是作为各个微服务的统一入口,但是有时候我们可能 也需要在API网关服务上做一些特殊的业务逻辑处理,那么我们可以让请求到 达API网关后,再转发给自己本身,由API网关自己来处理,那么我们可以进 行如下的操作: 在06-springcloud-api-gateway 项目中新建如下Controller: `@RestController public class GatewayController { @RequestMapping("/api/loca1") public String hello() { return "exec the api gateway. " ; } }` 然后在application.properties文件中配置: `zuul.routes.gateway.path=/gateway/** zuul.routes.gateway.url=forward:/api/1ocal` ` org.springframework.boot spring-boot-devtools true ` **Zuul的异常处理** 1、正常情况下所有的请求都是按照pre、route.post的顺序来执行, 然后由post返回response 2、在pre阶段,如果有自定义的过滤器则执行自定义的过滤器 3、pre、routinq、post的任意一个阶段如果抛异常了,则执行error过滤器 我们可以有两种方式统一处理异常: ①禁用zuul默认的异常处理SendErrorFilter过滤器,然后自定义我们自己的 Errorfilter过滤器 `zuul.SendErrorFilter.error.disable=true` `@Component public class ErroFilter extends ZuulFilter { private static final Logger logger= LoggerFactory.getLogger(ErroFilter.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=utf-8"); 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(value = "/error") public Object error(){ RequestContext ctx=RequestContext.getCurrentContext(); ZuulException exception= (ZuulException) ctx.getThrowable(); return exception.nStatusCode+"--"+exception.getMessage(); } }` **十九、SpringCloud Config** 在分布式系统中,尤其是当我们的分布式项目越来越多, 每个项目都有自己的配置文件,对配置文件的统一管理就成了一种需要, 而Spring Cloud Config就提供了对各个分布式项目配置文件的统一管理支持。 Spring Cloud Config 也叫分布式配置中心,市面上开源的分布式配置中心有很多, 比如国内的,360的QConf、淘宝的diamond、百度的 disconf都是解决分布式系统配置管理问题, 国外也有很多开源的配置中心 Apache的 Apache Commons Configuration、owner、cfg4j等等; Spring Cloud Config是一个解决分布式系统的配置管理方案,它包含Client 和Server两个部分,Server提供配置文件的存储、 以接口的形式将配置文件的内容提供出去,Client通过接口获取数据、 并依据此数据初始化自己的应用。Spring cloud使用git或svn存放配置文件, 默认情况下使用git。 *构建SpringCloud配置中心* 1、创建一个SpringBoot项目 2、在添加依赖 ` org.springframework.cloud spring-cloud-config-server ` 3、在入口类也就是main方法的类上添加注解@EnableconfigServer 4、在lpplication.properties 中配置一下git仓库信息, 我们可以使用GitHub(也可以使用码云 gitee), 首先创建一个名为spring-cloud-config的项目, 创建之后,再做如下配置: server.port=3722 spring.application.name=o7-springcloud-config-server spring.cloud.config.server.git.uri=https://gitee.com/wen_biao/springcloud-config.git spring.cloud.config.server.git.search-paths=config-center spring.cloud.config.server.git.username=xxxx@163.com spring.cloud.config.server.git.password=xxxx123456 ①、uri表示配置中心所在的仓库 ②、search-paths表示仓库下的子目录 ③、username 和password 表示git的用户名和密码 此时启动我们的配置中心,通过/{application}/{profile}/{label} 就能访问到我们的配置文件了; 其中: (application}表示配置文件的名字,对应的配置文件即application, {profile}表示环境,有dev、test、online及默认, {label}表示分支,默认我们放在master分支上 通过浏览器上访问http://localhost:3722/application/dev/master 返回的JSON格式的数据: name表示配置文件名application部分,profiles表示环境部分,label表示分支, version表示 gitee上提交时产生的版本号, 同时当我们访问成功后,在控制台会打印了相关的日志信息; 当访问成功后配置中心会通过git clone命令将远程配置文件在本地也保存一份, 以确保在git仓库故障时我们的应用还可以继续正常使用。 *构建springcloud config配置中心客户端* 1、创建一个SpringBoot工程 08-springcloud-config-client并添加如下依赖: ` org.springframework.cloud spring-cloud-starter-config ` 2、创建bootstrap.properties文件,用于获取配置信息,文件内容如下: ` server.port=3723 spring.application.name=application spring.cloud.config.profile=dev spring.cloud.config.label=master spring.cloud.config.uri=http://localhost:3722/ ` name对应配置文件中的application部分 profile对应了profile部分 label对应了label部分 uri表示配置中心的地址 3、创建一个Controller测试 `@RestController @RefreshScope public class ConfigController { @Value("${url}") private String url; @Autowired private Environment env; @RequestMapping("/cloud/url") public String url(){ return url; } @RequestMapping("/cloud/url2") public String url2 () { return env.getProperty("url"); } }` 我们可以直接使用@Value 注解注入配置的属性值, 也可以通过Environment对象来获取配置的属性值。 *SpringCloud config配置中心工作原理* 1、首先需要一个远程Git仓库I平时测试可以使用GitHub,在实际生产环境 中,需要自己搭建一个Git服务器,远程Git仓库的主要作用是用来保存我们的 配置文件; 2、除了远程Git仓库之外,我们还需要一个本地Git仓库, 每当Config Server访问远程Git仓库时,都会克隆一份到本地, 这样当远程仓库无法连接时,就直接使用本地存储的配置信息; 3、微服务A、微服务B则是我们的具体应用, 这些应用在启动的时会从 ConfigServer中获取相应的配置信息; 4.当微服务A、微服务B尝试从Config Server中加载配置信息的时候, ConfigServer 会先通过git clone命令克隆一份配置文件保存到本地; 5、由于配置文件是存储在Git仓库中,所以配置文件天然具有版本管理功能; **二十、SpringCloud的安全保护** 生产环境中我们的配置中心肯定是不能随随便便被人访问的, 我们可以加上适当的保护机制,由于微服务是构建在Spring Boot之上, 所以整合Spring SecurityI是最方便的方式。 1、在springcloud config server项目添加依赖 ` org.springframework.boot spring-boot-starter-security ` 2、在springcloud config server项目的application.properties 中配置用户名密码: ` spring.security.user.name=wenbiao spring.security.user.password=123456 ` 3、在springcloud config client上配置用户名和密码: ` spring.cloud.config.username=wenbiao spring.cloud.config.password=123456 `