spring cloud
spring cloud
gitee:链接: sc-demo.
什么是微服务?
来自百度百科
https://baike.baidu.com/item/微服务/18758759?fr=aladdin
维基上对其定义为:
一种软件开发技术- 面向服务的体系结构(SOA)架构样式的一种变体,它提倡将单一应用程序划分成一组小的服务,服务之间互相协调、互相配合,为用户提供最终价值。每个服务运行在其独立的进程中,服务与服务间采用轻量级的通信机制互相沟通(通常是基于HTTP的RESTful API)。每个服务都围绕着具体业务进行构建,并且能够独立地部署到生产环境、类生产环境等。另外,应尽量避免统一的、集中式的服务管理机制,对具体的一个服务而言,应根据上下文,选择合适的语言、工具对其进行构建。
结构
spring cloud 结构
其中Eureka、Hystrix、Config、Bus现已不在更新、阿里基于springcloud整合其中组件
推出nacos代替Eureka、Config、Bus
Sentienl代替Hystrix
客户端和服务端
服务端 --- eureka、nacos 客户端 client --- 调用服务中心(消费者、提供者)
eureka(CAP — AP )
由Netflix开源,并被Pivatal集成到SpringCloud体系中,它是基于 RestfulAPI 风格开发的服务注册与发现组件。
CAP定理又称CAP原则,指的是在一个分布式系统中,Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),最多只能同时三个特性中的两个,三者不可兼得。
P:分区容错性:分布式系统在遇到某节点或网络分区故障的时候,仍然能够对外提供满足一致性或可用性的服务(一定的要满足的)
C:数据一致性:all nodes see the same data at the same time
A:高可用:Reads and writes always succeed
简单使用
注意:Jdk9之后,在父工程的pom文件中手动引入jaxb的jar,因为Jdk9之后默认没有加载该模块,Eureka Server使用到,所以需要手动导入,否则Eureka Server服务无法启动
导入maven依赖
父工程
spring boot 版本 2.2.5.RELEASE
<spring.cloud-version>Hoxton.SR8</spring.cloud-version>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring.cloud-version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
Eureka模块
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
创建启动类 EurekaApplication 加上@EnableEurekaServer开启服务
@SpringBootApplication
@EnableEurekaServer
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class,args);
}
}
修改 application.yml文件
eureka:
client:
serviceUrl:
defaultZone: http://localhost:7001/eureka/
# 是否在服务中心注册(不注册)
register-with-eureka: false
# 就是服务需要从Eureka Server获取服务信息,默认为true,设置为false
fetch-registry: false
instance:
hostname: localhost
spring:
application:
# 应用名称,会在Eureka中作为服务的id标识(serviceId)
name: sc-eureka
server:
port: 7001
访问localhost:7001 点击
集群
创建eureka2模块,重复上面的操作
eureka2的application.yml文件
eureka:
client:
serviceUrl:
defaultZone: http://eureka1:7001/eureka/,http://eureka2:7002/eureka/
register-with-eureka: false
fetch-registry: false
instance:
hostname: eureka2
spring:
application:
name: sc-eureka1
server:
port: 7002
修改 eureka模块的application.yml文件
eureka:
client:
serviceUrl:
defaultZone: http://eureka1:7001/eureka/,http://eureka2:7002/eureka/
register-with-eureka: false
# #就是服务需要从Eureka Server获取服务信息,默认为true,置为false
fetch-registry: false
instance:
hostname: eureka1
# 非集群
# hostname: localhost
spring:
application:
name: sc-eureka
server:
port: 7001
访问localhost:7001 点击
消费者、提供者
provider—提供者
在eureka服务中心注册,供消费者使用
简单使用
创建 provider模块,导入maven依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
创建启动类ProviderApplication
@SpringBootApplication
@EnableEurekaClient
public class ProviderApplication{
public static void main(String[] args) {
SpringApplication.run(Application.class,args);
}
}
创建controller
@RestController
@RequestMapping("/provider")
public class ProviderController {
@Value("${server.port}")
private String port;
@GetMapping("/port")
public String getPort() {
return "来自provider的端口号 : "+port;
}
}
访问localhost:7001 点击
consumer—消费者
简单使用
和上面的provider一样,创建完模块,其yml文件
eureka:
client:
serviceUrl:
defaultZone: http://eureka1:7001/eureka/,http://eureka2:7002/eureka/
register-with-eureka: false
instance:
prefer-ip-address: true
spring:
application:
name: consumer
server:
port: 9001
创建config
@SpringBootConfiguration
public class RestTemplateConfig {
// @Bean
// public RestTemplate restTemplate(RestTemplate restTemplate){
// return new RestTemplate();
// }
//
@Bean
@LoadBalanced
public RestTemplate restTemplate(RestTemplateBuilder builder){
return builder.build();
}
}
其controller
@RestController
@RequestMapping("/consumer")
public class ConsumerController {
private final RestTemplate restTemplate;
private final DiscoveryClient discoveryClient;
public ConsumerController(DiscoveryClient discoveryClient, RestTemplate restTemplate) {
this.discoveryClient = discoveryClient;
this.restTemplate = restTemplate;
}
@GetMapping("/port")
public String getPort() {
// ServiceInstance consumer = discoveryClient.getInstances("PROVIDER").get(0);
// System.out.println("provider = " + consumer.getUri());
// String url= consumer.getUri() + "/provider/port/";
return restTemplate.getForObject("http://provider/provider/port",String.class);
}
}
访问 consumer/port: 点击
ribbon和openfeign
什么是 — ribbon
Ribbon是Netflix发布的负载均衡器。Eureka一般配合Ribbon进行使用,Ribbon利用从Eureka中读取到服务信息,在调用服务提供者提供的服务时,会根据一定的算法进行负载
负载均衡一般分为服务器端负载均衡和客户端负载均衡
所谓服务器端负载均衡,比如Nginx、F5这些,请求到达服务器之后由这些负载均衡器根据一定的算法将请求路由到目标服务器处理。
所谓客户端负载均衡,比如我们要说的Ribbon,服务消费者客户端会有一个服务器地址列表,调用方在请求前通过一定的负载均衡算法选择一个服务器进行访问,负载均衡算法的执行是在请求客户端进行。
为什么
分摊压力
简单使用
在消费者模块中添加规则
public class MyselfRule {
@Bean
public IRule myRule(){
//把默认的轮询策略改为随机策略。
return new RandomRule();
}
}
消费者ConsumerApplication添加注解
@RibbonClient(name = "provider",configuration = MyselfRule.class)
新建一个提供者,只改变端口,或者在idea,中将providerController
设置中选择
改动provider中的yml文件的端口
server:
port: 8002
即可模拟多个模块
访问 consumer/port: 点击
什么是openfeign
远程调用组件
本质:封装了Http调用流程,更符合面向接口化的编程习惯,类似于Dubbo的服务调用
简单使用
新建consumer-openfeign模块或者在consumer中添加maven依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
启动类
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class OpenFeignApplication {
public static void main(String[] args) {
SpringApplication.run(OpenFeignApplication.class,args);
}
}
application.yml文件
server:
port: 9001
eureka:
client:
# eureka服务端的路劲
service-url:
defaultZone: http://eureka1:7001/eureka/,http://eureka2:7002/eureka/
# 如果不想将改服务注册进注册中心,一定要写成false
register-with-eureka: false
spring:
application:
name: consumer-openfeign
#设置feign客户端超时时间(OpenFeign默认支持ribbon)
ribbon:
#指的是建立连接后从服务器读取到可用资源所用的时间
ReadTimeout: 5000
#指的是建立连接所用的时间,适用于网络状况正常的情况下,两端连接所用的时间
ConnectTimeout: 5000
#针对的被调用方微服务名称(serviceId),不加就是全局生效
#provider:
# ribbon:
# NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #随机策略
接口
@FeignClient("provider") // 参数 serviceId
public interface ProviderService {
@GetMapping("/provider/port")
String getPort();
@GetMapping("/provider/feign/timeout")
String providerFeignTimeOut();
}
在provider模块的Controller中添加
@GetMapping("/feign/timeout")
public String providerFeignTimeOut() {
try {
// 休眠3秒
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
return port;
}
实现
@RestController
@RequestMapping("/consumer")
public class ConsumerController {
private final ProviderService providerService;
public ConsumerController(ProviderService providerService) {
this.providerService = providerService;
}
@GetMapping("/port")
public String getPort() {
return providerService.getPort();
}
@GetMapping("/feign/timeout")
public String providerFeignTimeOut() {
System.out.println("providerFeignTimeOut--------------->");
return providerService.providerFeignTimeOut();
}
}
Hystrix
什么是Hystrix
属于一种容错机制
[来自官网]Hystrix(豪猪),宣言“defend your application”是由Netflix开源的一个延迟和容错库,用于隔离访问远程系统、服务或者第三方库,防止级联失败,从而提升系统的可用性与容错性。
为什么
当山坡积雪内部的内聚力抗拒不了它所受到的重力拉引时,便向下滑动,引起大量雪体崩塌,人们把这种自然现象称作雪崩。
微服务中,一个请求可能需要多个微服务接口才能实现,会形成复杂的调用链路。
服务雪崩效应是一种因“服务提供者的不可用”(原因)导致“服务调用者不可用”(结果),并将不可用逐渐放大的现象。
简单使用
新建consumer-hystrix模块
较之前的consumer模块添加maven依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
启动类
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
@EnableHystrix
public class ConsumerHystrixApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerHystrixApplication.class,args);
}
}
application.yml文件
server:
port: 9001
eureka:
client:
# eureka服务端的路劲
service-url:
defaultZone: http://localhost:7001/eureka/
# 如果不想将改服务注册进注册中心,一定要写成false
register-with-eureka: true
spring:
application:
name: consumer-hystrix
#设置feign客户端超时时间(OpenFeign默认支持ribbon)
ribbon:
#指的是建立连接后从服务器读取到可用资源所用的时间
ReadTimeout: 5000
#指的是建立连接所用的时间,适用于网络状况正常的情况下,两端连接所用的时间
ConnectTimeout: 5000
#针对的被调用方微服务名称(serviceId),不加就是全局生效
#provider:
# ribbon:
feign:
hystrix:
enabled: true
# NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #随机策略
#设置最大请求时间为5秒
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 5000
熔断,降级
在provider上使用
启动类添加 @EnableHystrix
在controller层 添加方法,来模拟实现熔断,再进行降级
public String providerHystrixTimeOutHandler(){
return "当前服务器忙,请稍后再试 /(T o T)/~";
}
@HystrixCommand(commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "2000")
})
@GetMapping("/hystrix/timeout")
public String providerFeignTimeOut() {
int time=3;
// int a=10/0;
try {
// 休眠3秒
TimeUnit.SECONDS.sleep(time);
} catch (InterruptedException e) {
e.printStackTrace();
}
return Thread.currentThread().getName()+" -- out -- providerHystrixTimeOut 耗时 == " +time;
}
在consumer-hystrix消费者模块中
添加降级创建ProviderHystrixServiceFactory 类
@Component
public class ProviderHystrixServiceFactory implements FallbackFactory<ProviderService> {
@Override
public ProviderService create(Throwable throwable) {
return new ProviderService() {
String msg="系统异常";
@Override
public String getHystrixOK() {
return msg;
}
@Override
public String providerHystrixTimeOut() {
return msg;
}
@Override
public String providerOrder(Long id) {
return msg;
}
};
}
}
添加service接口
@FeignClient(value = "provider-hystrix",fallbackFactory = ProviderHystrixServiceFactory.class) // 参数 serviceId
//@FeignClient(value = "provider-hystrix",fallback = ProviderHystrixService.class) // 参数 serviceId
public interface ProviderService {
@GetMapping("/provider/port")
String getHystrixOK();
@GetMapping("/provider/hystrix/timeout")
String providerHystrixTimeOut();
}
也可以这样
取消上面操作,保证纯洁性
在controller中添加
@HystrixCommand(fallbackMethod ="global_FallbackMethod",commandProperties = {
@HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds",value = "10000"),
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"),
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "30000"),
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "50")
})
@GetMapping("hystrix/order/{id}")
public String providerOrder(@PathVariable Long id){
return providerService.providerOrder(id);
}
public String global_FallbackMethod(Long id){
return "Global全局降级";
}
了解更多参数可以访问: link.
gateway
什么是gateway 网关
Spring Cloud GateWay不仅提供统一的路由方式(反向代理)并且基于 Filter(定义过滤器对请求过滤,完成一些功能) 链的方式提供了网关基本的功能,例如:鉴权、流量控制、熔断、路径重写、日志监控等。
网关在架构中的位置
工作流程
Spring 官方介绍:
客户端向Spring Cloud GateWay发出请求,然后在GateWay Handler Mapping中找到与请求相匹配的路由,将其发送到GateWay Web Handler;Handler再通过指定的过滤器链来将请求发送到我们实际的服务执行业务逻辑,然后返回。过滤器之间用虚线分开是因为过滤器可能会在发送代理请求之前(pre)或者之后(post)执行业务逻辑。
Filter在“pre”类型过滤器中可以做参数校验、权限校验、流量监控、日志输出、协议转换等,在“post”类型的过滤器中可以做响应内容、响应头的修改、日志的输出、流量监控等。
简单使用
注意不能有web依赖,有冲突
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
</dependencies>
启动类
@SpringBootApplication
@EnableDiscoveryClient
public class GateWayApplication {
public static void main(String[] args) {
SpringApplication.run(GateWayApplication.class,args);
}
}
application.yml文件
server:
port: 9527
eureka:
client:
serviceUrl: # eureka server的路径
defaultZone: http://eureka1:7001/eureka/,http://eureka2:7002/eureka/
fetch-registry: true
register-with-eureka: true
instance:
prefer-ip-address: true
# instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}:@project.version@
spring:
application:
name: gateway
#网关的配置
cloud:
gateway:
routes: #配置路由
- id: provider
# uri: http://localhost:8001
# uri: http://provider
uri: lb://provider
predicates: #当断言成功后,交给某一个微服务处理时使用的是转发
- Before=2023-01-20T17:42:47.789-07:00[America/Denver]
# - id: provider
# uri: http://127.0.0.1:8001
# predicates:
# - Path=/product/**
# filters:
# # http://127.0.0.1:9300/product/service/port --> /service/port --> 商品微服务
# - StripPrefix=1 #去掉uri中的第一部分,所以就要求我们通过网关访问的时候,把uri的第一部分设置为product,从uri的第二部分开始才是真正的uri
黑名单
@Component
public class BlackListFilter implements GlobalFilter, Ordered {
private static List<String > blackList=new ArrayList<>();
static {
blackList.add("0:0:0:0:0:0:0:1"); // 模拟本机地址
blackList.add("127.0.0.1");
}
/**
* 过滤器核心方法
* @param exchange 封装了request和response对象的上下文
* @param chain 网关过滤器链(包含全局过滤器和单路由过滤器)
* @return
*/
// @Order(1)
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
String clientIp = request.getRemoteAddress().getHostString();
response.getHeaders().add("Content-Type","application/json;charset=UTF-8");
if(blackList.contains(clientIp)){
//todo
response.setStatusCode(HttpStatus.UNAUTHORIZED);
String data="Request be denied 请求拒绝";
System.out.println("data = " + data);
DataBuffer wrap = response.bufferFactory().wrap(data.getBytes(StandardCharsets.UTF_8));
return response.writeWith(Mono.just(wrap));
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 0;
}
}
config和bus
spring config
单体应用架构
配置信息的管理、维护并不会显得特别麻烦,手动操作就可以,因为就一个工程
微服务架构
根据各个微服务的负载情况,动态调整数据源连接池大小,我们希望配置内容发生变化的时候,微服务可以自动更新
应用场景
对配置文件进行 集中式管理
- 集中配置管理,一个微服务架构中可能有成百上千个微服务,所以集中配置管理是很重要的(一次修改、到处生效)
- 不同环境不同配置,比如数据源配置在不同环境(开发dev,测试test,生产prod)中是不同的
- 运行期间可动态调整。例如,可根据各个微服务的负载情况,动态调整数据源连接池大小等配置修改后可自动更新
- 如配置内容发生变化,微服务可以自动更新配置
spring cloud config 简介
- Server 端:提供配置文件的存储、以接口的形式将配置文件的内容提供出去,通过使用@EnableConfigServer注解在 Spring boot 应用中非常简单的嵌入
- Client 端:通过接口获取配置数据并初始化自己的应用
Config分布式配置应用
Config Server是集中式的配置服务,用于集中管理应用程序各个环境下的配置,使用gitee(git)管理
eg:对“静态化微服务或者商品微服务”的application.yml进行管理(区分开发环境(dev)、测试环境(test)、生产环境(prod))
创建一个新的仓库,在新仓库里上传application-dev.yml ( eg: {}-{}.yml )
自动更新简单使用
导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
创建bootstrap.yml
server:
port: 9500 # 后期该微服务多实例,端口从9100递增(10个以内)
Spring:
application:
name: config-client
rabbitmq:
host: 39.106.35.116
port: 5672
username: guest
password: guest
# datasource:
# driver-class-name: com.mysql.jdbc.Driver
# url: jdbc:mysql://localhost:3306/demodb?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC
# username: root
# password: root
cloud:
config:
#config客户端配置,和ConfigServer通信,并告知ConfigServer希望获取的配置信息在哪个文件中
name: config #前缀 eg:application-dev (application是前缀,dev是后缀)
profile: test #后缀名称
label: master #分支名称
uri: http://localhost:9400 #ConfigServer配置中心地址
eureka:
client:
serviceUrl:
defaultZone: http://localhost:7001/eureka/
register-with-eureka: false
instance:
prefer-ip-address: true
#management:
# endpoint:
# refresh:
# enabled: true
management:
endpoints:
web:
exposure:
include: refresh
创建配置的服务端config-server
其application.yml
eureka:
client:
serviceUrl:
defaultZone: http://eureka1:7001/eureka/,http://eureka2:7002/eureka/
register-with-eureka: true
instance:
prefer-ip-address: true
spring:
application:
name: config-server
# 下载rabbitmq,这里在linux中docker下载,官网下载 rabbitmq.com
rabbitmq:
host: 39.106.35.116
port: 5672
username: guest
password: guest
cloud:
config:
server:
git:
uri: ###地址
username: ###用户名
password: ###密码
# search-paths:
# - config
label: master
server:
port: 9400
management:
endpoints:
web:
exposure:
include: "*"
启动类
@SpringBootApplication
@EnableEurekaClient
@EnableConfigServer
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
}