动态列的几种设计思路 在需求开发过程中,我们有时会遇到一种场景:某个具体业务中的属性是动态的。在理想情况下,我们可以使用穷举法对所有可能的属性进行分析,然后进行分类,最终形成一套解决方案。然而,现实往往是骨感的,Leader和客户通常不会给我们这个时间。因此,我们需要探讨一些更为实际的解决方案。
一、使用数据库DDL进行动态创建 优点:
操作简单,只需通过SQL管理即可实现。 缺点:
不同情况下的动态字段增加会导致表结构膨胀。 在已有数据的表中修改字段容易导致锁表,影响性能。 二、使用数据库预留字段 优点:
与数据库无关,对业务侵入性小。 缺点:
扩展性差,超出预留字段范围后如何处理新字段? 可读性差,预留字段通常为attr1、attr2等,影响字段的可读性。 性能较低,为兼容多种数据类型,预留字段通常采用较长的文本数据类型存储,影响数据库性能。 三、使用数据库中的JSON数据类型 优点:
使用简单,绝大多数编程语言都支持JSON操作,方便快捷。 对于MySQL或PostgreSQL等数据库,已原生支持JSON字段,可基于JSON进行扩展查询。 JSON采用key:value形式存储数据,可避免字段可读性差的问题,通过规范命名提高可读性。 扩展性高,增加或删除字段实现简单,直接移除key即可,不影响表性能。 缺点:
JSON字段查询操作与普通字段稍有差异,有一定复杂度。 JSON字段的索引性能有待提高。 四、使用NoSQL数据库 优点:
采用MongoDB等JSON数据库,可以快速扩展。 专业数据存储,查询等性能可针对优化,性能高。 缺点:
需要一定的学习成本。 综上所述,第一种和第二种方案若非必要,不建议采用。第三种方案在中小项目中能应对绝大多数需求。如果存储的数据较多且性能要求较高,可以考虑采用第四种方案或第三、四种方案相结合。
支持动态列的数据库 MariaDB 通过创建BLOB列(最大64k?),可以使用mariadb-dynamic-columns实现动态列。
示例: CREATE TABLE items ( id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, name varchar(100) NOT NULL, attributes BLOB ); 插入数据时使用特定函数(COLUMN_CREATE)指定动态列的数据结构,key/value形式: INSERT INTO items (name, attributes) VALUES ('MariaDB t-shirt', COLUMN_CREATE('colour','blue', 'size','XXL')), ('MariaDB t-shirt', COLUMN_CREATE('colour','blue', 'size','XL')), ('Samsung Galaxy S5', COLUMN_CREATE('colour','white', 'OS', 'Android', 'type', 'phone')), ('Samsung Galaxy Pro 3', COLUMN_CREATE('colour','white', 'size',8, 'OS', 'Android', 'resolution','1920x1200', 'type','tablet')); 查询时使用COLUMN_JSON函数,返回JSON格式的数据: SELECT name AS Item, COLUMN_JSON(attributes) AS 'Dynamic Columns' FROM items LIMIT 1; 使用COLUMN_LIST函数列举列中包含的属性,如colour、size: SELECT name AS Item, COLUMN_LIST(attributes) AS 'Attribute Names' FROM items; 查询动态列中具体的某个属性,如colour: SELECT name AS Item, COLUMN_GET(attributes, 'colour' AS CHAR) AS Colour FROM items; PostgreSQL 支持JSON数据类型,相比普通text文本字段类型,JSON数据类型强制要求列中每个存储的值都符合JSON格式规则。
多租户软件架构概述 多租户软件架构简介 在多租户软件架构中,一个应用程序实例及其底层的数据库和硬件资源服务于多个租户(或用户账户)。一个租户可以是单个用户,但更常见的是一组用户,例如一个客户组织,他们共享同一个应用程序实例。每个租户的数据与其他租户的数据相互隔离,彼此不可见,从而确保所有租户的数据安全和隐私。
多租户架构的优势 1. 低成本 成本效益:软件提供商可以通过单一应用程序实例和基础设施为多个租户提供服务,租户共同分担软件维护、基础设施和数据中心运营的成本,因此持续成本通常低于单租户安排。SaaS软件通常以可预测的月度或年度订阅价格提供,价格基于用户数量、使用级别或应用程序中管理的数据量。 2. 可伸缩性 按需扩展:租户可以根据需求进行扩展,新用户可以访问相同的应用程序实例,通常只需增加相应的订阅费用。 3. 无代码定制化 高度可配置:SaaS多租户产品具有高度可配置性,每个租户客户无需昂贵、耗时且有时风险较高的定制开发,即可根据其特定业务需求定制应用程序。 4. 持续更新与维护 持续更新:多租户软件提供商负责更新和补丁。新功能添加或修复应用无需客户参与,且只需更新一次(与单租户架构不同,提供商必须更新每个软件实例)。 5. 提高生产率 专注核心业务:租户无需管理基础设施或软件,可以专注于更重要的任务,从而提高生产率。 多租户实现方式 Red Hat 多租户实现 Red Hat 使用以下三种方式实现多租户:
1. 应用层多租户 应用层多租户:在应用程序级别实现多租户,通过逻辑隔离不同租户的数据和资源。 2. 使用命名空间 命名空间:命名空间将全局系统资源封装在一个抽象中,使其在命名空间内的进程看来,它们拥有全局资源的独立实例。对全局资源的更改对命名空间内的其他进程可见,但对其他进程不可见。命名空间的一个用途是实现容器。 3. 集群层多租户 集群层多租户:在集群级别实现多租户,通过物理隔离不同租户的资源和数据。 Linux 命名空间 Linux 命名空间是一种操作系统层级的资源隔离技术,能够将 Linux 的全局资源划分为命名空间范围内的资源,不同命名空间间的资源彼此透明,不同命名空间里的进程无法感知到其他命名空间里面的进程和资源。Linux 命名空间实现了 6 项资源隔离,涵盖了一个小型操作系统的运行要素,包括主机名、用户权限、文件系统、网络、进程号、进程间通信。Linux 命名空间是操作系统虚拟化技术(如容器)的底层实现支撑。
Approaches to implementing multi-tenancy in SaaS applications linux namespace
Crispe Matt Nigh 的 CRISPE Framework,比较适合用于编写 prompt 模板。CRISPE 分别代表以下含义:
CR: Capacity and Role(能力与角色)。你希望 ChatGPT 扮演怎样的角色。
I:Insight(洞察力),背景信息和上下文(坦率的说我觉得用Context 更好)。
S: Statement(指令),你希望 ChatGPT 做什么。
P:Personality(个性),你希望 ChatGPT 以什么风格或方式回答你。
E:Experiment(尝试),要求 ChatGPT 为你提供多个答案。
以下是这几个参数的例子:
Step Example Capacity and Role Act as an expert on software development on the topic of machine learning frameworks, and an expert blog writer. 把你想象成机器学习框架主题的软件开发专家,以及专业博客作者。 Insight The audience for this blog is technical professionals who are interested in learning about the latest advancements in machine learning.
consumer 是推还是拉? https://kafka.apache.org/documentation/#design_pull kafka 生产者端生成消息推送(push)到 broker,消费者端从 broker 拉取(pull)消息。
统一采用 pull 的方式? boker 从生产者 pull 消息。在生产者数量庞大的场景下,broker 需要管理维护很多的关系,简直是梦魇。所以采用的是生产者往 broker push 消息。
统一采用 push 的方式? broker 往 consumer push 消息。在 consumer 多样化的场景下,如果生产者的生产速率远远大于消费者,broker 控制不好,push 会造成 consumer 不堪重负。反之,由 consumer 根据自身处理速率来决定何时从 broker 拉取消息,会更好。采用 pull 的弊端在于,如果 broker 没有消息,那么就会空转,这可以通过在调用 poll 接口时传入等待时间阻塞或者传入批次数据包大小来等待阻塞。
kafka 的 ack 机制 客户端连接到 leader broker 上,发送消息之后,等待或不等待 leader broker 的 ack(是否等待取决于参数request.required.acks 配置)。比如: 客户端设置 acks=0,客户端不作任何等待,即使消息没有写入 kafka 集群。 客户端设置 acks=1,客户端会等待 Leader 副本成功写入后返回的确认,但如果 Leader 副本在消息被同步到其他副本之前崩溃,消息可能会丢失。 客户端设置 acks=all 或 acks=-1参数,leader 在接收到客户端的消息之后,先写入日志文件,然后往同步副本(ISR)发送数据,等所有的 follower 都确认消息写入成功后,leader 再给客户端发送 ack 确认。
将博客生成器从原来的 Hexo 换成了 Hugo。
特别感谢闫博推荐 Hugo 这个静态网站生成器并提供 技术支持!
通过 Docker 命令行工具与 Docker Daemon 进程通讯使用。
使用步骤 安装 Docker 到宿主机(物理机) 到仓库拉取镜像
https://hub.docker.com 运行镜像产生容器(实例,一个镜像可以运行多个) 镜像操作 搜索镜像 docker search imageName 拉取镜像 docker pull imageName:tag tag 版本号,如不指定默认是 latest 查看本地镜像 docker images 删除镜像 docker rmi imageID docker rmi [repositoryname]:[tag] 删除所有未加标签的镜像(untagged) docker rmi $(docker images | grep "^<none>" | awk "{print $3}") 重命名镜像 docker tag imageId imageName:tag 通过本地 Dockerfile 文件编译镜像文件 docker build -t mop:latest - < mopDockerfile 容器操作 进入容器 sudo docker exec -it ubuntu bash docker exec -it ardupilot bash 查看容器 docker ps -a -a:所有容器,包括停止的 -q:查看停止的容器,不加选项默认查看运行中的容器 保存容器 sudo docker save ubuntu > ubuntu_save.
介绍 Lombok 是提升 Java 编码效率常用的工具,借助它开发人员可以使用注解来自动生成一些模版代码。比如 getter、setter、equals、toString 等方法。
安装 在 maven 中添加依赖 <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.4</version> <scope>provided</scope> </dependency> 在编辑器中添加插件 如:Intellij Idea,在 setting 的 plugin 里搜索lombok plugin,安装插件
使用 常用的几个注解
@Data :注在类上,自动生成类的 get、set、equals、hashCode、canEqual、toString 方法
@AllArgsConstructor :注在类上,自动生成类的全参构造方法
@NoArgsConstructor :注在类上,自动生成类的无参构造
@Setter :注在属性上,自动生成 set 方法
@Getter :注在属性上,自动生成 get 方法
@EqualsAndHashCode :注在类上,自动生成对应的 equals 和 hashCode 方法
@Log4j/@Slf4j :注在类上,自动生成对应的 Logger 对象,变量名为 log
@Cleanup(“close”):注在本地变量上,自动释放资源(如:关闭 InputStream)
@Synchronized:注在方法上,自动生成一个私有锁变量
@SneakyThrows:自动生成异常处理语句
注意继承关系中使用 Lombok @EqualsAndHashCode 与 @ToString 注解默认情况下忽略父类的成员变量。譬如打印时 toString 返回的结果中缺少父类的成员变量,解决办法是在注解中设置 callSuper 属性为 true, @ToString(callSuper = true) 、@EqualsAndHashCode(callsuper = true)。
WebViewJavascriptBridge 是一个可以让 OC 与 JS 进行交互通信的第三方开源库。相比其他热门的第三方库,WebViewJavascriptBridge 代码量比较少,并且设计优雅巧妙,可以说是 “小而美”。
WebViewJavascriptBridge 库在 OC 端和 JS 端都有对等的逻辑实现,事先注册 handler,内部维护一个消息队列。透明的 iframe HTML 元素和 webview 的 stringByEvaluatingJavaScriptFromString 是通信的关键。OC 端发消息给 JS 端比较直观,调起 stringByEvaluatingJavaScriptFromString 执行脚本传入消息即可。JS 端发消息给 OC 端,需要事先把消息存到队列中,然后借助 iframe 发起一个伪请求,伪请求会被 webview 的代理方法拦截下来,OC 端因此得知 JS 端消息队列中有消息,最后调起 stringByEvaluatingJavaScriptFromString 方法解析 JS 方法拿到队列中的消息并处理。交互流程见下图:
整个库只有以下几个文件:
WebViewJavascriptBridge.h WebViewJavascriptBridge.m WKWebViewJavascriptBridge.h WKWebViewJavascriptBridge.m WebViewJavascriptBridgeBase.h WebViewJavascriptBridgeBase.m WebViewJavascriptBridge_JS.h WebViewJavascriptBridge_JS.m 一般使用只需要关注 WebViewJavascriptBridge 类提供的接口,这个类的主要职责是用来做 Mac 和 iOS webview 的适配(包括 WKWebView,但是这部分代理出去给 WKWebViewJavaScriptBridge 类)并为客户端提供便利的使用接口。WebViewJavascriptBridgeBase 类负责有关数据加工、消息队列管理、消息派发及回调的处理工作。WebViewJavascriptBridge_JS 类包含 JS 端的实现代码,通过宏处理返回 JS 端实现代码的一个 OC 字符串,便于在适当时机将其注入到文档模型中完成 bridge 的初始化。
在 iOS 开发中,一般打发布包都是在本地打包,也就是工程师在自己开发电脑上使用 Xcode 编译并导出安装包来进行发布,为了提高效率可能会制作一些自动化打包脚本。本文聊的是远程打包的内容,通过资源拷贝及参数替换然后编译完成打包。
由于 HTML5 跨平台的特点,很多技术团队考虑到代码复用,在部分模块中会采用 h5 来描述界面。甚至有些不需要太复杂交互的 app,全部界面采用 h5 来编写,也就是一个 web 工程。对于大部分现有的 web 工程,能打包成 app 就已经满足了业务诉求。DCloud 团队开发的 HBuilder(IDE)工具中提供了云打包的功能,用起来很方便,简单的说,就是把 web 工程上传到云打包服务器,最后打包生成 app,点击下载即可安装使用。
虽然云打包服务很方便,但上传源码总感觉不太妥当,总有些秘密不想让别人看见,并且其他同事也有打包的需求,但不一定会使用 HBuilder。因此,搭建一个自己的打包服务很有必要。
按照 HBuilder 提供的云打包功能,先定一个初步的需求:
支持修改应用 id、版本号 、icon、启动图 支持导入签名文件 开工!!!
准备工作 首先,需要一台安装了 MacOS 的电脑(当做服务器使用)。
笔者手头上刚好有台闲置的电脑就拿来当服务器使用了,装了 WMWare,然后装了 MacOS 虚拟机(问题较多,不建议使用虚拟机)。
物理机 windows7,内存 4G;虚拟机 MacOS,内存 3G。
其次,在服务器上部署一个 web 服务,提供打包交互界面方便客户端上传资源文件及下载安装包。我们的界面只提供了一个 www zip 包的上传入口,所有应用资源及打包相关的配置文件都在里面。www 目录结构如下:
appConfig.json 文件内容 { "id":"com.domain.pack", "appName":"我的应用", "debug":true, "launchPath": "index.html", "version": { "name": "1.0.0", "code": "100" }, ... } launchPath 对应 web 应用入口文件,iOS 工程使用这个文件路径作为 webview 的加载入口。