以前的架构还是最原始阶段,官网、论坛、云平台等应用即一台服务器搞定一切。对应的web服务器、数据库、静态文件资源等,部署到一台服务器上即可。一般每秒几百请求没啥问题,结合内核参数调优、web应用性能参数调优、数据库调优,基本上能够稳定的运行。随着业务量越来越大和越重要,单体的架构模式已经无法对应大规模的应用场景。大量的web请求被堵塞,同时服务器的CPU、磁盘IO、带宽都有压力。系统功能越来越复杂,功能模块之间耦合性强相互影响。而且系统中决不能存在单点故障导致整体不可用。
所以只有垂直或是水平拆分业务系统,使其形成一个分布式的架构,利用分布式架构来冗余系统消除单点的故障,从而提高整个系统的可用性。同时分布式系统的模块重用度更高,高并发下的速度更快,扩展性更高是大型的项目必不可少的环节。
这一词源于 Martin Fowler 的 Microservices 博文,于2014年三月25日发表,简单地说,微服务是系统架构上的一种设计风格,它的主旨是将一个原本独立的系统拆分成多个小型服务
,这些小型服务都在各自独立的进程中运行,服务之间通过基于HTTP的RESTful API进行通信协作。
被拆分成的每一个小型服务都围绕着系统中的某一项或一些耦合度较高的业务功能进行构建,并且每个服务都维护着自身的数据存储、业务开发、自动化测试案例以及独立部署机制。由于有了轻量级的通信协作基础,所以这些微服务可以使用不同的语言来编写。
SpringCloud项目不同于其他 Spring 的优秀项目, 它不再是一个基础框架类, 而是一个更高层次的、 架构视角的综合性大型项目, 其目标旨在构建一套标准化的微服务解决方案, 让架构师、 开发者在使用微服务理念构建应用系统的时候, 面对各个环节的问题都可以找到相应的组件来处理。 引用网友戏称的一个比喻:Spring Cloud可以说是Spring社区为微服务架构提供的一个全家桶套餐
。 由于套餐中的组件通过一个社区进行包装与整合, 使得套餐中各个组件之间的配合变得更加和谐,这可以有效减少我们在组件的选型和整合上花费的精力,所以它可以帮助我们快速构建起基础的微服务架构系统。
基于SpringBoot提供了一套微服务解决方案,包括服务注册与发现,配置中心,全链路监控,服务网关,负载均衡,熔断器等组件,除了基于NetFlix的开源组件做高度抽象封装之外,还有一些选型中立的开源组件。
SpringCloud利用SpringBoot的开发便利性巧妙地简化了分布式系统基础设施的开发,SpringCloud为开发人员提供了快速构建分布式系统的一些工具,包括配置管理、服务发现、断路器、路由、微代理、事件总线、全局锁、决策竞选、分布式会话等等,它们都可以用SpringBoot的开发风格做到一键启动和部署。
同 Spring Cloud 一样,Spring Cloud Alibaba 也是一套微服务解决方案,包含开发分布式应用微服务的必需组件,方便开发者通过 Spring Cloud 编程模型轻松使用这些组件来开发分布式应用服务。
依托 Spring Cloud Alibaba,您只需要添加一些注解和少量配置,就可以将 Spring Cloud 应用接入阿里微服务解决方案,通过阿里中间件(主要为Nacos、Sentinel、Dubbo、Seata)来迅速搭建分布式应用系统。
#开始SpringCloud
父pom.xml
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-dependenciesartifactId>
<version>2.2.2.RELEASEversion>
<type>pomtype>
<scope>importscope>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>Hoxton.SR1version>
<type>pomtype>
<scope>importscope>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-alibaba-dependenciesartifactId>
<version>2.1.0.RELEASEversion>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
Spring-Cloud Euraka是Spring Cloud集合中一个组件,它是对Euraka的集成,用于服务注册和发现。Eureka是Netflix中的一个开源框架。它和 zookeeper、Consul一样,都是用于服务注册管理的。
Eureka由多个instance(服务实例)组成,这些服务实例可以分为两种:Eureka Server和Eureka Client。为了便于理解,我们将Eureka client再分为Service Provider和Service Consumer。
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-serverartifactId>
dependency>
启动类:
@SpringBootApplication
@EnableEurekaServer
public class EurekaMain7001 {
public static void main(String[] args) {
SpringApplication.run(EurekaMain7001.class, args);
}
}
application.yml
server:
port: 7001
eureka:
instance:
hostname: localhost
client:
registerWithEureka: false
fetchRegistry: false
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:7001/eureka/
运行后访问lhttp://ocalhost:7001/eureka 就能看到注册进Eureka的服务实例
pom.xml
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
主启动加上三个注解:
@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
application.yml
server:
port: 8001
spring:
application:
name: cloud-payment-service
eureka:
client:
#表示是否将自己注册进EurekaServer,默认为true
register-with-eureka: true
#是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetch-registry: true
service-url:
#defaultZone: http://localhost:7001/eureka
instance:
instance-id: payment8001
#访问路径可以显示IP地址
prefer-ip-address: true
#Eureka客户端向服务端发送信心跳时间间隔,单位为秒(默认是30秒)
lease-renewal-interval-in-seconds: 1
#Eureka服务端在收到最后一次心跳后等待时间上限,单位为秒(默认是90秒),超时将剔除服务
lease-expiration-duration-in-seconds: 2
Provider则为数据库查询数据接口,Customer通过RestTemplate用http请求让Provider去数据库查过来自己用。
public static final String PAYMENT_URL = "http://CLOUD-PAYMENT-SERVICE";
@GetMapping("/consumer/payment/create")
public CommonResult<Payment> create(Payment payment) {
return restTemplate.postForObject(PAYMENT_URL + "/payment/create", payment, CommonResult.class);
}
https://github.com/rawchen/SpringCloud
http://www.heartthinkdo.com/?p=1933
https://github.com/Netflix/eureka/wiki
C(Consistency):一致性
A(Availablitity):可用性
P(Partition tolerance):分区容错性
Euere是AP,高可用与可伸缩的Service发现服务,突出可用性。相对于Zookeeper而言,可能返回数据没有一致性,但是保证能够返回数据,服务是可用的。
Zookeeper是CP,分布式协同服务,突出一致性。对ZooKeeper的的每次请求都能得到一致的数据结果,但是无法保证每次访问服务可用性。如请求到来时,zookeer正在做leader选举,此时不能提供服务,即不满足A可用性。
在Eureka平台中,如果某台服务器宕机,Eureka不会有类似于ZooKeeper的选举leader的过程;客户端请求会自动切换到新的Eureka节点;当宕机的服务器重新恢复后,Eureka会再次将其纳入到服务器集群管理之中;而对于它来说,所有要做的无非是同步一些新的服务注册信息而已。所以,再也不用担心有“掉队”的服务器恢复以后,会从Eureka服务器集群中剔除出去的风险了。
pom.xml
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-zookeeper-discoveryartifactId>
dependency>
application.yml
server:
port: 8004
spring:
application:
name: cloud-provider-payment
cloud:
zookeeper:
connect-string: 192.168.0.104:2181
@EnableDiscoveryClient
./zkServer.sh start
application.yml
server:
port: 80
spring:
application:
# 服务别名
name: cloud-consumer-order
cloud:
zookeeper:
# 注册到zookeeper地址
connect-string: localhost:2181
https://github.com/rawchen/SpringCloud
https://www.cnblogs.com/zgghb/p/6515062.html
http://zookeeper.apache.org/doc/current/index.html
https://blog.csdn.net/java_66666/article/details/81015302
是google开源的一个使用go语言开发的服务发现、配置管理中心服务。内置了服务注册与发现框架、分布一致性协议实现、健康检查、Key/Value存储、多数据中心方案,不再需要依赖其他工具(比如ZooKeeper等)。服务部署简单,只有一个可运行的二进制的包。每个节点都需要运行agent,他有两种运行模式server和client。每个数据中心官方建议需要3或5个server节点以保证数据安全,同时保证server-leader的选举能够正确的进行。
pom.xml
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-consul-discoveryartifactId>
dependency>
application.yml
server:
port: 8006
spring:
application:
name: consul-provider-payment
#consul注册中心地址
cloud:
consul:
host: localhost
port: 8500
discovery:
#hostname: 127.0.0.1
service-name: ${spring.application.name}
@EnableDiscoveryClient
提供各种负载均衡算法。Ribbon核心组件IRule。
有如下几种复杂均衡算法:
com.netflix.loadbalancer.RoundRobinRule 轮询
com.netflix.loadbalancer.RandomRule 随机
com.netflix.loadbalancer.RetryRule 先按照RoundRobinRule的策略获取服务,如果获取服务失败则在指定时间内进行重试,获取可用的服务
WeightedResponseTimeRule 对RoundRobinRule的扩展,响应速度越快的实例选择权重越多大,越容易被选择
BestAvailableRule 会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务
AvailabilityFilteringRule 先过滤掉故障实例,再选择并发较小的实例
ZoneAvoidanceRule 默认规则,复合判断server所在区域的性能和server的可用性选择服务器
新建MySelfRule规格类
@Configuration
public class MySelfRule {
@Bean
public IRule myRule() {
// 定义为随机
return new RoundRobinRule();
}
}
主启动加上RibbonClient
@SpringBootApplication
@EnableEurekaClient
@RibbonClient(name = "CLOUD-PAYMENT-SERVICE", configuration = MySelfRule.class)
public class OrderMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderMain80.class, args);
}
}
MyLB.java
@Component
public class MyLB implements LoadBalancer {
private AtomicInteger atomicInteger = new AtomicInteger(0);
public final int getAndIncrement() {
int current;
int next;
do {
current = this.atomicInteger.get();
next = current >= 2147483647 ? 0 : current + 1; //Integer.MAX_VALUE 会创建多个栈
} while (!this.atomicInteger.compareAndSet(current, next));
System.out.println("****第几次访问,次数next为****: " + next);
return next;
}
//负载均衡算法:rest接口第几次请求数 % 服务器集群总数量 = 实际调用服务器位置下标
//每次服务重启后rest接口计数从1开始
@Override
public ServiceInstance instances(List<ServiceInstance> serviceInstances) {
int index = getAndIncrement() % serviceInstances.size();
return serviceInstances.get(index);
}
}
https://github.com/rawchen/SpringCloud
https://github.com/Netflix/ribbon
之前例子都是用的RestTemplate的HTTP请求工具。
OpenFeign为微服务架构下服务之间的调用提供了解决方案,OpenFeign是一种声明式、模板化的HTTP客户端。在Spring Cloud中使用OpenFeign,可以做到使用HTTP请求访问远程服务,就像调用本地方法一样的,开发者完全感知不到这是在调用远程方法,更感知不到在访问HTTP请求。
pom.xml
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-openfeignartifactId>
dependency>
主启动:@EnableFeignClients
service
@Component
@FeignClient(value = "CLOUD-PAYMENT-SERVICE")
public interface PaymentFeignService {
@GetMapping(value = "/payment/get/{id}")
public CommonResult getPaymentById(@PathVariable("id") Long id);
}
application.yml
#设置feign客户端超时时间(OpenFeign默认支持Ribbon)
ribbon:
#建立所需时间,适用于网络状况正常情况下,两端连接所需时间
connectTimeout: 5000
#指的是建立连接后从服务器读到可用资源所用时间
ReadTimeout: 5000
https://github.com/rawchen/SpringCloud
https://github.com/OpenFeign/feign
在微服务架构中通常会有多个服务层调用,基础服务的故障可能会导致级联故障,进而造成整个系统不可用的情况,这种现象被称为服务雪崩效应。服务雪崩效应是一种因“服务提供者”的不可用导致“服务消费者”的不可用,并将不可用逐渐放大的过程。
如果下图所示:A作为服务提供者,B为A的服务消费者,C和D是B的服务消费者。A不可用引起了B的不可用,并将不可用像滚雪球一样放大到C和D时,雪崩效应就形成了。
熔断器的原理很简单,如同电力过载保护器。它可以实现快速失败,如果它在一段时间内侦测到许多类似的错误,会强迫其以后的多个调用快速失败,不再访问远程服务器,从而防止应用程序不断地尝试执行可能会失败的操作,使得应用程序继续执行而不用等待修正错误,或者浪费CPU时间去等到长时间的超时产生。熔断器也可以使应用程序能够诊断错误是否已经修正,如果已经修正,应用程序会再次尝试调用操作。
熔断器模式就像是那些容易导致错误的操作的一种代理。这种代理能够记录最近调用发生错误的次数,然后决定使用允许操作继续,或者立即返回错误。 熔断器开关相互转换的逻辑如下图:
当在一定窗口期时间内(默认10s),请求数大于一定值(默认20次)且错误率大于一个值(默认50%)则熔断器会打开也就是跳闸,一段时间后(默认5s)进入半开状态,允许放行一个试探请求,否则不允许放行。
相关值在HystrixCommandProperties类中。
https://github.com/Netflix/Hystrix/wiki/Configuration
对于查询操作,我们可以实现一个fallback方法,当请求后端服务出现异常的时候,可以使用fallback方法返回值,fallback方法的返回值一般是设置的默认值或者来自缓存。
在Hystrix中,主要通过线程池来实现资源隔离。通常在使用的时候我们会根据调用的远程服务划分出多个线程池。例如调用产品服务的Command放入A线程池,调用账户服务的Command放入B线程池。这样做的主要优点是运行环境被隔离开了。
pom.xml
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-hystrixartifactId>
dependency>
application.yml
feign:
hystrix:
enabled: true
主启动加上@EnableHystrix,也同时加上了@EnableCircuitBreaker
PaymentHystrixService接口上的FeignClient添加fallback方法
@Component
@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT", fallback = PaymentFallbackService.class)
public interface PaymentHystrixService {
@GetMapping(value = "/payment/hystrix/ok/{id}")
public String paymentInfo_OK(@PathVariable("id") Integer id);
@GetMapping(value = "/payment/hystrix/timeout/{id}")
public String paymentInfo_TimeOut(@PathVariable("id") Integer id);
}
PaymentFallbackService类则实现PaymentHystrixService实现降级方法
@Component
public class PaymentFallbackService implements PaymentHystrixService {
@Override
public String paymentInfo_OK(Integer id) {
return "-----PaymentFallbackService OK-----";
}
@Override
public String paymentInfo_TimeOut(Integer id) {
return "-----PaymentFallbackService Timeout-----";
}
}
超时熔断:
@HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler",commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000")
})
服务熔断:
@HystrixCommand(fallbackMethod = "paymentCircuitBreakerFallack", commandProperties = {
@HystrixProperty(name = "circuitBreaker.enabled", value = "true"),//是否开启断路器
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),//请求次数
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "10000"),//时间窗口期
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "60"),//失败率达到多少后跳闸
})
pom.xml
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboardartifactId>
dependency>
主启动@EnableHystrixDashboard
微服务提供者需要spring-boot-starter-actuator的jar依赖
http://localhost:9001/hystrix
输入框输入要监控的服务,比如http://localhost:8001/hystrix.stream
延时写1000ms,标题随意,则可看到要监控的Hystrix熔断器的服务,相关参数自查。
https://github.com/rawchen/SpringCloud
网关的角色是作为一个 API 架构,用来保护、增强和控制对于 API 服务的访问。
API 网关是一个处于应用程序或服务(提供 REST API 接口服务)之前的系统,用来管理授权、访问控制和流量限制等,这样 REST API 接口服务就被 API 网关保护起来,对所有的调用者透明。因此,隐藏在 API 网关后面的业务系统就可以专注于创建和管理服务,而不用去处理这些策略性的基础设施。
Gateway使用的是Webflux中的reactor-netty响应式编程组件,底层使用了Netty通讯框架。
pom.xml
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-gatewayartifactId>
dependency>
application.yml
server:
port: 9527
spring:
application:
name: cloud-gateway
cloud:
gateway:
discovery:
locator:
enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由
routes:
- id: payment_route # 路由的id,没有规定规则但要求唯一,建议配合服务名
#匹配后提供服务的路由地址
# uri: http://localhost:8001
uri: lb://cloud-payment-service
predicates:
- Path=/payment/get/** # 断言,路径相匹配的进行路由
- id: payment_route2
# uri: http://localhost:8001
uri: lb://cloud-payment-service
predicates:
- Path=/payment/lb/** #断言,路径相匹配的进行路由
- After=2021-08-20T16:48:18.967+08:00[Asia/Shanghai] #路由断言,在这时间之后才能访问
# - Before=2021-08-20T16:48:18.967+08:00[Asia/Shanghai]
# - Between=2021-08-20T16:48:18.967+08:00[Asia/Shanghai],2021-08-20T17:48:18.967+08:00[Asia/Shanghai]
# - Cookie=username,rawchen
# - Header=X-Request-Id, \d+ #请求头要有X-Request-Id属性并且值为整数的正则表达式
# - Host=**.rawchen.com
# - Method=GET
# - Query=username, \d+ #要有参数名username并且值还要是整数才能路由
filters:
- AddRequestParameter=X-Request-Id, 1024 #过滤器工厂会在匹配的请求头加上一对该请求头
MyLogGateWayFilter.java
@Component
@Slf4j
public class MyLogGateWayFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("MyLogGateWayFilter: " + new Date());
String uname = exchange.getRequest().getQueryParams().getFirst("uname");
if (uname == null) {
log.info("用户名为null,非法用户");
exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 0;
}
}
https://github.com/rawchen/SpringCloud
pom.xml
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-config-serverartifactId>
dependency>
application.yml
可用ssh或者http的方式连接到github
server:
port: 3344
spring:
application:
name: cloud-config-center
cloud:
config:
server:
git:
#ssh-keygen -m PEM -t rsa -b 4096 -C "[email protected]" 使用此命令生成git密钥对
uri: [email protected]:rawchen/springcloud-config.git #远端仓库名
# uri: https://github.com/rawchen/springcloud-config.git #远端仓库名
#搜索目录
search-paths:
- springcloud-config
# username: rawchen
# password: xxx
#读取分支
label: main
访问http://localhost:3344/main/config-dev.yml
bootstrap.yml
server:
port: 3355
spring:
application:
name: config-client
cloud:
config:
label: main #分支名称
name: config #配置文件名称
profile: test #读取后缀名称 上述三个综合http://localhost:3344/main/config-test.yml
uri: http://localhost:3344 #配置中心的地址
Controller
@RestController
@RefreshScope
public class ConfigClientController {
@Value("${config.info}")
private String configInfo;
@GetMapping("/configInfo")
public String getConfigInfo() {
return configInfo;
}
}
访问http://localhost:3355/configInfo
多个客户端都需要调用一次api才能刷新属性config
curl -X POST "http://localhost:3355/actuator/refresh"
curl -X POST "http://localhost:3366/actuator/refresh"
https://github.com/rawchen/SpringCloud
在微服务架构的系统中,通常会使用轻量级的消息代理来构建一个公用的消息主题,并让系统中的所有微服务实例都连接上来。由于该主题中产生的消息会被所有实例监听和消费,所以称为消息总线。在总线上的各个实例,都可以方便地广播一些需要让其他链接在该主题行的实例都知道的消息。
ConfigClient实例都监听MQ中的同一个topic(默认是SpringcloudBus)。当一个服务刷新数据的时候,它会把这个信息放入到topic中,这样其它监听同一个topic的服务就能得到通知,然后去更新自身的配置。
Config配置中心服务端pom.xml
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-bus-amqpartifactId>
dependency>
<dependency>
application.yml
#rabbitmq相关配置 15672是web管理端的端口,5672是MQ访问的端口
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
#rabbitmq相关配置,暴露bus刷新配置的端点
management:
endpoints:
web:
exposure:
include: 'bus-refresh'
客户端bootstrap.yml
#暴露监控端点
management:
endpoints:
web:
exposure:
include: "*"
此时只需调用配置中心服务的刷新总线api通知其它服务:
curl -X POST "http://localhost:3344/actuator/bus-refresh"
https://github.com/rawchen/SpringCloud
Spring Cloud Stream 是一个用来为微服务应用构建消息驱动能力的框架。它可以基于 Spring Boot 来创建独立的、可用于生产的 Spring 应用程序。Spring Cloud Stream 为一些供应商的消息中间件产品提供了个性化的自动化配置实现,并引入了发布-订阅、消费组、分区这三个核心概念。通过使用 Spring Cloud Stream,可以有效简化开发人员对消息中间件的使用复杂度,让系统开发人员可以有更多的精力关注于核心业务逻辑的处理。但是目前 Spring Cloud Stream 只支持 RabbitMQ 和 Kafka 的自动化配置。
pom.xml
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-stream-rabbitartifactId>
dependency>
application.yml
server:
port: 8801
spring:
application:
name: cloud-stream-privider
cloud:
stream:
binders: #自此处配置要绑定的rabbitmq的服务信息
defaultRabbit: #表示定义的名称,用于binding整合
type: rabbit #消息组件类型
environment: # 设置rabbitmq的相关的环境配置
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
bindings: #服务的整合处理
output: #这个名字是一个通道的名称
destination: studyExchange #表示要使用的exchange名称定义
content-type: application/json #设置消息类型,本次为json
binder: defaultRabbit #设置要绑定的消息服务的具体设置
group: MQGroupA
https://github.com/rawchen/SpringCloud
为Spring Cloud实现了分布式跟踪解决方案。
需运行zipkin.jar服务
pom.xml
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-zipkinartifactId>
dependency>
application.yml
spring:
application:
name: cloud-payment-service
zipkin:
base-url: http://localhost:9411
sleuth:
sampler:
#采样率值介于0到1之间,1则表示全部采集
probability: 1
https://rawchen.com/se/nacos.html