In short, the microservice architectural style is an approach to developing a single application as a suite of small services,
each running in its own process and communicating with lightweight mechanisms, often an HTTP resource API.
These services are built around business capabilities and independently deployable by fully automated deployment machinery.
There is a bare minimum of centralized management of these services, which may be written in different programming languages and use different data storage technologies. ——James Lewis and Martin Fowler (2014)
大意:简而言之,微服务体系结构风格是一种将单个应用程序开发为一套小型服务的方法,每个服务运行在自己的进程中,并与轻量级机制(通常是HTTP资源API)通信。
这些服务是围绕业务功能构建的,可以通过全自动部署机制进行独立部署。对这些服务的集中管理是最低限度的,这些服务可能用不同的编程语言编写,并使用不同的数据存储技术。
微服务是一种架构风格,将服务围绕业务功能拆分,一个应用拆分为一组小型服务,每个服务运行在自己的进程内,也就是可独立部署和升级,服务之间使用轻量级HTTP交互,可以由全自动部署机制独立部署,去中心化,服务自治。服务可以使用不同的语言、不同的存储技术。
说的微服务就不得不说微服务之前的单体应用,有对比才能看的出微服务对比之前的单体应用到底强在哪?为什么我们要抛弃单体应用?
微服务虽然好处多多,但是缺点就是服务众多,治理成本高,不利于维护系统,项目成本会升高,所以项目架构设计时应综合考量。
建议设计的原则是业务驱动、设计保障、演进式迭代、保守治疗的方式。搞不清楚,有争议的地方先尽量不要拆,如果确实要拆,要经过业务分析后慎重设计,把真正相对独立的部分拆分出来,可以借鉴 DDD 的方式。拆了以后要观察微服务的接口是否稳定,针对业务需求的变更微服务的模块是否可以保持相对稳定,是否可以独立演进。
DDD: Domain-driven design的简称,译为领域驱动设计,是一种通过将实现连接到持续进化的模型来满足复杂需求的软件开发方法。
核心思想:DDD其实是面向对象方法论的一个升华。无外乎是通过划分领域(聚合根、实体、值对象)、领域行为封装到领域对象(充血模式)、内外交互封装到防腐层、职责封装到对应的模块和分层,从而实现了高内聚低耦合。
链接:https://blog.csdn.net/qq_31960623/article/details/119840131
随着微服务模式的使用,服务之间的调用带来的问题有很多,例如:数据一致性、网络波动、缓存、事务等问题,针对这一系列的问题就要有对应的框架来解决,目前主流一站式微服务解决方案有Spring Cloud
、Spring Cloud Alibaba
。
Spring Cloud
它并不是一个框架,而是很多个框架。它是分布式微服务架构的一站式解决方案,是多种微服务架构落地技术的集合体,俗称微服务全家桶。
在服务注册与发现中,有一个注册中心。当服务器启动的时候,会把当前自己服务器的信息比如服务地址通讯地址等以别名方式注册到注册中心上。另一方消费者服务提供者,以该别名的方式去注册中心上获取到实际的服务通讯地址,然后再实现本地RPC调用。RPC远程调用框架核心设计思想:在于注册中心,因为使用注册中心管理每个服务与服务之间的一个依赖关系。在任何RPC远程框架中,都会有一个注册中心存放服务地址相关信息。
参考文章:
Eureka is a REST (Representational State Transfer) based service that is primarily used in the AWS cloud for locating services for the purpose of load balancing and failover of middle-tier servers. We call this service, the Eureka Server. Eureka also comes with a Java-based client component,the Eureka Client, which makes interactions with the service much easier. The client also has a built-in load balancer that does basic round-robin load balancing. —https://github.com/Netflix/eureka
大意:Eureka是一个REST (Representational State Transfer)服务,它主要用于AWS云,用于定位服务,以实现中间层服务器的负载平衡和故障转移,我们称此服务为Eureka服务器。Eureka也有一个基于java的客户端组件,Eureka客户端,这使得与服务的交互更加容易,同时客户端也有一个内置的负载平衡器,它执行基本的循环负载均衡。
Eureka采用了CS的设计架构,Eureka Sever作为服务注册功能的服务器,它是服务注册中心。而系统中的其他微服务,使用Eureka的客户端连接到 Eureka Server并维持心跳连接。这样系统的维护人员就可以通过Eureka Server来监控系统中各个微服务是否正常运行。
Eureka的自我保护机制:
默认情况下,如果Eureka Server在一定时间内(默认90秒)没有接收到某个微服务实例的心跳,Eureka Server将会移除该实例。但是当网络分区故障发生时,微服务与Eureka Server之间无法正常通信,而微服务本身是正常运行的,此时不应该移除这个微服务,所以引入了自我保护机制。
自我保护模式正是一种针对网络异常波动的安全保护措施,使用自我保护模式能使Eureka集群更加的健壮、稳定的运行。
如果在15分钟内超过85%的客户端节点都没有正常的心跳,那么Eureka就认为客户端与注册中心出现了网络故障,Eureka Server自动进入自我保护机制:
简而言之,某时刻某一个微服务不可用了,Eureka不会立刻清理,依旧会对该微服务的信息进行保存。所以Eureka属于AP。
由于分布式架构中应用程序依赖关系可能非常多,每个依赖关系在某些时候将不可避免地失败,针对服务调用失败,为了缩小调用失败的影响,引入了服务熔断降级的思想。
多个微服务之间调用的时候,假设微服务A调用微服务B和微服务C,微服务B和微服务C又调用其它的微服务,这就是所谓的“扇出”。如果扇出的链路上某个微服务的调用响应时间过长或者不可用,对微服务A的调用就会占用越来越多的系统资源,进而引起系统崩溃,所谓的“雪崩效应”.
服务熔断是应对系统服务雪崩的一种保险措施,给出的一种特殊降级措施。而服务降级则是更加宽泛的概念,主要是对系统整体资源的合理分配以应对压力。服务熔断可看作特殊降级。
Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时、异常等,Hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性。
"断路器”本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个符合预期的、可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方无法处理的异常,这样就保证了服务调用方的线程不会被长时间、不必要地占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。
"断路器"思想:https://martinfowler.com/bliki/CircuitBreaker.html
Hystrix工作流程:
HystrixCommand
或HystrixObserableCommand
对象。HystrixCommand
实现了下面前两种执行方式:
execute
:同步执行,从依赖的服务返回一个单一的结果对象或是在发生错误的时候抛出异常。queue
:异步执行,直接返回一个Future对象,其中包含了服务执行结束时要返回的单一结果对象。HystrixObservableCommand
实现了后两种执行方式:
obseve()
:返回Observable对象,它代表了操作的多个结果,它是一个Hot Observable,不论“事件源”是否有“订阅者”,都会在创建后对事件进行发布,所以对于Hot Observable的每一个“订阅者”都有可能是从“事件源”的中途开始的,并可能只是看到了整个操作的局部过程。toObservable()
:同样会返回Observable对象,也代表了操作的多个结果,但它返回的是一个Cold Observable,没有“订间者”的时候并不会发布事件,而是进行等待,直到有“订阅者"之后才发布事件,所以对于Cold Observable 的订阅者,它可以保证从一开始看到整个操作的全部过程。HystrixCommand.run()
:返回一个单一的结果,或者抛出异常。HystrixObservableCommand.construct()
:返回一个Observable对象来发射多个结果,或通过onError发送错误通知。HystrixObsevableCommand.construct()
或HytrixCommand.run()
抛出异常的时候。参考文章:
网关的角色是作为一个 API 架构,用来保护、增强和控制对于 API 服务的访问。API 网关是一个处于应用程序或服务(提供 REST API 接口服务)之前的系统,用来管理授权、访问控制和流量限制等,这样 REST API 接口服务就被 API 网关保护起来,对所有的调用者透明。因此,隐藏在 API 网关后面的业务系统就可以专注于创建和管理服务,而不用去处理这些策略性的基础设施。
网关作用:
Spring Cloud Gateway是Spring官方基于Spring 5.0,Spring Boot 2.0和Project Reactor等技术开发的网关,Spring Cloud Gateway旨在为微服务架构提供一种简单而有效的统一的API路由管理方式。Spring Cloud Gateway作为Spring Cloud生态系中的网关,目标是替代ZUUL,其不仅提供统一的路由方式,并且基于Filter链的方式提供了网关基本的功能,例如:安全,监控/埋点,和限流等。
核心组件:
java.util.function.Predicate
,开发人员可以匹配HTTP请求中的所有内容,例如请求头或请求参数,如果请求与断言相匹配则进行路由;客户端向Spring Cloud Gateway发出请求。然后在Gateway Handler Mapping 中找到与请求相匹配的路由,将其发送到GatewayWeb Handler,Handler再通过指定的过滤器链来将请求发送到我们实际的服务执行业务逻辑,然后返回。
过滤器分为前置过滤器和后置过滤器,可以在发送代理请求之前或之后执行业务逻辑。
参考文章:
服务调用可以说是微服务中最关键的,如果没有服务调用不会出现熔断降级、也不会出现服务负载、服务注册,分布式的服务治理可以说是围绕服务调用展开的。
常见的服务之间的调用方式有两种:
现在热门的Rest风格,就可以通过HTTP协议来实现,如果公司全部采用Java技术栈,那么使用Dubbo作为微服务框架是一个不错的选择;如果公司的技术栈多样化,而且你更青睐Spring家族,那么SpringCloud搭建微服务是不二之选。
Feign is a declarative web service client. It makes writing web service clients easier. To use Feign create an interface and annotate it. It has pluggable annotation support including Feign annotations and JAX-RS annotations. Feign also supports pluggable encoders and decoders. Spring Cloud adds support for Spring MVC annotations and for using the same HttpMessageConverters used by default in Spring Web. Spring Cloud integrates Ribbon and Eureka, as well as Spring Cloud LoadBalancer to provide a load-balanced http client when using Feign.
大意:Feign是一个声明式WebService客户端。使用Feign能让编写Web Service客户端更加简单。它的使用方法是定义一个服务接口然后在上面添加注解。Feign也支持可拔插式的编码器和解码器。Spring Cloud对Feign进行了封装,使其支持了Spring MVC标准注解和HttpMessageConverters。Feign可以与Eureka和Ribbon组合使用以支持负载均衡。
Feign和OpenFeign:
通过使用OpenFeign
可以使代码变得更加简洁,减轻程序员负担:
// 伪代码
@Component
// 括号中,为在注册中心注册的服务名称
@FeignClient("CLOUD-PAYMENT-SERVICE")
public interface PaymentFeignService {
@GetMapping("/payment/get/{id}")
CommonResult getPayment(@PathVariable("id") Long id);
@PostMapping("/payment/timeout")
String feignTimeoutTest();
}
openFeign工作原理:
@FeignClient
注册FactoryBean
到IOC容器, 最终产生了一个虚假的实现类代理;负载均衡,英文名称为Load Balance,其含义就是指将工作任务进行平衡、分摊到多个操作单元上进行运行,例如FTP服务器、Web服务器、企业核心应用服务器和其它主要任务服务器等,从而协同完成工作任务。
当前服务与服务之间进行相互调用时,在分布式架构下应用都是集群部署,所以这个时候就需要进行服务负载,即将收到的请求分摊到对应的服务器上,从而达到系统的高可用。
常见负载均衡算法:
Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡的工具,是一个内置软件负载平衡器的进程间通信(远程过程调用)库。主要功能是提供客户端的软件负载均衡算法和服务调用。Ribbon客户端组件提供一系列完善的配置项,如连接超时、重试等。
本地负载与服务端负载:
Ribbon其实是一个软负载均衡的客户端组件,它可以和其他所需请求的客户端结合使用,例如与Eureka结合:
消费方和服务方在注册中心注册服务,当消费方发起请求时,Ribbon会去注册中心寻找请求的服务名,即服务方集群,Ribbon默认负载算法会根据接口第几次请求 % 服务器集群总数量
算出实际消费方服务器的位置,每次服务重启动后rest接口计数从1开始。
模拟Ribbon默认负载均衡算法:
public interface ILoadBalance {
ServiceInstance instance(List instances);
}
@Component
public class MyLoadBalance implements ILoadBalance{
/**
* 轮询索引
*/
private final AtomicInteger index = new AtomicInteger(0);
/**
* 负载均衡算法:rest接口第几次请求数 % 服务器集群总数量 = 实际调用服务器位置下标,每次服务重启动后rest接口计数从1开始。
* @param instances 服务器集群数量
* @return 实际服务器的下标
*/
@Override
public ServiceInstance instance(List instances) {
return instances.get(incrementAndGet() % instances.size());
}
public final int incrementAndGet() {
int current = 0;
int next = 0;
do {
current = index.get();
// 当最大数量超过 Integer.MAX_VALUE 归0
next = current >= 2147483647 ? 0 : current + 1;
}while (!index.compareAndSet(current,next));
return next;
}
}
@Resource
private RestTemplate restTemplate;
@Resource
private DiscoveryClient discoveryClient;
@Resource
private ILoadBalance iLoadBalance;
@GetMapping("/myLoadBalance")
public String myLoadBalanceTest() {
List instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
ServiceInstance instance = iLoadBalance.instance(instances);
URI uri = instance.getUri();
String finalUri = String.format("%s/%s", uri, PaymentConstant.PAYMENT_GETPORT_API);
log.info("自定义负载均衡,请求地址:{}", finalUri);
return restTemplate.getForObject(finalUri, String.class);
}
在分布式微服务意味着要将单体应用中的业务拆分成一个个子服务,每个服务的粒度相对较小,因此系统中会出现大量的服务。由于每个服务都需要必要的配置信息才能运行,所以一套集中式的、动态的配置管理设施是必不可少的。
配置中心应运而生。配置中心,顾名思义,就是来统一管理项目中所有配置的系统。对于单机版,我们称之为配置文件;对于分布式集群系统,我们称之为配置中心。
配置中心作用:
参考文章:
SpringCloud Config为微服务架构中的微服务提供集中化的外部配置支持,配置服务器为各个不同微服务应用的所有环境提供了一个中心化的外部配置。
SpringCloud Config
是配置中心的一种,它分为服务端和客户端两部分:
使用SpringCloud Config
非常简单,需要在服务端和客户端分别进行改造:
@EnableConfigServer
注解,就大功告成了; ## http://127.0.0.1:3344/master/config-dev.yml
1. /{label}/{application}-{profile}.yml
## http://127.0.0.1:3344/config-dev.yml
2. /{application}-{profile}.yml
## http://127.0.0.1:3344/config/dev/master
3. /{application}/{profile}[/{label}]
application.yml
文件改为bootstrap.yml
,再将主启动类加上@EnableEurekaClient
注解;
当配置客户端启动时,它绑定到配置服务器(通过spring.cloud.config.uri引导配置属性),并使用远程属性源初始化Spring Environment。
这种行为的最终结果是,所有想要使用Config Server的客户端应用程序都需要bootstrap.yml或一个环境变量applicaiton.yml是用户级的资源配置项,而bootstrap.yml是系统级的,优先级更加高,在加载配置时优先加载bootstrap.yml
当将SpringCloud Config
客户端服务端都配置好之后,修改配置时会发现修改的配置文件不能实时生效;针对这个问题,可以将服务重启或者调用actuator
的刷新接口使其生效,使用之前需要引入actuator
的依赖:
# 使用SpringCloud Config修改完配置后,调用刷新接口使客户端配置生效
curl -X POST "http://localhost:3355/actuator/refresh"
为了避免手动的调用刷新接口,可以使用SpringCloud Bus
配合SpringCloud Config
实现配置的动态刷新。
长期以来 Java 的开发一直让人所诟病:项目开发复杂度极其高、项目的维护非常困难;即便使用了大量的开发框架,发现我们的开发也没少多少。为了解决让开发更佳简单,项目更容易管理,SpringBoot诞生了。
Spring Boot是一个广泛用来构建Java微服务的框架,它基于Spring依赖注入框架来进行工作。
官网地址:https://spring.io/projects/spring-boot
SpringBoot
是由Pivotal
团队提供的全新框架,其设计目的是用来简化新Spring
应用的初始搭建以及开发过程。
该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。SpringBoot
提供了一种新的编程范式,可以更加快速便捷地开发Spring
项目,在开发过程当中可以专注于应用程序本身的功能开发,而无需在Spring
配置上花太大的工夫。
SpringBoot
基于 Sring4
进行设计,继承了原有 Spring
框架的优秀基因。
SpringBoot
准确的说并不是一个框架,而是一些类库的集合。
maven
或者 gradle
项目导入相应依赖即可使用 SpringBoot
,而无需自行管理这些类库的版本。
特点:
Spring
项目:SpringBoot
可以以 jar 包的形式独立运行,运行一个 SpringBoot
项目只需通过 java–jar xx.jar
来运行。Servlet
容器:SpringBoot
可选择内嵌 Tomcat
、Jetty
或者 Undertow
,这样我们无须以 war
包形式部署项目。starter
简化 Maven
配置:Spring
提供了一系列的 starter
pom 来简化 Maven
的依赖加载,例如,当你使用了spring-boot-starter-web
时,会自动加入依赖包。Spring
:SpringBoot
会根据在类路径中的 jar 包、类,为 jar 包里的类自动配置 Bean,这样会极大地减少我们要使用的配置。当然,SpringBoot
只是考虑了大多数的开发场景,并不是所有的场景,若在实际开发中我们需要自动配置 Bean
,而 SpringBoot
没有提供支持,则可以自定义自动配置。SpringBoot
提供基于 http、ssh、telnet
对运行时的项目进行监控。SpringBoot
的神奇的不是借助于代码生成来实现的,而是通过条件注解来实现的,这是 Spring 4.x
提供的新特性。Spring 4.x
提倡使用 Java 配置和注解配置组合,而 SpringBoot
不需要任何 xml 配置即可实现 Spring
的所有配置。