0%

在之前特意穿插了一篇介绍bitmap的笔记bitmap与布隆过滤器,这里将介绍一个bitmap的应用场景,为了避免可能的风险,将具体的业务替换成了类似的场景。

需要开发一个图片标签功能:

  • 用户发布图片时,可以为图片选择预置标签或新增标签;已发布的图片可以增加/移除标签;一张图片的标签不重复,标签数量限制不能完全确定但是不会很多(十个以内)
  • 系统预置标签主要是常用的、较为标准化的,如二次元、水墨画、动漫风等。系统预置标签的文本可以修改,比如 动漫–>动漫风
  • 对用户新增的标签,不能再修改或删除标签内容;标签任意人可以使用,标签文本一致即认为相同
  • 可以按单个标签或者多个标签联合使用(交、并、差)来筛选图片

针对这一场景,设想了以下的几种方案,一一来进行说明

阅读全文 »

bitmap,这个词在许多开发场景中都会出现,比如安卓原生开发中Bitmap用于存储位图图片,但这里我们说到的bitmap是指更常见的一个数据结构,将数据按照某种规则(比如整数按照索引位置)映射到某个bit位,从而使用紧凑的bit结构记录大规模数据集合的数据状态。

bitmap的一大特点是适合的是记录大规模整数的有无状态:每个bit存储一条整数的有无状态,bitmap这一紧凑的结构就能使用极少的空间完成大量整数的状态的记录,进行快捷的查找、排序和统计。而bit为单位的存储又将带来位运算上便捷的交、并等集合运算,多个bitmap之间便捷的交、并等集合运算,在索引、标签存储等场景又极适合,我们将在另一篇文章中尝试给出一个示例。

阅读全文 »

在开发的任务调度平台中,任务执行日志分布在各个机器上,希望用户可以无感知的查看任务的执行日志内容。但日志的数量、大小较大,统一存储将带来较大的存储压力;使用任务执行器后端程序读取指定日志文件并在API响应中返回,这种方式可定制程度最高,在许多的大数据、任务调度开源项目中使用了这种方式实现。

本文将介绍另一种简单易行的方式,使用nginx代理静态日志文件,通过lua脚本读取日志文件的指定行并返回;这种方式需要每台任务执行器所在的机器都提供一个nginx代理,希望只对外暴露一个入口的话那么可以在这些nginx节点之上再增加一层nginx代理,按照节点IP路由。或者,可以通过NFS等方式将日志文件写到网络文件地址中,这样只需要一个nginx读取NFS的共享文件,大量写入的情况下NFS的稳定性、读写性能是否会存在问题?其实按照之前的实际经历,大数据平台任务的shuffle数据写入NFS/Ceph,难免会出现性能下降或者IO的“毛刺”等诸多问题,不过对一般的任务调度系统相信并不会达到这种写入量级,其实大可以放心使用。

本文主要将介绍使用nginx+lua实现指定读取日志行的实现,其他问题我们略过,诸位若想尝试可自行验证。

另外,nginx可以在编译时加入lua模块来支持lua脚本,但其实可以直接使用OpenResty(ngx_openresty,github地址在这里),内置了nginx,支持进行嵌入lua编程脚本,并扩展了大量的第三方模块,使得nginx不仅可用于静态代理,也完全可以通过各类module作为通用型的web应用服务器使用。本文将直接使用OpenResty来实现。

阅读全文 »

最近需要为系统添加三方应用接入的功能,对接入的三方应用允许获取用户的部分信息——但显然,不能将用户的账号、密码暴露给三方应用。这个过程中产生了一些思考,在此简单记录下。如果要一句话总结的话,就是:多学知识,学习前人的成果。

三方应用接入需要解决的问题

三方应用接入关键的是服务端能够识别请求来源,判断是否是已授权的三方应用,所以这首先是一个身份识别的问题。

第二个隐含的问题是系统权限设计的问题:三方应用使用用户个人数据,是否需要用户的授权。通常这是必须的,但这次系统中面向的是内部管理应用子系统的接入授权问题,认为内部数据流转不需要此流程。

