精华内容
下载资源
问答
  • 多租户

    2020-12-21 15:43:12
    多租户 多租户技术或称多重租赁技术,它是一种软件架构技术,是实现如何在多用户环境下(一般是面向企业用户)共用相同的系统或应用组件,并且可确保各用户间数据的隔离性。简单来说就是,在一台服务器上运行单个...

    多租户技术或称多重租赁技术,它是一种软件架构技术,是实现如何在多用户环境下(一般是面向企业用户)共用相同的系统或应用组件,并且可确保各用户间数据的隔离性。简单来说就是,在一台服务器上运行单个应用实例,它为多个租户(客户)提供服务。从定义中我们可以理解:多租户是一种架构,目的是为了让多用户环境下使用同一套应用程序,且保证用户间数据隔离。那么关于多租户的重点就显得浅显易懂了,多租户的重点就是同一套应用下实现多用户数据的隔离

    业务隔离主要包括:

    ❶ 物理隔离,租户开展业务所依赖的全部计算资源全然独立;

    ❷ 逻辑隔离,通过技术手段,隔离租户业务流程和业务数据。一个租户仅仅能访问自身的数据。

    存储方案分为:

     资源共享度数据隔离度应用复杂度应用成本 

    独立数据库

    每个租户通过数据库隔离
    共享数据库,隔离数据架构共享一个数据库,每个租户通过schema隔离
    共享数据库,共享数据架构共享同一个数据库,同一个schema,每个租户通过TenantID隔离

     

    展开全文
  • 多租户多租户-源码

    2021-02-13 18:28:11
    多租户多租户
  • 多租户】初识多租户

    千次阅读 热门讨论 2018-03-25 20:44:14
    什么是多租户呢,你了解多租户吗,多租户是什么呢

    前言

     

             什么是多租户呢,你了解多租户吗,多租户是什么呢

     

    叙述

    多租户的历史:

     

         多租户技术源于1960年代,许多公司为了要使用更多的运算资源,向持有大型主机(Mainframe)的供应商租用一部分的运算资源,而这些用户经常会用到相同的应用程序,当时会以用户在登录系统时输入的数据来决定用户的帐户ID,基于这个ID,Mainframe的供应商即可利用此ID来计算运算的资源使用量,包含CPU,存储器与软盘或磁带等,这个作法也被SAP公司用在其R/1到R/3的产品线。
         到了1990年代,应用程序服务提供者服务(application service provider)模式出现,它的作法与运作模式与租用大型主机时相同,不过租用的资源是在软件上,除了操作系统以外也包含了其上的应用程序,例如ERP系统或是CRM等应用,系统可能会运行在数台不同的机器上,或是在相同的主机但共享不同的数据库,以区分并计算客户的资源使用量,藉以作为计费的标准,而此技术也有效的缩减供应商的实体机器成本(因为可以在一台电脑上同时运行多个用户所租用的应用程序进程)。到了现代,受欢迎的消费者导向Web应用程序(如Hotmail或Gmail等)也是以单一应用程序平台来支持所有的用户,这已经是多租户技术的自然演化的结果,多租户技术也可以让客户中的一部份用户得以进一步定制化他们的应用程序。
         在虚拟化(virtualization)技术的成熟与应用性的扩张之下,多租户技术可以驾驭虚拟化的平台,更强化在用户应用程序与数据之间的隔离,让多租户技术能更加发挥它的特色。

     

    什么是多租户

     

     内涵:
        定义:

           多租户技术(英语:multi-tenancy technology)或称多重租赁技术,是一种软件架构技术,它是在探讨与实现如何于多用户的环境下共用相同的系统或程序组件,并且仍可确保各用户间数据的隔离性。
        本质上讲:软件架构技术
        通俗上讲:
          在多租户技术中,租户(tenant)是指使用系统或电脑运算资源的客户,但在多租户技术中,租户包含在系统中可识别为指定用户的一切数据,举凡帐户与统计信息(accounting data),用户在系统中建置的各式数据,以及用户本身的客制化应用程序环境等,都属于租户的范围,而租户所使用的则是基于供应商所开发或建置的应用系统或运算资源等,供应商所设计的应用系统会容纳数个以上的用户在同一个环境下使用,为了要让多个用户的环境能力同一个应用程序与运算环境上使用,则应用程序与运算环境必须要特别设计,除了可以让系统平台可以允许同时让多份相同的应用程序运行外,保护租户数据的隐私与安全也是多租户技术的关键之一。
     外延:
          技术上,多租户技术可以通过许多不同的方式来切割用户的应用程序环境或数据。
        数据面(data approach):供应商可以利用切割数据库(database),切割存储区(storage),切割结构描述(schema)或是表格(table)来隔离租户的数据,必要时会需要进行对称或非对称加密以保护敏感数据,但不同的隔离作法有不同的实现复杂度与风险。
        程序面(application approach):供应商可以利用应用程序挂载(hosting)环境,于进程(process)上切割不同租户的应用程序运行环境,在无法跨越进程通信的情况下,保护各租户的应用程序运行环境,但供应商的运算环境要够强。
        系统面(system approach):供应商可以利用虚拟化技术,将实体运算单元切割成不同的虚拟机,各租户可以使用其中一至数台的虚拟机来作为应用程序与数据的保存环境,但对供应商的运算能力要更要求。

     

    为什么要用多租户

     

    使用多租户带来什么好处,意义是什么
    第一,系统维护成本低
         多租户系统在系统升级时,只需要更新一次。
         维护人员不需要对每个用户更新,节省了很大的运维成本!
         经济:因为通过一个软件实例被多个组织共享,从而减低了整体资源的消耗,也同时减低应用运行的成本和相应的管理开支。
    第二,提高了数据安全性
        在云计算环境下,很多应用都放到了云端,导致在应用入口,敏感数据泄露、数据访问无详细记录、应用冒名访问开放接口;
         在运维入口,开发人员账号混用、操作无详细记录、高危险误操作无法控制、敏感数据泄露
    通过多租户数据资源隔离机制,就可以保证数据的安全性。
    三、管理方便:
          首先,通过使用了多租户架构能减少物理资源和软件资源,这将简化管理,其次。由于多租户软件主要由有经验的云供应商运营,所以能依赖那些非常经验的管理人员来提升效率。
    怎么实现多租户
        实现的原理是什么
           由于多租户技术可以让多个租户共用一个应用程序或运算环境,且租户大多不会使用太多运算资源的情况下,对供应商来说多租户技术可以有效的降低环境建置的成本。包含硬件本身的成本,操作系统与相关软件的授权成本都可以因为多租户技术,而由多个租户一起分担。
    通过不同的数据管理手段,多租户技术的数据可以用不同的方式进行数据隔离,在供应商的架构设计下,数据的隔离方式也会不同,而良好的数据隔离法可以降低供应商的维护成本(包含设备与人力),而供应商可以在合理的授权范围内取用这些数据分析,以作为改善服务的依据。
    多租户架构下所有用户都共用相同的软件环境,因此在软件改版时可以只发布一次,就能在所有租户的环境上生效。
    具多租户架构的应用软件虽可客制,但客制难度较高,通常需要平台层的支持与工具的支持,才可降低客制化的复杂度。
          多租户实现的过程中涉及到的技术是什么
          多租户技术的实现重点,在于不同租户间应用程序环境的隔离(application context isolation)以及数据的隔离(data isolation),以维持不同租户间应用程序不会相互干扰,同时数据的保密性也够强。
    应用程序部份:通过进程或是支持多应用程序同时运行的装载环境(例如Web Server,像是Apache或IIS等)来做进程间的隔离,或是在同一个伺服程序(server)进程内以运行绪的方式隔离。
    数据部份:通过不同的机制将不同租户的数据隔离,Force是采用中介数据(metadata)的技术来切割,微软 MSDN 的技术文件则是展示了使用结构描述的方式隔离。
          多租户就是说多个租户共用一个实例,租户的数据既有隔离又有共享,从而解决数据存储的问题。从架构层面来分析,SaaS区别于传统技术的重要差别就是Multi-Tenant模式。
    SaaS多租户在数据存储上存在三种主要的方案,分别是
    1.独立数据库
            这是第一种方案,即一个租户一个数据库,这种方案的用户数据隔离级别最高,安全性最好,但成本也高。独立数据库是一个租户独享一个数据库实例,它提供了最强的分离度,租户的数据彼此物理不可见,备份与恢复都很灵活
    优点:
         为不同的租户提供独立的数据库,有助于简化数据模型的扩展设计,满足不同租户的独特需求;
    如果出现故障,恢复数据比较简单。
    缺点:
          增大了数据库的安装数量,随之带来维护成本和购置成本的增加。
    这种方案与传统的一个客户、一套数据、一套部署类似,差别只在于软件统一部署在运营商那里。如果面对的是银行、医院等需要非常高数据隔离级别的租户,可以选择这种模式,提高租用的定价。如果定价较低,产品走低价路线,这种方案一般对运营商来说是无法承受的。
    2.共享数据库,独立Schema
          这是第二种方案,即多个或所有租户共享Database,但一个Tenant一个Schema。将每个租户关联到同一个数据库的不同 Schema,租户间数据彼此逻辑不可见,上层应用程序的实现和独立数据库一样简单,但备份恢复稍显复杂;
    优点:
          为安全性要求较高的租户提供了一定程度的逻辑数据隔离,并不是完全隔离;每个数据库可以支持更多的租户数量。
    缺点:
      如果出现故障,数据恢复比较困难,因为恢复数据库将牵扯到其他租户的数据;
      如果需要跨租户统计数据,存在一定困难。
    3.共享数据库、共享 Schema、共享数据表
          这是第三种方案,即租户共享同一个Database、同一个Schema,但在表中通过TenantID区分租户的数据。这是共享程度最高、隔离级别最低的模式。租户间数据彼此逻辑不可见,上层应用程序的实现和独立数据库一样简单,但备份恢复稍显复杂; 最后一种模式则是租户数据在数据表级别实现共享,它提供了最低的成本,但引入了额外的编程复杂性(程序的数据访问需要用 tenantId 来区分不同租户),备份与恢复也更复杂。
    优点:
           三种方案比较,第三种方案的维护和购置成本最低,允许每个数据库支持的租户数量最多。
    缺点:
         隔离级别最低,安全性最低,需要在设计开发时加大对安全的开发量;
      数据备份和恢复最困难,需要逐表逐条备份和还原。

     

      如果希望以最少的服务器为最多的租户提供服务,并且租户接受以牺牲隔离级别换取降低成本,这种方案最适合。

     

     应用场景
         小飞,小象,小君三人大学毕业后同租了一套三室两厅的房子,三人各占一间独立卧室,每间房各配一把钥匙,从而保证每个人都有自己的独立私密空间,如果别人要进入,必须通过权限验证(也就是配套的开门钥匙)才行,但厨房、餐厅、客厅这些资源是共用的(ps:为啥没提厕所?因为每间卧室都带厕所,这三人租的房有点豪!)
          这里的小飞,小象,小君就是多租户,别的租户要访问必须通过权限验证的独立卧室就是数据隔离共用的资源(厨房、餐厅、客厅)就是多租户环境下的系统和应用程序、组件
     基本步骤demo
            数据层的多租户浅谈
            Spring boot + Hibernate 多租户的使用
            eclipselink+jboss实现多租户基本配置

    具体的实现方式
          如何选型
        一般情况下,租户模型不会影响应用程序的功能,但可能会影响总体解决方案的其他方面。 以下条件用于评估每个模型:
    可伸缩性:
           租户数目。
           每个租户的存储量。
           总存储量。
           工作负荷。

     

    租户隔离: 

            数据隔离和性能(一个租户的工作负荷是否影响其他租户)。

    每个租户的成本: 

           数据库成本。

     

    开发复杂性:
           架构更改。
          查询更改(模式所需)。
          操作复杂性:
          监视和管理性能。
         架构管理。
         还原租户。
         灾难恢复。

     

    可自定义性: 

           易于支持租户特定或租户类特定的架构自定义。

     

          有关租户的讨论侧重于数据层。 但是,请花费片刻时间思考一下应用程序层。 应用程序层被视为单一实体。 如果将应用程序划分成多个小型组件,所选的租户模型可能会更改。 在所用的租户和存储技术或平台方面,可以不同的方式对待某些组件。
    实现多租户的方式:
    JPA Provider
          JSR 338 定义了 JPA 规范 2.1,但如我们已经了解到的,Oracle 把多租户的多数特性推迟到了 Java EE 8 中。尽管这些曾经在 JavaOne 大会中有过演示,但无论是在 JPA 2.0(JSR 317)还是 2.1 规范中,都依然没有明文提及多租户。不过这并不妨碍一些 JPA provider 在这部分领域的实现,Hibernate 和 EclipseLink 已提供了全部或部分的多租户数据层的解决方案。
        Hibernate 是当今最为流行的开源的对象关系映射(ORM)实现,并能很好地和 Spring 等框架集成,目前 Hibernate 支持多租户的独立数据库和独立 Schema 模式。EclipseLink 也是企业级数据持久层JPA标准的参考实现,对最新 JPA2.1 完整支持,在目前 JPA 标准尚未引入多租户概念之际,已对多租户支持完好,其前身是诞生已久、功能丰富的对象关系映射工具 Oracle TopLink。因此本文采用 Hibernate 和 EclipseLink 对多租户数据层进行分析。
    Hibernate
           Hibernate 是一个开放源代码的对象/关系映射框架和查询服务。它对 JDBC 进行了轻量级的对象封装,负责从 Java 类映射到数据库表,并从 Java 数据类型映射到 SQL 数据类型。在 4.0 版本 Hibenate 开始支持多租户架构——对不同租户使用独立数据库或独立 Sechma,并计划在 5.0 中支持共享数据表模式。
    EclipseLink
        EclipseLink 是 Eclipse 基金会管理下的开源持久层服务项目,为 Java 开发人员与各种数据服务(比如:数据库、web services、对象XML映射(OXM)、企业信息系统(EIS)等)交互提供了一个可扩展框架。
         在完整实现 JPA 标准之外,针对 SaaS 环境,在多租户的隔离方面 EclipseLink 提供了很好的支持以及灵活地解决方案。
    如何判定能否支持多租户   
         我们通常所说的应用程序对多租户的支持程度,其判定依据是基于多少核心应用层(或者SaaS)是可以让各个“租户”共享的。完全支持多租户模式指的是允许多个“租户”共享数据库的表空间、支持对业务逻辑、工作流和用户界面的定制。换句话说,所有SaaS的子层都提供对“多租户”的支撑能力;而最低程度的支持“多租户”也至少意味IaaS和PaaS层可以共享,只是每个“租户”有自己专有的SaaS层;中等程度地支持“多租户”,则是具有同样特征的一组“租户”共享数据库的表空间(schemae)及其他应用层,而不同组的“租户”有其自己的数据库和应用程序。

     

    避坑指南

     

    多租户的缺点:
          多租户架构的一个缺点就是,某一客户的问题会影响整个系统。 另外,如果集中式运作出问题,所有客户都会受到影响。没有哪家软件即服务(SaaS)提供商是完美无缺的。它们都遇到过严重的服务停用事件。不过与大多数内部数据中心的糟糕记录相比,它们的情况似乎都相当好。
          隔离:因为承租者共享软件和硬件的同一实例,所以一个承租者可能会影响其他承租者的软件的可用性和性能。例如,如果共享的软件没有充足的安全保障措施,则可能会发生因一个承租者关闭共享软件而导致对共享该实例的所有承租者拒绝服务。
          安全性:如果共享软件没有充分的安全保障措施,则一个承租者的用户可能会访问属于另一个承租者的数据。
          自定义能力:因为软件是在承租者之间共享的,所以各个承租者不可能自定义该软件。例如,在没有充足的扩展点的情况下,一个承租者不可能为业务流程提供自己的实现。
    应用程序升级将为承租者带来问题:同时升级共享的软件可能并不是所有承租者都希望的。
           恢复:在承租者之间共享数据库使得很难为每个承租者单独备份和恢复数据。
    面临的挑战
          跟我上“云”端(一)认识多租户

     

    扩展问题

     

         .net平台下多租户是怎么做的
          多租户技术可被其他技术替代吗?
          多租户架构之外的技术?

        多租户架构之外的选择是单租户架构; 在这种模式中,每个客户都运行自己的软件实例,软件可通过元数据或其他方式来配置。SAP公司为其Business by Design软件采用了单租户模式,该软件实施了众多商业应用程序。
          两者之间的区别
         多租户模式与单租户架构模式存在大片的潜在灰色区,往往被人们所忽视。单租户应用程序可由云环境中的虚拟化服务器或数据中心内的服务器来提供,单租户应用程序的各部分可以共享或不共享。比方说,应用程序采用单租户模式、而数据库进行共享这种现象并不罕见

     

    小结

         对于多租户先有一个基本的认识,接下来就是用到项目中啦,实践很重要

    展开全文
  • 多租户方案

    2016-08-01 10:33:12
    mycat多租户
  • multitenant多租户

    2018-08-10 21:18:31
    multitenant多租户 multitenant-wp-12c-1949736.pdf
  • 云计算中的多租户多租户架构 ;云计算中的多租户;为什么要多租户;一个旅馆的例子;一个旅馆的例子结论;业内的实践;业内的实践;附录关于PaaS应用容器集群;附录关于PaaS应用容器集群;Thank you. xiaoqingfeng@
  • 多租户适配 很多产品只有专属化版本,需要从产品底层进行尽量少的改造,满足上云之后多租户的数据、缓存、定时任务等隔离 多租户适配条目 条目名称 适配方案 持久层适配 支持schema和字段隔离两种方案 ...

    多租户适配

    很多产品只有专属化版本,需要从产品底层进行尽量少的改造,满足上云之后多租户的数据、缓存、定时任务等隔离

    多租户适配条目

    条目名称适配方案
    持久层适配支持schema和字段隔离两种方案
    quartz定时任务上下文无法获取租户信息,通过JobGroup识别
    reids缓存缓存key体现租户id即可
    websocket场景从cookie获取、前端调用diwork的api获取租户信息塞到cookie,后端websocket握手后从cookie获取

    1. 持久层适配

    考虑到本身业务的实际情况,要求数据源同时支持schema隔离和字段隔离,持久层的多租户适配业务代码需要零感知、无侵入,适配实现过程如下:

    STEP-1. 表结构改造,追加租户字段、有预置脚本的表,需要跟租户字段建立联合主键;
    STEP-2. 引入动态数据源,动态数据源查询租户信息,切换schema实现租户按schema隔离;
    STEP-3. 改造dao,采用cglib加入Interceptor,在dao层方法的执> 行前加入拦截;
    STEP-4. 用jsqlParser编写sql解析类,第3步拦截到的sql追加租户ID的条件;

    动态数据源关键代码

    获取租户信息中的schema信息,根据schema信息切换,租户信息通过rest接口获取,考虑了到性能已加ThreadLocal和redis两重缓存

    protected Connection changeCatalog(Connection con) throws SQLException {
            String tenantId = InvocationInfoProxy.getTenantid();
            if (StringUtils.isBlank(tenantId)) {
                tenantId = "tenant";
            }
            String catalog = this.getCatalog(tenantId);
            if (StringUtils.isNotBlank(catalog)) {
                try {
                    con.setCatalog(catalog);
                } catch (SQLException e) {
                    logger.error("Error occurred when setting catalog for connection, Tenant ID is {}", tenantId);
                    con.close();
                    throw e;
                }
            } else {
    //            logger.error("Switching catalog failed, check tenant ID -> {}!", tenantId);
                String defaultCatalog = PropertyUtil.getPropertyByKey("jdbc.catalog");
                if (StringUtils.isNotBlank(defaultCatalog) && !defaultCatalog.equals(con.getCatalog())) {
                    con.setCatalog(defaultCatalog);
                    logger.info("reset catalog for connection success!");
                }
            }
            return con;
        }
    
    dao层改造关键代码

    通过cglib代理的方式改造dao层,业务代码对租户隔离零感知

        protected MdmJdbcPersistenceManager createPersistenceManager() throws DbException {
            if (this.manager == null) {
                try {
                    this.lock.lock();
                    if (this.manager == null) {
                        MdmJdbcSession jdbcSession = ProxyFactory.getProxy(
                                MdmJdbcSession.class,
                            new Class[]{JdbcTemplate.class, DBMetaHelper.class},
                            new Object[] {jdbcTemplate, dbMetaHelper},
                            new MdmJdbcPersistenceFilter(),
                            //0 无操作
                            NoOp.INSTANCE,
                            // 执行SQL
                            new ExecuteInterceptor(jdbcTemplate, dbMetaHelper));
                        manager = new MdmJdbcPersistenceManager(jdbcTemplate, dbMetaHelper, jdbcSession);
                    }
                } finally {
                    this.lock.unlock();
                }
            }
            return (MdmJdbcPersistenceManager) this.manager;
        }
    

    Interceptor 关键代码

     @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            List<String> sqlList = new ArrayList<>();
            try {
                if (objects[SQL_INDEX] instanceof String) {
                    sqlList = Collections.singletonList(String.valueOf(objects[SQL_INDEX]));
                } else if (objects[SQL_INDEX] instanceof List) {
                    sqlList = (List<String>) objects[SQL_INDEX];
                }
            } catch (Exception e) {
                logger.error("Errors occurred when extract sql from jdbc session, details:" + e.getMessage(), e);
            }
            if (CollectionUtils.isNotEmpty(sqlList)) {
                List<String> processedSqlList = MdmSQLParser.process(sqlList);
                if (CollectionUtils.isNotEmpty(processedSqlList)) {
                    if (objects[SQL_INDEX] instanceof String) {
                        objects[SQL_INDEX] = processedSqlList.get(0);
                    } else if (objects[SQL_INDEX] instanceof List) {
                        objects[SQL_INDEX] = processedSqlList;
                    }
                }
            }
            return methodProxy.invokeSuper(o, objects);
    
        }
    
    sqlParser关键代码

    采用jSqlParser解析sql语句,并拼接租户id的条件,sql语法解析会消耗部分性能,为了提高性能加入了缓存

    public static String parseAndProcess(String oldSql) throws Exception {
        	String cacheSql = getCache(oldSql);
        	if(!CommonUtils.isNULL(cacheSql)) {
        		return cacheSql;
        	}
            Statement stmt = CCJSqlParserUtil.parse(oldSql);
            if (stmt instanceof Select) {
                Select select = (Select) stmt;
                logger.debug("select-sql处理前:" + select);
                //检查、处理select
                checkAndHandleSelectBody(select.getSelectBody());
                logger.debug("select-sql处理后:" + select);
    
            } else if (stmt instanceof Insert){
                Insert insert = (Insert) stmt;
                logger.debug("insert-sql处理前:" + stmt);
                processInsert(insert);
                logger.debug("insert-sql处理后:" + stmt);
            } else if (stmt instanceof Update) {
                Update update = (Update) stmt;
                logger.debug("update-sql处理前:" + stmt);
                processUpdate(update);
                logger.debug("update-sql处理后:" + stmt);
            } else if (stmt instanceof Delete) {
                Delete delete = (Delete) stmt;
                logger.debug("delete-sql处理前:" + stmt);
                processDelete(delete);
                logger.debug("delete-sql处理后:" + stmt);
            }
            //其他形式语句暂不处理
            putCache(oldSql, stmt.toString());
            return stmt.toString();
        }
    

    2. 定时任务适配

    通过租户开通的回调函数,在其中通过消息驱动的方式,在主数据实例中通过消费方式,来给租户启动定时任务,租户的id即为定时任务的JobGroup,这样job在执行业务逻辑时,可以通过JobGroup获取租户信息,以下代码是通过redis发布订阅方式实现,也可以通过mq实现

    final JedisPubSub jedisPubSub = new JedisPubSub() {
                @SuppressWarnings("unchecked")
                @Override
                public void onMessage(String channel, String message) {
                    try {
                        if (CHANNEL.equals(channel) && StringUtils.isNotBlank(message.trim())) {
                            channelMessage[0] = message;
                            InvocationInfoProxy.setTenantid(message);
                            //数据统计的定时任务
                            String statisticJobGroup = STATISTIC_ANALYSIS_JOB_GROUP;
                            String statisticIdleJobName = "jobDetailStatisticAnalysisBgJob";
                            String statisticIdleJobNameByDayJobName = "jobDetailStatisticAnalysisByDayBgJob";
                            if (!QuartzManager.checkExists(statisticJobGroup, statisticIdleJobName)) {
                                Class idleClazz = Class.forName("com.yonyou.iuapmdm.scheduleJob.task.StatisticAnalysisBgJob");
                                //"0 30 1,12 * * ?"
                                String idleCronExp = AppUtils.getPropertyValue(APPLICATION_PROPERTIES, "idleCronExp");
                                QuartzManager.addJob(statisticJobGroup, statisticIdleJobName, idleClazz, null, idleCronExp);
                            }
                            if (!QuartzManager.checkExists(statisticJobGroup, statisticIdleJobNameByDayJobName)) {
                                Class byDayClazz = Class.forName("com.yonyou.iuapmdm.scheduleJob.task.StatisticAnalysisByDayBgJob");
                                //"0 0 1 * * ?"
                                String byDayCronExp = AppUtils.getPropertyValue(APPLICATION_PROPERTIES, "byDayCronExp");
                                QuartzManager.addJob(statisticJobGroup, statisticIdleJobNameByDayJobName, byDayClazz, null, byDayCronExp);
                            }
                            //标签过期扫描定时任务
                            String tagExpireScanJobGroup = TAG_EXPIRE_SCAN_JOB_GROUP;
                            String tagExpireScanJobName = "jobDetailTagExpireScanBgJob";
                            if (!QuartzManager.checkExists(tagExpireScanJobGroup, tagExpireScanJobName)) {
                                Class expireScanClz = Class.forName("com.yonyou.iuapmdm.scheduleJob.task.TagExpireScanBgJob");
                                //"0 0 * * * ?" 每一小时执行一次
                                String expireScanCronExp = AppUtils.getPropertyValue(APPLICATION_PROPERTIES, "tagExpireScanCronExp");
                                QuartzManager.addJob(tagExpireScanJobGroup, tagExpireScanJobName, expireScanClz, null, expireScanCronExp);
                            }
                        }
                    } catch (Exception e) {
                        logger.error("Error occurred adding statistic analysis job, details:" + e.getMessage(), e);
                    }
                }
            };
            Thread daemon = new Thread(() -> {
                MdmCacheManager.getInstance().subscribe(jedisPubSub, CHANNEL);
            });
    

    3.redis缓存适配

    比较简单,构造key的时候体现tenantId即可

    private String buildKey(String key) {
    		String tenantId = InvocationInfoProxy.getTenantid();
    		if (StringUtils.isBlank(tenantId)) {
    			tenantId = "tenant";
    		}
    		return StringUtils.join(new String[]{tenantId, key}, ":");
    	}
    

    4.websocket场景

    从WebSocketSession中获取cookie信息,设置上下文即可

    @Override
    	public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
    		final List<String> cookie = session.getHandshakeHeaders().get("cookie");
    		setLoginInfo(cookie);
    	}
        private void setLoginInfo(List<String> cookieList) {
    		if(cookieList != null && cookieList.size()>0) {
    			String cookie = cookieList.get(0);
    			String local = CommonUtils.getULocale(cookie);
    			String _A_P_userId = CommonUtils.getCookieValue(cookie, "_A_P_userId");
    			String _A_P_userLoginName = CommonUtils.getCookieValue(cookie, "_A_P_userLoginName");
    			if (StringUtils.isBlank(_A_P_userId) || StringUtils.isBlank(_A_P_userLoginName)) {
    				_A_P_userId = CommonUtils.getCookieValue(cookie, "yonyou_uid");
    				_A_P_userLoginName = CommonUtils.decodeTwice(CommonUtils.getCookieValue(cookie, "yonyou_uname"));
    			}
    			InvocationInfoProxy.setLocale(local);
    			InvocationInfoProxy.setUserid(_A_P_userId);
    			InvocationInfoProxy.setUsername(_A_P_userLoginName);
    			String tenantId = CommonUtils.getCookieValue(cookie, "tenantId");
    			String tenantIdValue = StringUtils.isBlank(tenantId) ? "t6ecrakt" : tenantId;
    			InvocationInfoProxy.setTenantid(tenantIdValue);
    		}
    	}
    

    关注同名公主号,获取更多优质文章

    展开全文
  • 从单租户IaaS到多租户PaaS——金融级别大数据平台MaxCompute的多租户隔离实践.pdf
  • HDFS多租户实现

    2017-11-16 15:06:56
    HDFS多租户的相关介绍说明 多租户应用 多个租户共享硬件资源,硬件资源提供一个共享的应用和数据库实例。每个租户认为自己独占资源,因为实例提供高度的定制以满足租户所需。 租户 是指求租SaaS解决方案的企业...
  • 行业分类-物理装置-多租户管理系统和多租户管理系统的实现方法.zip
  • :作者们分享了他们将单租户SOA应用程序转换为多租户云应用程序的过程中获得的经验;这些最佳实践共包含7个重要的技巧。作为专业技术人员,我们的目标之一就是分享我们的经验和教训,在本例中,这些知识来自构建可以...
  • 多租户:Flux v1:使用Flux和Kustomize管理多租户集群
  • 多租户理解

    2020-10-14 17:24:29
    一、对多租户的理解  多租户定义:多租户技术或称多重租赁技术,简称SaaS,是一种软件架构技术,是实现如何在多用户环境下(此处的多用户一般是面向企业用户)共用相同的系统或程序组件,并且可确保各用户间数据的...

    一、对多租户的理解

      多租户定义:多租户技术或称多重租赁技术,简称SaaS,是一种软件架构技术,是实现如何在多用户环境下(此处的多用户一般是面向企业用户)共用相同的系统或程序组件,并且可确保各用户间数据的隔离性。简单讲:在一台服务器上运行单个应用实例,它为多个租户(客户)提供服务。从定义中我们可以理解:多租户是一种架构,目的是为了让多用户环境下使用同一套程序,且保证用户间数据隔离。那么重点就很浅显易懂了,多租户的重点就是同一套程序下实现多用户数据的隔离。对于实现方式,我们下面会讨论到。

      在了解详细一点:在一个多租户的结构下,应用都是运行在同样的或者是一组服务器下,这种结构被称为“单实例”架构(Single Instance),单实例多租户。多个租户的数据是保存在相同位置,依靠对数据库分区来实现隔离操作。既然用户都在运行相同的应用实例,服务运行在服务供应商的服务器上,用户无法去进行定制化的操作,所以这对于对该产品有特殊需要定制化的客户就无法适用,所以多租户适合通用类需求的客户。那么缺点来了,多租户下无法实现用户的定制化操作。

      在翻阅多租户的资料时,还有一个名词与之相对应,那就是单租户SaaS架构(也被称作多实例架构(Multiple Instance))。单租户架构与多租户的区别在于,单租户是为每个客户单独创建各自的软件应用和支撑环境。单租户SaaS被广泛引用在客户需要支持定制化的应用场合,而这种定制或者是因为地域,抑或是他们需要更高的安全控制。通过单租户的模式,每个客户都有一份分别放在独立的服务器上的数据库和操作系统,或者使用强的安全措施进行隔离的虚拟网络环境中。因为本篇主要是讨论多租户,所以单租户的相关知识就简单了解一下,不做过多的阐述了。

    二、多租户数据隔离的三种方案

      在当下云计算时代,多租户技术在共用的数据中心以单一系统架构与服务提供多数客户端相同甚至可定制化的服务,并且仍可以保障客户的数据隔离。目前各种各样的云计算服务就是这类技术范畴,例如阿里云数据库服务(RDS)、阿里云服务器等等。

      多租户在数据存储上存在三种主要的方案,分别是:

      1. 独立数据库

      这是第一种方案,即一个租户一个数据库,这种方案的用户数据隔离级别最高,安全性最好,但成本较高。 
      优点: 
        为不同的租户提供独立的数据库,有助于简化数据模型的扩展设计,满足不同租户的独特需求;如果出现故障,恢复数据比较简单。 
      缺点: 
        增多了数据库的安装数量,随之带来维护成本和购置成本的增加。 
      这种方案与传统的一个客户、一套数据、一套部署类似,差别只在于软件统一部署在运营商那里。如果面对的是银行、医院等需要非常高数据隔离级别的租户,可以选择这种模式,提高租用的定价。如果定价较低,产品走低价路线,这种方案一般对运营商来说是无法承受的。

      2. 共享数据库,独立 Schema 
      这是第二种方案,即多个或所有租户共享Database,但是每个租户一个Schema(也可叫做一个user)。底层库比如是:DB2、ORACLE等,一个数据库下可以有多个SCHEMA 
      优点: 
        为安全性要求较高的租户提供了一定程度的逻辑数据隔离,并不是完全隔离;每个数据库可支持更多的租户数量。
      缺点: 
        如果出现故障,数据恢复比较困难,因为恢复数据库将牵涉到其他租户的数据; 
      如果需要跨租户统计数据,存在一定困难。

      3. 共享数据库,共享 Schema,共享数据表
      这是第三种方案,即租户共享同一个Database、同一个Schema,但在表中增加TenantID多租户的数据字段。这是共享程度最高、隔离级别最低的模式。 
      即每插入一条数据时都需要有一个客户的标识。这样才能在同一张表中区分出不同客户的数据。
      优点: 
        三种方案比较,第三种方案的维护和购置成本最低,允许每个数据库支持的租户数量最多。 
      缺点: 
        隔离级别最低,安全性最低,需要在设计开发时加大对安全的开发量; 数据备份和恢复最困难,需要逐表逐条备份和还原。

      如果希望以最少的服务器为最多的租户提供服务,并且租户接受牺牲隔离级别换取降低成本,这种方案最适合。 
        
      在SaaS实施过程中,有一个显著的考量点,就是如何对应用数据进行设计,以支持多租户,而这种设计的思路,是要在数据的共享安全隔离性能间取得平衡。

      因为我们用的底层库是MySQL,且要保证数据的完全隔离,所以用的方案属于第一种。独立数据库。因为MySQL下SCHEMA就是他的数据库名。所以每多服务一个用户,都需要新建一个数据库。如果是DB2或者是ORACLE的话,一个数据库下,可以采用独立的SCHEMA来进行数据隔离,这样会相对节省成本,且数据隔离的强度高。

    三、选择合理的实现模式 
      衡量三种模式主要考虑的因素是隔离还是共享

      成本角度因素 

        隔离性越好,设计和实现的难度和成本越高,初始成本越高。共享性越好,同一运营成本下支持的用户越多,运营成本越低。

      安全因素 

        要考虑业务和客户的安全方面的要求。安全性要求越高,越要倾向于隔离。

      从租户数量上考虑
        主要考虑下面一些因素 
        系统要支持多少租户?上百?上千还是上万?可能的租户越多,越倾向于共享。 
        平均每个租户要存储数据需要的空间大小。存贮的数据越多,越倾向于隔离。 
        每个租户的同时访问系统的最终用户数量。需要支持的越多,越倾向于隔离。 
        是否想针对每一租户提供附加的服务,例如数据的备份和恢复等。这方面的需求越多, 越倾向于隔离

      技术储备 
        共享性越高,对技术的要求越高。

    展开全文
  • 多租户模型

    万次阅读 2019-03-05 20:38:27
    真正意义上的SaaS一定是多租户的,但是多租户根据隔离程度的不同又分为不同模式。 多租户根据隔离程度和共享程度分为三种模型,其实就是在共享程度与隔离程度的权衡选择。 共享程度越高,租户成本自然越低,技术...
  • 多租户JVM最近以技术预览的形式作为IBM SDK Java™技术版本7版1的一部分提供。 通过在单个多租户JVM中运行多个应用程序,云系统可以加快应用程序的启动时间并减少其内存占用。 本文介绍了多租户云JVM背后的技术,并...
  • oracle多租户架构讲座

    2018-10-24 13:56:40
    oracle12c 多租户架构实践,主要针对oracle多租户的架构进行详细讲解。
  • 多租户代码 根据域名切换
  • 多租户容器网络.pdf

    2021-07-24 10:11:41
    刘梦馨:多租户容器网络.pdf
  • 多租户理解(转载)

    2021-01-07 14:57:30
    什么是多租户呢,你了解多租户吗,多租户是什么呢 叙述 多租户的历史: 多租户技术源于1960年代,许多公司为了要使用更多的运算资源,向持有大型主机(Mainframe)的供应商租用一部分的运算资源,而这些用户经常会...
  • 多租户 多租户(Multi Tenancy / Tenant)是一种软件架构,其定义是:在一台服务器上运行分区应用实例,它为多个租户提供服务。 概念是抽象的,但是理解起来并不困难,简单来说就是分组,举个例子:我们管理学校学生...
  • 多租户技术

    2019-10-12 17:41:38
    多租户(Muti-Tenancy),探讨与实现于多用户的环境下功能用相同的系统或组件,并且仍可确保各用户间数据的隔离性。 多租户技术关键:不同租户间的环境隔离与数据隔离 数据面:数据切割 程序面:程序挂载 系统面:...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 41,379
精华内容 16,551
关键字:

多租户