当前位置: 首页 > news >正文

SaaS 系统架构设计经验总结

2B SaaS系统最近几年都很火。很多创业公司都在尝试创建企业级别的应用 cRM, HR,销售, Desk SaaS系统。很多SaaS创业公司也拿了大额风投。毕竟SaaS相对传统软件的优势非常明显。

最近一年,有幸架构一个Crm SaaS 系统,上线了几个月来,各方面都比满意。整个系统创建过程,踩了很多坑,收获也比较多。总结一下SaaS系统架构一些特点:

1.分层设计

SaaS 系统架构经验总结

SaaS系统分层大概是:

租户识别>应用层>数据访问层>缓存层>数据库

  • 业务代码都是写在应用层。
  • 租户识别可以用spring拦截器实现,然后使用ThreadLocal传递给后端
  • 数据库和缓存层对应用层应该是透明的。程序员在写代码的时候,只关心业务逻辑,不应该担心多租户的问题。

2.数据隔离要透明

SaaS系统说起来很简单,任何系统似乎加个tenant_id(租户id)就变成SaaS系统了。比如原来的用户登录是:

 
 
  1. select username,password from users where email='abc@qq.com' 

改成

 
 
  1. select username,password from users where email='abc@qq.com' and tenant_id =1; 

对于复杂业务的SaaS系统,这样做法非常危险,而且开发效率很低。你想想如果那个程序员写sql时候忘了加 “ and tenant_id =1” . 结果不堪设想。

比较好做法是在数据库访问层对SQL进行改写。

 
 
  1. TenantContext.exec("select username,password from users where email='abc@qq.com' "); 

在连接池根据TenatnContext改写Sql.

这样做好处是,一来程序猿最多把系统搞down了,也不至于信息串了互相泄露。二来将来做分表分库也很方便,上层应用不用修改。

3. 租户识别方案

比较好做法是通过url识别租户。系统是给租户生成一个随机的三级域名,比如 abc.crm.baidu.com. 如果客户想使用自己的域名,可以在cname到我们生成的三级域名,并在管理系统里面做绑定。

这样一个租户可以有两个域名,访问SaaS,一个随机生成的三级域名,另外一个租户自己的域名.代码里面可以根据过来的域名,判断是那个租户然后初始化TenantContext.

如果不想通过域名来做,也可以通过登录名来判断。这种方式要涉及到租户切换问题。

4. 智能DNS

(以后补充。)

5. 租户管理系统(计费,订购,定制,充值,催缴)

SaaS系统是必须考虑计费系统和租户控制系统。这个系统需要都是独立设计。比如那个租户购买了那些模块,一个月多少钱。租户可以创建最多的用户数。计费到期邮件提醒等功能。

计费方式一般有两种,周期性计费,类似月租方案,和使用量计费,用多少付多少。 周期性计费比较简单。也可以两者结合起来。

6. 定制化开发

SaaS的优势在于一套系统多人使用,似乎和定制化开发有冲突。比如A客户想要A功能,B客户不想要。但定制化开发是无法避免的,比如CRM系统这样复杂的系统,不可能一套系统满足所有公司的要求。定制化开发尽可能分系统,分模块去做。然后通过控制台中配置不同租户订购不同模块,那些模块可以在前端页面上显示。不同的子系统需要分开部署。前端可通过nginx根据url分发,比如 abc.crm.baidu.com/bi/xxx/xx这个地址,就分发到BI子系统。不要尝试OSGI去搞模块化,这个是个大坑。

还有开发和产品,现有需求一定要分析清楚,不要一上线发现后患无穷。新功能尽量做的独立可以配置。

7. 灰度升级

SaaS付费企业客户对系统问题都特别敏感。 为了减少升级可能出现问题的影响范围,一般都采用灰度升级策略。如果使用了url来区分不同租户,灰度升级配置就会很方便。可以配置nginx 来根据域名做分发,比如租户A(aaa.com)到实例1(版本1.0),租户B(bbb.com)到实例2(版本). 当需要域名配置非常多的时候,nginx配置文档会乱。这块时候可以考虑使用nignx_lua来写一些扩展模块。

8. 容量估计

暂时先写这么多了。有时间再补充。


本文作者:garytalk

来源:51CTO

相关文章:

  • 再总结一下周金根老师的自我导向型学习方法
  • 《工业控制网络安全技术与实践》一一2.5 PLC设备的技术原理
  • 《深度学习:Java语言实现》一一第1章 深度学习概述
  • Greenplum在企业生产中的最佳实践(下)
  • freemarker积累
  • 微信小程序UI组件、开发框架、实用库
  • Charles抓包(iOS的http/https请求)
  • Python模块-pygal之条形图
  • Rancher如何对接Ceph-RBD块存储
  • 鹏博士前三季度营收66亿 同比增长15.24%
  • Spring根据包名获取包路径下的所有类
  • SCRUM敏捷开发规则一栏
  • mysql 下 计算 两点 经纬度 之间的距离
  • iOS-点击状态栏返回到顶部效果
  • Golang高性能json包:easyjson
  • 《用数据讲故事》作者Cole N. Knaflic:消除一切无效的图表
  • 【140天】尚学堂高淇Java300集视频精华笔记(86-87)
  • 【node学习】协程
  • 【个人向】《HTTP图解》阅后小结
  • angular学习第一篇-----环境搭建
  • JavaSE小实践1:Java爬取斗图网站的所有表情包
  • js正则,这点儿就够用了
  • PHP 的 SAPI 是个什么东西
  • python docx文档转html页面
  • React 快速上手 - 07 前端路由 react-router
  • Sequelize 中文文档 v4 - Getting started - 入门
  • SpingCloudBus整合RabbitMQ
  • spring-boot List转Page
  • TiDB 源码阅读系列文章(十)Chunk 和执行框架简介
  • vue中实现单选
  • 爬虫模拟登陆 SegmentFault
  • 让你的分享飞起来——极光推出社会化分享组件
  • 试着探索高并发下的系统架构面貌
  • 小试R空间处理新库sf
  • linux 淘宝开源监控工具tsar
  • Linux权限管理(week1_day5)--技术流ken
  • Play Store发现SimBad恶意软件,1.5亿Android用户成受害者 ...
  • $jQuery 重写Alert样式方法
  • (1)Map集合 (2)异常机制 (3)File类 (4)I/O流
  • (12)Hive调优——count distinct去重优化
  • (C语言)球球大作战
  • (ISPRS,2023)深度语义-视觉对齐用于zero-shot遥感图像场景分类
  • (react踩过的坑)Antd Select(设置了labelInValue)在FormItem中initialValue的问题
  • (react踩过的坑)antd 如何同时获取一个select 的value和 label值
  • (八)Docker网络跨主机通讯vxlan和vlan
  • (差分)胡桃爱原石
  • (第61天)多租户架构(CDB/PDB)
  • (附源码)springboot猪场管理系统 毕业设计 160901
  • (机器学习的矩阵)(向量、矩阵与多元线性回归)
  • (力扣记录)1448. 统计二叉树中好节点的数目
  • (三十五)大数据实战——Superset可视化平台搭建
  • (一)ClickHouse 中的 `MaterializedMySQL` 数据库引擎的使用方法、设置、特性和限制。
  • (原創) 如何讓IE7按第二次Ctrl + Tab時,回到原來的索引標籤? (Web) (IE) (OS) (Windows)...
  • (转)详解PHP处理密码的几种方式
  • (转)总结使用Unity 3D优化游戏运行性能的经验