身份识别那么可以使用公钥私钥——但是这样,对于服务端就需要保存所有三方应用的公钥。那么也可以按照这种思路,使用一种更轻的方式:为每个合法的三方应用分配一个ID和secret密钥,三方应用请求时使用secret对内容进行签名,服务端验证签名,则可以识别请求来源的合法性。

基于这一思路,可以设计出一种方式:

  • 服务端为三方应用分配一个可公开的ID、一个不可公开的secret
  • 三方应用使用secret对ID签名得到sign,携带自身ID与sign向服务端请求一个随机数(面向管理端子系统,默认用户同意授权),这一随机数和三方应用ID一一对应、在一定时间内有效
  • 三方应用使用secret对随机数及时间戳签名,携带签名、时间戳和自身ID发起请求
  • 服务端校验根据三方应用ID、随机数验证签名,并比对入参时间戳限制请求发起时间,从而确认请求来自合法的三方应用

这里身份认证的可信来自于签名算法的单向和secret的私密——但也并不是非常完善的,尤其是与OAuth协议的流程对比之后。不过,稍进一步分析之后也几乎可以得到结论是,不使用https(或者类似https的非对称、对称组合)的情况下,怎么做也不安全

阅读全文 »

在定位线上问题时,经常需要对JVM内存情况进行分析。在java类加载器问题思考与简单模拟热部署中为了查看类代理生成情况,介绍了图形化工具HSDB的使用,而生产上进行分析更多的是使用jmap、jstat等工具,在java 8又提供了jcmd这一新的升级版工具;查看gc日志可以了解到详细的GC信息;linux提供了GDB、trace、perf等调试利器

然而,jmap等工具有着一些难以克服的问题:需要JVM pause导致服务暂停;开发人员需要登录服务器操作(虽然可以连接远程server,但也需要开放网络端口);修改代码、测试需要重新打包发布。Arthas是阿里开源的线上监控诊断产品,克服了这些缺点,支持在线解决生产问题,无需JVM重启,无需代码更改,不会暂停正在运行的线程

不得不提的是,不管是jmap、jstat,又或者arthas,在生产环境直接使用都是有风险的。内存的dump、线程堆栈的获取,不得不暂停JVM,即使这个过程很快,也难以完全避免服务中断;部分操作可能引起JVM崩溃,影响稳定性;等等。如何线上排查?首先的大原则是,解决问题优先于定位原因,只有在解决问题且有条件的情况下才应该扎进去定位原因;然后,根据部署情况评估是否要切走问题进程的流量后再进行排查;保护现场,尽可能留存日志、线程堆栈、内存dump等信息;想办法复现验证

阅读全文 »

话说程序员是块砖,哪里需要哪里搬——组里打算试用下禅道做项目管理,这里就记录下源码包部署的过程和一些基本的配置修改

从网上搜来的介绍:禅道是第一款国产的开源项目管理软件,她的核心管理思想基于敏捷方法scrum,内置了产品管理和项目管理,同时又根据国内研发现状补充了测试管理、计划管理、发布管理、文档管理、事务管理等功能,在一个软件中就可以将软件研发中的需求、任务、bug、用例、计划、发布等要素有序的跟踪管理起来,完整地覆盖了项目管理的核心流程。

禅道选用16.5版本,本身提供了一键式部署方式,内置了php(7.1)、mysql(5.7)和httpd,拖到指定路径启动就可以开始使用,但是现在已经有了mysql、nginx环境,因此使用源码包进行部署,用nginx做转发

阅读全文 »

最近为项目整合动态多数据源,忽然想到一个问题:每种数据库需要特定的JDBC,在使用的时候只需要引入对应依赖包,那么java包是怎么引用的呢?这里至少需要解决这样的问题:

  • 必须是由java定义统一接口,各个数据库厂商或者开源社区针对数据库提供实现,不然适配这么多数据库是完全不可能也不合理的,毫无扩展性可言
  • 只能在运行时加载JDBC驱动实现类,所以需要提供一种方式,使得类加载器能够找到各个驱动的实现类完成加载

带着问题浏览了java.sql下的关键类,了解到这种机制称为SPI,即Service Provider Interface。这种模式适合为框架提供扩展点,不难联想到,spring通过spring.factories加载类也是SPI模式的应用

