学习springcloud之前可以参考本文回顾基础知识,需要有Maven+springmvc+mybatis+mysql基础

1.Maven的分包分模块架构

一个Project带着多个Module子模块

ServiceCloud父工程(Project)管理依赖

包含3个子模块(Module)


cloud-api:封装的整体Entity/接口/公共配置等

cloud-provider-dept-8001:微服务落地的服务提供者

cloud-consumer-dept-80:微服务调用的客户端使用

2.创建servicecloud父工程

idea创建一个maven项目,创建好后之后一个src文件和一个pom.xml文件

pom文件内容如下:


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

<modelVersion>4.0.0</modelVersion>


<groupId>com.shawn</groupId>

<artifactId>service-cloud</artifactId>

<version>1.0-SNAPSHOT</version>

<packaging>pom</packaging>



<properties>

<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

<maven.compiler.source>1.8</maven.compiler.source>

<maven.compiler.target>1.8</maven.compiler.target>

<junit.version>4.12</junit.version>

<log4j.version>1.2.17</log4j.version>

<lombok.version>1.16.18</lombok.version>

</properties>


<!--父工程依赖管理-->

<dependencyManagement>

<dependencies>

<dependency>

<groupId>org.springframework.cloud</groupId>

<artifactId>spring-cloud-dependencies</artifactId>

<version>Dalston.SR1</version>

<type>pom</type>

<scope>import</scope>

</dependency>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-dependencies</artifactId>

<version>1.5.9.RELEASE</version>

<type>pom</type>

<scope>import</scope>

</dependency>

<dependency>

<groupId>mysql</groupId>

<artifactId>mysql-connector-java</artifactId>

<version>5.0.4</version>

</dependency>

<dependency>

<groupId>com.alibaba</groupId>

<artifactId>druid</artifactId>

<version>1.0.31</version>

</dependency>

<dependency>

<groupId>org.mybatis.spring.boot</groupId>

<artifactId>mybatis-spring-boot-starter</artifactId>

<version>1.3.0</version>

</dependency>

<dependency>

<groupId>ch.qos.logback</groupId>

<artifactId>logback-core</artifactId>

<version>1.2.3</version>

</dependency>

<dependency>

<groupId>junit</groupId>

<artifactId>junit</artifactId>

<version>${junit.version}</version>

<scope>test</scope>

</dependency>

<dependency>

<groupId>log4j</groupId>

<artifactId>log4j</artifactId>

<version>${log4j.version}</version>

</dependency>

</dependencies>

</dependencyManagement>


<build>

<finalName>service-cloud</finalName>

<resources>

<resource>

<directory>src/main/resources</directory>

<filtering>true</filtering>

</resource>

</resources>

<plugins>

<plugin>

<groupId>org.apache.maven.plugins</groupId>

<artifactId>maven-resources-plugin</artifactId>

<configuration>

<delimiters>

<delimit>$</delimit>

</delimiters>

</configuration>

</plugin>

</plugins>

</build>


<!--以下是和其他模块依赖,报错可以先注释掉-->

<modules>

<module>cloud-api</module>

<module>cloud-provider-dept-8001</module>

        <module>cloud-consume-dept-80</module>

    </modules>


</project>

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

servicecloud父工程主要是定义POM文件,将后续各个子模块公用的jar包等统一提出来,类似一个抽象父类


3.创建cloud-api 封装的整体Entity/接口/公共配置等


直接在父工程上点击右键创建新的Module



next下一步 再Finish

目录结构:



修改Pom文件:


<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0"

         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <parent>

        <artifactId>service-cloud</artifactId>

        <groupId>com.shawn</groupId>

        <version>1.0-SNAPSHOT</version>

    </parent>

    <modelVersion>4.0.0</modelVersion>


    <dependencies><!-- 当前Module需要用到的jar包,按自己需求添加,如果父类已经包含了,可以不用写版本号 -->

        <dependency>

            <groupId>org.projectlombok</groupId>

            <artifactId>lombok</artifactId>

        </dependency>

       

    </dependencies>


    <artifactId>cloud-api</artifactId>

