spring cloud从Greenwich升级到Hoxton

版本

project version from version to
spring framework 5.1.x 5.2x
spring boot 2.1.x 2.3.x
spring cloud Greenwich Hoxton

迁移碰到的问题

spring data mongodb sort

  • Sort构造方法稀有话,改用Sort.by。
before
1
Sort sort = new Sort(Sort.Direction.DESC, "createTs");
after
1
Sort.by(Sort.Direction.DESC, "createTs");

spring data mongodb configuration

  • mongodb configuration 大改,并且有一些参数没找到哪里可以配。
before
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
@Configuration
@ConditionalOnClass(MongodbProperties.class)
@ComponentScan(value = {"com.ivc.data.mongodb.**"})
public class MongodbAutoConfiguration {
private static final Log LOGGER = LogFactory.get(MongodbAutoConfiguration.class);

@Bean
public MongoDbFactory mongoDbFactory(MongodbProperties properties) {
// 客户端配置(连接数,副本集群验证)
MongoClientOptions.Builder builder = new MongoClientOptions.Builder();
builder.connectionsPerHost(properties.getMaxConnectionsPerHost());
builder.minConnectionsPerHost(properties.getMinConnectionsPerHost());
if (properties.getReplicaSet() != null) {
builder.requiredReplicaSetName(properties.getReplicaSet());
}
builder
.threadsAllowedToBlockForConnectionMultiplier(properties.getThreadsAllowedToBlockForConnectionMultiplier());
builder.serverSelectionTimeout(properties.getServerSelectionTimeout());
builder.maxWaitTime(properties.getMaxWaitTime());
builder.maxConnectionIdleTime(properties.getMaxConnectionIdleTime());
builder.maxConnectionLifeTime(properties.getMaxConnectionLifeTime());
builder.connectTimeout(properties.getConnectTimeout());
builder.socketTimeout(properties.getSocketTimeout());
builder.sslEnabled(properties.getSslEnabled());
builder.sslInvalidHostNameAllowed(properties.getSslInvalidHostNameAllowed());
builder.alwaysUseMBeans(properties.getAlwaysUseMBeans());
builder.heartbeatFrequency(properties.getHeartbeatFrequency());
builder.minHeartbeatFrequency(properties.getMinHeartbeatFrequency());
builder.heartbeatConnectTimeout(properties.getHeartbeatConnectTimeout());
builder.heartbeatSocketTimeout(properties.getHeartbeatSocketTimeout());
builder.localThreshold(properties.getLocalThreshold());
MongoClientOptions mongoClientOptions = builder.build();

// MongoDB地址列表
List<ServerAddress> serverAddresses = Lists.newArrayList();
for (String address : properties.getAddress()) {
String[] hostAndPort = address.split(":");
String host = hostAndPort[0];
if (hostAndPort.length > 1) {
Integer port = Integer.parseInt(hostAndPort[1]);
ServerAddress serverAddress = new ServerAddress(host, port);
serverAddresses.add(serverAddress);
} else {
ServerAddress serverAddress = new ServerAddress(host);
serverAddresses.add(serverAddress);
}
}

LOGGER.info("serverAddresses:" + serverAddresses.toString());

// 连接认证
MongoCredential mongoCredential;
MongoClient mongoClient;
if (StringUtils.isNotBlank(properties.getUsername())) {
mongoCredential = MongoCredential.createScramSha1Credential(
properties.getUsername(), properties.getAuthenticationDatabase() != null
? properties.getAuthenticationDatabase() : properties.getDatabase(),
properties.getPassword().toCharArray());
mongoClient = new MongoClient(serverAddresses, mongoCredential, mongoClientOptions);
} else {
mongoClient = new MongoClient(serverAddresses, mongoClientOptions);
}

// 创建MongoDbFactory
return new SimpleMongoDbFactory(mongoClient, properties.getDatabase());
}

@Bean
public MongoCustomConversions customConversions() {
List<Converter<?, ?>> converters = new ArrayList<>();
converters.add(new EnumWriterConverter());
converters.add(new EnumReadFactory.EnumReadConverter<>(null));
return new MongoCustomConversions(converters);
}

@Bean
public MongoTemplate mongoTemplate(MongoDbFactory mongoDbFactory, MappingMongoConverter mappingMongoConverter) {
mappingMongoConverter.setMapKeyDotReplacement("__DOT__");

ConversionService conversionService = mappingMongoConverter.getConversionService();
((GenericConversionService)conversionService).addConverterFactory(new EnumReadFactory());
mappingMongoConverter.afterPropertiesSet();

return new MongoTemplate(mongoDbFactory, mappingMongoConverter);
}

@Bean
public MongoPageHelper mongoPageHelper(MongoTemplate mongoTemplate) {
return new MongoPageHelper(mongoTemplate);
}
}
after
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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
@Configuration
@ConditionalOnClass(MongodbProperties.class)
@ComponentScan(value = {"com.ivc.data.mongodb.**"})
public class MongodbAutoConfiguration extends AbstractMongoClientConfiguration {
private final MongodbProperties properties;

public MongodbAutoConfiguration(MongodbProperties properties) {
this.properties = properties;
}

@Nonnull
@Override
protected String getDatabaseName() {
return properties.getDatabase();
}

@Override
protected void configureClientSettings(@Nonnull MongoClientSettings.Builder builder) {
super.configureClientSettings(builder);

builder.applyToServerSettings(b -> b.applySettings(
ServerSettings.builder().minHeartbeatFrequency(properties.getMinHeartbeatFrequency(), TimeUnit.MILLISECONDS)
.heartbeatFrequency(properties.getHeartbeatFrequency(), TimeUnit.MILLISECONDS).build()));

builder.applyToClusterSettings(b -> {
ClusterSettings.Builder clusterSettingsBuilder = ClusterSettings.builder();

if (isReplicaSet()) {
clusterSettingsBuilder.requiredClusterType(ClusterType.REPLICA_SET);
clusterSettingsBuilder.requiredReplicaSetName(properties.getReplicaSet());
} else {
clusterSettingsBuilder.requiredClusterType(ClusterType.STANDALONE);
}

clusterSettingsBuilder.hosts(getServerAddressList());

clusterSettingsBuilder.localThreshold(properties.getLocalThreshold(), TimeUnit.MILLISECONDS);

b.applySettings(clusterSettingsBuilder
.serverSelectionTimeout(properties.getServerSelectionTimeout(), TimeUnit.MILLISECONDS).build());
});

builder.applyToConnectionPoolSettings(b -> b.applySettings(
ConnectionPoolSettings.builder().maxWaitTime(properties.getMaxWaitTime(), TimeUnit.MILLISECONDS)
.maxConnectionIdleTime(properties.getMaxConnectionIdleTime(), TimeUnit.MILLISECONDS)
.maxConnectionLifeTime(properties.getMaxConnectionLifeTime(), TimeUnit.MILLISECONDS).build()));

builder.applyToSslSettings(b -> b.applySettings(SslSettings.builder().enabled(properties.getSslEnabled())
.invalidHostNameAllowed(properties.getInvalidHostNameAllowed()).build()));

builder.applyToSocketSettings(b -> b.applySettings(
SocketSettings.builder().connectTimeout(properties.getConnectTimeout(), TimeUnit.MILLISECONDS)
.readTimeout(properties.getReadTimeout(), TimeUnit.MILLISECONDS).build()));

if (needCredential()) {
builder.credential(getCredential());
}
}

private MongoCredential getCredential() {
return MongoCredential.createScramSha1Credential(properties.getUsername(), getSource(),
properties.getPassword().toCharArray());
}

private String getSource() {
return properties.getAuthenticationDatabase() != null ? properties.getAuthenticationDatabase()
: properties.getDatabase();
}

private boolean needCredential() {
return StringUtils.isNotBlank(properties.getUsername());
}

private boolean isReplicaSet() {
return StringUtils.isNotBlank(properties.getReplicaSet());
}

private List<ServerAddress> getServerAddressList() {
List<ServerAddress> serverAddresses = Lists.newArrayList();
for (String address : properties.getAddress()) {
String[] hostAndPort = address.split(":");
String host = hostAndPort[0];
if (hostAndPort.length > 1) {
int port = Integer.parseInt(hostAndPort[1]);
ServerAddress serverAddress = new ServerAddress(host, port);
serverAddresses.add(serverAddress);
} else {
ServerAddress serverAddress = new ServerAddress(host);
serverAddresses.add(serverAddress);
}
}
return serverAddresses;
}

@Bean
@Nonnull
@Override
public MongoCustomConversions customConversions() {
List<Converter<?, ?>> converters = new ArrayList<>();
converters.add(new EnumWriterConverter());
converters.add(new EnumReadFactory.EnumReadConverter<>(null));
return new MongoCustomConversions(converters);
}

@Bean
@Nonnull
@Override
public MongoTemplate mongoTemplate(@Nonnull MongoDatabaseFactory mongoDatabaseFactory,
MappingMongoConverter mappingMongoConverter) {
mappingMongoConverter.setMapKeyDotReplacement("__DOT__");

ConversionService conversionService = mappingMongoConverter.getConversionService();
((GenericConversionService)conversionService).addConverterFactory(new EnumReadFactory());
mappingMongoConverter.afterPropertiesSet();

return new MongoTemplate(mongoDatabaseFactory, mappingMongoConverter);
}

@Bean
public MongoPageHelper mongoPageHelper(MongoTemplate mongoTemplate) {
return new MongoPageHelper(mongoTemplate);
}
}

