SpringCloud-第二章-配置文件

SpringCloud-第二章-配置文件

1.配置文件

在SpringBoot构建完之后,会在resource目录下创建一个application.properties文件,这是一个空文件。SpringBoot应用默认就会读取application.properties文件,但我们可以修改为application.yml,个人建议使用yml,因为YMAL的风格会更加适合配置文件。两者的功能上是一致的,只是语法不同,本文不重点介绍语法。

##2.实战

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105

@Configuration
@EnableConfigurationProperties
public class ConditionConfig {

@Bean
public GatewayProperties gatewayProperties() {
return new GatewayProperties();
}
}

@Validated
@ConfigurationProperties(prefix = "spring.cloud.gateway")
public class GatewayProperties {
@NotNull
@Valid
private List<RouteDefinition> routes = new ArrayList<>();

public void setRoutes(List<RouteDefinition> routes) {
this.routes = routes;
}

@PostConstruct
private void init() {
System.out.println(JSON.toJSONString(this));
}
}

@Validated
public class PredicateDefinition {
@NotNull
private String name;

private Map<String, String> args = new LinkedHashMap<>();

public PredicateDefinition(){}
public PredicateDefinition(String text) {
int eqIdx = text.indexOf('=');
if (eqIdx <= 0) {
throw new ValidationException("Unable to parse PredicateDefinition text '"
+ text + "'" + ", must be of the form name=value");
}
setName(text.substring(0, eqIdx));

String[] args = tokenizeToStringArray(text.substring(eqIdx + 1), ",");

for (int i = 0; i < args.length; i++) {
this.args.put(NameUtils.generateName(i), args[i]);
}
}
public void setName(String name) {
this.name = name;
}

public void setArgs(Map<String, String> args) {
this.args = args;
}
}