</project>

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

lombok

在项目中使用Lombok可以减少很多重复代码。比如说getter/setter/toString等方法的编写。

插件安装方法:打开IDEA的Setting –> 选择Plugins选项 –> 选择Browse repositories –> 搜索lombok –> 点击安装 –> 安装完成重启IDEA

如果安装不了可以选择去官网下载jar包,然后本地安装。



配合lombok创建部门Entity:


package com.shawn.springcloud.entites;

import java.io.Serializable;

import lombok.AllArgsConstructor;

import lombok.Data;

import lombok.NoArgsConstructor;

import lombok.experimental.Accessors;


@NoArgsConstructor //无参构造

@AllArgsConstructor//全餐构造

@Data//get/set方法

@Accessors(chain=true)//链式调用

public class Dept implements Serializable// entity --orm--- db_table

{

private Long deptno; // 主键

private String dname; // 部门名称

private String db_source;// 来自那个数据库,因为微服务架构可以一个服务对应一个数据库,同一个信息被存储到不同数据库

public Dept(String dname)

{

super();

this.dname = dname;

}

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

4.创建cloud-provider-dept-8001 服务提供者

参照cloud-api创建cloud-provider-dept-8001工程

修改pom.xml文件

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>


    <parent>

        <artifactId>service-cloud</artifactId>

        <groupId>com.shawn</groupId>

        <version>1.0-SNAPSHOT</version>

    </parent>


    <artifactId>cloud-provider-dept-8001</artifactId>


    <dependencies>

        <dependency><!-- 引入自己定义的api通用包,可以使用Dept部门Entity -->

            <groupId>com.shawn</groupId>

            <artifactId>cloud-api</artifactId>

            <version>${project.version}</version>

        </dependency>

        <dependency>

            <groupId>junit</groupId>

            <artifactId>junit</artifactId>

        </dependency>

        <dependency>

            <groupId>mysql</groupId>

            <artifactId>mysql-connector-java</artifactId>

        </dependency>

        <dependency>

            <groupId>com.alibaba</groupId>

            <artifactId>druid</artifactId>

        </dependency>

        <dependency>

            <groupId>ch.qos.logback</groupId>

            <artifactId>logback-core</artifactId>

        </dependency>

        <dependency>

            <groupId>org.mybatis.spring.boot</groupId>

            <artifactId>mybatis-spring-boot-starter</artifactId>

        </dependency>

        <dependency>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-starter-jetty</artifactId>

        </dependency>

        <dependency>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-starter-web</artifactId>

        </dependency>

        <dependency>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-starter-test</artifactId>

        </dependency>

        <!-- 修改后立即生效,热部署 -->

        <dependency>

            <groupId>org.springframework</groupId>

            <artifactId>springloaded</artifactId>

        </dependency>

        <dependency>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-devtools</artifactId>

        </dependency>

    </dependencies>

</project>

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

新建yml文件,并配置信息


server:

  port: 8001


mybatis:

  config-location: classpath:mybatis/mybatis.cfg.xml        # mybatis配置文件所在路径

  type-aliases-package: com.shawn.springcloud.entites  # 所有Entity别名类所在包

  mapper-locations:

  - classpath:mybatis/mapper/**/*.xml                       # mapper映射文件


spring:

   application:

    name: cloud-dept

   datasource:

    type: com.alibaba.druid.pool.DruidDataSource            # 当前数据源操作类型

    driver-class-name: org.gjt.mm.mysql.Driver              # mysql驱动包

    url: jdbc:mysql://localhost:3306/clouddb01              # 数据库名称

    username: root

    password: 123456

    dbcp2:

      min-idle: 5                                           # 数据库连接池的最小维持连接数

      initial-size: 5                                       # 初始化连接数

      max-total: 5                                          # 最大连接数

