项目技术
购物车使用了 Cookied 和 Redis 缓存
当用户在未登录的情况下,将此购物车存入cookies , 在用户登陆的情况下,将购物车数据存入redis 。如果用户登陆时,cookies 中存在购物车,需要将cookies的购物车合并到 redis 中存储。,降低了数据库的读写次数,提升服务性能。
- 存储的是:商家ID、商家名称、购物车列表(存储商品明细),采用组合实体类封装
- 传入数据:SKU ID、SKU数量(可能为负数)
access="IS_AUTHENTICATED_ANONYMOUSLY"
用于设置资源可以在不登陆时可以访问,此配置与security="none"
的区别在于当用户未登陆时获取登陆人账号的值为anonymousUser
,而security="none"
的话,无论是否登陆都不能获取登录人账号的值(在获取时就报空指针异常,因为不走SpringSecurity)
利用Spring Security获取当前登录的用户名,存在如下两种情况:
未登录
从Cookie中提取我的购物车
向Cookie中添加商品
因为有第三方商家,所以需要判断该商品所属商家的购物车是否在我的购物车中
不存在则创建商品所属的商家购物车,并添加到的购物车列表中
存在则需要判断当前商品是否存在于商家购物车
不存在则创建商品明细并添加至该商家购物车中
存在则修改商品数量即可
数量为0时则删除该商品;购物车列表长度小于等于0时删除该商家购物车
最终将我的购物车存入Cookie
已登录
- 从Redis中提取我的购物车,根据登陆名称存放的
- 查询Cookie中是否存在我的购物车(size>0),存在则合并我的购物车(遍历,并调用上面1.2中方法)
- 向购物车添加商品(还是上面1.2中方法)
- 最终将我的购物车存入Redis
Dubbo
dubbo 在我们项目中主要用来实现不同系统之间的服务调用,由于我们项目是按照不同的功能分了不同的系统,按照三层架构又分了不同的服务,其中三层架构中的控制层作为服务的消费方,业务层和持久层共同作为服务的发布方,这样的架构实现了系统的服务化,提高了开发效率,实现了业务的解耦。
我们主要在服务的暴露方通过<dubbo:service>
标签来暴露服务,在服务的消费方通过<dubbo:reference>
标签来引用服务,注册中心我们选用的是 zookeeper,对服务的URL 进行了管理和配置。
- Dubbo 是阿里开源的 RPC(远程过程调用) 的分布式框架,提供了 SOA 服务治理方案。有5个分为是Provider(服务 的提供方)、Consumer(服务的消费方)、Container(容器)、Registry(注册中心)、Monitor(监控中心)
- 注册中心:只负责发现和注册服务。不参与数据传输,不转发请求,压力较小
- 注册中心宕机问题
- 注册中心会集群部署,其中一台宕机,自动切换到另一台。
- 全部宕机后还可以通过本地缓存通信
- 注册中心宕机问题
- 监控中心:负责统计各服务调用次数、调用时间等,统计先在内存汇总后每分钟一次发送到监控中心服务器,并以报表展示
在使用Duubo 的过程中你们遇到过什么问题?
序列化和反序列化异常
调用服务,出现消息发送失败的时候,通常是接口方法的传入传出参数是新增加的扩展类,没有实现序列化接口
怎么禁止一个服务?
通过监控中心的可视化界面,我们可以对具体的服务设置禁止,也可以设置对应的权重
Zookeeper
简介
我们项目中主要用 zookeeper 作为 Dubbo 的注册中心,集中管理所有服务的 URL;同时集中的管理集群的配置。
为什么要用zookeeper 作为dubbo 的注册中心?
zookeeper 的数据全部存储在内存中,性能高;其次, zookeeper 也支持集群,实现了高可用;同时基于 zookeeper 的特性,也支持事件监听(服务的暴露方发生变化,可以进行推送),所以zookeeper 适合作为 dubbo 的注册中心区使用。
集群简介
为什么搭建Zookeeper集群?
- 大部分分布式应用需要一个主控、协调器或者控制器来管理物理分布的子进程。目前,大多数都要开发私有的协调程序,缺乏一个通用机制,协调程序的反复编写浪费,且难以形成通用、伸缩性好的协调器,zookeeper提供通用的分布式锁服务,用以协调分布式应用。所以说zookeeper是分布式应用的协作服务。
- zookeeper作为注册中心,服务器和客户端都要访问,如果有大量的并发肯定会有等待。所以可通过zookeeper集群解决。
Leader选举
Zookeeper的启动过程中leader选举是非常重要而且最复杂的一个环节。那么什么是leader选举呢?zookeeper为什么需要leader选举呢?zookeeper的leader选举的过程又是什么样子的?
首先我们来看看什么是leader选举。其实这个很好理解,leader选举就像总统选举一样,每人一票,获得多数票的人就当选为总统了。在zookeeper集群中也是一样,每个节点都会投票,如果某个节点获得超过半数以上的节点的投票,则该节点就是leader节点了。
以一个简单的例子来说明整个选举的过程: 假设有**五台服务器组成的zookeeper集群,**它们的id从1-5,同时它们都是最新启动的,也就是没有历史数据,在存放数据量这一点上,都是一样的.假设这些服务器依序启动,来看看会发生什么 。
- 服务器1启动,此时只有它一台服务器启动了,它发出去的报没有任何响应,所以它的选举状态一直是LOOKING状态
- 服务器2启动,它与最开始启动的服务器1进行通信,互相交换自己的选举结果,由于两者都没有历史数据,所以id值较大的服务器2胜出,但是由于没有达到超过半数以上的服务器都同意选举它(这个例子中的半数以上是3),所以服务器1,2还是继续保持LOOKING状态.
- 服务器3启动,根据前面的理论分析,服务器3成为服务器1,2,3中的老大,而与上面不同的是,此时有三台服务器选举了它,所以它成为了这次选举的leader.
- 服务器4启动,根据前面的分析,理论上服务器4应该是服务器1,2,3,4中最大的,但是由于前面已经有半数以上的服务器选举了服务器3,所以它只能接收当小弟的命了.
- 服务器5启动,同4一样,当小弟
RabbitMQ
为什么使用消息队列?
我们CMS服务有个发布接口,发布成功后需要将生成的静态HTML文件下载到其对应的服务器。可以使用MQ来实现解耦和异步
消息队列有什么优缺点?
- 优点:解耦、异步、削峰
- 缺点:系统复杂度提高、可用性降低,会有一致性问题
ActiveMQ、RabbitMQ、RocketMQ、Kafka 有什么优缺点?
- ActiveMQ:社区不活跃
- RabbitMQ:时效性为微秒级,基于erlang开发,并发性好
- RocketMQ:阿里开源,捐赠给Apache,功能完善,分布式。可以消息0丢失
- Kafka:用于大数据领域
如何保证消息队列的高可用?
采用镜像集群模式。创建的 queue,无论元数据还是 queue 里的消息都会存在于多个实例上,每次你写消息到 queue 的时候,都会自动把消息同步到多个实例的 queue 上。
如何保证消息不被重复消费?如何保证消息消费的幂等性?
保证被消费的数据在数据库中只有一条记录!
- 每次写数据库时根据主键之类查询,有就不保存了
- Redis每次都是set,天然幂等
- 若是用类似分布式id等全局id,消费前查询一下,保证别重复消费即可。或用唯一约束来制约
如何保证消息的可靠性传输?如何处理消息丢失的问题?
不用RabbitMQ的事务机制,同步会吞吐量下降,损耗性能
MQ开启持久化,持久化交换机/队列/消息
生产者开启confirm模式(分配id),发送到MQ并持久化则返回成功,否则返回失败,此时可以重试。定时重试
消费者关闭自动ACK机制,在处理完业务后才ACK,否则MQ会把消息交给其他消费者处理
如何保证消息的顺序性?
拆分多个 queue,每个 queue 一个 consumer,就是多一些 queue 而已,确实是麻烦点。
如何解决消息队列的延时以及过期失效问题?消息队列满了以后该怎么处理?
消费端每次消费之后要写 mysql,结果 mysql 挂了,消费端 hang 那儿了,不动了;或者是消费端出了个什么岔子,导致消费速度极其慢。
- 扩容
- 过期消失:将过期消息查询出来,并传入MQ
发布订阅模式
1、每个消费者监听自己的队列。
2、生产者将消息发给broker,由交换机将消息转发到绑定此交换机的每个队列,每个绑定交换机的队列都将接收到消息
==案例:用户通知,当用户充值成功或转账完成系统通知用户,通知方式有短信、邮件多种方法 。==
路由模式
1、每个消费者监听自己的队列,并且设置routingkey。
2、生产者将消息发给交换机,由交换机根据routingkey来转发消息到指定的队列。
带通配符的路由模式
==根据用户的通知设置去通知用户,设置接收Email的用户只接收Email,设置接收sms的用户只接收sms,设置两种通知类型都接收的则两种通知都有效。==
//绑定email通知队列
channel.queueBind(QUEUE_INFORM_EMAIL,EXCHANGE_TOPICS_INFORM,"inform.#.email.#");
//绑定sms通知队列
channel.queueBind(QUEUE_INFORM_SMS,EXCHANGE_TOPICS_INFORM,"inform.#.sms.#");
为什么使用消息队列?
- 解耦:其他系统都要这个数据,只需要订阅这个队列即可,不和本系统直接打交道
- 异步:对于写操作来说,不需要获取结果,可以采用异步方式
- 削峰:存放在MQ中,慢慢消费,无论有多少请求进入MQ
如何保证消息队列的高可用?
镜像集群模式:创建的 queue,无论元数据还是 queue 里的消息都会存在于多个实例上,每次你写消息到 queue 的时候,都会自动把消息同步到多个实例的 queue 上。
RabbitMQ如何保证可靠性传输?
持久化:持久化交换机、队列、消息
ACK确认机制:
- 消息发送确认:
- ConfirmCallback消息正确到达 Exchange 中时回调(有个消息唯一标示来确定哪条消息)
- ReturnCallback消息没有正确到达QUEUE时触发回调,如果正确到达队列不执行
- 手动消息接收确认(根据是否包含error来确认或拒绝)
消息发送、消息接收时记录日志,定时轮询,没发送的继续发送。
SpringTask-Cron表达式
Seconds Minutes Hours DayofMonth Month DayofWeek Year(只能省略最后一位,且俩最长的不能同时出现,其中一个必为?)
@Scheduled(cron = "0 * * * * ?")
0 0 10,14,16 * * ? 每天上午10点,下午2点,4点 0 0/30 9-17 * * ? 朝九晚五工作时间内每半小时 0 0 12 ? * WED 表示每个星期三中午12点 "0 0 12 * * ?" 每天中午12点触发 "0 15 10 ? * *" 每天上午10:15触发 "0 15 10 * * ?" 每天上午10:15触发 "0 15 10 * * ? *" 每天上午10:15触发 "0 15 10 * * ? 2005" 2005年的每天上午10:15触发 "0 * 14 * * ?" 在每天下午2点到下午2:59期间的每1分钟触发 "0 0/5 14 * * ?" 在每天下午2点到下午2:55期间的每5分钟触发 "0 0/5 14,18 * * ?" 在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发 "0 0-5 14 * * ?" 在每天下午2点到下午2:05期间的每1分钟触发 "0 10,44 14 ? 3 WED" 每年三月的星期三的下午2:10和2:44触发 "0 15 10 ? * MON-FRI" 周一至周五的上午10:15触发 "0 15 10 15 * ?" 每月15日上午10:15触发 "0 15 10 L * ?" 每月最后一日的上午10:15触发 "0 15 10 ? * 6L" 每月的最后一个星期五上午10:15触发 "0 15 10 ? * 6L 2002-2005" 2002年至2005年的每月的最后一个星期五上午10:15触发 "0 15 10 ? * 6#3" 每月的第三个星期五上午10:15触发
Spring Cloud
springcloud如何实现服务注册发现
服务在发布时 指定对应的服务名(服务名包括了IP地址和端口) 将服务注册到注册中心(eureka或者zookeeper),@EnableDisscoveryClient会被带@EnableEurekaServer的Eureka服务发现并注册。
← 系统、工具