Spring 应用合并之路(一):摸石头过河

京东云开发者
• 阅读 166

作者:京东科技 李君

公司最近一年在推进降本增效,在用尽各种手段之后,发现应用太多,每个应用都做跨机房容灾部署,则最少需要 4 台机器(称为容器更合适)。那么,将相近应用做一个合并,减少维护项目,提高机器利用率就是一个可选方案。



经过前后三次不同的折腾,最后探索出来一个可行方案。记录一下,分享出来,希望对有相关需求的研发童鞋有所帮助。下面按照四种可能的方案,分别做介绍。另外,为了方便做演示,专门整了两个演示项目:



•diguage/merge-demo-boot — 合并项目,下面简称为 boot。

•diguage/merge-demo-web — 被合并项目,下面简称为 web。



一、Jar 包引用



这个方式,可能是给人印象最容易的方式。仔细思考一下,从维护性的角度来看,这个方式反而是最麻烦的方式,理由如下:



1.web 项目每次更新,都需要重新打包发布新版; boot 项目也需要跟着更新发布。拉一次屎,脱两次裤子。属实麻烦。

2.还需要考虑 web 项目的加载问题,类似下面要描述的,是否共用容器:共用容器 — 这是最容器想到的方式。但是这种方式,需要解决 Bean 冲突的问题。不共用容器 — 这种方式需要处理 web 容器如何加载的问题。默认应该是无法识别。

3.

基于这些考虑,这种方式直接被抛弃了。



二、仓库合并,公用一套容器



这是第一次尝试使用的方案。也是遇到问题最多的方案。



1.将两个仓库做合并。

1.将 web 仓库的地址配置到 boot 项目里: git remote add web git@github.com:diguage/merge-demo-web.git;

2.在 boot 项目里,切出来一个分支: git switch -c web;

3.将 web 分支的提交清空: git update-ref -d HEAD,然后做一次提交;

4.将 web 项目的代码克隆到 web 分支上: git pull --rebase --allow-unrelated-histories web master;注意,这里需要加 --allow-unrelated-histories 参数,以允许不相干的仓库进行合并。

5.从 boot 项目的 master 分支上,切出来一个合并分支: git switch -c merge;

6.将 web 项目向 boot 项目合并: git merge --allow-unrelated-histories web;注意,这里需要加 --allow-unrelated-histories 参数,以允许不相干的仓库进行合并。

7.处理代码冲突,完成合并即可。

2.配置文件的合并于归整。为了防止同名配置文件冲突,需要把 web 项目的配置文件调整到一个文件夹下,这里设定为 web 目录。然后,需要把 web 项目的配置文件,让 boot 可以加载到。这个调整相对简单,只需要一个注解即可 @ImportResource({"classpath:web/spring-cfg.xml"})。

3.调整完配置文件,接着遇到的问题就是上面提到的 Bean 冲突的问题。由于两个项目都访问相同的数据库, Dao 及 Service 层很多很多类都是同名的。另外,在 web 项目里,Dao 是基于 iBATIS 开发的,而在 boot 项目里,DAO 是基于 MyBATIS 开发的。所以,只能给 web 项目的相关代码做重命名(严谨一点是给 Spring Bean 的 beanName 做重命名操作)。这又带来了新问题:原来的项目里,注入方式是根据名称注入的,就需要改动大量的代码,给相关的 Bean 变量做重命名操作。这无形中增加了很多的复杂度和不确定性。



经过不断折腾,这种方式被迫放弃。



三、仓库合并,Spring Boot 父子容器



在经过上述方式折腾后,就想到了另外一个方案:可以考虑使用父子容器的方式来搞。接着就查到了这篇文章: Context Hierarchy with the Spring Boot Fluent Builder API。感觉这种方式挺不错,就尝试了一下。



1.代码合并及文件调整,跟上述步骤类似,这个后面就不再赘述。

2.按照文章中的介绍,使用父子容器的方式来加载两个项目。代码如下:

3.原以为,这种方式属于父子两个容器,即使有同名的 Bean 应该也没有影响。但是,经过实践才发现,上面这个猜测是错误的。Spring Boot 在启动的时候,它背后做了检查,如果两个容器有同名的 Bean,它也会报错。也会带来像上述方式那样的大量重命名。折腾一两天,最后还是放弃了这种寄予厚望的方式。

package com.diguage.demo.boot;

import org.springframework.boot.WebApplicationType;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;

/**
 * @author D瓜哥 · https://www.diguage.com
 */
public class DemoBootApplication {

    public static void main(String[] args) {
        new SpringApplicationBuilder()
                .parent(BootConfig.class).web(WebApplicationType.NONE)
                .child(WebConfig.class)
                // 如果有第三个项目,可以作为子容器的兄弟容器加载。
                // .sibling(SiblingConfig.class)
                .run(args);
    }

    @Configuration
    @ImportResource({"classpath:spring-cfg.xml"})
    @ComponentScan(basePackages = "com.diguage.demo.boot")
    public static class BootConfig {
    }

