万网建网站,小程序定制深圳,网站开发课程建议,dw网页制作作业文章目录 Spring Cloud#xff0c;注册中心#xff0c;配置中心#xff0c;原理详解谈谈我个人对 spring Cloud 的理解 注册中心Eureka#xff1a;服务搭建小结 Ribbo - 负载均衡1. 负载均衡流程2. 负载均衡策略 nacos注册中心1. 配置集群1. 创建 namespace2. 配置命名空间… 文章目录 Spring Cloud注册中心配置中心原理详解谈谈我个人对 spring Cloud 的理解 注册中心Eureka服务搭建小结 Ribbo - 负载均衡1. 负载均衡流程2. 负载均衡策略 nacos注册中心1. 配置集群1. 创建 namespace2. 配置命名空间1. nacos添加配置文件2. 从nacos拉取配置 配置中心Nacos1.搭建服务端2.搭建客户端3.动态刷新4.nacos config高可用5.nacos config高级配置 ConfigApollo 一、业务场景介绍二、Spring Cloud核心组件Eureka三、Spring Cloud核心组件Feign四、Spring Cloud核心组件Ribbon五、Spring Cloud核心组件Hystrix六、Spring Cloud核心组件Zuul七、总结使用及配置EurekaFeignRibbonHystrix 今天通过这篇文章想和大家来聊聊springCloud相关的知识点由于现在的系统不断的扩大业务不断的复杂所以出现了Spring Cloud. Spring Cloud注册中心配置中心原理详解
谈谈我个人对 spring Cloud 的理解
spring Cloud 是一套分布式微服务的技术解决方案它提供了快速构建分布式系统常用的一些组件比如说配置管理、服务的注册与发现、服务调用的负载均衡、资源隔离、熔断降级等等。
不过 Spring Cloud 只是 Spring 官方提供的一套微服务标准定义
而真正的实现目前有两套体系用的比较多一个是 Spring Cloud Netflix 另一个是 Spring Cloud Alibaba
Spring Cloud Netflix 是基于 Netflix 这个公司的开源组件集成的一套微服务解决方案其中的组件有:
**1. Eureka——服务注册与发现 ** 2. Feign——服务调用
**3. Ribbon——负载均衡 **
4. Hystrix——服务熔断
5. Zuul——网关;
Spring Cloud Alibaba 是基于阿里巴巴开源组件集成的一套微服务解决方案其中包括:
Dubbo——消息通讯Nacos——服务注册与发现Seata——事务隔离Sentinel——熔断降级
有了 Spring Cloud 这样的技术生态使得我们在落地微服务架构时不用去考虑第三方技术集成带来额外成本只要通过配置组件来完成架构下的技术问题即可从而可以让我们更加侧重性能方面 以上这些就是我个人对 Spring Cloud 的理解
注册中心
需求当一个服务提供者 Service 部署了多个实例交给 User 远程调用时 服务消费者 User 应该调用哪个实例如何获取其对应地址和端口User 如何获知实例是否健康
注册中心作用
帮助管理服务并帮助服务调用者选择并调用服务实时监测服务实例是否健康
Eureka
构成 eureka-server服务端注册中心 记录服务信息心跳监控
eureka-client客户端 服务提供者注册到服务端定期向服务端发送心跳、服务消费者从服务端拉取服务列表基于负载均衡选择服务 作用 服务注册 Service实例启动后会将自己的信息注册到 eureka服务端 服务拉取 User 根据 实例名 获取 Service 地址列表
服务搭建
配置文件:
我们需要将 eureka 注册到spring容器中所以需要在配置文件中做相关配置。
server:port: 8099
spring:application:name: eureka_server
eureka:client:# 配置eureka服务地址service-url:defaultZone: http://127.0.0.1:8099/eureka
项目启动
为了让项目能启动 eureka需要在启动类上加一个注解EnableEurekaServer
SpringBootApplication
EnableEurekaServer
public class ServerApplication {public static void main(String[] args) {SpringApplication.run(ServerApplication.class, args);}
}启动项目访问地址http://127.0.0.1:8099 注册的服务名称就是配置文件中的名称的大写。
服务注册
在上一步已经将 eureka-server eureka服务中心搭建完毕现在就开始注册服务实例了。
注意
无论是 服务提供者 还是 服务消费者他们的身份都是 eureka-client
记得添加 spring-boot-starter-web 的依赖不然会报错Field optionalArgs in org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration$RefreshableEurekaClientConfiguration required a bean of type ‘com.netflix.discovery.AbstractDiscoveryClientOptionalArgs’ that could not be found.
服务发现、服务注册统一都封装在eureka-client依赖。
需要在配置文件中配置 eureka-server 的地址。
spring:application:name: service_provider
eureka:client:service-url:defaultZone: http://127.0.0.1:8099/eureka
启动多个实例
为了让项目能启动 eureka需要在启动类上加一个注解EnableEurekaClient。
SpringBootApplication
EnableEurekaClient
public class ProviderApplication {public static void main(String[] args) {SpringApplication.run(ProviderApplication.class, args);}
}我们可以通过 IDEA 自带功能模仿启动多个服务实例。
打开 Service 面板
****
复制原来的 provider 启动配置 查看 eureka注册中心 服务发现
依赖导入
dependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-netflix-eureka-client/artifactId
/dependency配置文件
spring:application:name: service_user
server:port: 8084eureka:client:service-url:defaultZone: http://127.0.0.1:8099/eureka服务拉取和负载均衡
服务拉取 修改 controller 代码将 url 路径的 ip、端口 修改为 服务名 RestController
RequestMapping(user)
public class UserController {Autowiredprivate RestTemplate restTemplate;GetMapping(/{id})public Book getBookById(PathVariable(id) Integer id) {// String url http://127.0.0.1:8081/provider id;String url http://service_provider/provider;if (id ! null) {url url id;}Book book restTemplate.getForObject(url, Book.class);return book;}
}注册 RestTemplate 的时候加上注解 LoadBalanced LoadBalancedBeanpublic RestTemplate restTemplate() {return new RestTemplate();}接口调用
RestController
RequestMapping(user)
public class UserController {Autowiredprivate RestTemplate restTemplate;GetMapping(/{id})public Book getBookById(PathVariable(id) Integer id) {// String url http://127.0.0.1:8081/provider id;String url http://PROVIDER/pro/;if (id ! null) {url url id;}Book book restTemplate.getForObject(url, Book.class);return book;}
}访问接口 访问报错 解决方案 多测试几下接口可以发现user一会儿调用的是 provider:8081 一会儿调用的是 provider:8082。这就是负载均衡算法选择的。
小结
eureka-server 搭建
引入依赖
dependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-netflix-eureka-server/artifactId
/dependency
启动类添加 EnableEurekaServer配置文件配置 eureka 地址
server:port: 8099
spring:application:name: server
eureka:client:# 配置eureka服务地址service-url:defaultZone: http://127.0.0.1:8099/eureka服务注册
引入依赖
dependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-netflix-eureka-client/artifactId
/dependency启动类添加 EnableEurekaClient配置文件配置 eureka 地址
server:port: 8081
spring:application:name: provider
eureka:client:# 配置eureka服务地址service-url:defaultZone: http://127.0.0.1:8099/eureka服务发现
引入依赖
dependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-netflix-eureka-client/artifactId
/dependency启动类添加 EnableEurekaClient配置文件配置 eureka 地址
server:port: 8089
spring:application:name: user
eureka:client:# 配置eureka服务地址service-url:defaultZone: http://127.0.0.1:8099/eurekaRibbo - 负载均衡
1. 负载均衡流程
Ribbon负载均衡流程图 http://PROVIDER/pro/4 并非真实的地址这个需要Ribbon负载均衡去拦截然后选择具体的服务地址。而Ribbon就是通过 LoadBalancerInterceptor 的 intercept 方法来实现拦截请求并解析选择地址。 public ClientHttpResponse intercept(final HttpRequest request, final byte[] body, final ClientHttpRequestExecution execution) throws IOException {URI originalUri request.getURI();String serviceName originalUri.getHost();Assert.state(serviceName ! null, Request URI does not contain a valid hostname: originalUri);return (ClientHttpResponse)this.loadBalancer.execute(serviceName, this.requestFactory.createRequest(request, body, execution));}负载均衡流程 2. 负载均衡策略
Ribbon的负载均衡策略是由 IRule 接口来定义的。 自定义负载均衡策略
方式一 在user中Bean 注入自定义 IRule
Bean
public IRule randomRule(){return new RandomRule();
}方式二 在user配置文件修改 IRule
provider: # 给某个微服务配置负载均衡规则这里是userservice服务ribbon:NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 负载均衡规则注意
方式一的作用范围是在访问任何微服务的时候都是使用 RandomRule 负载均衡策略
方式二的作用范围是在访问 provider 微服务的时候才是采用 RandomRule 策略其他的还是使用默认策略。
加载策略
Ribbon默认采用懒加载即会在第一次访问时才会去创建 LoadBalanceClient 所以会在第一次请求的时候花上较长的等待时间。
可以通过配置文件更改加载策略为饿加载策略即初始化时就创建 LoadBalanceClient 降低第一次访问的耗时。
ribbon:eager-load:enabled: true # 开启饥饿加载clients: provider # 指定饥饿加载的服务名称nacos
概念 服务注册中心 作用 心跳检查 不同于 eureka 只能 Service实例 主动发起心跳nacos 对于非临时实例可以主动发起心跳检查 临时心跳检查异常的会被剔除出 服务地址列表
非临时心跳检查异常的不会被剔除定时推送变更 nacos 支持服务列表变更的消息推送模式服务列表更新更及时
注册中心
引入依赖、修改配置
父工程添加 SpringCloudAlibaba 的管理依赖。
!--spring-cloud-alibaba--
dependencygroupIdcom.alibaba.cloud/groupIdartifactIdspring-cloud-alibaba-dependencies/artifactIdversion2.2.6.RELEASE/versiontypepom/typescopeimport/scope
/dependency子工程中注释掉 eureka 依赖引入 nacos 依赖。
dependencygroupIdcom.alibaba.cloud/groupIdartifactIdspring-cloud-starter-alibaba-nacos-discovery/artifactId
/dependency配置文件配置nacos地址
spring:cloud:nacos:server-addr: localhost:8848 # nacos服务地址启动 user 和 provider 注意 eureka 注册名会变成大写nacos 不会所以改成 nacos 之后需要把访问地址改成小写。
请求测试 服务分级存储模型 一个服务可以拥有多个实例如provider 的 8081 和 8082如果这些实例分布于全国不同的机房如provider:8081 在成都机房、provider:8082 在重庆机房Nacos就将同一机房内的实例 划分为一个集群。
即一个服务可以拥有多个集群一个集群中可以拥有多个实例。 微服务相互之间访问时访问本地的速度更快所以应该尽可能访问相同集群的实例只有当本集群内存不够时才去访问其他集群。
1. 配置集群
配置服务集群
spring:cloud:nacos:server-addr: localhost:8848 # nacos服务地址discovery:cluster-name: CDVM Options
-Dserver.port8082 -Dspring.cloud.nacos.discovery.cluster-nameCQ2. 同集群优先的负载均衡
默认的 ZoneAvoidanceRule 负载均衡策略并不能实现据同集群优先来实现负载均衡因此Nacos中提供了一个NacosRule的实现可以优先从同集群中挑选实例。
修改负载均衡策略
方式一
Bean
public IRule nacosRule(){return new NacosRule();
}方式二
provider: # 访问的服务名ribbon:NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule # 负载均衡规则3. 权重配置
服务器的性能之间存在差异为了让性能能好的服务器承担更多的用户请求Nacos提供了权重配置来控制访问率权重越大、访问率越高。 注意 权重为0的服务永远不会被访问。
4. 环境隔离
Nacos提供了namespace来实现环境隔离功能。
nacos中可以有多个namespacenamespace下可以有group、service等不同namespace之间相互隔离不同namespace的服务互相不可见 1. 创建 namespace
Nacos默认的namespace是public
命名空间 Nacos创建命名空间流程 2. 配置命名空间
spring:cloud:nacos:discovery:namespace: e11eb3bc-8eed-4cdd-93f0-7a6c01b85eb4 # 命名空间填IDspring:cloud:nacos:discovery:ephemeral: false # 设置为永久实例3. 配置管理
Nacos不仅可以担任微服务的注册中心还可以担任配置管理。
统一配置管理
可以使用统一配置管理来处理因为部署的微服务数量过多配置繁杂等问题。 Nacos一方面可以将配置集中管理另一方可以在配置变更时及时通知微服务实现配置的热更新。
注意 只把那些需要热更新的配置文件交给Nacos
1. nacos添加配置文件 Data ID 配置文件id服务名称-profile.后缀名 。
2. 从nacos拉取配置
spring引入了一种新的配置文件bootstrap.yaml文件会在application.yml之前被读取流程如下 引入nacos-config依赖
!--nacos配置管理依赖--
dependencygroupIdcom.alibaba.cloud/groupIdartifactIdspring-cloud-starter-alibaba-nacos-config/artifactId
/dependency添加bootstrap.yml
spring:application:name: user # 服务名称profiles:active: dev #开发环境这里是devcloud:nacos:server-addr: localhost:8848 # Nacos地址config:file-extension: yaml # 文件后缀名这里会根据spring.cloud.nacos.server-addr获取nacos地址再根据 s p r i n g . a p p l i c a t i o n . n a m e − {spring.application.name}- spring.application.name−{spring.profiles.active}.${spring.cloud.nacos.config.file-extension}作为文件id来读取配置。 读取nacos配置 Value(${pattern.dateformat})private String dateformat;GetMapping(now)public String now() {return LocalDateTime.now().format(DateTimeFormatter.ofPattern(dateformat));}启动报错 org.springframework.beans.factory.BeanCreationException: Error creating bean with name xxx
解决方案
检查 namespace配置文件与服务要在同一个 namespace 中 名称是否拼错 升级 nacos 版本 Value 切换为 NacosValue
配置热更新 配置热更新 修改nacos中的配置后微服务中无需重启即可让配置生效。
实现方式
方式一 在Value注入的变量所在类上添加注解 RefreshScope 方式二 使用 ConfigurationProperties 注解代替Value注解 在 user 服务中添加一个类读取patterrn.dateformat属性
Component
Data
ConfigurationProperties(prefix pattern)
public class PatternProperties {private String dateformat;
}在UserController中使用这个类代替Value:
Autowired
private PatternProperties patternProperties;GetMapping(now)
public String now() {return LocalDateTime.now().format(DateTimeFormatter.ofPattern(patternProperties.getDateformat));
}配置共享
其实微服务启动时会去nacos读取多个配置文件因为nacos管理的配置文件不包含环境信息可以被多个环境共享。
只需要取名的时候不加上 profile 就能被共享如下 配置共享时的优先级 配置中心
Nacos
微服务-配置中心
微服务架构中存在很多服务每个服务都有自己的配置文件这些配置文件如果集成在服务中也是难以维护的于是便可以使用配置中心来管理这些配置文件。
nacos既可以作为 注册中心 也可以作为 配置中心 使用nacos配置中心你可以将服务里的配置文件写在nacos上通过nacos面板来修改管理服务的配置而无需停止服务。如果使用nacos作为注册中心而不作为配置中心添加配置spring.cloud.nacos.config.enabled false 即可。可以以请求方式发布、获取配置发布配置posthttp://127.0.0.1:8848/nacos/v1/cs/configs?dataIdnacos.cfg.dataIdgrouptestcontentHelloWorld
获取配置gethttp://127.0.0.1:8848/nacos/v1/cs/configs?dataIdnacos.cfg.dataIdgrouptest
1.搭建服务端
nacos服务端搭建是十分容易的只需下载配置启动就行参考注册中心这篇点击前往
建议使用mysql作为持久化。
2.搭建客户端
客户端即每个配置要被管理的服务服务的搭建还如往常一样搭建只不过配置文件发生一些变化以下就认为你已经创建了一个服务模块/项目并以此起步。
1.客户端引入nacos config依赖
dependencygroupIdcom.alibaba.cloud/groupIdartifactIdspring-cloud-starter-alibaba-nacos-config/artifactId
/dependency2.创建一个bootstrap.yml(或properties)配置nacos config的配置 :
spring:cloud:nacos:config: #nacos的信息添加后可以省略注册中心的nacos此类信息server-addr: 127.0.0.1:8848username: nacos password: nacoscontextPath: /nacos#--------------------------file-extension: yaml #显示的声明dataId文件扩展名3.application.yml(或properties)除特殊配置外只留下下面2个配置
server:port: 41150
spring:application:name: server-producer到这里你会发现你有2个配置文件 bootstrap.yml、application.yml 。这时你可能就有疑问了不是说好的将配置交给配置中心管理了吗怎么服务里还变多了呢。
那么我来给你解释一下吧
bootsteap.yml这里只配置配置中心的信息服务通过它去获取服务所需配置而且这里的内容基本不变。 application.yml这里只配置端口服务名因为首次启动服务时需要通过它注册达到nacos然后才能获取配置。 可以看出上面的配置基本上就是固定的而且基本上不会被修改实际项目中配置文件远远不止这些其他配置都将被放在配置中心统一管理。
4.在nacos配置中心添加配置文件 点击添加后页面如下配置名要和服务名一致,后缀带不带都行。 第一次发布配置后重启项目即可。之后修改配置都不需要启动服务。
服务与配置文件的匹配
文件名服务名建议显示指定配置文件后缀。 多同名配置会互相覆盖比如server、server.yml在指定yml格式后两个文件会覆盖。 更多特性自己摸索使用服务名.yml 并 在bootstrap.yml指定格式为yaml 一定能用。
测试一下效果按照阿里的示例进行举例。
在客户端创建一个测试类并在nacos上配置中文件添加属性然后注入到属性中启动后控制台就会打印。
Configuration
public class TestConfig {Value(${user.names})public String test1;Value(${user.pwds})public String test2;PostConstructpublic void getUser(){System.out.println(name test1 |pwd test2);}
}启动后会发现打印的数据就是配置中心上的数据了。
3.动态刷新 此节代码来自于阿里云-知行动手实验室 配置发生变化后服务中绑定的属性值将刷新会刷新的情况有3种
类上加注解RefreshScope、类种非静态属性加Value注解
RestController
RefreshScope
public class NacosConfigDemo {Value(${user.name})private String userName;Value(${user.age})private int userAge;PostConstructpublic void init() {System.out.printf([init] user name : %s , age : %d%n, userName, userAge);}RequestMapping(/user)public String user() {return String.format([HTTP] user name : %s , age : %d, userName, userAge);}
}类上加注解RefreshScope、ConfigurationProperties
RefreshScope
ConfigurationProperties(prefix user)
public class User {private String name;private int age;public String getName() {return name;}public void setName(String name) {this.name name;}public int getAge() {return age;}public void setAge(int age) {this.age age;}Overridepublic String toString() {return User{ name name \ , age age };}
}使用Nacos Config监听来对Bean属性实施监听。
RestController
RefreshScope
EnableConfigurationProperties(User.class)
public class NacosConfigDemo {Value(${user.name})private String userName;Value(${user.age})private int userAge;PostConstructpublic void init() {System.out.printf([init] user name : %s , age : %d%n, userName, userAge);}PreDestroypublic void destroy() {System.out.printf([destroy] user name : %s , age : %d%n, userName, userAge);}RequestMapping(/user)public String user() {return String.format([HTTP] user name : %s , age : %d, userName, userAge);}
}4.nacos config高可用
nacos是配置中心也是注册中心实现nacos的高可用和 注册中心 高可用一样。
5.nacos config高级配置
namespace命名空间 nacos可以实现配置文件之间隔离不同的命名空间下可以存在相同的 Group 或 Data ID 的配置可以实现不同开发环境开发、测试、生产等配置文件的隔离。
创建命名空间也很容易 切换命名空间 创建后在客户端bootstrap.yml添加如下配置进行绑定
spring:cloud:nacos:config:namespace: c70a9f91-a687-427f-97bb-5a6e54289a6eGroup分组
在创建配置时可以指定分组自定义分组在bootstrap.yml添加如下
spring:cloud:nacos:config:group: DEVELOP_GROUP自定义配置名
三种情况完整配置案例
spring:application:name: myservercloud:nacos:config:server-addr: 127.0.0.1:8848extension-configs[0]: # 1.在默认组,不支持动态刷新data-id: myconfig-1.yamlextension-configs[1]: # 2.不在默认组不支持动态刷新data-id: myconfig-2.yamlgroup: MY_GROUPextension-configs[2]: # 3.不在默认的组支持动态刷新data-id: myconfig-3.yamlgroup: MY_GROUPrefresh: true总结默认配置名默认组时才会自动刷新否则需指定refresh: true才自动刷新。 注意
多个相同配置文件存在优先级extension-configs[a]里a越大优先级越高。data-id结尾必须有properties/yaml/yml等扩展名。
Config
Apollo
Spring Cloud是目前微服务架构领域的翘楚无数的书籍博客都在讲解这个技术。不过大多数讲解还停留在对Spring Cloud功能使用的层面其底层的很多原理很多人可能并不知晓。因此本文将通过大量的手绘图给大家谈谈Spring Cloud微服务架构的底层原理。
实际上Spring Cloud 是一个全家桶式的技术栈包含了很多组件。本文先从其最核心的几个组件入手来剖析一下其底层的工作原理。也就是 Eureka、Feign、Ribbon、Hystrix、Zuul 这几个组件。 EFRHZ
一、业务场景介绍
先来给大家说一个业务场景假设咱们现在开发一个电商网站要实现支付订单的功能流程如下
创建一个订单后如果用户立刻支付了这个订单我们需要将订单状态更新为“已支付”扣减相应的商品库存通知仓储中心进行发货给用户的这次购物增加相应的积分
针对上述流程我们需要有: 订单服务、库存服务、仓储服务、积分服务。整个流程的大体思路如下
用户针对一个订单完成支付之后就会去找订单服务更新订单状态订单服务调用库存服务完成相应功能订单服务调用仓储服务完成相应功能订单服务调用积分服务完成相应功能
至此整个支付订单的业务流程结束
下图这张图清晰表明了各服务间的调用过程 好有了业务场景之后咱们就一起来看看Spring Cloud微服务架构中这几个组件如何相互协作各自发挥的作用以及其背后的原理。
二、Spring Cloud核心组件Eureka
咱们来考虑第一个问题订单服务想要调用库存服务、仓储服务或者积分服务怎么调用
订单服务压根儿就不知道人家库存服务在哪台机器上啊他就算想要发起一个请求都不知道发送给谁有心无力这时候就轮到Spring Cloud Eureka出场了。Eureka是微服务架构中的注册中心专门负责服务的注册与发现。
咱们来看看下面的这张图结合图来仔细剖析一下整个流程 如上图所示库存服务、仓储服务、积分服务中都有一个Eureka Client组件这个组件专门负责将这个服务的信息注册到Eureka Server中。说白了就是告诉Eureka Server自己在哪台机器上监听着哪个端口。而Eureka Server是一个注册中心里面有一个注册表保存了各服务所在的机器和端口号
订单服务里也有一个Eureka Client组件这个Eureka Client组件会找Eureka Server问一下库存服务在哪台机器啊监听着哪个端口啊仓储服务呢积分服务呢然后就可以把这些相关信息从Eureka Server的注册表中拉取到自己本地缓存起来。
这时如果订单服务想要调用库存服务不就可以找自己本地的Eureka Client问一下库存服务在哪台机器监听哪个端口吗收到响应后紧接着就可以发送一个请求过去调用库存服务扣减库存的那个接口同理如果订单服务要调用仓储服务、积分服务也是如法炮制。
总结一下
Eureka Client负责将这个服务的信息注册到Eureka Server中Eureka Server注册中心里面有一个注册表保存了各个服务所在的机器和端口号
三、Spring Cloud核心组件Feign
现在订单服务确实知道库存服务、积分服务、仓库服务在哪里了同时也监听着哪些端口号了。但是新问题又来了难道订单服务要自己写一大堆代码跟其他服务建立网络连接然后构造一个复杂的请求接着发送请求过去最后对返回的响应结果再写一大堆代码来处理吗
这是上述流程翻译的代码片段咱们一起来看看体会一下这种绝望而无助的感受
友情提示前方高能 看完上面那一大段代码有没有感到后背发凉、一身冷汗实际上你进行服务间调用时如果每次都手写代码代码量比上面那段要多至少几倍所以这个事压根儿就不是地球人能干的。
既然如此那怎么办呢别急Feign早已为我们提供好了优雅的解决方案。来看看如果用Feign的话你的订单服务调用库存服务的代码会变成啥样 看完上面的代码什么感觉是不是感觉整个世界都干净了又找到了活下去的勇气没有底层的建立连接、构造请求、解析响应的代码直接就是用注解定义一个 FeignClient 接口然后调用那个接口就可以了。人家Feign Client会在底层根据你的注解跟你指定的服务建立连接、构造请求、发起靕求、获取响应、解析响应等等。这一系列脏活累活人家Feign全给你干了。
那么问题来了Feign是如何做到这么神奇的呢很简单Feign的一个关键机制就是使用了动态代理。咱们一起来看看下面的图结合图来分析
首先如果你对某个接口定义了**FeignClient**注解Feign就会针对这个接口创建一个动态代理接着你要是调用那个接口本质就是会调用 Feign创建的动态代理这是核心中的核心Feign的动态代理会根据你在接口上的**RequestMapping**等注解来动态构造出你要请求的服务的地址最后针对这个地址发起请求、解析响应 四、Spring Cloud核心组件Ribbon
说完了Feign还没完。现在新的问题又来了如果人家库存服务部署在了5台机器上如下所示
192.168.169:9000192.168.170:9000192.168.171:9000192.168.172:9000192.168.173:9000
这下麻烦了人家Feign怎么知道该请求哪台机器呢
这时Spring Cloud Ribbon就派上用场了。Ribbon就是专门解决这个问题的。它的作用是负载均衡会帮你在每次请求时选择一台机器均匀的把请求分发到各个机器上Ribbon 的负载均衡默认使用的最经典的Round Robin轮询算法。这是啥简单来说就是如果订单服务对库存服务发起10次请求那就先让你请求第1台机器、然后是第2台机器、第3台机器、第4台机器、第5台机器接着再来—个循环第1台机器、第2台机器。。。以此类推。
此外Ribbon是和Feign以及Eureka紧密协作完成工作的具体如下
首先Ribbon会从 Eureka Client里获取到对应的服务注册表也就知道了所有的服务都部署在了哪些机器上在监听哪些端口号。然后Ribbon就可以使用默认的Round Robin算法从中选择一台机器Feign就会针对这台机器构造并发起请求。
对上述整个过程再来一张图帮助大家更深刻的理解 五、Spring Cloud核心组件Hystrix
在微服务架构里一个系统会有很多的服务。以本文的业务场景为例订单服务在一个业务流程里需要调用三个服务。现在假设订单服务自己最多只有100个线程可以处理请求然后呢积分服务不幸的挂了每次订单服务调用积分服务的时候都会卡住几秒钟然后抛出—个超时异常。
咱们一起来分析一下这样会导致什么问题
如果系统处于高并发的场景下大量请求涌过来的时候订单服务的100个线程都会卡在请求积分服务这块。导致订单服务没有一个线程可以处理请求然后就会导致别人请求订单服务的时候发现订单服务也挂了不响应任何请求了
上面这个就是微服务架构中恐怖的服务雪崩问题如下图所示 如上图这么多服务互相调用要是不做任何保护的话某一个服务挂了就会引起连锁反应导致别的服务也挂。比如积分服务挂了会导致订单服务的线程全部卡在请求积分服务这里没有一个线程可以工作瞬间导致订单服务也挂了别人请求订单服务全部会卡住无法响应。
但是我们思考一下就算积分服务挂了订单服务也可以不用挂啊为什么
我们结合业务来看支付订单的时候只要把库存扣减了然后通知仓库发货就OK了如果积分服务挂了大不了等他恢复之后慢慢人肉手工恢复数据为啥一定要因为一个积分服务挂了就直接导致订单服务也挂了呢不可以接受
现在问题分析完了如何解决
这时就轮到Hystrix闪亮登场了。Hystrix是隔离、熔断以及降级的一个框架。啥意思呢说白了Hystrix会搞很多个小小的线程池比如订单服务请求库存服务是一个线程池请求仓储服务是一个线程池请求积分服务是一个线程池。每个线程池里的线程就仅仅用于请求那个服务。
打个比方现在很不幸积分服务挂了会咋样
当然会导致订单服务里那个用来调用积分服务的线程都卡死不能工作了啊但由于订单服务调用库存服务、仓储服务的这两个线程池都是正常工作的所以这两个服务不会受到任何影响。
这个时候如果别人请求订单服务订单服务还是可以正常调用库存服务扣减库存调用仓储服务通知发货。只不过调用积分服务的时候每次都会报错。**但是如果积分服务都挂了每次调用都要去卡住几秒钟干啥呢有意义吗当然没有**所以我们直接对积分服务熔断不就得了比如在5分钟内请求积分服务直接就返回了不要去走网络请求卡住几秒钟这个过程就是所谓的熔断
**那人家又说兄弟积分服务挂了你就熔断好歹你干点儿什么啊别啥都不干就直接返回啊**没问题咱们就来个降级每次调用积分服务你就在数据库里记录一条消息说给某某用户增加了多少积分因为积分服务挂了导致没增加成功这样等积分服务恢复了你可以根据这些记录手工加一下积分。这个过程就是所谓的降级。
为帮助大家更直观的理解接下来用一张图梳理一下Hystrix隔离、熔断和降级的全流程 六、Spring Cloud核心组件Zuul
说完了Hystrix接着给大家说说最后一个组件Zuul也就是微服务网关。**这个组件是负责网络路由的。**不懂网络路由行那我给你说说如果没有Zuul的日常工作会怎样
假设你后台部署了几百个服务现在有个前端兄弟人家请求是直接从浏览器那儿发过来的。打个比方人家要请求一下库存服务你难道还让人家记着这服务的名字叫做inventory-service部署在5台机器上就算人家肯记住这一个你后台可有几百个服务的名称和地址呢难不成人家请求一个就得记住一个你要这样玩儿那真是友谊的小船说翻就翻
上面这种情况压根儿是不现实的。所以一般微服务架构中都必然会设计一个网关在里面像android、ios、pc前端、微信小程序、H5等等不用去关心后端有几百个服务就知道有一个网关所有请求都往网关走网关会根据请求中的一些特征将请求转发给后端的各个服务。
而且有一个网关之后还有很多好处比如可以做统一的降级、限流、认证授权、安全等等。
七、总结使用及配置
最后再来总结一下上述几个Spring Cloud核心组件在微服务架构中分别扮演的角色 Eureka各个服务启动时Eureka Client 都会将服务注册到 Eureka Server并且 Eureka Client 还可以反过来从 Eureka Server 拉取注册表从而知道其他服务在哪里 Feign基于Feign的动态代理机制根据注解和选择的机器拼接请求URL地址发起请求 Ribbon服务间发起请求的时候基于Ribbon做负载均衡从一个服务的多台机器中选择一台 Hystrix发起请求是通过 Hystrix 的线程池来走的不同的服务走不同的线程池实现了不同服务调用的隔离避免了服务雪崩的问题 Zuul如果前端、移动端要调用后端系统统一从 Zuul 网关进入由Zuul网关转发请求给对应的服务 以上就是我们通过一个电商业务场景阐述了 Spring Cloud 微服务架构几个核心组件的底层原理。 文字总结还不够直观没问题我们将Spring Cloud的5个核心组件通过一张图串联起来再来直观的感受一下其底层的架构原理
Eureka
一、erueka 客户端配置 1、Eureka 启禁用
eureka.client.enabledtrue
2、Eureka 连接超时时间
eureka.client.eureka-server-connect-timeout-seconds5
eureka.client.eureka-connection-idle-timeout-seconds30
3、Eureka Server等待超时时间
eureka.client.eureka-server-read-timeout-seconds8
4、拉取Eureka注册信息
eureka.client.fetch-registrytrue
5、注册自身到Eureka
eureka.client.register-with-eurekatrue
6、过滤状态存活的实例
eureka.client.filter-only-up-instancestrue
7、注册实例的名称
eureka.instance.appnameunknown
8、健康检查相对路径
eureka.instance.health-check-url-path/health
9、HOME页面相对路径
eureka.instance.home-page-url-path/
10、服务续约到期时间
eureka.instance.lease-expiration-duration-in-seconds90
11、服务续约间隔时间
eureka.instance.lease-renewal-interval-in-seconds30
12、注册元数据
eureka.instance.metadata-map
13、通过配置文件找到namespace忽略springcloud的配置
eureka.instance.namespace
14、注册是否显示IP地址第一个非回环地址
eureka.instance.prefer-ip-addressfalse
15、注册时使用的IP地址
eureka.instance.ip-address
16、压缩Eureka Server的数据
eureka.client.g-zip-contenttrue
17、心跳执行器的线程池初始值
eureka.client.heartbeat-executor-thread-pool-size2
18、最初复制实例信息到Eureka服务器所需的时间
eureka.client.initial-instance-info-replication-interval-seconds40
19、同步实例变更信息到Eureka服务到周期
eureka.client.instance-info-replication-interval-seconds30
20、从Eureka服务器拉取服务信息周期
eureka.client.registry-fetch-interval-seconds30
21、Eureka Server地址
eureka.client.serviceUrl.defaultZonexxx,xxx,xxx
二、eureka 服务端配置 1、注册实例
eureka.instance.registry.default-open-for-traffic-count1
eureka.instance.registry.expected-number-of-renews-per-min1
2、服务面板
eureka.dashborad.enabledtrue
eureka.dashboard.path/
3、自我保护
eureka.server.enable-self-preservation: true eureka.server.eviction-interval-timer-in-ms: 60000
eureka.server.renewal-percent-threshold: 0.85
4、限流
eureka.server.rate-limiter-enabled: false eureka.server.rate-limiter-throttle-standard-clients: false eureka.server.rate-limiter-burst-size: 10 eureka.server.rate-limiter-full-fetch-average-rate: 100
eureka.server.rate-limiter-registry-fetch-average-rate: 500
5、相应缓存
eureka.server.response-cache-auto-expiration-in-seconds: 180 eureka.server.response-cache-update-interval-ms: 30000
eureka.server.use-read-only-response-cache: true
6、安全校验
#启用安全校验
security.basic.enabledtrue #授权用户名 security.user.nameroot #授权密码
security.user.password123456
三、高可用配置 1、节点数据同步
eureka.server.batch-replication: false
eureka.enable-replicated-request-compression: false
2、同步线程池
eureka.server.min-threads-for-status-replication: 1 eureka.server.max-threads-for-status-replication: 1 eureka.server.max-idle-thread-in-minutes-age-for-status-replication: 10 eureka.server.min-threads-for-peer-replication: 5 eureka.server.max-threads-for-peer-replication: 20 eureka.server.max-idle-thread-age-in-minutes-for-peer-replication: 15
eureka.server.number-of-replication-retries: 5
3、定时任务
eureka.server.peer-eureka-status-refresh-time-interval-ms: 3000 eureka.server.peer-eureka-nodes-update-interval-ms: 600000
eureka.server.min-available-instances-for-peer-replication: -1
4、连接
eureka.server.peer-node-total-connections-per-host: 500 eureka.server.peer-node-total-connections: 1000 eureka.server.peer-node-read-timeout-ms: 200 eureka.server.peer-node-connect-timeout-ms: 200
eureka.server.peer-node-connection-idle-timeout-seconds: 30
5、重置注册
eureka.server.registry-sync-retries: 0 eureka.server.registry-sync-retry-wait-ms: 30000
Feign
使用及配置url: SpringCloudFeign的使用及配置_feignclient配置_Poetry-Distance的博客-CSDN博客 Ribbon
Hystrix RestController
RequestMapping(/circuitBreaker)
public class CircuitBreakConfigController {GetMappingHystrixCommand(fallbackMethod fallback,commandProperties {HystrixProperty(name execution.isolation.thread.timeoutInMilliseconds,value 1000),HystrixProperty(name circuitBreaker.requestVolumeThreshold,value 4),HystrixProperty(name circuitBreaker.errorThresholdPercentage,value 50),HystrixProperty(name metrics.rollingStats.timeInMilliseconds,value 60000),HystrixProperty(name circuitBreaker.sleepWindowInMilliseconds,value 60000)})public ResponseEntityString demo() {try {// 模拟接口请求TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}return new ResponseEntity(Hello!, HttpStatus.OK);}private ResponseEntityString fallback() {return new ResponseEntity(Timeout!, HttpStatus.OK);}
}