同步操作将从 陈俊波/短信平台 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
我们是一个分布式的项目,采用Spring Cloud 的方式进行开发,注册中心暂时使用Eureka,各个服务的配置可以保存在配置中心中
在分布式系统注册中心是一个非常重要的组件,因为所有的服务的地址不确定,数量不确定,所以需要有一个地方来帮我们保存所有的服务器列表
这个是本项目的注册中心
spring:
application:
name: smsplatform-eureka
server:
port: 10000
eureka:
client:
service-url:
defaultZone: http://localhost:10000/eureka
fetch-registry: false
register-with-eureka: false #使用的单机的
region: 华北 #区域是华北,客户度注册的时候也要加上这个region
我们的项目会分很多个模块,每个模块分很多个机器,一旦配置文件发生变化,需要更新很多个服务器,维护起来非常不方便 我们通过将配置统一保存到某个位置,然后通过某种机制同步即可,我们使用的是config
这个是我们项目中统一配置中心的服务端, 配置文件保存在git中,然后通过注册中心进行注册,客户端通过服务发现找到配置中心拉取配置
2020之后的springcloud config客户端需要添加以下单独的依赖包,否则会出错
我们的项目有大量的地方需要通过缓存来获取数据,所以我们将缓存单独剥离出来作为一个服务,方便修改扩展 缓存也可以作为SDK封装起来给其他的程序导入,但是不方便扩展 我们的缓存使用的是redis来实现的,redis是一个基于KEY-VALUE类型的内存型的非关系型数据库,因为在内存中,速度比较快,处理请求的线程是单线程,没有切换带来的开销,但是处理其他操作的仍然是额外的线程
这个是我们的缓存的模块,然后在内部进行缓存的相关操作 因为redis 的配置会放在配置中心,所以需要bootstrap文件来帮忙加载配置文件
#在当亲文件中进行配置文件的初始化,在线读取配置,因为我们的程序在启动起来进行对象初始化的时候就必须有配置了
eureka:
client:
service-url:
defaultZone: http://localhost:10000/eureka
region: 华北
spring:
application:
name: smsplatform-cache
cloud:
inetutils:
ignored-interfaces: [ 'VMware.*' ] #忽略以Vmware开头的网卡,防止注册服务的时候ip错误导致无法访问
config:
discovery:
enabled: true #开启注册中心的服务发现,也就是从注册中心获取配置中心服务器的相关信息
service-id: smsplatform-config
profile: dev #实际查找的文件叫 smsplatform-cache-dev.yml
# redis:
# host: 10.9.12.200
# port: 8400
# password: redis001
我们的项目中对缓存的操作会保存各种类型的数据, 那么到底以什么统一的格式保存到redis中呢? 我们此处选了key是string,value是json的方式
@Bean
public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory connectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
StringRedisSerializer keySerializer = new StringRedisSerializer();//这是一个string类型的序列化方式
redisTemplate.setKeySerializer(keySerializer);
redisTemplate.setHashKeySerializer(keySerializer);
Jackson2JsonRedisSerializer<Object> jsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
redisTemplate.setValueSerializer(jsonRedisSerializer);//我们的value到底如何序列化成内容,比如我们以json的方式,就需要一个json类型的序列化
redisTemplate.setHashValueSerializer(jsonRedisSerializer);
return redisTemplate;
}
我们在操作缓存的时候可能会给某个key单独设置过期时间,过期时间到底能不能是负数 这里取决于我们是否对过期时间的功能进行了扩大化(耦合),比如可以通过过期时间删除指定的key 但是我们一般不会对任何功能进行扩大化处理,因为一旦面临修改可能会出现问题,所以过期时间理论上不能为负数 如果要让一个key过期删掉,可以直接请求删除操作
自增的时候正数是增加,负数是减少,步长可以是正数,也可以是负数,但是就是不能为0,因为你自增0就没有任何意义,不应该发起请求 如果期望 通过自增0来拿到当前的值实际上是对自增功能的扩大化,实际上可以直接通过get操作进行获取
当前模块的主要作用是接收客户群体发来的请求,请求的目的是给指定的手机号码发送指定的内容
有客户在我们的平台上注册充值了,目的发送短信,结果账号和密码泄露了,有人伪装是这个客户来进行发送短信的操作 我们为了尽量避免出现该情况,所以我们应该做一个处理措施,即便是访问者在知道账号密码的情况下也不能发送短信
客户传递过来的手机号很有可能不符合规则,虽然客户可能会有前端校验等等措施,但是我们为了保证安全健壮性我们一定要校验 客户可能会传递多个手机号过来,我们需要将多个手机号进行分拆,从一个字符串(数组)转成多个字符串
我们需要对发送短信的人的信息进行校验,不能随便就允许别人发送短信,需要进行相应校验措施
短信的长度是有限制的,不会无限制的长,我们需要对内容进行长度检测,如果超出长度则拆分短信为多条
客户通过我们发送的短信的最终结果到底是成功还是失败, 失败的原因是什么,我们都需要告诉客户,所以这边还有返回结果的操作,但是有些结果并不是实时返回的
策略的作用是通过不同的策略将信息进行相关的操作,比如判断手机号码是不是黑名单,有没有敏感词,有没有钱,有没有限流 包括数据的补全, 省市信息等进行补全 经过上面的分析我们发现策略模块需要做的事情非常多,有多少? 不确定,那么就出现了一个问题,我们到底执行什么策略? 如果增加了一个策略,导致代码发生重大变化怎么办? 最好的方式是不管有什么策略,你增加也好,减少也好,甚至调整顺序也好,我们预先写好的代码不变,只是单纯增加策略类即可 我们想想web中的 filter,我们通过配置文件配置了很多个过滤器, 过滤器就执行了,有个地方一定能从配置文件中读取到所有的配置的过滤器 然后执行里面的方法,执行什么方法?所以过滤器必须实现filter`,就是为了保证不乱传递类型以及可以确定传递过来的对象中一定有某个方法 然后我们就可以写一个类, 在类里面解析xml文件,拿到所有的filter然后遍历执行指定的方法
此处策略暂时使用统一策略机制,也就是所有的用户使用同一个策略分组,后续可以针对不同的用户开启不同的策略分组,比如计费可以按次,包月无限次,包月有限次,包次数不限时间,包次数限时间等不同策略
策略模块会从MQ中获取所有的消息,第一给事情需要先连接MQ,拿到消息后需要经过策略模块的处理,就需要用上面的思路来对消息进行处理
黑名单怎么处理? 首先要判断是不是在黑名单中? 如何判断? 首先要找到黑名单是如何存档的,存放在什么地方 我们当前的黑名单是通过 BLACK:手机号的方式作为key保存到redis中的,所以我们可以通过 将手机号拼接 BLACK:的方式去查询
如果存储的方式换一个,则判断方式也跟着换, 比如我可以用一个key 将所有的黑名单保存起来到redis的set,然后判断手机号在不在这个set中
方式三, 我们的策略模块在程序启动的时候从redis中同步一份黑名单过来,保存在本地, 后面进行判断的时候从本地判断
在内容中不允许出现的内容叫敏感词 如何判断内容中有敏感词, 首先我们得先知道我们的敏感词是什么,然后通过内容的包含关系来判断内容中是否包含指定数据, 字符串的contains()
针对同一个客户向同一个手机号进行发送的次数限制,比如1分钟2次,一小时5次, 一天10次,这个值可以动态变化 当然客户也可以指定自己每天最多发多少条短信,超出后不再发送,此处我们暂时设置统一的限流机制,后续可以增加针对不同客户不同限流机制的措施
如果数据我们保存在redis,首先要确定的是key是什么,当前的策略是每个客户针对某个手机号的, 比如建设银行给13888888888发送的次数不应该影响工商银行给13888888888发送 那么这个key就必须包含 客户信息和手机号信息 比如客户id:手机号 当我们确定了key之后,需要确定value的类型,zset key:value:score 我们以当前发送短信的时间为分数, 去查询指定时间分数范围内的数据长度是不是在限制的范围内,如果不是被限制的 则发送短信,并将当前时间作为分数随便设置一个value 保存到zset
我们限流的标准, 比如多少次 是可以定义的, 但是时间呢? 比如我们默认1分钟2条, 我们可以将条数自定义,通过查询缓存来获取, 但是我们如果想把限流改成五分钟三次 我们代码中写的时间固定是1 分钟 1小时, 1天,说明代码中的时间也不能直接写死, 而是可以通过某种方式获取 比如我们保存到zset, 以某个固定值如limitparam作为key, 以限流的时间作为 score,以次数作为值保存起来 这样我们可以通过查询这个zset获取到所有的限流的时间和次数 ,比如我们保存了一个 300 3 意味着是5分钟内限制3次 而且因为时间的天然顺序原因,我们的不同的限流措施还可以排序,比如 5分钟的和2小时的在这个zset中顺序可以排序
在产品的需求中,我们可能会对目标手机号的区域进行一些操作,比如说我们经过统计发现,这个发送的短信号码中,有三分之一发送到了北京,那我们可以去和北京的运营商谈合作,市内短信相对比较便宜,所以可以多加几个对应地区的合作商 那么问题来了,在什么地方能找到手机号属于什么地区(既然需要统计,就需要保存数据,数据在哪里保存了),我们在短信的内容对象中设置了两个属性,一个是省id,一个是市id,我们只需要给这两个数据设置值即可 在我们的缓存中,我们给每个号段设置了一个key,value是对应的省市id,我们可以根据手机号的前7位来获取这个数据
在我们的搜索功能中,遇到了一个问题,我们传递的搜索的条件个数和类型不一定, 可能会有某个类型的参数,可能没有,并且后续可能会添加参数个数以及类型 我们期望写一个方法,这个方法和参数的个数以及类型无关,也就是理论上不论怎么传递参数都可以实现查询
用户可以传递的参数虽然是变化的, 但是仍然是我们服务端定义好的,所以我们可以先设计一个模板,里面定义每个参数的名字以及查询的类型 用户在传递了参数过来后,我们先去匹配一下是什么类型的查询,然后才去拼接查询条件 比如: name:"phonenum",type:"term" 代表有一个参数叫phonenum 是term类型 name:"content",type:"match" 代表有一个参数叫content 是match类型 name:"starttime",type:"date",cloum :"sendate",guanlian:"stoptime",order:0 有一个参数叫starttime 是日期类型,是sendate条件的起点时间,关联的终点参数是stoptime name:"stoptime",type:"date",cloum :"sendate",guanlian:"starttime",order:1
定时任务的主要作用是在特定的时间做一些事情,比如我们的闹钟,我们用过quartz实现的定时任务 但是定时任务会遇到问题, 定时任务所在的程序理论上是独立的,那么就会出现单点故障问题,定时任务需要集群 如果是集群,就意味着每个机器上面都有一模一样的任务,那任务就重复执行了,也不行 所以我们需要一种技术,1 一定是集群,2 在集群的情况下仍然只有一个机器在执行任务,如果执行任务的机器坏了,最好能自动漂移到其他机器 我有很多个机器在执行任务,但是最终只有一个机器执行,其他机器都浪费了,万一长时间都没有损坏的机器,就会出现其他机器全部浪费的情况 比如我要是执行一个大点的耗时任务,要是这些机器能都参与进来即好了
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。