之前在创业公司,产品基于Spring Cloud做了微服务化的改造。在这个过程,因为时间原因我个人没有直接参与技术的引入及整体架构考量,只是简单做了一些基础架构层面的工作,比如Gradle替换Maven,日志和异常的封装处理,notification的统一抽象等。最终微服务化改造后的产品技术架构在我看来就是demo级别的,也就是说只是基于Spring cloud做了一个可以运行的初级产品。
从技术层面来看,包含的问题主要有两方面:
离开这家公司后,一直对这种混乱的微服务架构耿耿于怀,因为毕竟当时自己负责技术而且可以参与技术时出现的这些问题。所以可能是为了弥补心中的遗憾,另外也想再深入的了解一下Spring Cloud框架,就和几个以前的同事,准备基于Spring Cloud框架做一个微服务的最佳实践,说白了,就是想做一个demo,包含应该有的核心模块,并尽可能的给出不同模块的最佳实践。
一般情况下,想让基于微服务的demo能够正常的跑起来。最基本的我们需要构造三个模块:Eureka Server,Config Server,业务模块如订单、支付、产品。那么假设我们有三个人,分别负责其中的一个微服务模块。在本机调试时,自然都要启动Eureka,Config,各自负责的业务模块比如订单。
问题是像Eureka,Config等模块基本不会做太多改变,所以不会像调试Web那样需要频繁的重启。另外本地启动这些模块会占用本机的不少内存,尤其是基于IDE如Intellij IDEA来启动。所以能不能有一种方式开发者不需要在本地管理这些非业务模块,这样就可以把主要时间用在业务模块的开发上。
一个好的方式是将一些非业务相关的微服务模块,如Erueka等放在云端,本地在调试业务模块时,可以通过配置指向云端的这些模块。另外从微服务的切分角度,也可以将这些非业务的模块抽离由一个单独团队维护。
此次微服务上云计划初步只部署Eureka Server和Config server,本地则使用一个订单模块作为测试客户端。Config Server和订单模块作为两个Eureka client注册到Eureka Server。测试成功的条件为在订单模块里能通过Config Server成功读取在Git上的配置文件里的内容。
本地调试时所有配置里需要主机信息均指定为localhost,并按照上述测试可以正常工作。Spring Cloud 版本为Finchley.SR2,Spring Boot版本2.0.6.RELEASE
两台阿里云服务器,一台安装Git服务器(A),并且已同步最新的配置库。另外一台(B)用于安装Eureka Server和Config Server。服务器B的外网地址是:47.100.xxx.xx.
只要demo过Spring Cloud的同学都知道,如果不涉及到高可用,单个的Eureka Server和Config Server代码及配置都是教科书级的,标准而简单。很多书和网上的文章都可以找到相应的信息。在这次部署之前,我也是这么认为的,这是来自于有10+年java经验的老程序员的一份自信,尤其是本地已经完全跑通。
首先本地打包,然后上传到阿里云服务器B,启动后,访问 http://47.100.xxx.xx:8101/, 出现登录页面,填写相应的用户名和密码后进入管理主页面。说明Eureka运行正常。实在太简单,此时没有丝毫的惊喜,只是感觉工作完成了一半,如果按照同样的步骤,Config Server应该会被很快部署完成。
打开Config Server的配置文件里,将指向Eureka的配置的主机地址改成47.100.xxx.xx。之后按照和Eureka同样的步骤,将Config Server部署到同一台机器上,启动后,刷新Eureka的管理界面,发现Config Server已经作为一个服务注册到Eureka。到这里,剩下的就是使用订单微服务模块进行测试,看能不能取到Git上的配置信息。
开始测试订单微服务模块,既然Eureka Server和Config Server都部署在47.100.xxx.8101,那么需要将订单模块的连接Config和Eureka的主机信息都改成此IP,然后在本地启动。结果并不如想象的中的那样,启动失败。如果按照所有问题得到解决的时间轴从后往前看,现在是本次上云多个坑中的第一个。
第一个坑,服务没有启动成功,找不到配置信息。从订单模块启动的控制台可以看到,它尝试连接的Config Server的URL是 http://localhost:8888 。这个错误比较奇怪,明明在配置文件指定了Config Server的配置信息,为什么会连接到本地的8888端口。网上搜索一下,答案比较简单,Config Server的配置信息应该放到bootstrap,以便在应用启动之前加载。
修改后启动,错误如前,查看本地log,这次连接到Config Server的URL是 http://172.19.147.xx:8901 。这时,如果去查看Eureka的管理界面,也会发现同样的问题。这个以172开头的IP地址是什么鬼?登录上服务器B,通过命令ifconfig查看,列表里没有以47开头的公网IP,但是确实能看到以172开头内网IP。这显然不是我们想要的。那么有可能的爬坑方案是在Config Server的配置里指定公网IP作为在Eureka Server注册的实例的访问地址。相应的修改为: eureka.instance.ip-address=47.100.xxx.xx.
修改后再启动,这次连接Config Server的URL没有问题,但是在本地还是读取不到Git上的配置信息,查看配置服务器上的日志文件,发现有异常:Caused by: org.eclipse.jgit.errors.NoRemoteRepositoryException: http://106.14.xx.xxx:8181/spring-cloud-demo/config-repo: http://106.14.xx.xxx:8181/spring-cloud-demo/config-repo/git-upload-pack not found。
倾尽洪荒之力,使用谷歌加百度花了半天时间查找,试过n种方案后,问题依旧。就在准备放弃在网上查找解决方案,转而从源码找答案时,偶然地看到有一篇文章提到,在git的repository后面加上.git的扩展。好吧,再试试,问题还真解决了。
期间,在查看Eureka的日志文件时,发现有一个错误报出来:ERROR c.netflix.eureka.cluster.ReplicationTaskProcessor – Batch update failure with HTTP status code 401; discarding 1 replication tasks。在没有找到最终解决方案时,怀疑可能与这个异常有关系,并且通过google搜到有人在Spring Cloud的官方Github上报一个bug( https://github.com/spring-cloud/spring-cloud-netflix/issues/291 ),但是最终因为官方不能重现,所以这个问题被直接关闭。事实证明在最终调通上述三个模块后,这个异常还是会出现。
此次上云的模块如此简单,但是前前后后还是出现了多个问题。很多事情看起来简单,但是在实操层面会碰到这种那种不同的问题。这也是我们经常会听到一些忠告,做软件开发,无论是工作还是学习,除了理论外,一定要去实践。因为这个过程中会碰到很多问题,每一次的解决都是经验的积累。
一个Demo甚至一个产品以最终交付为目标,完成本地开发测试确实是很重要的一步,但是仅仅走了一小步。后面的打包、部署、集成测试、上线等等都需要付出很大的精力和时间。
从解决问题的角度,需要技术人员的一个综合能力,此次解决问题需要对spring cloud不同模块的配置参数、模块之间的工作原理、部署环境、信息搜索后归纳总结等。