Java从单体架构升级到微服务要注意的一些问题

前言

由于近年来的移动端的发展和 2C模式 的红利,一些在风口的企业的业务得到爆发式增长。从架构层面来说,业务驱动技术的变革,所以微服务架构的概念得到很多企业的青睐,因为可以解决服务的大流量和高并发以及稳定性的要求。

但是任何架构设计不是一蹴而就的,不能从起步就开始使用微服务,一般都是先通过单体架构来快速实现需求和抢占市场,然后再迭代式扩展。不能一口气吃个胖子。

这几年自己有经历从单体到微服务的架构演变,也有直接参与到已经落地的微服务架构的项目中。见过好的架构设计,也见过一些孬的设计。好的架构设计,代码结构优雅,分层清晰,业务边界划分明朗,业务开发人员职责清晰。不好的设计就会导致代码混乱难以维护,对新需求无法快速应变,开发人员容易在补丁上打补丁,最后造成积重难返不得不重构。

架构师需要从业务层面和未来业务发展有个全面的规划,让架构高可用,易扩展,灵活易使用,隐藏其复杂性。好的架构会让下面的业务开发人员按照既定的模式“傻瓜式”编程。

既然第一步是单体架构,那么好的单体架构设计,为我们后期的微服务拆分会有事半功倍的效果。避免重复劳动和过多的重写。我们可以从这些方面进行一些有效的设计。

划清业务边界

如果对未来的架构有微服务的考虑,那么在单体架构的时候就需要理清业务边界的问题,常见的简单划分就是以业务区分,例如:用户,商品,订单,支付,权限等等,具体的拆分程度可根据自身业务量和需要做划分。

当前流行的 DDD(领域驱动设计)可以作为一个指导原则,但是 DDD 比较偏向于理论,需要执行人员有良好的专业能力才能实施的比较好。

代码层次结构

业务区分好之后,就是项目代码模块的设计。在代码层我们需要根据MVC的模式,建议的代码设计层次如下:

├─demo-common
│  │  demo-common.iml
│  │  pom.xml
│  │
│  └─src
│      ├─main
│      │  ├─java
│      │  └─resources
│      └─test
│          └─java
├─demo-dao
│  │  demo-dao.iml
│  │  pom.xml
│  │
│  └─src
│      ├─main
│      │  ├─java
│      │  └─resources
│      └─test
│          └─java
├─demo-service
│  │  demo-service.iml
│  │  pom.xml
│  │
│  └─src
│      ├─main
│      │  ├─java
│      │  └─resources
│      └─test
│          └─java
└─demo-web
    │  demo-web.iml
    │  pom.xml
    │
    └─src
        ├─main
        │  ├─java
        │  └─resources
        └─test
            └─java

主要包含4个 module 模块

  • demo-common:基础模块,枚举,常亮类,工具类,配置类。
  • demo-dao:Dao层,mapper接口,mapper.xml。
  • demo-service:服务接口提供层,业务service接口。
  • demo-web:web层,Controller类,服务接口,与外部进行交互。

各模块之间的依赖关系为:

项目 Module 模块设计完成之后,每个模块的内部 package 包如何设计呢?通常有两种划分模式:根据业务模块然后内部按MVC划分,根据MVC模式然后内部按业务划分。

1、根据业务模块划分,就是将每个业务模块作为一个 package,然后每个package里面有自己的 MVC,这样就做到业务模块的隔离。

2、根据 MVC 模式划分,先根据 MVC模式划分不同的包,service,serviceImpl,dto等,然后再是各个业务自己的模型和服务接口。

针对上述的两个划分模式,个人的选择是根据业务模式划分,这样的包设计与后期微服务拆分有良好的匹配度,拆分的时候只需要将每个业务包下的代码 Copy 到新的微服务中就行了,易迁移变动小。每个模块中对不同的业务通过 package 包名进行划分,例如:com.example.jajian.service.order、com.example.jajian.service.user等。

