搭建Spring Cloud+Dubbo
公司要测试一下zipkin是否可以跟踪全流程,项目的架构比较复杂,不要问我为什么,基本架构如下:前端门户,调用spring cloud组件,spring cloud在调用dubbo,这样一套流程。于是在spring cloud以及dubbo搭建过程中的所坑所思所想,记录一下。
ZUUI配置服务出错
不知为何在ZUUI里面配置serviceId无法从Eureka里面获取服务名称;但是如果配置为url,则可以跳转。
问题:为什么服务没有配通,怎么配服务,serviceId是对应Eureka里面的Application Name吗?
拿“http://localhost:8083/springcloudapp/hello/JIm”来举例,其实springcloudapp部分其实服务部分,即你想要请求的服务(或者说服务所在机器的IP:Port,这些信息在Eureka里面存放,交给ZuuL就行了),/hello/Jim才是真正的要IP地址之后的请求信息。但是发现如果是服务部分是服务名称(Eureka里面的Application)没有问题,但是但是假如是下面的定义方式,路径转服务,则失败;
1 zuul: 3 routes: 5 hello: 7 path: /hello/* 9 serviceId: springcloudapp
后来调研发现原来path路径一个“*”只能匹配一个"/"内容,想要匹配多个“/"内容,需要添加"**",才可以匹配上http://localhost:8083/springcloudapp/hello/JIm;这种匹配方式是参考了Ant的匹配规则
zuul: routes: hello: path: /hello/** serviceId: springcloudapp
问题解决。
问题:为什么ZUUI没有在Eureka里面注册?
添加一下依赖即可。
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency>
其实经过配置,只要在日志中看到如下的内容,及说明配置注册到Eureka是生效的:
demoService为空
原来需要把consumer.xml文件放到WEB-INF下面,然后在web.xml中进行配置(在servlet节点下配置param-value);这里其实有一个问题,就是application和WEB工程之间的差别,就是Spring配置文件的放置;application有默认放置就是class-path下面;但是对于web工程,则需要进行显式的声明(或者默认是servlet-name同名的.xml文件)。也就是说配置文件编译的路径有差异;
1 <servlet> 2 <servlet-name>spring-webmvc</servlet-name> 3 <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> 4 <load-on-startup>1</load-on-startup> 5 <init-param> 6 <param-name>contextConfigLocation</param-name> 7 <param-value>/WEB-INF/consumer.xml,/WEB-INF/spring-webmvc-servlet.xml</param-value> 8 </init-param> 9 </servlet>
除此之外工程引用上也有区别,体现在application的工程只需要在pom文件中引用对应的内容;但是对于web工程处理在pom文件中需要处理(用于保证代码编译不出错),还需要在asembly deployment中添加工程引用,才能保证运行是能够正常找到类文件。
其实到了最后你会发现,consumer.xml和spring-webmvc-servlet.xml其实可以合并为一个,关键是要理解xml里面的标签,比如xml文件中声明dubbo标签(例如,dubbo:annotation),你就需要在beans中声明dubbo的命名空间xmlns:dubbo="http://code.alibabatech.com/schema/dubbo";当然这个网址是否存在并不重要;但是xml解析器将会校验所有的出现的标签需要在beans中声明其命名空间。之所以很多时候spring-mvc.xml和applicationContext会分开更多的是出于清晰的原因,否则其实所有的spring的配置文件都可以捏在一起。
添加了zipkin之后,dubbo的web-consumer报错:
Invalid bean definition with name 'tracing' defined in ServletContext resource [/WEB-INF/applicationContext.xml]: Could not resolve placeholder 'zipkin.dubbo-consumer-web' in string value "${zipkin.dubbo-consumer-web}"; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'zipkin.dubbo-consumer-web' in string value "${zipkin.dubbo-consumer-web}"
直接写名字不再用${}的形式。
java.lang.ClassNotFoundException: org.springframework.web.servlet.DispatcherServlet
惊讶于竟然是因为在Deployment Assembly中,把maven Dependency路径给删掉!重新添加上,问题解决。这个是在google上的stackoverflow中找到的答案。整整调了我大概一个小时。好像是说对pom文件做了比较大的改动之后,Deployment assembly这个引用就会丢失。
log4j的2.0版本下载失败;
但是我的pom文件里面并没有引用log4j,parent的pom文件里面使用的是log4j的1.2.7版本;后来发现:我在properties里面定义了<log4j.version>2.10.0</log4j.version>这样的内容;其实只是靠过来的有腐臭的配置而已;但是也不行,他会自己去找并认为是maven中的一项,因为下载失败而报错。
发现dubbo的web工程并没有整合zipkin的日志。
我推测可能是因为web.xml里面没有配置filter导致,添加了filter,后又报错,说是需要添加contextListener,它两是同步添加的吗?研究了一下代码是因为DelegateFilter(brave里面的默认Filter)将会调用spring的WebApplicationContextUtils.getRequiredWebApplicationContext去获取Context,在Context里面将会要求配置context listener,没配置将会报错。
添加上了listener和filter之后可以正常输出日志。
Eureka启动的时候报错tomcat异常Address already in use: bind/Tomcat connector in failed state
这种内嵌tomcat的机制不是spring cloud,其实是spring boot的机制;除了内嵌tomcat,还内置了jetty;后来通过netstat -ano | findstr 8761(8761是Eureka的监听端口)发现确实还在被监听;通过任务管理器通过PID找到了对应的进程删除,问题解决。
在spring cloud的AppServer里面调用restTemplate报错:java.lang.IllegalStateException: No instances available for 127.0.0.1
这个异常是因为我在代码总restTemplate是通过autowired方式获取实例的,这种方式restTemplate已经绑定到了Ribbon上面去了;可以通过直接实例化restTemplate的方式来获取restTempate,不再使用@autowired。
但是,现在的问题是,什么是ribbon?
Spring cloud提供的负载均衡组件;当两个注册服务同属于一个服务的时候,在客户端通过ribbon进行控制,第一次访问A设备,第二次访问B设备。
springCloud在添加zipkin发生了问题,就是配置问题;appServer不是web工程,很多之前基于web工程的配置都用上。
我突然想到其实spring cloud原生应该就该支持zipkin的;在网上一搜果然如此。我下载了一份源码,其实只要添加如下依赖即可。
1 <dependency> 2 <groupId>org.springframework.cloud</groupId> 3 <artifactId>spring-cloud-starter-sleuth</artifactId> 4 </dependency> 5 <dependency> 6 <groupId>org.springframework.cloud</groupId> 7 <artifactId>spring-cloud-starter-zipkin</artifactId> 8 </dependency> 9 <dependency> 10 <groupId>org.springframework.boot</groupId> 11 <artifactId>spring-boot-starter-data-rest</artifactId> 12 </dependency> 13 <dependency> 14 <groupId>org.springframework.boot</groupId> 15 <artifactId>spring-boot-starter-web</artifactId> 16 </dependency> 17 <dependency> 18 <groupId>org.springframework.boot</groupId> 19 <artifactId>spring-boot-starter-test</artifactId> 20 <scope>test</scope> 21 </dependency>
然后还要在配置文件(yaml或者properties文件)中添加:spring.zipkin.base-url=http://localhost:9411/;
在调查的过程中还发现zipkin也可以作为一个单独的工程出现,然后所有的对于zipkin的配置都可以在这个工程中进行配置。
ClassNotFound: org.springframework.boot.autoconfigure.condition.ConditionMessage
发现是因为少包,spring-boot-autoconfigure,于是添加了这个包的依赖,但是没有写版本;结果发现了下面的异常;
NoSuchMethodException: org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer
因为autoconfig默认是1.2.8版本的;所以我觉得可能是因为版本问题,于是直接升到最新的是2.0x,但是不行,改为爆了上面的异常,XX方法找不到,之后就是N次降版本尝试。无解啊,于是我又拿下了svn的上面的demo,照样搞了一下,编译是成功了,但是zipkin没有反应。
后来我觉得很可能是springcloud配置问题,我又从网上下载了一套spring cloud的demo源码,发现可用。值得深思。
问题:为什么公司项目的@autowired不好用,无法实例化restTemplate?
未解决
参考
https://www.cnblogs.com/brant/p/6298342.html
https://blog.csdn.net/qq_26562641/article/details/53336899#reply
https://blog.csdn.net/caicongyang/article/details/52974406
http://www.baeldung.com/zuul-load-balancing
https://blog.csdn.net/noaman_wgs/article/details/70214612
https://github.com/ameizi/dubbo-example
spring cloud zip的demo
https://howtodoinjava.com/spring/spring-cloud/spring-cloud-zipkin-sleuth-tutorial/
ZUUL的配置到Eureka服务发现
https://blog.csdn.net/zhanglh046/article/details/78651914