      max-wait-millis: 200                                  # 等待连接获取的最大超时时间

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

工程src/main/resources目录下新建mybatis文件夹后新建mybatis.cfg.xml文件

<?xml version="1.0" encoding="UTF-8" ?>

<!DOCTYPE configuration

        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"

        "http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>

    <settings>

        <setting name="cacheEnabled" value="true"/><!-- 二级缓存开启 -->

    </settings>

</configuration>

1

2

3

4

5

6

7

8

9

MySQL创建部门数据库脚本

新建数据库直接执行就可以了

DROP DATABASE IF EXISTS cloudDB01;

CREATE DATABASE cloudDB01 CHARACTER SET UTF8;

USE cloudDB01;

CREATE TABLE dept

(

  deptno BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT,

  dname VARCHAR(60),

  db_source   VARCHAR(60)

);

INSERT INTO dept(dname,db_source) VALUES('开发部',DATABASE());

INSERT INTO dept(dname,db_source) VALUES('人事部',DATABASE());

INSERT INTO dept(dname,db_source) VALUES('财务部',DATABASE());

INSERT INTO dept(dname,db_source) VALUES('市场部',DATABASE());

INSERT INTO dept(dname,db_source) VALUES('运维部',DATABASE());

 

SELECT * FROM dept;

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

DeptDao部门接口

@Mapper

public interface DeptDao

{

  public boolean addDept(Dept dept);

 

  public Dept findById(Long id);

 

  public List<Dept> findAll();

}

1

2

3

4

5

6

7

8

9

工程src/main/resources/mybatis目录下新建mapper文件夹后再建DeptMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>

<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"

        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">


<mapper namespace="com.shawn.springcloud.dao.DeptDao">


    <select id="findById" resultType="Dept" parameterType="Long">

   select deptno,dname,db_source from dept where deptno=#{deptno};

  </select>

    <select id="findAll" resultType="Dept">

   select deptno,dname,db_source from dept;

  </select>

    <insert id="addDept" parameterType="Dept">