└─src
    ├─main
    │  ├─java
    │  │  └─com
    │  │      └─example
    │  │          └─jajian
    │  │              ├─common
    │  │              │      BaserService.java
    │  │              │
    │  │              └─service
    │  │                  ├─order
    │  │                  │  ├─dto
    │  │                  │  │      OrderDto.java
    │  │                  │  │
    │  │                  │  └─service
    │  │                  │      │  OrderService.java
    │  │                  │      │
    │  │                  │      └─impl
    │  │                  │              OrderServiceImpl.java
    │  │                  │
    │  │                  ├─pay
    │  │                  │  ├─dto
    │  │                  │  │      PayDto.java
    │  │                  │  │
    │  │                  │  └─service
    │  │                  │      │  PayService.java
    │  │                  │      │
    │  │                  │      └─impl
    │  │                  │              PayServiceImpl.java
    │  │                  │
    │  │                  └─user
    │  │                      ├─dto
    │  │                      │      UserDto.java
    │  │                      │
    │  │                      └─service
    │  │                          │  UserService.java
    │  │                          │
    │  │                          └─impl
    │  │                                  UserServiceImpl.java
    │  │
    │  └─resources
    └─test
        └─java

这样划分有什么好处?我们单体架构的时候这样开发,当需要拆分成微服务的时候就可以直接将业务包拆分出去,因为每个业务包里面就已经包含了所有的当前业务的关联业务类。

避免多边界业务的关联查询

单表关联由于业务需要而且简单方便易使用,所以多表关联查询在单体服务中是普遍存在的,如果我们后期不需要做服务拆分则可以不需要考虑这方面的限制。

但是如果后期有微服务的规划,那么单体服务的时候如果没有做这个方面的限制,mybatis 的 mapper.xml中有过多的多表关联查询,这些关联查询会严重影响服务拆分的进度和复杂度。

如果同属于一个业务领域则可以使用关联查询,而那些微服务拆分后属于不同领域的业务则应避免使用多表关联查询,因为不同的业务领域后期会被隔离拆分到不同的服务当中,即数据库表都是分布在不同的服务器上,所有服务之间都是通过RPC方式进行通信,关联查询这时是无法处理的。

Controller层尽量不做业务逻辑处理

常看到很多 coder 会在Controller 层做一些业务处理,个人认为这是很不规范的。Controller层是控制层,是和前端进行数据转换的,这里我们应该只做请求的接受和返回,也无需做一些异常的try...catch...的捕获,异常可以通过全局通用拦截器统一进行拦截然后返回给前端异常提示语,提升代码的简洁性。

所有的参数校验也放到 service层,因为如果服务内部调用也可以使用提高代码的共用度。当然分层领域模型最好也能区分开,

  • DO(Data Object):此对象与数据库表结构--对应,通过DAO层向上传输数据源对象。
  • DTO(Date Transfer Object):数据传输对象,service或Manager向外传输的对象。
  • VO(View Object):显示层的对象,通常是Web向模板渲染引擎层传输的对象。

这样区分开的好处是,当你需要对展示层数据进行特殊定制化的时候可以灵活变通,例如针对用户隐私信息身份证号,手机号码脱敏处理,或者用户ID加密显示等。

最后就是统一通用返回类了,通过这种格式的封装我们将数据格式进行全局格式化,这里的状态码可以自己设计的更详细一点。

public class CommonResult<T> {

    public static final String CODE_SUCCESS = "0";
    public static final String CODE_FAILED = "9999";

    private String code;
    private T data;

    private String msg;

    private CommonResult(String code, T data, String msg) {
        this.code = code;
        this.data = data;
        this.msg = msg;
    }

    public boolean isSuccess() {
        return CODE_SUCCESS.equals(code);
    }

    public static <T> CommonResult<T> success() {
        return new CommonResult<>(CODE_SUCCESS, null, null);
    }

    public static <T> CommonResult<T> success(T data) {
        return new CommonResult<>(CODE_SUCCESS, data, null);
    }

    public static <T> CommonResult<T> success(T data, String msg) {
        return new CommonResult<>(CODE_SUCCESS, data, msg);
    }

    public static <T> CommonResult<T> failed() {
        return new CommonResult<>(CODE_FAILED, null, null);
    }

    public static <T> CommonResult<T> failed(String errorCode, String msg) {
        return new CommonResult<>(errorCode, null, msg);
    }