openfeign

  • feign增强包导致请求复制头的时候,多复制了content-type导致请求失败。
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
@Configuration
@ConditionalOnClass({Logger.class, RequestInterceptor.class, UniversalReversedEnumConverter.class,
FeignBasicAuthRequestInterceptor.class})
public class FeignAutoConfiguration {
private final List<AnnotatedParameterProcessor> parameterProcessors;

public FeignAutoConfiguration(List<AnnotatedParameterProcessor> parameterProcessors) {
this.parameterProcessors = parameterProcessors;
}

@Bean
public Contract feignContract(FormattingConversionService feignConversionService) {
feignConversionService.addConverter(new UniversalReversedEnumConverter());
return new SpringMvcContract(this.parameterProcessors, feignConversionService);
}

@Bean
public Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}

@Bean
public RequestInterceptor feignRequestInterceptor() {
return requestTemplate -> {
ServletRequestAttributes attributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
if (attributes != null) {
HttpServletRequest request = attributes.getRequest();
Enumeration<String> headerNames = request.getHeaderNames();
if (headerNames != null) {
while (headerNames.hasMoreElements()) {
String name = headerNames.nextElement();
String values = request.getHeader(name);

// 因为body不同,所以不复制
if ("content-length".equalsIgnoreCase(name)) {
continue;
}

requestTemplate.header(name, values);
}
}
}
};
}

