前言
此文只是记录在实践 Spring Cloud构建微服务架构:分布式服务跟踪(整合zipkin)【Dalston版】过程中遇到问题的解决过程,由于文章是1.x版本,我使用的为最新2.1.7.RELEASE版本,在后续解决问题查资料过程中了解到,关于 Zipkin 的服务端,在使用 Spring Boot 2.x 版本后,官方就不推荐自行定制编译了,反而是直接提供了编译好的 jar 包来给我们使用,详情请看 upgrade to Spring Boot 2.0 NoClassDefFoundError UndertowEmbeddedServletContainerFactory · Issue #1962 · openzipkin/zipkin · GitHub。所以如果是实践Spring Cloud2 Zipkin集成的话,可参考如下资料:
spring boot 2.0.3+spring cloud (Finchley)7、服务链路追踪Spring Cloud Sleuth
springboot新版本(2.1.0)、springcloud新版本(Greenwich.M1)实现链路追踪的一些坑
如下内容作为记录,大家如果碰上相关异常,能参考一二即好
一、组件引入配置
Spring Cloud2 Zipkin集成实践Spring Cloud构建微服务架构:分布式服务跟踪(整合zipkin)【Dalston版】,pom.xml配置如下:
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.1.7.RELEASE
com.dreamer.demo.springcloud
zipkin-server
jar
0.0.1-SNAPSHOT
zipkin-server Maven Webapp
http://maven.apache.org
11
11
UTF-8
UTF-8
11
Greenwich.SR2
2.12.9
io.zipkin.java
zipkin-server
${zipkin.version}
io.zipkin.java
zipkin-autoconfigure-ui
${zipkin.version}
zipkin-server
二、web应用服务未集成异常
启动报如下异常:
org.springframework.context.ApplicationContextException: Unable to start web server; nested exception is org.springframework.context.ApplicationContextException: Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean.
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:156) ~[spring-boot-2.1.7.RELEASE.jar:2.1.7.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:543) ~[spring-context-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:141) ~[spring-boot-2.1.7.RELEASE.jar:2.1.7.RELEASE]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:743) [spring-boot-2.1.7.RELEASE.jar:2.1.7.RELEASE]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:390) [spring-boot-2.1.7.RELEASE.jar:2.1.7.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:312) [spring-boot-2.1.7.RELEASE.jar:2.1.7.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1214) [spring-boot-2.1.7.RELEASE.jar:2.1.7.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1203) [spring-boot-2.1.7.RELEASE.jar:2.1.7.RELEASE]
at com.dreamer.Application.main(Application.java:13) [classes/:?]
Caused by: org.springframework.context.ApplicationContextException: Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean.
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.getWebServerFactory(ServletWebServerApplicationContext.java:203) ~[spring-boot-2.1.7.RELEASE.jar:2.1.7.RELEASE]
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.createWebServer(ServletWebServerApplicationContext.java:179) ~[spring-boot-2.1.7.RELEASE.jar:2.1.7.RELEASE]
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:153) ~[spring-boot-2.1.7.RELEASE.jar:2.1.7.RELEASE]
... 8 more
查看异常信息 Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean
【由于缺少ServletWebServerFactory bean,无法启动ServletWebServerApplicationContext】
我们知道spring boot默认应用服务器是tomcat,这里说缺少,查找包依赖,确实是没有引入tomcat,把spring-boot-starter-tomcat引入,重新启动,上面异常解决。
三、'armeriaServer' 初始实例化空指针
但有新的异常,如下:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'armeriaServer' defined in class path resource [com/linecorp/armeria/spring/ArmeriaAutoConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.linecorp.armeria.server.Server]: Factory method 'armeriaServer' threw exception; nested exception is java.lang.NullPointerException
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:627) ~[spring-beans-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:607) ~[spring-beans-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1321) ~[spring-beans-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1160) ~[spring-beans-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555) ~[spring-beans-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515) ~[spring-beans-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320) ~[spring-beans-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) ~[spring-beans-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318) ~[spring-beans-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) ~[spring-beans-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:845) ~[spring-beans-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:877) ~[spring-context-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:549) ~[spring-context-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:141) ~[spring-boot-2.1.7.RELEASE.jar:2.1.7.RELEASE]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:743) [spring-boot-2.1.7.RELEASE.jar:2.1.7.RELEASE]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:390) [spring-boot-2.1.7.RELEASE.jar:2.1.7.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:312) [spring-boot-2.1.7.RELEASE.jar:2.1.7.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1214) [spring-boot-2.1.7.RELEASE.jar:2.1.7.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1203) [spring-boot-2.1.7.RELEASE.jar:2.1.7.RELEASE]
at com.dreamer.Application.main(Application.java:13) [classes/:?]
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.linecorp.armeria.server.Server]: Factory method 'armeriaServer' threw exception; nested exception is java.lang.NullPointerException
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185) ~[spring-beans-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:622) ~[spring-beans-5.1.9.RELEASE.jar:5.1.9.RELEASE]
... 19 more
Caused by: java.lang.NullPointerException
at zipkin2.autoconfigure.ui.ZipkinUiAutoConfiguration.lambda$uiServerConfigurator$0(ZipkinUiAutoConfiguration.java:179) ~[zipkin-autoconfigure-ui-2.12.9.jar:?]
at com.linecorp.armeria.spring.ArmeriaAutoConfiguration.lambda$armeriaServer$0(ArmeriaAutoConfiguration.java:111) ~[armeria-spring-boot-autoconfigure-0.83.0.jar:?]
at java.util.ArrayList.forEach(ArrayList.java:1540) ~[?:?]
at com.linecorp.armeria.spring.ArmeriaAutoConfiguration.lambda$armeriaServer$1(ArmeriaAutoConfiguration.java:110) ~[armeria-spring-boot-autoconfigure-0.83.0.jar:?]
at java.util.Optional.ifPresent(Optional.java:183) ~[?:?]
at com.linecorp.armeria.spring.ArmeriaAutoConfiguration.armeriaServer(ArmeriaAutoConfiguration.java:109) ~[armeria-spring-boot-autoconfigure-0.83.0.jar:?]
at com.linecorp.armeria.spring.ArmeriaAutoConfiguration$$EnhancerBySpringCGLIB$$7013b1d3.CGLIB$armeriaServer$0() ~[armeria-spring-boot-autoconfigure-0.83.0.jar:?]
at com.linecorp.armeria.spring.ArmeriaAutoConfiguration$$EnhancerBySpringCGLIB$$7013b1d3$$FastClassBySpringCGLIB$$893247fb.invoke() ~[armeria-spring-boot-autoconfigure-0.83.0.jar:?]
at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:244) ~[spring-core-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:363) ~[spring-context-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at com.linecorp.armeria.spring.ArmeriaAutoConfiguration$$EnhancerBySpringCGLIB$$7013b1d3.armeriaServer() ~[armeria-spring-boot-autoconfigure-0.83.0.jar:?]
at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:?]
at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:?]
at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:?]
at java.lang.reflect.Method.invoke(Method.java:566) ~[?:?]
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154) ~[spring-beans-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:622) ~[spring-beans-5.1.9.RELEASE.jar:5.1.9.RELEASE]
... 19 more
此处异常信息为:Error creating bean with name 'armeriaServer' defined in class path resource [com/linecorp/armeria/spring/ArmeriaAutoConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.linecorp.armeria.server.Server]: Factory method 'armeriaServer' threw exception; nested exception is java.lang.NullPointerException
从异常信息可以看出创建 armeriaServer 失败,有 java.lang.NullPointerException,但具体是哪里导致NullPointerException,在异常信息上看不出来,于是想到把日志设置为DEBUG,打印详细日志,看能否查看到具体java.lang.NullPointerException原因。
四、添加logback配置
添加logback-spring.xml文件至项目resources中,配置内容如下:
logback
%d{HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{36} - %msg%n
${log.path}
logback.%d{yyyy-MM-dd}.log
%d{HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{36} - %msg%n
重新启动项目,未见打印DEBUG信息。于是想到是否未引入/集成logback组件,但spring boot web默认日志组件为logback,经查引入依赖,确实是未有logback的jar。也想到上面我们未引入spring-boot-starter-web。
五、引入logback组件
于是我把spring-boot-starter-tomcat去掉【这个是spring-boot-starter-web默认引入】,引入spring-boot-starter-web,pom.xml配置如下:
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.1.7.RELEASE
com.dreamer.demo.springcloud
zipkin-server
jar
0.0.1-SNAPSHOT
zipkin-server Maven Webapp
http://maven.apache.org
11
11
UTF-8
UTF-8
11
Greenwich.SR2
2.12.9
org.springframework.boot
spring-boot-starter-web
io.zipkin.java
zipkin-server
${zipkin.version}
io.zipkin.java
zipkin-autoconfigure-ui
${zipkin.version}
zipkin-server
重启项目,DEBUG日志正常输出,而且有看到 java.lang.NullPointerException 原因,详细异常如下:
16:19:45.452 logback [main] DEBUG o.s.b.diagnostics.FailureAnalyzers - FailureAnalyzer org.springframework.boot.autoconfigure.jdbc.HikariDriverConfigurationFailureAnalyzer@a826ff8 failed
java.lang.TypeNotPresentException: Type org.springframework.jdbc.CannotGetJdbcConnectionException not present
at java.base/sun.reflect.generics.factory.CoreReflectionFactory.makeNamedType(CoreReflectionFactory.java:117)
at java.base/sun.reflect.generics.visitor.Reifier.visitClassTypeSignature(Reifier.java:125)
at java.base/sun.reflect.generics.tree.ClassTypeSignature.accept(ClassTypeSignature.java:49)
at java.base/sun.reflect.generics.visitor.Reifier.reifyTypeArguments(Reifier.java:68)
at java.base/sun.reflect.generics.visitor.Reifier.visitClassTypeSignature(Reifier.java:138)
at java.base/sun.reflect.generics.tree.ClassTypeSignature.accept(ClassTypeSignature.java:49)
at java.base/sun.reflect.generics.repository.ClassRepository.computeSuperclass(ClassRepository.java:104)
at java.base/sun.reflect.generics.repository.ClassRepository.getSuperclass(ClassRepository.java:86)
at java.base/java.lang.Class.getGenericSuperclass(Class.java:950)
at org.springframework.core.ResolvableType.getSuperType(ResolvableType.java:466)
at org.springframework.core.ResolvableType.as(ResolvableType.java:455)
at org.springframework.core.ResolvableType.forClass(ResolvableType.java:1037)
at org.springframework.boot.diagnostics.AbstractFailureAnalyzer.getCauseType(AbstractFailureAnalyzer.java:56)
at org.springframework.boot.diagnostics.AbstractFailureAnalyzer.analyze(AbstractFailureAnalyzer.java:33)
at org.springframework.boot.diagnostics.FailureAnalyzers.analyze(FailureAnalyzers.java:110)
at org.springframework.boot.diagnostics.FailureAnalyzers.reportException(FailureAnalyzers.java:103)
at org.springframework.boot.SpringApplication.reportFailure(SpringApplication.java:812)
at org.springframework.boot.SpringApplication.handleRunFailure(SpringApplication.java:797)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:322)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1214)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1203)
at com.dreamer.Application.main(Application.java:13)
Caused by: java.lang.ClassNotFoundException: org.springframework.jdbc.CannotGetJdbcConnectionException
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:583)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
at java.base/java.lang.Class.forName0(Native Method)
at java.base/java.lang.Class.forName(Class.java:398)
at java.base/sun.reflect.generics.factory.CoreReflectionFactory.makeNamedType(CoreReflectionFactory.java:114)
... 21 common frames omitted
六、添加mysql组件/配置
组件
mysql
mysql-connector-java
8.0.14
org.springframework.boot
spring-boot-starter-jdbc
配置
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://xxx.xxx.xxx.xxx:3306/test?autoReconnect=true&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true
spring.datasource.username=xxxxxx
spring.datasource.password=xxxxxx
重新启动服务,java.lang.TypeNotPresentException: Type org.springframework.jdbc.CannotGetJdbcConnectionException 消失,但org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'armeriaServer' 异常还是存在,这就有点郁闷了,直接根据异常信息查询资料,未见相应的解决方案。哪就简单点,直接撸源代码查下原因。根据以下异常信息:
Caused by: java.lang.NullPointerException
at zipkin2.autoconfigure.ui.ZipkinUiAutoConfiguration.lambda$uiServerConfigurator$0(ZipkinUiAutoConfiguration.java:179) ~[zipkin-autoconfigure-ui-2.12.9.jar:?]
at com.linecorp.armeria.spring.ArmeriaAutoConfiguration.lambda$armeriaServer$0(ArmeriaAutoConfiguration.java:111) ~[armeria-spring-boot-autoconfigure-0.83.0.jar:?]
直接点击进入ZipkinUiAutoConfiguration.java:179,看到代码如下:
Compression compression = compressionProperties.getCompression();
if (compression.getEnabled()) {
sb.decorator(contentEncodingDecorator(compression));
}
报错代码为:if (compression.getEnabled()) { 这一行。一看就可以明白,是 compression 对象为空导致。但为什么会为空?怎么解决,就得看看代码上下文。当前方法完整代码如下:
@Bean @Lazy ArmeriaServerConfigurator uiServerConfigurator(
CompressionProperties compressionProperties,
IndexSwitchingService indexSwitchingService) throws IOException {
ServerCacheControl maxAgeYear =
new ServerCacheControlBuilder().maxAgeSeconds(TimeUnit.DAYS.toSeconds(365)).build();
Service uiFileService =
HttpFileServiceBuilder.forClassPath("zipkin-ui").cacheControl(maxAgeYear).build()
.orElse(
HttpFileServiceBuilder.forClassPath("zipkin-lens").cacheControl(maxAgeYear).build());
byte[] config = new ObjectMapper().writeValueAsBytes(ui);
return sb -> {
sb.service("/zipkin/config.json", HttpFileBuilder.of(HttpData.of(config))
.cacheControl(new ServerCacheControlBuilder().maxAgeSeconds(600).build())
.contentType(MediaType.JSON_UTF_8)
.build()
.asService());
sb.serviceUnder("/zipkin/", uiFileService);
// TODO This approach requires maintenance when new UI routes are added. Change to the following:
// If the path is a a file w/an extension, treat normally.
// Otherwise instead of returning 404, forward to the index.
// See https://github.com/twitter/finatra/blob/458c6b639c3afb4e29873d123125eeeb2b02e2cd/http/src/main/scala/com/twitter/finatra/http/response/ResponseBuilder.scala#L321
sb.service("/zipkin/", indexSwitchingService)
.service("/zipkin/index.html", indexSwitchingService)
.service("/zipkin/traces/{id}", indexSwitchingService)
.service("/zipkin/dependency", indexSwitchingService)
.service("/zipkin/traceViewer", indexSwitchingService);
sb.service("/favicon.ico", new RedirectService(HttpStatus.FOUND, "/zipkin/favicon.ico"))
.service("/", new RedirectService(HttpStatus.FOUND, "/zipkin/"))
.service("/zipkin", new RedirectService(HttpStatus.FOUND, "/zipkin/"));
Compression compression = compressionProperties.getCompression();
if (compression.getEnabled()) {
sb.decorator(contentEncodingDecorator(compression));
}
};
}
经一步步代码查看,可以发现 Compression 对象中有一个属性默认值为false[ private boolean enabled = false;],经查资料这个是配置应用服务器压缩的,详情配置说明可见:SpringBoot配置属性之Server,我试着把此属性值配置为打开,重启服务终于没见异常了,好J-DONG, 配置如下:
server.compression.enabled=true
不过在查资料过程中,从github看到一个配置,我试着配置了下,是可以正常运行的。链接如下:
https://github.com/line/armeria-examples/blob/master/spring-boot-minimal/src/main/resources/config/application.yml
spring.main.web-application-type: none
此配置指定当前spring boot为非web应用,至于为何这样配置?zipkin不就是web应用服务?这里就不讨论
服务启动正常,访问http://127.0.0.1:8080,会跳转至http://127.0.0.1:8080/zipkin/并看到管理界面,K.O
项目源代码详见:
https://gitee.com/showlike/springcloud2-demo/tree/master/zipkin-server
参考资料
SpringBoot 2.0.0新版和SpringBoot1.5.2版本中Tomcat配置的差别