记一次生产环境上线的经历

最近写了一个项目,也是两三年没写后端的我再次尝试后端项目,在开发和部署的过程中,都遇到了许多奇奇怪怪的问题,
记录下来,也算是对这段时间惊心动魄经历的一次总结吧。

K8s容器内部时间错误

项目上线后,是使用Docker+K8s进行运维和部署的,但是测试环境是普通ECS主机。测试环境一切正常,
但是在生产环境出现了订单时间出现了错误,起初是认为自己项目时间没有做"UTC+8"处理,或者是mysql连接时
未指定时区。不过这两点检查之后,都没有问题。最后把目光放到了容器里。

在测试机使用命令date出现的时间是这样的:

1
Fri Apr 30 04:53:38 UTC 2021

但是在K8s那边检查时间,出来的时间是:

1
Fri Apr 30 04:53:38 EDT 2021

如果容器的时间出现问题,那么就很好解释为什么存到数据库里的时间出现问题了。因为运维的同学不在国内,所以他的时区也不是UTC+8,就没有发现这个问题。
时间调整好后,问题消失。

最后补充一下常见的几个时区吧:

  • GMT,即格林尼治标准时间,也就是世界时。
  • UTC,即协调世界时。现在用的最多的也是UTC。
  • CST,中国标准时间(China Standard Time) GMT + 8 = UTC + 8 = CST
    提示:前提系统的区域设置为Asia/Shanghai,CST还有其他释义,是与区域有关。
  • EDT,美国东部夏令时间,波士顿、纽约市、华盛顿哥伦比亚特区,都在这个时区内,跟北京时间有12小时的时差,晚12小时。

关联问题:

由于容器时间错误,所以导致微信支付SDK SSL异常,报错401。如果有集成微信支付项目启动时报错401,就需要检查下系统时间和时区是否正确了。


ApplicationRunner里面不允许执行死循环操作,需要独立线程

由于使用Redis的Zset来做订单超时队列(轻量实现),在启动项目的时候,需要启动一个死循环线程来执行zRangeWithScores()
但是发现不是所有继承ApplicationRunner的类都会被启动,这是什么原因呢,难道是没有指定顺序么?
经查阅,找到了控制顺序的注解@Order(1)里面的数字则为具体启动的顺序。示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
@Component
@Order(3)
public class ApplicationRunnerImpl implements ApplicationRunner {

Logger logger = LoggerFactory.getLogger(this.getClass());

@Override
public void run(ApplicationArguments args) throws Exception {
logger.info("====== Service All Init Complete ======");
}
}

经过调整将死循环任务放在最后一个执行可以解决此问题,但是治标不治本,查看下源码实现可以看到:
他是通过foreach遍历每一个实现ApplicationRunner的类,执行每一个类的内容来实现的,那么不可以在ApplicationRunner里面出现死循环就很好理解了,我们创建一个线程就可以解决此问题了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//SpringApplication.class
private void callRunners(ApplicationContext context, ApplicationArguments args) {
List<Object> runners = new ArrayList();
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
AnnotationAwareOrderComparator.sort(runners);
Iterator var4 = (new LinkedHashSet(runners)).iterator();

while(var4.hasNext()) {
Object runner = var4.next();
if (runner instanceof ApplicationRunner) {
this.callRunner((ApplicationRunner)runner, args);
}

if (runner instanceof CommandLineRunner) {
this.callRunner((CommandLineRunner)runner, args);
}
}

}

Maven镜像源地址可以配阿里的

由于K8s每次构建镜像速度都是纯净环境从零构建的,每次都需要重新拉取一遍依赖,但是由于网速限制,拉取占用了太多时间。
于是将镜像源配置为阿里的源,这样使用阿里的K8s服务时,构建速度会快很多。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<repositories>
<repository>
<id>maven-ali</id>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
<updatePolicy>always</updatePolicy>
<checksumPolicy>fail</checksumPolicy>
</snapshots>
</repository>
</repositories>

JSON变成了XML

由于微信的SDK接入比较复杂,所以使用了一个简单的库来解决这个问题payment-spring-boot-starter但是引入之后,返回的内容都默认变成了XML格式。
经过排查,是由于这个库引入了jackson-dataformat-xml导致的,那么我们直接移除它就可以了。

1
2
3
4
5
6
7
8
9
10
11
<dependency>
<groupId>cn.felord</groupId>
<artifactId>payment-spring-boot-starter</artifactId>
<version>1.0.8.RELEASE</version>
<exclusions>
<exclusion>
<artifactId>jackson-dataformat-xml</artifactId>
<groupId>com.fasterxml.jackson.dataformat</groupId>
</exclusion>
</exclusions>
</dependency>
#
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×