    public static <T> CommonResult<T> failed(String msg) {
        return new CommonResult<>(CODE_FAILED, null, msg);
    }

    public static <T> CommonResult<T> failed(T data, String msg) {
        return new CommonResult<>(CODE_FAILED, data, msg);
    }

    public static <T> CommonResult<T> failed(String errorCode, T data, String msg) {
        return new CommonResult<>(errorCode, data, msg);
    }

   // 省略 setter、getter
}

后记

以上只是列举了单体服务未来规划做微服务时需要注意的一部分简单内容,每个人在做单体架构拆分成微服务的时候都会踩到各种各样的坑,这些坑成了我们的开发经验,有了这些坑就会形成注意点,在我们下次开发时就会具有指导意义。也许我们程序员就是在踩坑和填坑的过程中成长壮大起来的。

以上就是Java从单体架构升级到微服务要注意的一些问题的详细内容,更多关于Java的资料请关注我们其它相关文章!

(0)

相关推荐

  • JavaWeb实战之开发网上购物系统(超详细)

    一.项目描述 基于B/S三层架构设计的购物系统,可以更好地符合"高内聚,低耦合"的设计思想.表现层(UI)通过JSP在浏览器上实现界面展示,通过Servlet接受用户请求并生成验证码等实时信息:数据访问层(DAL)通过JDBC对DBMS进行访问:二者之间的业务逻辑层(BLL)通过Service进行连接.同时面向接口编程提高规范化.这只是我自己用两三天的时间做的小项目,距离真正的购物系统差距还是很大的,仅供初学者参考,很多逻辑缺陷不必太纠结,以后有时间会再写一篇博客对这个项目进行相关测试

  • javaCV开发详解之收流器实现

    本章基于javaCV实现收流器功能和录制功能 补充:基于本功能可以实现远程流媒体服务器实时视频录制到本地 一.开发所依赖的包 javacv.jar,javacpp.jar,ffmpeg.jar,ffmpeg-系统平台.jar,opencv.jar,opencv-系统平台.jar. 其中ffmpeg-系统平台.jar,opencv-系统平台.jar中的系统平台根据开发环境或者测试部署环境自行更改为对应的jar包,比如windows7 64位系统替换为ffmpeg-x86-x64.jar 本章使用w

  • JAVA后端应该学什么技术

    Java语言是最常见的后端开发语言之一,Java语言由于自身具备构建多线程的能力,且体系结构比较中立,所以在大型互联网平台的开发中得到了广泛的采用. 目前要想从事Java的后端开发应该学习以下内容: 一.Servlet技术 Servlet技术是Java后端的重要技术之一,作为Java Web开发的核心组件,Servlet承担了Web MVC结构中的核心作用(功能导航).传统的Model2结构(Servlet+JavaBean+JSP)虽然在目前已经很少使用了,但是Web开发的基本结构依然没有改变

  • Java基于IDEA实现http编程的示例代码

    http开发前言之为什么要有应用层 我们已经学过TCP/IP , 已经知道目前数据能从客户端进程经过路径选择跨网络传送到服务器端进程 [ IP+Port ],可是,仅仅把数据从A点传送到B点就完了吗?这就好比,在淘宝上买了一部手机,卖家[ 客户端 ]把手机通过顺丰[ 传送+路径选择 ] 送到买家 [ 服务器 ] 手里就完了吗?当然不是,买家还要使用这款产品,还要在使用之后,给卖家打分评论.所以,我们把数据从A端传送到B端, TCP/IP 解决的是顺丰的功能,而两端还要对数据进行加工处理或者使用,

  • Java中用POI实现将数据导出到Excel

    一.前言 数据导出为Excel在我们写项目的过程中经常用到 需要用到的jar包 poi-3.17.jar 二.具体实现步骤 //第一步创建一个webbook,对应一个Excel文件 HSSFWorkbook wb=new HSSFWorkbook(); //第二步,在webbook中添加一个sheet,对应Excel文件中的sheet HSSFSheet sheet=wb.createSheet("食物信息数据"); //第三步,在sheet中添加表头第0行 HSSFRow row =

  • javacv开发详解之调用本机摄像头视频

    前言 javacv开发包是用于支持java多媒体开发的一套开发包,可以适用于本地多媒体(音视频)调用以及音视频,图片等文件后期操作(图片修改,音视频解码剪辑等等功能),这里只使用最简单的本地摄像头调用来演示一下javacv的基础功能 重要: 建议使用最新javaCV1.5版本,该版本已解决更早版本中已发现的大部分bug javacv系列文章使用6个jar包: javacv.jar,javacpp.jar,ffmpeg.jar,ffmpeg-系统平台.jar,opencv.jar,opencv-系

  • 了解java架构之微服务架构—雪崩效应

    前言 微服务化产品线,每一个服务专心于自己的业务逻辑,并对外提供相应的接口,看上去似乎很明了,其实还有很多的东西需要考虑,比如:服务的自动扩充,熔断和限流等,随着业务的扩展,服务的数量也会随之增多,逻辑会更加复杂,一个服务的某个逻辑需要依赖多个其他服务才能完成. 一但一个依赖不能提供服务很可能会产生雪崩效应,最后导致整个服务不可访问. 微服务之间进行rpc或者http调用时,我们一般都会设置调用超时,失败重试等机制来确保服务的成功执行,看上去很美,如果不考虑服务的熔断和限流,就是雪崩的源头. 假

  • 教你用Java验证服务器登录系统

    一.前言 代码全部由自己所写,作者是一名小白请多多包涵,如果代码有什么不好的地方大佬们可以指出问题 单独写一个这样简易的登录是因为比较方便,由于我尝试了多次在写好的程序内直接写这个登录系统测试,很麻烦.不方便,所以单独写出了这套代码,个人觉得这样把写好的程序放进去修改就比较方便多了 二.登录系统服务端 import java.io.*; import java.net.ServerSocket; import java.net.Socket; public class ServerLogin {

  • javaCV开发详解之推流器和录制器的实现

    功能 实现边播放边录制/推流,停止预览即停止录制/推流 开发所依赖的包 javacv.jar,javacpp.jar,ffmpeg.jar,ffmpeg-系统平台.jar,opencv.jar,opencv-系统平台.jar. 其中ffmpeg-系统平台.jar,opencv-系统平台.jar中的系统平台根据开发环境或者测试部署环境自行更改为对应的jar包,比如windows7 64位系统替换为ffmpeg-x86-x64.jar 为什么要这样做:因为ffmpeg-系统平台.jar中存放的是c/

  • 详解Java 微服务架构

    一.传统的整体式架构 传统的整体式架构都是模块化的设计逻辑,如展示(Views).应用程序逻辑(Controller).业务逻辑(Service)和数据访问对象(Dao),程序在编写完成后被打包部署为一个具体的应用.如图所示: 系统的水平扩展 如果要对系统进行水平扩展,通常情况下,只需要增加服务器的数量,并将打包好的应用拷贝到不同的服务器,然后通过负载均衡器(Nginx)就可以轻松实现应用的水平扩展. 整体式架构的缺点 应用复杂度增加,更新.维护困难. 易造成系统资源浪费. 影响开发效率. 应用

  • java各种类型对象占用内存情况分析

    前言 其实一般的程序猿根本不用了解这么深,只有当你到了一定层次,需要了解jvm内部运行机制,或者高并发多线程下,你写的代码对内存有影响,你想做性能优化.等等等等,一句话,当你想深入了解java对象在内存中,如何存储,或者每个对象占用多大空间时,你会感谢这篇文章 本文主要分析jvm中的情况,实验环境为64位window10系统.JDK1.8,使用JProfiler进行结论验证 很多描述以及 概念是基于你懂基本java知识的,如果你看起来有点吃力,要加油咯 本片更偏重验证,更多理论,请参考:http

  • JavaAgent的简单例子

    JavaAgent 是JDK 1.5 以后引入的,也可以叫做Java代理. JavaAgent 是运行在 main方法之前的拦截器,它内定的方法名叫 premain ,也就是说先执行 premain 方法然后再执行 main 方法. 那么如何实现一个 JavaAgent 呢?很简单,只需要增加 premain 方法即可. 看下面的代码和代码中的注释说明: package com.shanhy.demo.agent; import java.lang.instrument.Instrumentat

随机推荐