阅读全文 »

这几天给项目整合动态多数据源,遇到了事务和connection切换相关的问题,发现对Spring事务这块的代码没有仔细看过,就大体看下了Spring事务管理的实现

以下实现来自于sping-boot 2.6.4(spring-core 5.3.16)

这篇文章曾经发在掘金社区,是本人自己发布。

阅读全文 »

mysql8引入with语法,可将一个临时表作为本次查询生命周期内的公共表达式使用(Common Table Expressions,CTE),来简化复杂的嵌套查询,并可使用with recursive语法进行递归查询。本文对基本的使用方法进行示例,并给出了一个实际的应用场景:用户邀请记录查询

阅读全文 »

mysql在group by之后如何取出每个group下的topN?在mysql 8之前,使用max/min函数能够取出group后某列的top1的值,但是面对topN的问题无能为力,通过子查询的方式进行解决;mysql 8引入的窗口函数对于此类组内统计很实用

阅读全文 »

在mysql 5.6之前的版本,MyISAM引擎支持全文索引,但是InnoDB不支持;从mysql 5.6开始,InnoDB支持全文索引,但是不支持中文分词;从mysql 5.7.6开始,mysql内置了ngram分词器,可支持中文、日语等亚洲语种的分词。

全文索引fulltext index,可用于char/varchar/text列。在需要进行模糊查询时,如果使用 like “%target%” 进行查询,无法使用到索引。如果文本较大,为了优化效率,可以考虑全文索引。

本文将依次介绍以下内容:

  • 分词与倒排索引,了解全文索引的基本原理
  • mysql分词器配置与全文索引的创建
  • 全文索引的使用示例
阅读全文 »

java中,ClassLoader负责读取class文件、加载到JVM

类加载过程

类加载包含的过程:

  • 加载,根据类的完全限定查找字节码文件并加载、创建一个Class对象
  • 验证,验证class文件符合JVM要求
  • 准备,为static变量分配内存并初始化为初始值(不管是否显式指定了初始化,都先初始化为变量类型默认的初始值)
  • 解析,为常量值创建直接引用
  • 初始化,类的初始化(static变量初始化、static代码块执行)
阅读全文 »

之前自己使用rocketmq时写过一篇笔记,近期需要搭建一个rocketmq集群,发现rocketmq项目已经从apache毕业正式成为顶级项目,这里对笔记内容进行了更新,使用的版本和部署方式也按照rocketmq 4.9.4版本重新进行了整理

rocketmq集群角色

rocketmq架构与kafka非常相似:rocketmq集群角色分为namesrv和broker,namesrv负责broker的注册和管理、消息向broker上队列的投递/消费的路由,是管理节点。broker负责消息的存储、投递、查询,相当于工作节点,向指定的所有namesrv分别注册自身及管理的队列信息;每组broker可分为master和slave,写操作只可在master进行,读操作可以在任意节点进行。

producer向namesrv查询队列所在的broker,向这一组broker的master节点写入;consumer向namesrv查询队列所在的broker,向这一组broker的master或slave读取消息。不同组broker之间是数据分片的形式,每组broker持有一部分数据,因此可以通过扩容broker扩展消息存储量和读写性能。

阅读全文 »

使用@Import可以导入一个指定的@Configuration类作为一个bean(@Configuration类中定义的bean自然会注入),也可以使用@ImportResource导入other non-{@code @Configuration} bean definition resources;或者使用ImportSelector或ImportBeanDefinitionRegistrar来选择加载的bean。@Import导入的bean在所在类上使用@Autowired就可注入

比如,@SpringBootApplication类默认只导入所在包层级及以下层级的bean,在这一个包之外的@Configuration类就需要使用@Import才能注入(这种情况可以使用@ComponentScan等);比如,使用ImportSelector或ImportBeanDefinitionRegistrar,配合自定义注解,根据某些参数或者配置加载bean

如自定义starter时使用的@EnableAutoConfiguration,使用了@Import(AutoConfigurationImportSelector.class)注解,AutoConfigurationImportSelector类中从META-INF/spring.factories找出所有需要加载的configurations返回

阅读全文 »