多租户架构设计与实践
写在前面本篇是笔者一年来对智能照明SaaS平台多租户改造升级的一些实践与体会,这些内容在技术分享会上已做了介绍,下面是一些重要的概念和思想。
多租户多租户简介多租户技术,它是一种软件架构技术,用于实现多用户环境下,共用相同的系统或者程序,并确保各个用户间数据的安全隔离。即一个单独的实例,可以为多个组织提供服务。
SaaS多租户(1)SaaS是Software-as-a-Service的缩写,意为软件即服务,即通过网络提供软件服务。(2)SaaS平台供应商将应用部署到自己的服务器上,客户可根据实际需要,通过互联网向厂商订购所需的应用软件服务,按照订购的服务多少及时间长短向厂商支付费用,并通过互联网获得SaaS平台供应商提供的服务。(3)SaaS服务基于一套标准的软件系统为成百上千个不同的客户(又称为租户)提供服务。这要求SaaS服务能够支持不同租户间的数据和配置隔离,同时支持对界面、业务逻辑、数据结构等方面的个性化需求开发。(4)由于SaaS同时支持多个租户,而每个租户又有很多用户,因此对支撑软件的基础设施平台的性能、稳定性和扩展性有很高的要求。(5)多租户是SaaS领域特有的产物。对于 ...
详解MySQL中的JSON数据类型
写在前面本篇来学习MySQL中的JSON数据类型,这在某些场景下比text类型好用很多。
JSONJSON 是 JavaScript Object Notation(JavaScript 对象表示法)的缩写,是一个轻量级的,基于文本的,跨语言的数据交换格式,易于阅读和编写。
JSON基本数据类型JSON 的基本数据类型如下:(1)数值:十进制数,前面不能有0,可以为负数或小数,也可以为 e 或 E 表示的指数。(2)字符串:字符串必须用双引号括起来。(3)布尔值:true,false。(4)数组:一个由零或多个值组成的有序序列。每个值可以为任意类型。数组使用方括号[] 括起来,元素之间用逗号,分隔。举个例子,如下所示:
1[1, "abc", null, false, "10:29:06.000000", {"id": 2}]
(5)对象:一个由零或者多个键值对组成的无序集合。其中键必须是字符串,值可以为任意类型。对象使用花括号{}括起来,键值对之间使用逗号,分隔,键与值之间用冒号:分隔。举个例子,如下所示 ...
聊一聊六种常用的属性配置读取方式
写在前面本篇来学习使用SpringBoot进行日常开发过程中,经常使用到的6种读取配置文件内容的方式,掌握和熟练使用对于提升自我能力有极大帮助。使用的SpringBoot版本为 2.7.11 。
EnvironmentEnvironment简介Environment 是 SpringBoot 的核心环境配置接口,它提供了很多方法用于访问应用程序属性,包括系统属性、操作系统环境变量、命令行参数和应用程序配置文件中定义的属性等。源码如下:
123456789101112131415161718192021222324252627282930313233public interface Environment extends PropertyResolver { String[] getActiveProfiles(); String[] getDefaultProfiles(); /** @deprecated */ @Deprecated boolean acceptsProfiles(String... profiles); boolean ...
聊一聊九种常用的分布式ID生成方案
写在前面本篇来学习9种常用的分布式ID生成方案,掌握这些对于提升自身能力有极大帮助。
为什么使用分布式ID分布式ID的定义这里以MySQL数据库为例进行说明,当我们的业务数据量不大时,单库单表完全可以支撑现有业务,数据再大一点时,使用MySQL主从同步、读写分离模式也能应付。但是当数据日益增长,达到亿级别的时候,就需要对数据库进行分库分表操作,而分库分表后需要有一个唯一的ID来标识每条数据,显然数据库的自增ID无法满足需求,那么这个 全局唯一ID 就叫 分布式ID 。
分布式ID满足的条件一般来说,分布式ID需要满足如下五个要求:
全局唯一:必须保证ID是全局性唯一;
高性能:高可用低延时,ID生成响应快,否则会成为业务瓶颈;
高可用:100%可用无法做到,但是要无限接近于100%的可用;
好接入:秉承拿来即用的设计原则,在系统设计和实现上要尽可能的简单;
趋势递增:最好有趋势递增,不过要结合实际业务进行分析。分布式ID的生成方式分布式ID的生成方式有很多种,此处列举常用的9种方式,如下所示:(1)UUID;(2)数据库自增ID;(3)数据库多主模式;(4)号段模式;(5)Redis ...
巧用分页列表缓存,快速响应用户请求
写在前面最近在写数据报表逻辑,里面大部分场景都是统计数据,对数据时效性要求不高,不过要求数据能立即响应用户。于是尝试借助缓存来加快响应用户的时间,主要用到了分页列表缓存。
直接缓存分页列表说明最简单,也是最能想到的方法就是直接缓存分页列表。举个例子,如下所示,直接将每页的数据以列表形式进行缓存:
对应的伪代码如下所示:
123456789public List<Product> getPageList(String param, int pageNum, int pageSize) { String key = "productPageList:pageNum:" + pageNum+ "pageSize:" + pageSize+ "param:" + param ; List<Product> productList = cacheUtil.get(key); if(productList != null)return productList; productL ...
基于请求参数校验的接口幂等性实现方案
写在前面前面笔者使用的都是基于token令牌校验这一方式实现接口幂等,这种方式其实比较复杂,今天来介绍另一种比较简单的方式—基于请求参数的校验,这种方式在高并发环境下优势更明显。由于请求只有一次,所以不需要从服务端获取令牌。
原理介绍基于请求参数校验这一方式原理很简单,如果在某一个时间间隔内,同一个接口接收到的请求参数一样,则说明前后请求是重复的,服务端则拒绝处理后续请求。注意由于前后端通过JOSN格式传递数据,且需要多次重复读取JSON数据,所以前面介绍的文章还是有很大的帮助。
实战第一步,新建一个名为repeat-submit的SpringBoot项目,然后在POM文件中引入redis、web和aop依赖:
123456789101112<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId></dependency><depe ...
Redis各种数据类型巧用
写在前面最近参与了一个社交系统的前期需求评审会议,里面涉及到各种社交应用场景,使用Redis无疑是最合适不过的了。通常会保存这样的信息:一个key关联一个数据集合,同时要对集合中的数据进行各种操作,诸如统计、排序等。
那么本篇笔者将结合工作实际,列举六种典型使用Redis的业务场景,如下所示:(1)判断用户登录状态;(2)统计用户连续签到情况;(3)统计每天新增和第二天用户留存数;(4)统计网站访客量(Unique Visitor,UV);(5)最新评论列表;(6)积分排行榜。
一般来说,我们面临的用户数量和访问量都是巨大的,如百万、千万级别用户数量,或者千万甚至亿级别的访问量,因此必须选择能够高效统计大量数据的集合类型。不过在此之前,首先需要了解常用的统计模式,并使用合理的数据类型来解决实际问题。
这里我们一般会使用如下四种统计类型:二值状态统计、基数统计、排序统计和聚合统计。
二值状态统计二值状态统计概念二值状态统计,即集合中的元素只有0和1这两种状态,统计对应状态出现的次数。
举个例子,用户在进行打卡签到的时候,只有签到(1)或未签到(0)这两种;判断用户是否登录,也只有已登录( ...
聊一聊Redis中的布隆过滤器
写在前面写在前面本篇来聊一聊Redis中的布隆过滤器,主要包括布隆过滤器原理、Redis集成布隆过滤器以及一个demo实战。
业务场景在实际开发过程中遇到过这种情况,用户在使用APP阅读文章时,如何做到每次推荐给该用户的文章不重复,即需要过滤掉他已经阅读过的文章。
此时有人可能会说我们可以记录每个用户的浏览历史,然后每次在推荐的时候查询用户的浏览记录并进行过滤,进而实现文章的去重。如果开发者将用户的浏览历史存储在关系型数据库中,那么就需要频繁的对数据库进行exists判断,在并发量不高的情况下还能正常响应,但是一旦并发量上来,数据库是扛不住的。
那么又有人说,可以将这些浏览历史存在缓存中。请注意,千万不要将它们存在缓存中,这样会浪费很多内存,而且缓存适合更新频率比较低的情况,而用户的浏览历史可能每时每刻都在变化。
针对上述情况,即遇到数据量较大,又需要去重的时候,就可以考虑使用布隆过滤器。一般来说,布隆过滤器适用于如下场景:(1)解决Redis中缓存穿透问题;(2)爬虫过滤,对爬虫爬过的网站进行过滤,爬过的不再爬取;(3)内容推荐,对已经推荐过的内容进行过滤,不再推荐;(4)邮件过滤, ...
功能开关在业务中的实践
写在前面之前搞过一个报名送油卡的活动,这个活动不定期举办,因此需要考虑可配置性,起初考虑后台做个时间配置框,根据指定的时间来生效,但是后续发现这样有个弊端,即只能针对一个活动或者相同时间的多个活动生效,而实际上我们的活动有多个,权衡再三决定使用功能开关来实现。
功能开关功能开关其实是一种代码可配置性的实践,说到底就是通过控制开关的状态来实现对功能的决定控制。
功能开关的实现有很多种方式,可以使用MySQL或者Redis等数据库,出于对后续数据扩展和对存储容量的考量,此处使用Bit数组来实现。
Bit数组原理既然是数组,那么下标必然从0开始,bit只有两种取值,要么为0,要么为1:
而0和1正好对应开关的关闭和启用,即ON和OFF状态。之后开发者只需定义好每个开关所在的Bit数组的索引号和状态即可,这样后续就可以通过判断开关的状态来实现对功能的控制:
可以看到使用这种方式所占用的内存空间非常少,理论上只需占用2n位的内存,n为开关的数量。
实战演示Java中对于Bit数组可以使用BitSet来实现 ,里面有很多方法,这里我们摘几个用到的方法:
12345678public class ...
使用Jenkins+Gitlab一键打包部署SpringBoot应用
写在前面在前面我们已经学会了如何通过使用Maven插件或者Dockerfile的方式,来将SpringBoot应用构建为Docker镜像并运行。当我们的应用数量较多时,还可以使用Docker Compose的方式,但是毫无疑问上面的种种还都只是手动部署,本篇来学习如何使用Jenkins和Gitlab来实现一键打包部署SpringBoot应用。
GitLabGitLab简介GitLab是一款开源的基于Git的版本仓库管理工具,开发者可以使用它来搭建属于自己的Git仓库。
GitLab安装第一步,下载GitLab的docker镜像:
1docker pull gitlab/gitlab-ce
第二步,创建对应目录:
123mkdir -p /mydata/gitlab/configmkdir -p /mydata/gitlab/logsmkdir -p /mydata/gitlab/data
第三步,启动GitLab服务:
1234567docker run -p 10443:443 -p 1080:80 -p 1022:22 \--name gitlab \--restart alway ...