public class RouteDefinition {
@NotEmpty
private String id;

@NotEmpty
@Valid
private List<PredicateDefinition> predicates = new ArrayList<>();

@NotNull
private URI uri;
private int order = 0;
public RouteDefinition(){}

public RouteDefinition(String text) {
int eqIdx = text.indexOf('=');
if (eqIdx <= 0) {
throw new ValidationException("Unable to parse RouteDefinition text '" + text
+ "'" + ", must be of the form name=value");
}

setId(text.substring(0, eqIdx));

String[] args = tokenizeToStringArray(text.substring(eqIdx + 1), ",");

setUri(URI.create(args[0]));

for (int i = 1; i < args.length; i++) {
this.predicates.add(new PredicateDefinition(args[i]));
}
}
public void setId(String id) {
this.id = id;
}

public void setPredicates(List<PredicateDefinition> predicates) {
this.predicates = predicates;
}

public void setUri(URI uri) {
this.uri = uri;
}

public void setOrder(int order) {
this.order = order;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
spring:
cloud:
gateway:
routes:
- id: yml-id
uri: www.baidu.com
order: -1
predicates:
- name: predicatesName
args:
arg0: 1
arg1: 2
- Path=/echo

以上代码实现用yml文件,映射到类GatewayProperties中。最终的打印结果是

1
{"routes":[{"id":"yml-id","order":-1,"predicates":[{"args":{"arg0":"1","arg1":"2"},"name":"predicatesName"}],"uri":"www.baidu.com"}]}

细心的同学可能会注意到@EnableConfigurationProperties、@ConfigurationProperties(prefix = “spring.cloud.gateway”)、@Validated 这三个很重要的注解。是的,如果想实现配置文件直接映射成Bean,@EnableConfigurationProperties和@ConfigurationProperties是必须要配置的,prefix=”spring.cloud.gateway”代表从spring.cloud.gateway开始采集配置。

@Validated 是校验配置的工具注解,除了代码中应用到的注解外,其它常见注解:

  • @AssertFalse 校验false
  • @AssertTrue 校验true
  • @DecimalMax(value=,inclusive=) 小于等于value,inclusive=true,是小于等于
  • @DecimalMin(value=,inclusive=) 与上类似
  • @Max(value=) 小于等于value
  • @Min(value=) 大于等于value
  • @NotNull 检查Null
  • @Past 检查日期
  • @Pattern(regex=,flag=) 正则
  • @Size(min=, max=) 字符串,集合,map限制大小
  • @Validate 对po实体类进行校验
1
- Path=/echo

这行代码有些不好理解,其实就是RouteDefinition 有两个构造函数,这句配置的意思是调用了有参数的构造函数来定义RouteDefinition对象,使得构建网关的配置更加简洁。

##3.总结

以上的内容,是通过阅读SpringCloudGateway源代码,发现实现方式很优雅,便总结下来。利用好SpringBoot的配置,可以写出很优雅的代码,提高维护性。

SpringCloud-第一章 入门

SpringCloud-第一章 入门

单体应用架构 vs 微服务架构

​ 在说SpringCloud之前,首先要陈述一下单体应用架构和微服务架构。

​ 一个归档包,包含所有功能的应用程序叫单体应用。在系统初期或者系统本身的复杂度不高,采用单体应用架构是合理的。它比较容易部署、维护、测试,大多数功能的实现,采用函数本地调用,所以说从架构合理性和性能角度考量,都是一个合理的方案。

​ 但随着系统的复杂度越来越高,引入越来越多的功能,技术团队扩张,越来越多的人加入到系统的研发过程中。慢慢的,单体应用变得越来越臃肿,可维护性、灵活性逐渐降低,如果没有良好的编程规范约束和工程规范,一定会留下不少的技术债务(俗称”埋雷”),以上这些问题(当然还有其他的问题)共同导致了维护成本越来越高。

​ 以上这些问题,相信大家在工作中,一定会经常碰到。那么如何解决单体应用架构的问题呢?微服务应运而生,微服务架构风格是一种将一个单一应用程序开发为一组小型服务的方法,每个服务都运行在自己的进程中,服务间采用轻量级通信机制,一系列独立运行的微服务共同构建起整个系统。

​ 微服务虽然有很多吸引人的地方,但它并不是免费的午餐,使用它是有代价的。

​ 1、运维成本较高:更多的服务,意味着更多的运维投入。在单体架构中,只需要保证一个应用的正常运行。而在微服务中,需要保证几十甚至几百个服务正常运行与协作,这给运维带来了很大的挑战。

​ 2、分布式固有的复杂性:使用微服务架构即是分布式系统。对于一个分布式系统,系统容错、网络延迟、分布式事务等都会带来巨大的挑战。

​ 3、接口调整成本高:微服务之间通过接口通信,如果修改某一个微服务的API,可能所有使用了该接口的微服务都需要做调整。

​ 4、重复劳动:很多服务可能都会使用到相同的功能,而这个功能并没有达到分解为一个微服务的程度,这个时候,可能各个服务都会开发这一功能,从而导致代码重复。

​ 虽然微服务有如上的缺点,但是瑕不掩瑜,为了更好的应用微服务,可以选择SpringCloud作为开发框架。

SpringCloud

​ 上一章讲述了微服务的产生背景和微服务的优势和劣势,本章讲述应用SpringCloud生态,如何合理的搭建一套健壮,好维护的微服务的系统。

SpringBoot

​ SpringCloud是基于SpringBoot构建的,因此它延续了SpringBoot的契约模式以及开发方式。SpringBoot的出现,解决了Java应用配置繁杂的缺点,整合了最优秀的中间件,使得基于Java的应用开发和部署十分简单。

Eureka 服务中心

​ 没有服务中心,那么服务的调用必须采用硬编码的模式,那么每次服务的扩容,缩容,新的服务上线,都需要所有的服务上线,这显然是不合理的。

​ Eureka是一款开源的服务发现组件,https://github.com/Netflix/eureka

​ Eureka的功能:

​ 1、Eureka集群

​ 2、Eureka用户认证

​ 3、Eureka自我保护模式

​ 4、Eureka健康检查

Eureka最佳实践

如下图所示,要关注如下的参数

Renews threshold: 这个指标的计算是应用的数量int(count 2 0.85),图中实例正好是三个eureka-server实例,故而结果为5。

Renews(last min): 这个指标的含义是一分钟内接收到的心跳数量。如果renews(last min) < Renews threshold,则会触发Eureka的自我保护机制。

Instances currently registered with Eureka: 展示此时注册中心管理的应用及实例数量

Ceneral Info:最重要的是registered-replicas,available-replicas, 正常情况下unavailable-replicas是不应该有值的,如果有值,说明有replica不能使用。

eureka最佳实践

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
server:
port: 8760
spring:
profiles:
active: peer0
application:
name: eureka-server
eureka:
server:
eviction-interval-timer-in-ms: 3000 #eureka server清理无效节点的时间间隔,默认60000毫秒,即60秒
responseCacheUpdateIntervalMs: 3000 #eureka server刷新readCacheMap的时间,注意,client读取的是readCacheMap,这个时间决定了多久会把readWriteCacheMap的缓存更新到readCacheMap上
enable-self-preservation: true #是否开启自我保护,默认为true
client:
registry-fetch-interval-seconds: 5 #表示eureka client间隔多久去拉取服务注册信息,默认为30秒
healthcheck:
enabled: true #开启健康检查(依赖spring-boot-starter-actuator)
serviceUrl:
defaultZone: http://eureka1:8761/eureka/,http://eureka2:8762/eureka/
dashboard:
path: /eurekaui #eureka ui的路径
instance:
hostname: eureka0
lease-expiration-duration-in-seconds: 15 #该值至少应该大于leaseRenewalIntervalInSeconds,目前设置的是leaseRenewalIntervalInSeconds三倍,相当于三次心跳如果都没有则进行服务摘除
lease-renewal-interval-in-seconds: 5 #表示eureka client发送心跳给server端的频率
preferIpAddress: false
ribbon:
ServerListRefreshInterval: 1000
##prometheusj
management:
metrics:
export:
prometheus:
enabled: true
step: 1m
descriptions: true
endpoint:
metrics:
enabled: true
prometheus:
enabled: true
health:
show-details: always
endpoints:
web:
exposure:
include: "*"

Ribbon 实现客户端负载均衡

​ Ribbon是Netflix发布的负载均衡器。Ribbon的github地址,https://github.com/Netflix/ribbon

​ 在Spring Cloud中,当Ribbon与Eureka配合使用时,Ribbon可自动从Eureka Server获取服务提供者地址列表,并基于负载均衡算法,请求其中一个服务提供者实例。

Feign 实现声明式REST调用

Feign是Netflix开发的声明式、模板化的HTTP客户端,其灵感来自Retrofit,JAXRS-2.0以及WebSocket。Feign可帮助我们更加便捷、优雅的调用HTTP API。https://github.com/OpenFeign/feign

Hystrix 微服务容错处理

Hystrix是Netflix开源的一个延迟和容错库,用于隔离访问远程系统、服务或者第三方库,防止级联失败,从而提升系统的可用性和容错性。

SpringCloudGateway 网关服务

https://github.com/spring-cloud/spring-cloud-gateway

Apollo 配置中心

https://github.com/ctripcorp/apollo

Sleuth和Zipkin实现微服务跟踪

https://github.com/spring-cloud/spring-cloud-sleuth