    @Configuration
    @ImportResource({"classpath:web/spring-cfg.xml"})
    public static class WebConfig {
    }
}
Spring Boot 背后是否做了检查,这个是根据报错信息的猜测,没有翻看代码,所以这个猜测有一定的不确定性。有机会翻一下代码,查看一下具体原因。

革命尚未成功,且听下回分解……

下文:Spring 应用合并之路(二):峰回路转,柳暗花明

点赞
收藏
评论区
推荐文章
摸鱼飞弹 摸鱼飞弹
3年前
golang 实现配置中心 (一)
项目背景,之前在上家东家接触到golang,自学到项目上线,也是摸着石头过河,中间也遇到了一些小bug(生产环境出现内存泄露问题,导致业务占用内存,居高不下,实际是因为项目的service层部分应用没有应用到redis或数据库,但是也进行了实例化,最后没有释放资源导致,排查方法也是比较笨,就是一些流程在本地跑,看哪些环节导致内存居高不下,最后追查到的结果),
Wesley13 Wesley13
3年前
EDAS 微服务应用同城容灾最佳实践
前言上云目前已经是绝大数企业首选的IT基础设施建设方案,但是云上仍然存在一些不确定因素(机房硬件故障、网络故障、断网/断电、人为操作失误),导致各大云厂商每年在不同的数据中心都会发生一些故障,所以建设具备容灾能力的业务应用是必需的。公共云上容灾解决方案涵盖同城双活、跨Region容灾和异地多活等容灾场景,对公共云上大多数中长尾客户来说,更需要的是一
Stella981 Stella981
3年前
Angular 应用解决跨域访问的问题
在前后台分离的应用中,Angular与Java是一对好搭档。但是如果是分开部署应用,则势必会遇到跨域访问的问题。什么是跨域启动应用之后,有些浏览器会提示如下告警信息:No'AccessControlAllowOrigin'headerispresentontherequestedresource.
Easter79 Easter79
3年前
SpringBoot使用assembly进行项目打包教程1
一、基本介绍1.部署方式介绍目前来说,SpringBoot项目有如下两种常见的部署方式:一种是使用docker容器去部署。将SpringBoot的应用构建成一个dockerimage,然后通过容器去启动镜像,这种方式在需要部署大规模的应用、以及应用扩展时是非常方便的,属于目前工业级的部署方案,但是需要掌握
Easter79 Easter79
3年前
Springboot基于assembly的服务化打包方案
  在使用assembly来打包springboot微服务项目前,我想说一说,目前springboot项目的几种常见的部署方式。1. 使用docker容器去部署,将springboot的应用构建成一个dockerimage,然后通过容器去启动镜像,这种方式在需要部署大规模的应用和应用扩展时是非常方便的,属于目前工业级的部署方案,但是需要掌握d
Stella981 Stella981
3年前
SpringBoot使用assembly进行项目打包教程1
一、基本介绍1.部署方式介绍目前来说,SpringBoot项目有如下两种常见的部署方式:一种是使用docker容器去部署。将SpringBoot的应用构建成一个dockerimage,然后通过容器去启动镜像,这种方式在需要部署大规模的应用、以及应用扩展时是非常方便的,属于目前工业级的部署方案,但是需要掌握
Stella981 Stella981
3年前
Docker Get Started IV
4\.Swarms介绍在前面的部分,你知道了如何写一个应用以及如何运行在生产环境中,然后将它变为一个服务,在同一个进程中将服务能力伸缩到原来的5倍。在本部分,你将在集群上部署一个应用,运行在多台机器上。通过将多个机器加入到docker化的集群中,多容器多机器的应用是可能的,这个docker化的集群被称为蜂群。理解
Easter79 Easter79
3年前
Springboot项目与vue项目整合打包
我的环境\JDK1.8\maven3.6.0\node环境1.为什么需要前后端项目开发时分离,部署时合并?在一些公司,部署实施人员的技术无法和互联网公司的运维团队相比,由于各种不定的环境也无法做到自动构建,容器化部署等。因此在这种情况下尽量减少部署时的服务软件需求,打出的包数量也尽量少。针对这种情况这里采用的
京东云开发者 京东云开发者
11个月前
Spring 应用合并之路(一):摸石头过河 | 京东云技术团队
公司在推进降本增效,在尝试多种手段之后,发现应用太多,每个应用都做跨机房容灾部署,则最少需要4台机器(称为容器更合适)。那么,将相近应用做一个合并,减少维护项目,提高机器利用率就是一个可选方案。经过前后三次不同的折腾,最后探索出来一个可行方案。记录一下,分
Spring 应用合并之路(二):峰回路转,柳暗花明
作者:京东科技李君书接上文,前面在介绍了几种不成功的经验,下面继续折腾…四、仓库合并,独立容器在经历了上面的尝试,在同事为啥不搞两个独立的容器提醒下,决定抛开SpringBoot内置的父子容器方案,完全自己实现父子容器。如何加载web项目?现在的难题只有一