项目技术

购物车使用了 Cookied 和 Redis 缓存

当用户在未登录的情况下,将此购物车存入cookies , 在用户登陆的情况下,将购物车数据存入redis 。如果用户登陆时,cookies 中存在购物车,需要将cookies的购物车合并到 redis 中存储。,降低了数据库的读写次数,提升服务性能

  • 存储的是:商家ID、商家名称、购物车列表(存储商品明细),采用组合实体类封装
  • 传入数据:SKU ID、SKU数量(可能为负数)

access="IS_AUTHENTICATED_ANONYMOUSLY" 用于设置资源可以在不登陆时可以访问,此配置与 security="none"的区别在于当用户未登陆时获取登陆人账号的值为anonymousUser ,而security="none"的话,无论是否登陆都不能获取登录人账号的值(在获取时就报空指针异常,因为不走SpringSecurity)

利用Spring Security获取当前登录的用户名,存在如下两种情况:

  1. 未登录

    1. 从Cookie中提取我的购物车

    2. 向Cookie中添加商品

      因为有第三方商家,所以需要判断该商品所属商家的购物车是否在我的购物车中

      • 不存在则创建商品所属的商家购物车,并添加到的购物车列表中

      • 存在则需要判断当前商品是否存在于商家购物车

        • 不存在则创建商品明细并添加至该商家购物车

        • 存在则修改商品数量即可

          数量为0时则删除该商品;购物车列表长度小于等于0时删除该商家购物车

    3. 最终将我的购物车存入Cookie

  2. 已登录

    1. 从Redis中提取我的购物车,根据登陆名称存放的
    2. 查询Cookie中是否存在我的购物车(size>0),存在则合并我的购物车(遍历,并调用上面1.2中方法)
    3. 向购物车添加商品(还是上面1.2中方法)
    4. 最终将我的购物车存入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. 服务器1启动,此时只有它一台服务器启动了,它发出去的报没有任何响应,所以它的选举状态一直是LOOKING状态
  2. 服务器2启动,它与最开始启动的服务器1进行通信,互相交换自己的选举结果,由于两者都没有历史数据,所以id值较大的服务器2胜出,但是由于没有达到超过半数以上的服务器都同意选举它(这个例子中的半数以上是3),所以服务器1,2还是继续保持LOOKING状态.
  3. 服务器3启动,根据前面的理论分析,服务器3成为服务器1,2,3中的老大,而与上面不同的是,此时有三台服务器选举了它,所以它成为了这次选举的leader.
  4. 服务器4启动,根据前面的分析,理论上服务器4应该是服务器1,2,3,4中最大的,但是由于前面已经有半数以上的服务器选举了服务器3,所以它只能接收当小弟的命了.
  5. 服务器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服务发现并注册。