@Bean
public FeignBasicAuthRequestInterceptor feignBasicAuthRequestInterceptor() {
return new FeignBasicAuthRequestInterceptor();
}
}

spring boot actuator

  • 2.3 spring boot actuator 会检测连接是否有效,我目前的做法是关闭他,因为sharingsphere会用select 1去检测,
1
2
3
4
management:
health:
db:
enabled: false

application.yml

  • 我们用到的两个配置过期了,迁移至新配置
before
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
#
# -------------------------------------------
# SERVER CONFIGURATION
# -------------------------------------------
#
server:
port: 8407
tomcat:
max-connections: 0
max-threads: 0
#
# -------------------------------------------
# SPRING CONFIGURATION
# -------------------------------------------
#
spring:
log:
prefix: logs
http:
encoding:
force: true
servlet:
multipart:
max-file-size: 200MB
max-request-size: 200MB
datasource:
dynamic:
strict: true
primary: saas
after
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
#
# -------------------------------------------
# SERVER CONFIGURATION
# -------------------------------------------
#
server:
port: 8407
tomcat:
max-connections: 0
threads:
max: 0
servlet:
encoding:
force: true
#
# -------------------------------------------
# SPRING CONFIGURATION
# -------------------------------------------
#
spring:
log:
prefix: logs
servlet:
multipart:
max-file-size: 200MB
max-request-size: 200MB
datasource:
dynamic:
strict: true
primary: saas

todo list

  1. 在k8s中,可用探针替换当前health endpoint api,优化优雅上线(官方blog)。
  2. 在k8s中,优雅下线(baeldun指南)。
  3. oauth2 迁移至 security(官方迁移方案)。