学习参考视频:【狂神】SpringCloud
一、为什么要学习微服务?
此前学习的单体应用,这种简单的单元有很大的局限性。应用程序随着互联网日益壮大,业务需求不断增多,功能的追加扩展最终可能发展成庞然大物。最终导致代码耦合严重、复杂性增加、交付效率低、可靠性差(一个bug可能会导致整个服务器宕掉)伸缩性差问题等等…
那么如何解决单体的不足呢,通过迁移到微服务架构来解决,我们看一下什么是微服务。
微服务的定义
微服务架构:将单体应用拆分为多个高内聚低耦合的小型服务,每个小服务运行在独立进程,由不同的团队开发和维护,服务间采用轻量级通信机制,独立自动部署,可以采用不同的语言及存储。
微服务的优点
- 易于开发与维护
- 独立部署
- 伸缩性强
- 每个服务都可以在横向和纵向上扩展
- 每个服务都可按硬件资源的需求进行独立扩容
- 与组织结构相匹配
- 微服务架构可以更好将架构和组织相匹配
- 每个团队独立负责某些服务,获得更高的生产力
- 技术异构性
但是没有任何技术是完美的,微服务也是如此 ,都或多或少有一些缺点和问题。那么我们就必须针对这些问题一一解决. 我首先面对就是要将单体拆分成多个服务。
一个应用根据服务类别不同,从而拆分为多个服务,那么多个服务之间如何沟通;同一个服务又有可能有多个模块,该访问哪一个;可能有些服务会在某一时间访问量骤增,怎样开启降级或限流等等… 接下来就使用springCloud帮我们很好的解决。
二、SpringCloud概述
SpringCloud是什么?
Spring Cloud是一系列框架的有序集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。Spring Cloud并没有重复制造轮子,它只是将各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。
SpringCloud核心子项目
- Spring Cloud Netflix:核心组件,可以对多个Netflix OSS开源套件进行整合,包括以下几个组件:
- Eureka:服务治理组件,包含服务注册与发现
- Hystrix:容错管理组件,实现了熔断器
- Ribbon:客户端负载均衡的服务调用组件
- Feign:基于Ribbon和Hystrix的声明式服务调用组件
- Zuul:网关组件,提供智能路由、访问过滤等功能
- Archaius:外部化配置组件
- Spring Cloud Config:配置管理工具,实现应用配置的外部化存储,支持客户端配置信息刷新、加密/解密配置内容等。
- Spring Cloud Bus:事件、消息总线,用于传播集群中的状态变化或事件,以及触发后续的处理
- Spring Cloud Security:基于spring security的安全工具包,为我们的应用程序添加安全控制
- Spring Cloud Consul : 封装了Consul操作,Consul是一个服务发现与配置工具(与Eureka作用类似),与Docker容器可以无缝集成
- …
三、Eureka:服务发现与注册
1.什么是Eureka?
Eureka是Netflix的一个子模块,也是核心模块之一。Eureka是一个基于REST的服务,用于定位服务,以实现云端中间层服务发现和故障转移,服务注册与发现对于微服务来说是非常重要的,有了服务发现与注册,只需要使用服务的标识符,就可以访问到服务,而不需要修改服务调用的配置文件了,功能类似于Dubbo的注册中心,比如Zookeeper;
Netflix 在设计Eureka 时,遵循的就是AP原则 CAP原则又称CAP定理,指的是在一个分布式系统中
- 一致性(Consistency)
- 可用性(Availability)
- 分区容错性(Partition tolerance)
三个原则不可能同时实现
2.工作原理
Eureka 包含两个组件:Eureka Server 和 Eureka Client 。
Eureka Server 提供服务注册服务,各个节点启动后,会在EurekaServer中进行注册,这样EurekaServer中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观的看到。 Eureka Client是一个Java客户端,用于简化EurekaServer的交互,客户端同时也具备一个内置的,默认使用轮询负载算法的负载均衡器。在应用启动后,将会向EurekaServer发送心跳(默认周期为30秒)。如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,EurekaServer将会从服务注册表中把这个服务节点移除掉(默认周期为90秒)
3.如何构建
-
构建Eureka Server(注册中心)
-
导入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
<version>2.2.9.RELEASE</version>
</dependency>
-
配置 server:
port: 7001
eureka:
instance:
hostname: eureka7001.com
client:
register-with-eureka: false
fetch-registry: false
service-url:
defaultZone: http://localhost:7002/eureka/,http://localhost:7003/eureka/
-
启动类 @SpringBootApplication
@EnableEurekaServer
public class EurekaServer_7001 {
public static void main(String[] args) {
SpringApplication.run(EurekaServer_7001.class,args);
}
}
-
构建Eureka Client(服务提供者)
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!--eureka,注册到服务中心-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
四、Ribbon:负载均衡
1.Ribbon是什么?
Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡的工具。
简单的说,Ribbon是Netflix发布的开源项目,主要功能是提供客户端的软件负载均衡算法,将NetFlix的中间层服务连接在一起。Ribbon的客户端组件提供一系列完整的配置项如:连接超时、重试等等。简单的说,就是在配置文件中列出LoadBalancer(简称LB:负载均衡)后面所有的机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随机连接等等)去连接这些机器。我们也很容易使用Ribbon实现自定义的负载均衡算法!
2.Ribbon负载均衡的作用
当同一个服务部署在不同服务器上,为了使用户的请求平摊在各个服务器上,SpringCloud中均给我们提供了负载均衡,SpringCloud的负载均衡算法可以自定义,负载均衡简单分类:
- 集中式LB
- 即在服务的消费方和提供方之间使用独立的LB设施
- 如之前学习的Nginx,由该设施负责把访问请求通过某种策略转发至服务的提供方!
- 进程式LB
- 将LB逻辑集成到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选出一个合适的服务器。
- Ribbon就属于进程内LB,它只是一个类库,集成于消费方进程,消费方通过它来获取到服务提供方的地址!
3.工作原理
虚线代表服务提供者向Eureka中注册自己。
Ribbon分为两步:
- 第一步寻找注册中心,优先寻找同一个区域负载均衡较小的Server;
- 查询到该服务对应的地址列表,根据Ribbon策略选择进行访问。
Ribbon提供了多种策略,比如轮询(默认),随机和根据响应时间加权重,等等
4.负载均衡如何构建
-
导入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>2.2.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
<version>2.2.9.RELEASE</version>
</dependency>
-
配置类 @Configuration
public class ConfigBean {
@LoadBalanced
@Bean
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
-
根据服务实例名去注册中心查询列表,再由Ribbon策略选择
@Autowired
private RestTemplate restTemplate;
private static final String REST_URL_PREFIX="http://SPRINGCLOUD-PROVIDER-DEPT";
@GetMapping("/consumer/dept/get/{id}")
public Dept get(@PathVariable Long id){
return restTemplate.getForObject(REST_URL_PREFIX +"/dept/get/"+id, Dept.class);
}
五、Feign:负载均衡
1.什么是Feign?
feign和ribbon是Spring Cloud的Netflix中提供的两个实现软负载均衡的组件,Ribbon和Feign都是用于调用其他服务的,方式不同。Feign则是在Ribbon的基础上进行了一次改进,采用接口的方式。
也就是说假如我现在要查部门编号为1的/dept/get/1,
2.Feign的作用
- Feign旨在使编写Java Http客户端变得更容易
- 前面在使用Ribbon + RestTemplate时,利用RestTemplate对Http请求的封装处理,形成了一套模板化的调用方法。但是在实际开发中,由于对服务依赖的调用可能不止一处,往往一个接口会被多处调用,所以通常都会针对每个微服务自行封装一些客户端类来包装这些依赖服务的调用。所以,Feign在此基础上做了进一步封装,由他 来帮助我们定义和实现依赖服务接口的定义,在Feign的实现下,我们只需要创建一个接口并使用注解的方式来配置它(类似于以前Dao接口上标注Mapper注解,现在是一个微服务接口上面标注一个Feign注解即可。)即可完成对服务提供方的接口绑定,简化了使用Spring Cloud Ribbon时,自动封装服务调用客户端的开发量。
Feign集成了Ribbon
- 利用Ribbon维护了springcloud-Dept的服务列表信息,并且通过轮询实现了客户端的负载均衡,而与Ribbon不同的是,通过Feign只需要定义服务绑定接口且以声明式的方法,优雅而且简单的实现了服务调用。
Feign自带均衡配置项
Feign通过接口的方法调用Rest服务 ( 之前是Ribbon+RestTemplate )
该请求发送给Eureka服务器 (http://MICROSERVICECLOUD-PROVIDER-DEPT/dept/list)
通过Feign直接找到服务接口,由于在进行服务调用的时候融合了Ribbon技术,所以也支持负载均衡作用! feign其实不是做负载均衡的,负载均衡是ribbon的功能,feign只是集成了ribbon而已,但是负载均衡的功能还是feign内置的ribbon再做,而不是feign。 feign的作用的替代RestTemplate,性能比较低,但是可以使代码可读性很强。
3.Feign如何构建
-
导入依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
-
创建接口并添加注解@FeignClient @Component
@FeignClient(value = "SPRINGCLOUD-PROVIDER-DEPT")
public interface DeptClientService {
@GetMapping("/dept/add")
boolean addDept( Dept dept);
@GetMapping("/dept/get/{id}")
Dept queryById(@PathVariable("id") Long id);
@GetMapping("/dept/list")
List<Dept> queryAll();
}
-
调用者的启动类要加@EnableFeignClients @SpringBootApplication
@EnableEurekaClient
@EnableFeignClients(basePackages = {"com.chao.springcloud"})
public class FeignConsumer_80 {
public static void main(String[] args) {
SpringApplication.run(FeignConsumer_80.class,args);
}
}
六、Hystrix:服务熔断、降级、限流
1.什么是Hystrix?
Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时,异常等,Hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性。
“断路器” 本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个服务预期的,可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方法无法处理的异常,这样就可以保证了服务调用方的线程不会被长时间,不必要的占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩.
2.Hystrix的作用
3.服务熔断
熔断机制是对应雪崩效应的一种微服务保护机制
当某个微服务处理某个请求时,由于微服务不可用或者请求时间过长,使该进程一直在等待,为避免因为这种情况导致后面许许多多请求访问服务器,服务器处理不过来导致雪崩。
为解决这种问题,使用熔断机制,假如微服务不可用或者请求时间过长,应该快速返回 错误的响应信息,避免资源浪费影响后面的请求处理。
如何构建
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
public Dept hystrixGet(@PathVariable("id") Long id){
System.out.println("进入熔断方法");
return new Dept().setDeptno(id)
.setDname("id =>" + id +"没有对应的的信息,null-@Hystrix")
.setDb_source("not this database");
}
@HystrixCommand(fallbackMethod = "hystrixGet")
@GetMapping("/dept/get/{id}")
public Dept get(@PathVariable("id") Long id){
Dept dept= deptService.queryById(id);
return dept;
}
- 启动类添加@EnableCircuitBreaker
@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
@EnableCircuitBreaker
public class DeptProviderHystrix_8001 {
public static void main(String[] args) {
SpringApplication.run(DeptProviderHystrix_8001.class,args);
}
}
4.服务降级
4.1、什么是服务降级?
什么是服务降级?当服务器压力剧增的情况下,根据实际业务情况及流量,对一些服务和页面有策略的不处理或换种简单的方式处理,从而释放服务器资源以保证核心交易正常运作或高效运作。
4.2、使用场景
当整个微服务架构整体的负载超出了预设的上限阈值或即将到来的流量预计将会超过预设的阈值时,为了保证重要或基本的服务能正常运行,我们可以将一些 不重要 或 不紧急 的服务或任务进行服务的 延迟使用 或 暂停使用。
服务降级处理是在客户端实现完成的,与服务端没有关系
4.3、如何构建
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
feign:
hystrix:
enabled: true
@Component
public class DeptClientServiceFallbackFactory implements FallbackFactory {
@Override
public DeptClientService create(Throwable throwable) {
return new DeptClientService() {
@Override
public boolean addDept(Dept dept) {
return false;
}
@Override
public Dept queryById(Long id) {
return new Dept().setDeptno(id)
.setDname("id => "+ id + "没有对应的信息,该服务已经被关闭")
.setDb_source("null");
}
@Override
public List<Dept> queryAll() {
return null;
}
};
}
}
@Component
@FeignClient(value = "SPRINGCLOUD-PROVIDER-DEPT",
fallbackFactory = DeptClientServiceFallbackFactory.class)
public interface DeptClientService {
@GetMapping("/dept/add")
boolean addDept( Dept dept);
@GetMapping("/dept/get/{id}")
Dept queryById(@PathVariable("id") Long id);
@GetMapping("/dept/list")
List<Dept> queryAll();
}
- 熔断:依赖的下游服务器故障触发熔断,避免引发本地系统崩溃,系统自动执行和恢复
- 降级:服务分优先级,牺牲非核心业务,保证核心服务稳定,从整体符合考虑
- 限流:限制并发的请求访问量,超过阈值则拒绝
服务监控dashbord
除了隔离依赖服务的调用以外,Hystrix还提供了准实时的调用监控(Hystrix Dashboard),Hystrix会持续地记录所有通过Hystrix发起的请求的执行信息,并以统计报表和图形的形式展示给用户,包括每秒执行多少请求,多少成功,多少失败等等。
Netflix通过hystrix-metrics-event-stream项目实现了对以上指标的监控SpringCloud也提供了HystrixDashboard的整合,对监控内容转化成可视化界面!
如何构建
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>2.2.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
<version>2.2.9.RELEASE</version>
</dependency>
<dependency>
<groupId>com.chao</groupId>
<artifactId>springcloud-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.4.5</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<version>2.4.5</version>
</dependency>
@SpringBootApplication
@EnableHystrixDashboard
public class DeptConsumerDashboard_9001 {
public static void main(String[] args) {
SpringApplication.run(DeptConsumerDashboard_9001.class,args);
}
}
- 在服务端编写一个servlet,监听每一个进来的请求,并向监控模块发送消息
@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
@EnableCircuitBreaker
public class DeptProviderHystrix_8001 {
public static void main(String[] args) {
SpringApplication.run(DeptProviderHystrix_8001.class,args);
}
@Bean
public ServletRegistrationBean bean(){
ServletRegistrationBean registrationBean = new ServletRegistrationBean(new HystrixMetricsStreamServlet());
registrationBean .addUrlMappings("/actuator/hystrix.stream");
return registrationBean ;
}
}
七、Zull:网关
官方文档:https://github.com/Netflix/zuul/
1.什么是Zull
Zuul包含了对请求的路由和过滤两个最主要的功能:
其中路由功能负责将外部请求转发到具体的微服务实例上,是实现外部访问统一入口的基础,而过滤器功能则负责对请求的处理过程进行干预,是实现请求校验,服务聚合等功能的基础。Zuul和Eureka进行整合,将Zuul自身注册为Eureka服务治理下的应用,同时从Eureka中获得其他微服务的消息,也即以后的访问微服务都是通过Zuul跳转后获得。
提供:代理 + 路由 + 过滤 三大功能!
2.如何构建
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
<version>2.2.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>2.2.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
<version>2.2.9.RELEASE</version>
</dependency>
<dependency>
<groupId>com.chao</groupId>
<artifactId>springcloud-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.4.5</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<version>2.4.5</version>
</dependency>
</dependencies>
server:
port: 9527
spring:
application:
name: springcloud-zuul-gateway
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka/,http://localhost:7002/eureka/,http://localhost:7003/eureka/
instance:
instance-id: zuul9527.com
prefer-ip-address: true
info:
app.name: chao-springcloud
company.name: chao
zuul:
routes:
mydept.serviceId: springcloud-provider-dept
mydept.path: /mydept/**
ignored-services: springcloud-provider-dept
配置routes是为了使用一个路径代替服务实例名(这样也隐藏了实例名,也更容易让用户访问)
@SpringBootApplication
@EnableZuulProxy
public class ZuulApplication_9527 {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication_9527.class,args);
}
}
即使再长的路,一步步也能走完~~
|