   INSERT INTO dept(dname,db_source) VALUES(#{dname},DATABASE());

  </insert>

</mapper>

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

DeptService部门服务接口

public interface DeptService

{

    public boolean add(Dept dept);

    public Dept    get(Long id);

    public List<Dept> list();

}

1

2

3

4

5

6

DeptServiceImpl部门服务接口实现类

@Service

public class DeptServiceImpl implements DeptService {

    @Autowired

    private DeptDao dao ;

    @Override

    public boolean add(Dept dept)

    {

        return dao.addDept(dept);

    }

    @Override

    public Dept get(Long id)

    {

        return dao.findById(id);

    }

    @Override

    public List<Dept> list()

    {

        return dao.findAll();

    }

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

DeptController部门微服务提供者REST

@RestController

public class DeptController {

    @Autowired

    private DeptService service;


    @RequestMapping(value="/dept/add",method=RequestMethod.POST)

    public boolean add(@RequestBody Dept dept)

    {

        return service.add(dept);

    }


    @RequestMapping(value="/dept/get/{id}",method=RequestMethod.GET)

    public Dept get(@PathVariable("id") Long id)

    {

        return service.get(id);

    }


    @RequestMapping(value="/dept/list",method=RequestMethod.GET)

    public List<Dept> list()

    {

        return service.list();

    }

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

DeptProvider8001_App主启动类DeptProvider8001_App

@SpringBootApplication

public class DeptProvider8001_App {

    public static void main(String[] args) {

        SpringApplication.run(DeptProvider8001_App.class,args);

    }

}

1

2

3

4

5

6

构建完成,打开浏览器测试

http://localhost:8001/dept/get/2


http://localhost:8001/dept/list


补充一下项目结构:


5.创建cloud-consumer-dept-80 消费者

参照上文创建出maven工程cloud-consumer-dept-80

修改Pom文件

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0"

         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <parent>

        <artifactId>service-cloud</artifactId>

        <groupId>com.shawn</groupId>

        <version>1.0-SNAPSHOT</version>

    </parent>

    <modelVersion>4.0.0</modelVersion>

    <dependencies>

        <dependency><!-- 引入自己定义的api通用包,可以使用Dept部门Entity -->

            <groupId>com.shawn</groupId>

            <artifactId>cloud-api</artifactId>

            <version>${project.version}</version>

        </dependency>

        <dependency>

            <groupId>junit</groupId>

            <artifactId>junit</artifactId>

        </dependency>

        <dependency>

            <groupId>ch.qos.logback</groupId>

            <artifactId>logback-core</artifactId>

        </dependency>

        <dependency>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-starter-jetty</artifactId>

        </dependency>

        <dependency>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-starter-web</artifactId>

        </dependency>

        <dependency>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-starter-test</artifactId>

        </dependency>

        <!-- 修改后立即生效,热部署 -->

        <dependency>

            <groupId>org.springframework</groupId>

            <artifactId>springloaded</artifactId>

        </dependency>

        <dependency>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-devtools</artifactId>

        </dependency>

    </dependencies>

    <artifactId>cloud-consume-dept-80</artifactId>

</project>

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

新增yml文件application.yml

server:

  port: 80

1

2

编写配置文件configbean(类似spring里面的applicationContext.xml写入的注入Bean)

package com.shawn.springcloud.cfgbeans;

@Configuration

public class ConfigBean {


    @Bean

    public RestTemplate restTemplate(){

        return new RestTemplate();

    }

}


1

2

3

4

5

6

7

8

9

10

新建DeptController_Consumer部门微服务消费者REST

package com.shawn.springcloud.controller;

@RestController

public class DeptController_Consumer {


    private static final String REST_URL_PREFIX = "http://localhost:8001";


    @Autowired

    private RestTemplate restTemplate;


    @RequestMapping("/consumer/dept/add")

    public boolean add(Dept dept){

        return restTemplate.postForObject(REST_URL_PREFIX+"/dept/add",dept,Boolean.class);

    }


    @RequestMapping("/consumer/dept/get/{id}")

    public Dept get(@PathVariable("id") Long id ){

        return restTemplate.getForObject(REST_URL_PREFIX+"/dept/get/"+id,Dept.class);

    }


    @RequestMapping("/consumer/dept/list")

    public List<Dept> list(){

        return restTemplate.getForObject(REST_URL_PREFIX+"/dept/list",List.class);

    }

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

注:RestTemplate提供了多种便捷访问远程Http服务的方法,是一种简单便捷的访问restful服务模板类,是Spring提供的用于访问Rest服务的客户端模板工具集

使用:

restTemplate访问restful接口非常的简单粗暴无脑。(url, requestMap, ResponseBean.class)这三个参数分别代表

REST请求地址、请求参数、HTTP响应转换被转换成的对象类型。


最后主启动类编写

package com.shawn.springcloud;

@SpringBootApplication

public class DeptConsumer80_App

{

  public static void main(String[] args)

  {

   SpringApplication.run(DeptConsumer80_App.class, args);

  }

}

1

2

3

4

5

6

7

8

9



6. 编写完成,最后进行测试

访问消费者接口(注意,是访问消费者的!80端口)

http://localhost/consumer/dept/get/2


http://localhost/consumer/dept/list


END

————————————————

版权声明:本文为CSDN博主「LuckyShawn」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/weixin_40037053/article/details/85051405


JPAAS平台是基于SpringCloud的一套企业级的低代码微服务架构敏捷开发平台,它具有以下特点:

  1. 基于Spring Boot 2.0.X、Spring Cloud Finchley和Spring Cloud Alibaba

  2. 主要针对解决微服务和业务开发时常见的非功能性需求

  3. 提供了基础的企业级的应用开发,如流程、表单、组织架构、安全认证、门户等

  4. 提供应用管理,方便第三方系统接入,支持多租户(应用隔离)

  5. 前后端分离的企业级微服务架构

  6. 引入组件化的思想实现高内聚低耦合并且高度可配置化

  7. 注重代码规范,严格控制包依赖,每个工程基本都是最小依赖

  8. 适合企业级的应用功能扩展与开发

  9. 适合企业往技术中台、数据中台、业务中台进行模块与业务的规划扩展

平台架构模块:

平台架构图

分层的架构:


    这几年,在公司尝试转型做产品。所以引入了很多的产品的理念。不管是对产品的定义,还是针对产品的管理,以及摸索产品的落地等等。我之前更多的是接触的ToB端,所以想必也猜到了是一个SaaS模式的产品。其实,现在回想并总结,之前所做的产品并不理想。当然,在这里就不多的来介绍了。

    在一个月前不久,我给了一个朋友一份代码,还包含对产品的技术框架以及规划的文档。补充说一下,我很庆幸在一个新的环境接手在重新设计产品。所以基于之前的沉淀,有了一定的提升。那么这个朋友也是我之前在一次技术交流会上分享技术的时候所认识的。这次呢,我们在微信上一起讨论关于SaaS模式的产品时,我给他灌输了我对部分产品的理解,以及我参与过的产品的一些经验,并给他一些思路和参考。

    据悉,他现在也是在参与一个项目。大篇幅的概念过后,他显然有点不满足。加之我说我们现在所用到的一些技术方案时,他也变现得格外的感兴趣,并最后我也将一个产品的代码做了裁剪后,也一并分享给了他。

    好了,写到这里,我觉得应该要进入主题了。

    本篇文章呢,我想聊一聊关于SaaS产品里面一个重要的概念,多租户。这个也是他后来不断让我给他解释和讲解的内容,所以我索性整理一下。

    在以往传统的项目中,我们一般地是面向特定的客户实现一套特定的系统,并部署到对应的企业内部中。不同的企业或组织各自部署一套自己的软件系统。而现在面向ToB端的SaaS模式,是将一套软件服务部署到云端或特定服务环境中,面向不同的企业或组织提供相同的服务。这也很符合目前“轻资产”的模式。这里有一张图比较形象:

传统模式与SaaS模式对比

    在SaaS模式的平台中,我们需要考虑和规划好如何将一套相同的服务提供给不同的企业或组织使用,并且有且只能使用或操作所属范围中的数据。当然,不同企业或组织对平台功能的使用,通常我们把这种使用的企业或组织客户称之为“租户”。所以,我们可以总结出一句话:多租户技术,是一种架构模式,是实现如何在多用户环境下共用相同的系统或程序组件,并且达到各用户间数据的“独立”的技术

    在SaaS平台里,注重的就是数据的“独立性”,也是隔离性。如何在共有的一套系统架构与服务,仍可以保障客户的数据相对独立的正常使用。这也是SaaS产品必须面对的问题。由此带来了一些新的挑战。

    一般地,以支持多租户的运行技术总体可分为三种:独立数据库、共享数据库但独立Schema、共享数据库且共享数据表。他们各自又存在各自的优劣性。接下来,就具体聊一聊。

1、独立数据库

    针对独立数据库的这种方式,首先需要业务层能够支持多数据源的配置,并且为每个租户创建或初始化一个数据库。应用程序和数据库都是独立的实例,因此它不会与任何其他独立实例交互。只为一个租户提供服务,拥有独立的服务、独立的数据库以及独立的请求处理。

独立数据库

优点:为不同的租户提供独立的数据库,有助于简化数据模型的扩展设计,满足不同租户的独特需求;如果出现故障,数据影响小、恢复数据比较简单。

缺点:增多了数据库的安装数量,随之带来维护成本和购置成本的增加这种方案与传统的一个客户、一套数据、一套部署类似,差别只在于软件统一部署在运营商那里。由此可见此方案用户数据隔离级别最高安全性最好,但是成本较高。

2、共享数据库但独立Schema

共享数据库、独立Schema模式,是将多个或所有租户的数据放在一个数据库服务中,但是为每一个租户建立一个独立的schema。租户间数据彼此逻辑不可见,上层应用程序的实现和独立数据库一样简单。(补充:mysql数据中的schema比较特殊,并不是数据库的下一级,而是等同于数据库。)
优点:对于安全性要求较高的租户,是一种选择。提供了一定程度的逻辑数据隔离,但并不是完全隔离;每个数据库可支持更多的租户数量。
缺点:如果出现故障,数据恢复比较困难,因为恢复数据库将牵涉到其他租户的数据;如果需要跨租户统计数据,存在一定困难。这种方案是方案一的变种。只需要安装一份数据库服务,通过不同的Schema对不同租户的数据进行隔离。由于数据库服务是共享的,所以成本相对低廉。

3、共享数据库、共享数据表

    共享数据库、共享数据表,指的是多个或所有租户共享同一个数据库(Database)。所有的租户数据都存在同一个数据和同一套表中。通过数据库或表设计的租户ID租户标志字段,来表明该记录是属于哪个租户的。

共享数据库、共享数据表

优点:所有租户使用同一套数据库,所以成本低廉;能够简单进行数据聚合统计或分析。
缺点:隔离级别最低,安全性最低,需要在设计开发时加大对安全的开发量,数据备份和恢复最困难。

    接下来,我聊一聊我们在某个产品上的实践。这里仅针对实现逻辑做简单梳理。不深入展开。

    我们在开展某个ToB的SaaS产品规划时,采用的是微服务的模式。所以我们从路由网关、认证鉴权、以及对应的业务应用和数据库设计做了整体性的规划和实现。如下图,这是是我们之前设计的一版架构图,后来有所调整就不展开了。

架构模式

    在这个产品中,我们选择的是共享数据库、共享数据表的模式进行实践的。

    首先,我们为了租户能否方便的访问,以及平台能自动识别访问是哪个租户,我们在接入层采用通过url来识别租户。即系统在初始化租户信息时,会随机生成一个租户编码(租户编码允许修改一次),用于saas平台的三级域名监听,通过在业务系统的处理和绑定,当接收到请求时,拦截器会自动识别对应的租户编码,并加载对应的租户信息。

    其次,在业务处理时,租户标识编号作为必须条件带入,进行数据操作。这里可以拓展下。目前在构建时,我们预留了分库分表的处理策略。当我们的用户量和业务所残生的数据呈现线性增长模式时,采用一般性的单数据库或者读写分离已经远远不能满足访问性能的要求了。加上我们在很多微应用上采用的是一个数据库的原则。数据库的IO将是一个直接面临的问题。所以我们已经预留了按照租户分库的策略。

    针对数据库分库,我推荐两个方案。一个是代码编程式的sharding-jdbc、一个是采用中间件技术(MyCat、DBLE)等。其中dble也是基于mycat的版本。

    sharding-jdbc是代码编程的方式,通过定义对应的分库规则来引导对应的数据持久化到对应的库中。这种方式需要编码规范并且业务实现要关注这块逻辑。

    数据库中间件,是将分库分表的规则逻辑从应用程序提取出来,在中间件上来维护对应的规则。好处就是降低了业务人员对数据处理的关注,让业务实现更关注业务实现。

    在这里不具体展开对这些技术的详细讲解了。我们采用的是中间件MyCat1.6.5来实现。

    接着,在MyCat中配置rule规则,通过tenantId%4的规则,将数据分到4个物理数据库中。在业务代码层,只需要指向一个数据库,通过数据库中间件来维持多数据源的连接和数据处理(如果涉及到分布式事务的话,请谨慎使用)。

    最后,就是公约和规范了。其实,不管是采用编码式还是数据库中间件,我们都需要一个租户的唯一标识。然后建立数据库路由,以租户id为路由标识,来数据库语句路由到对应的数据库中去执行。在维护sql语句时,有一些需要注意或遵守的地方。

        1、如果有全局表,那么在执行全局表数据时,可以按照对应的业务需求进行编写。
        2、如果需要执行某个租户的信息,那么Sql必须要加上租户的唯一标识条件,且放在第一位条件。
        3、如需要进行全局表或夸库数据联表查询,分开查询。具体的要结合中间件支持程度。
        4、数据库联表执行,尽量保持在3张表以内。超过3张表联表,可以拆分执行。



作者:NeilZhou
链接:https://www.jianshu.com/p/74480dc0c779
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


d25f5af05fdaba9c4517c0275f6265fc.png

简介

基于SpringCloud(Hoxton.SR1) + SpringBoot(2.2.4.RELEASE) 的 SaaS型微服务脚手架,具备用户管理、资源权限管理、网关统一鉴权、Xss防跨站攻击、自动代码生成、多存储系统、分布式事务、分布式定时任务等多个模块,支持多业务系统并行开发, 支持多服务并行开发,可以作为后端服务的开发脚手架。代码简洁,架构清晰,非常适合学习使用。核心技术采用Nacos、Fegin、Ribbon、Zuul、Hystrix、JWT Token、Mybatis、SpringBoot、Seata、Nacos、Sentinel、 RabbitMQ、FastDFS等主要框架和中间件。

希望能努力打造一套从 SaaS基础框架 - 分布式微服务架构 - 持续集成 - 系统监测 的解决方案。本项目旨在实现基础能力,不涉及具体业务。

部署方面, 可以采用以下4种方式,并会陆续公布jenkins集合以下3种部署方式的脚本和配置文件:

  • IDEA 启动

  • jar部署

  • docker部署

  • k8s部署

架构图

62c80faf61190123cf830839d83053fc.png

技术栈/版本介绍

  • JSON序列化:Jackson

  • 消息队列:RabbitMQ

  • 缓存:Redis

  • 缓存框架:J2Cache

  • 数据库: MySQL 5.7.9 (驱动6.0.6)

  • 定时器:采用xxl-jobs项目进行二次改造

  • 前端:vue

  • 持久层框架: Mybatis-plus

  • 代码生成器:基于Mybatis-plus-generator自定义

  • API网关:Zuul

  • 服务注册与发现:Eureka -> Nacos

  • 服务消费:OpenFeign

  • 负载均衡:Ribbon

  • 配置中心:Nacos

  • 服务熔断:Hystrix

  • 项目构建:Maven 3.3

  • 分布式事务: seata

  • 分布式系统的流量防卫兵: Sentinel

  • 监控: spring-boot-admin 2.x

  • 链路调用跟踪: zipkin 2.x

  • 文件服务器:FastDFS 5.0.5/阿里云OSS/本地存储

  • Nginx

功能点

  • 服务注册&发现与调用

  • 服务鉴权

  • 负载均衡

  • 熔断机制

  • 监控

  • 链路调用监控

  • SaaS(多租户)的无感解决方案

  • 防跨站脚本攻击(XSS)

  • 代码生成器

  • 定时任务调度器

  • 大文件/断点/分片续传

  • 分布式事务

  • 跨表、跨库、跨服务的关联数据自动注入器

  • 灰度发布

项目截图

8834a66a992d5a255c2accda0c4975c1.png

4a97c246601631e5a5607b2950e85b38.png

91c03cdedaca22877813fe69d8393985.png

759cf55ef626c18d6df0751842fc0b4e.png

b5a84aeb9c0aa8cdea9b1bd2f75c26da.png

4d9f2af2b47a0345b989a7b9bfc373ac.png源码

https://gitee.com/zuihou111/zuihou-admin-cloud

小结

最后感谢撸主贡献出了这么优秀的微服务管理脚手架,大家可以阅读原文支持一下,如果觉得不错可以顺手给个小星星。

被举报了,只能推荐点风景了!

相关资源:Spring Cloud & SaaS 实战经验分享


可以通过应用服务的智能调度和多层级负载均衡,保障业务服务的连续性

可以通过自助平台界面部署所需要的应用。如在应用模板选择一个模板快速部署使用

可以通过交付中心模块实现企业应用标准化交付和管理

平台支持统一管理多个地域的不同数据中心的集群资源

平台支持从项目管理、开发、测试、运维多阶段的自动化IT管理