分布式微服务架构的站式解决方案,是多种微服务架构落地技术的集合体,俗称微服务全家桶
微服务体系架构:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QAFV4xj0-1648103440833)(E:\code\java\htrain\kuangjia\springcloudimg\QQ截图20220322200607.png)]
我的理解是每个服务是一个springboot项目,把一个大项目拆分成一个个小项目,每个小项目自己去做自己的事,开发得到简化
注意:每个springcloud版本对应的springboot版本都不一样,以防止版本冲突,我选型的版本:
springboot:2.2.2.RELEASE
springcloud:Hoxton.SR1
这里我选的是用maven创建项目,这样就可以把版本控制起来,只要父maven项目定死了版本,子maven的版本就和父maven的版本一样了!!!
4.0.0
org.example
miscoservice-demo
1.0-SNAPSHOT
miscoservice-api
miscoservice-eureka-server7001
miscoservice-eureka-server7002
miscoservice-provider-payment8001
miscoservice-consumer-order80
miscoservice-provider-payment8002
miscoservice-consumer-feign-order80
miscoservice-provider-hystrix-payment8003
miscoservice-consumer-hystrix-order80
miscoservice-hystrix-dashboard9001
pom
UTF-8
1.8
1.8
4.12
1.2.17
1.18.16
8.0.22
1.1.16
2.2.1
2.1.17.RELEASE
2.2.2.RELEASE
Hoxton.SR1
org.springframework.boot
spring-boot-dependencies
${spring.boot.version}
pom
import
org.springframework.cloud
spring-cloud-dependencies
${spring.cloud.version}
pom
import
mysql
mysql-connector-java
${mysql.version}
com.alibaba
druid-spring-boot-starter
1.1.17
org.mybatis.spring.boot
mybatis-spring-boot-starter
${mybatis.spring.boot.version}
junit
junit
${junit.version}
log4j
log4j
${log4j.version}
org.projectlombok
lombok
${lombok.version}
true
org.springframework.boot
spring-boot-devtools
${devtools.version}
miscoservice-demo
org.example
1.0-SNAPSHOT
4.0.0
miscoservice-eureka-server7001
8
8
org.springframework.cloud
spring-cloud-starter-netflix-eureka-server
org.springframework.boot
spring-boot-starter-web
org.example
miscoservice-api
1.0-SNAPSHOT
这样一个springcloud的项目就搭建好了
Spring Cloud封装了Netflix 公司开发的Eureka模块来实现服务治理
在传统的RPC远程调用框架中,管理每个服务与服务之间依赖关系比较复杂,管理比较复杂,所以需要使用服务治理,管理服务于服务之间依赖关系,可以实现服务调用、负载均衡、容错等,实现服务发现与注册。
Eureka采用了CS的设计架构,Eureka Sever作为服务注册功能的服务器,它是服务注册中心。而系统中的其他微服务,使用Eureka的客户端连接到 Eureka Server并维持心跳连接。这样系统的维护人员就可以通过Eureka Server来监控系统中各个微服务是否正常运行。
在服务注册与发现中,有一个注册中心。当服务器启动的时候,会把当前自己服务器的信息比如服务地址通讯地址等以别名方式注册到注册中心上。另一方(消费者服务提供者),以该别名的方式去注册中心上获取到实际的服务通讯地址,然后再实现本地RPC调用RPC远程调用框架核心设计思想:在于注册中心,因为使用注册中心管理每个服务与服务之间的一个依赖关系(服务治理概念)。在任何RPC远程框架中,都会有一个注册中心存放服务地址相关信息(接口地址)
Eureka包含两个组件:Eureka Server和Eureka Client
各个微服务节点通过配置启动后,会在EurekaServer中进行注册,这样EurekaServer中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观看到。
它是一个Java客户端,用于简化Eureka Server的交互,客户端同时也具备一个内置的、使用轮询(round-robin)负载算法的负载均衡器。在应用启动后,将会向Eureka Server发送心跳(默认周期为30秒)。如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,EurekaServer将会从服务注册表中把这个服务节点移除(默认90秒)
org.springframework.cloud
spring-cloud-starter-netflix-eureka-server
org.springframework.boot
spring-boot-starter-web
server:
port: 7001
eureka:
instance:
hostname: eureka7001.com
client:
#false表示不向注册中心注册自己。
register-with-eureka: false
#false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
fetch-registry: false
service-url:
#设置与Eureka server交互的地址查询服务和注册服务都需要依赖这个地址。
#defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
defaultZone: http://eureka7001.com:7001/eureka/
#defaultZone: http://eureka7002.com:7002/eureka/
server:
enable-self-preservation: true
#eviction-interval-timer-in-ms: 2000
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerMain7001 {
public static void main(String[] args) {
SpringApplication.run(EurekaServerMain7001.class,args);
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Erkj35SY-1648103440833)(E:\code\java\htrain\kuangjia\springcloudimg\QQ截图20220322202159.png)]
搭建成功!
目的:因为只有一台电脑,用不同的服务模拟不同的服务器,本质还是一个回环地址
位置:C:\Windows\System32\drivers\etc\hosts
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0XAdyGDe-1648103440833)(E:\code\java\htrain\kuangjia\springcloudimg\QQ截图20220322202538.png)]
server:
port: 7002
eureka:
instance:
hostname: eureka7002.com
client:
#false表示不向注册中心注册自己。
register-with-eureka: false
#false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
fetch-registry: false
service-url:
#设置与Eureka server交互的地址查询服务和注册服务都需要依赖这个地址。
#defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
defaultZone: http://eureka7001.com:7001/eureka/
#defaultZone: http://eureka7002.com:7002/eureka/
server:
enable-self-preservation: true
#eviction-interval-timer-in-ms: 2000
主启动类
@EnableEurekaClient
@SpringBootApplication
@EnableDiscoveryClient
public class PaymentMain8001 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain8001.class,args);
}
}
配置yml
server:
port: 8001
spring:
application:
name: cloud-payment-service
datasource:
type: com.alibaba.druid.pool.DruidDataSource # 当前数据源操作类型
driver-class-name: com.mysql.jdbc.Driver # mysql驱动包jdbc:mysql://localhost:3306/demo03?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true
username: root
password: 123456
druid:
url: jdbc:mysql://localhost:3306/demo03?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true
mybatis:
mapperLocations: classpath:mapper/*.xml
type-aliases-package: com.kuang.pojo # 所有Entity别名类所在包
eureka:
client:
#表示是否将自己注册进Eurekaserver默认为true。
register-with-eureka: true
#是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetchRegistry: true
service-url:
defaultZone: http://localhost:7001/eureka/,http://localhost:7002/eureka/
#defaultZone: http://localhost:7001/eureka
instance:
instance-id: payment8001
prefer-ip-address: true #添加此处
编写生产端的接口
server:
port: 8002
spring:
application:
name: cloud-payment-service
datasource:
type: com.alibaba.druid.pool.DruidDataSource # 当前数据源操作类型
driver-class-name: com.mysql.jdbc.Driver # mysql驱动包
username: root
password: 123456
druid:
url: jdbc:mysql://localhost:3306/demo03?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true
mybatis:
mapperLocations: classpath:mapper/*.xml
type-aliases-package: com.kuang.pojo # 所有Entity别名类所在包
eureka:
instance:
hostname: payment802
prefer-ip-address: true
client:
fetch-registry: true
register-with-eureka: true
service-url:
defaultZone: http://localhost:7001/eureka/,http://localhost:7002/eureka/
导入依赖
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
org.springframework.boot
spring-boot-starter-web
org.example
miscoservice-api
1.0-SNAPSHOT
主启动类
@SpringBootApplication
@EnableEurekaClient
@RibbonClient(name = "CLOUD-PAYMENT-SERVICE",configuration = MyRule.class)
public class ConsumerMain80 {
public static void main(String[] args) {
SpringApplication.run(ConsumerMain80.class,args);
}
}
写yaml
server:
port: 80
spring:
application:
name: cloud-order-service
eureka:
client:
#表示是否将自己注册进Eurekaserver默认为true。
register-with-eureka: true
#是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetchRegistry: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
#defaultZone: http://localhost:7001/eureka
结合resttemplate+ribbon去调对应的服务
@RestController
public class OrderController {
//private final static String PAYMENT_URL = "http://localhost:8001";
private final static String PAYMENT_URL = "http://CLOUD-PAYMENT-SERVICE";
@Resource
private RestTemplate restTemplate;
@GetMapping("/consumer/payment/get/{id}")
public CommonResult getStudentById(@PathVariable("id") String id){
return restTemplate.getForObject(PAYMENT_URL+"/payment/get/"+id,CommonResult.class);
}
}
Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡的工具。
简单的说,Ribbon是Netflix发布的开源项目,主要功能是提供客户端的软件负载均衡算法和服务调用。Ribbon客户端组件提供一系列完善的配置项如连接超时,重试等。
简单的说,就是在配置文件中列出Load Balancer(简称LB)后面所有的机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随机连接等)去连接这些机器。我们很容易使用Ribbon实现自定义的负载均衡算法。
Ribbon目前也进入维护模式。
Ribbon未来可能被Spring Cloud LoadBalacer替代。
LB负载均衡(Load Balance)是什么
简单的说就是将用户的请求平摊的分配到多个服务上,从而达到系统的HA (高可用)。
常见的负载均衡有软件Nginx,LVS,硬件F5等。
Ribbon本地负载均衡客户端VS Nginx服务端负载均衡区别
Nginx是服务器负载均衡,客户端所有请求都会交给nginx,然后由nginx实现转发请求。即负载均衡是由服务端实现的。
Ribbon本地负载均衡,在调用微服务接口时候,会在注册中心上获取注册信息服务列表之后缓存到JVM本地,从而在本地实现RPC远程服务调用技术。
集中式LB
即在服务的消费方和提供方之间使用独立的LB设施(可以是硬件,如F5, 也可以是软件,如nginx),由该设施负责把访问请求通过某种策略转发至服务的提供方;
进程内LB
将LB逻辑集成到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选择出一个合适的服务器。
Ribbon就属于进程内LB,它只是一个类库,集成于消费方进程,消费方通过它来获取到服务提供方的地址。
一句话
负载均衡 + RestTemplate调用
前面在使用Ribbon+RestTemplate时,利用RestTemplate对http请求的封装处理,形成了一套模版化的调用方法。但是在实际开发中,由于对服务依赖的调用可能不止一处,往往一个接口会被多处调用,所以通常都会针对每个微服务自行封装一些客户端类来包装这些依赖服务的调用。所以,Feign在此基础上做了进一步封装,由他来帮助我们定义和实现依赖服务接口的定义。在Feign的实现下,我们只需创建一个接口并使用注解的方式来配置它(以前是Dao接口上面标注Mapper注解,现在是一个微服务接口上面标注一个Feign注解即可),即可完成对服务提供方的接口绑定,简化了使用Spring cloud Ribbon时,自动封装服务调用客户端的开发量。
导入依赖
org.springframework.cloud
spring-cloud-starter-openfeign
写yml
server:
port: 80
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://localhost:7001/eureka/,http://localhost:7002/eureka/
spring:
application:
name: ORDER80
feign:
client:
config:
default: #这里就是指的所有被加载的默认FeignClient实现的服务配置都生效
#指的是建立连接后从服务器读取到可用资源所用的时间
connectTimeout: 5000
#指的是建立连接所用的时间,适用于网络状况正常的情况下,两端连接所用的时间
readTimeout: 5000
主启动类
@SpringBootApplication
@EnableFeignClients
public class OrderMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderMain80.class,args);
}
}
调服务(你要调哪个服务在注解FeignClient写明)
@Component
@FeignClient(value = "CLOUD-PAYMENT-SERVICE")
public interface PaymentService {
@GetMapping(value = "/payment/get/{id}")
public CommonResult getPaymentById(@PathVariable("id") String id);
@GetMapping("/payment/get/timeout")
public String getTimeOutServerPort();
}
复杂分布式体系结构中的应用程序有数十个依赖关系,每个依赖关系在某些时候将不可避免地失败。
多个微服务之间调用的时候,假设微服务A调用微服务B和微服务C,微服务B和微服务C又调用其它的微服务,这就是所谓的“扇出”。如果扇出的链路上某个微服务的调用响应时间过长或者不可用,对微服务A的调用就会占用越来越多的系统资源,进而引起系统崩溃,所谓的“雪崩效应”.
对于高流量的应用来说,单一的后避依赖可能会导致所有服务器上的所有资源都在几秒钟内饱和。比失败更糟糕的是,这些应用程序还可能导致服务之间的延迟增加,备份队列,线程和其他系统资源紧张,导致整个系统发生更多的级联故障。这些都表示需要对故障和延迟进行隔离和管理,以便单个依赖关系的失败,不能取消整个应用程序或系统。
所以,通常当你发现一个模块下的某个实例失败后,这时候这个模块依然还会接收流量,然后这个有问题的模块还调用了其他的模块,这样就会发生级联故障,或者叫雪崩。
Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时、异常等,Hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性。
"断路器”本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个符合预期的、可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方无法处理的异常,这样就保证了服务调用方的线程不会被长时间、不必要地占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。
服务降级
服务限流
服务熔断
1.编写一个生产者8003端
org.springframework.cloud
spring-cloud-starter-netflix-hystrix
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
@SpringBootApplication
@EnableEurekaClient
@EnableCircuitBreaker
public class PaymentMain8003 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain8003.class,args);
}
}
server:
port: 8003
spring:
application:
name: cloud-payment-service
eureka:
client:
#表示是否将自己注册进Eurekaserver默认为true。
register-with-eureka: true
#是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetchRegistry: true
service-url:
defaultZone: http://localhost:7001/eureka/,http://localhost:7002/eureka/
#defaultZone: http://localhost:7001/eureka
instance:
instance-id: payment8003
prefer-ip-address: true #添加此处
management:
endpoints:
web:
exposure:
include: "*"
服务降级
@Override
@HystrixCommand(fallbackMethod = "paymentInfoTimeOutHandler",commandProperties = {
@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="2000")
})
public String paymentInfo_TimeOut() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return Thread.currentThread().getName()+"耗时3秒!";
}
String paymentInfoTimeOutHandler(){
return Thread.currentThread().getName()+"8001端口服务器繁忙,请稍后再试!";
}
服务熔断
//=====服务熔断
@HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback",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"),// 失败率达到多少后跳闸
})
@Override
public String paymentCircuitBreaker(Integer id) {
if(id < 0) {
throw new RuntimeException("******id 不能负数");
}
return Thread.currentThread().getName()+"\t"+"调用成功,id号: " + id;
}
public String paymentCircuitBreaker_fallback(@PathVariable("id") Integer id) {
return "id 不能负数,请稍后再试,/(ㄒoㄒ)/~~ id: " +id;
}
@DefaultProperties(defaultFallback = "paymentTimeOutFallbackMethod")//如果需要服务降级默认走的方法
Gateway是在Spring生态系统之上构建的API网关服务,基于Spring 5,Spring Boot 2和Project Reactor等技术。
Gateway旨在提供一种简单而有效的方式来对API进行路由,以及提供一些强大的过滤器功能,例如:熔断、限流、重试等。
SpringCloud Gateway是Spring Cloud的一个全新项目,基于Spring 5.0+Spring Boot 2.0和Project Reactor等技术开发的网关,它旨在为微服务架构提供—种简单有效的统一的API路由管理方式。
SpringCloud Gateway作为Spring Cloud 生态系统中的网关,目标是替代Zuul,在Spring Cloud 2.0以上版本中,没有对新版本的Zul 2.0以上最新高性能版本进行集成,仍然还是使用的Zuul 1.x非Reactor模式的老版本。而为了提升网关的性能,SpringCloud Gateway是基于WebFlux框架实现的,而WebFlux框架底层则使用了高性能的Reactor模式通信框架Netty。
Spring Cloud Gateway的目标提供统一的路由方式且基于 Filter链的方式提供了网关基本的功能,例如:安全,监控/指标,和限流。
org.springframework.cloud
spring-cloud-starter-gateway
@SpringBootApplication
@EnableEurekaClient
public class GatewayMain9999 {
public static void main(String[] args) {
SpringApplication.run(GatewayMain9999.class,args);
}
}
server:
port: 9999
spring:
application:
name: cloud-gateway
#############################新增网关配置###########################
cloud:
gateway:
routes:
- id: payment_routh #payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名
uri: http://localhost:8001 #匹配后提供服务的路由地址
#uri: lb://cloud-payment-service #匹配后提供服务的路由地址
predicates:
- Path=/payment/get/** # 断言,路径相匹配的进行路由
- id: payment_routh2 #payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名
uri: http://localhost:8001 #匹配后提供服务的路由地址
#uri: lb://cloud-payment-service #匹配后提供服务的路由地址
predicates:
- Path=/payment/lb/** # 断言,路径相匹配的进行路由
####################################################################
eureka:
instance:
hostname: cloud-gateway-service
client: #服务提供者provider注册进eureka服务列表内
service-url:
register-with-eureka: true
fetch-registry: true
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
就可以通过9999这个端口号访问对应的服务了
微服务意味着要将单体应用中的业务拆分成一个个子服务,每个服务的粒度相对较小,因此系统中会出现大量的服务。由于每个服务都需要必要的配置信息才能运行,所以一套集中式的、动态的配置管理设施是必不可少的。
SpringCloud提供了ConfigServer来解决这个问题,我们每一个微服务自己带着一个application.yml,上百个配置文件的管理.……
集中管理配置文件
不同环境不同配置,动态化的配置更新,分环境部署比如dev/test/prod/beta/release
运行期间动态调整配置,不再需要在每个服务部署的机器上编写配置文件,服务会向配置中心统一拉取配置自己的信息
当配置发生变动时,服务不需要重启即可感知到配置的变化并应用新的配置
将配置信息以REST接口的形式暴露 - post/crul访问刷新即可…
org.springframework.cloud
spring-cloud-config-server
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
@SpringBootApplication
@EnableConfigServer
public class ConfigCenter7777 {
public static void main(String[] args) {
SpringApplication.run(ConfigCenter7777.class,args);
}
}
server:
port: 7777
spring:
application:
name: cloud-config-center #注册进Eureka服务器的微服务名
cloud:
config:
server:
git:
uri: https://gitee.com/kuang-kuang/springcloud-config.git #GitHub上面的git仓库名字
####搜索目录
search-paths:
# - springcloud-config
username: kuang-kuang
password: 1999101806a
####读取分支
label: master
#服务注册到eureka地址
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka,http://localhost:7002/eureka
http://config-7777.com:7777/master/config-dev.yml
org.springframework.cloud
spring-cloud-starter-config
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
@SpringBootApplication
@EnableEurekaClient
public class ConfigClient7778 {
public static void main(String[] args) {
SpringApplication.run(ConfigClient7778.class,args);
}
}
spring:
cloud:
config:
name: config #需要从github上读取的资源名称,注意没有yml后缀名
profile: prod #本次访问的配置项
label: master
uri: http://localhost:7777
@RestController
//@RefreshScope
public class ConfigClientController {
@Value("${spring.application.name}")
private String applicationName;
@Value("${eureka.client.service-url.defaultZone}")
private String eurekaServers;
@Value("${server.port}")
private String port;
@RequestMapping("/config")
public String getConfig()
{
String str = "applicationName: " + applicationName + "\t eurekaServers:" + eurekaServers + "\t port: " + port;
System.out.println("******str: " + str);
return "applicationName: " + applicationName + "\t eurekaServers:" + eurekaServers + "\t port: " + port;
}
}