小hub领读:

综合运用项目,可以学到分布式锁,wesocket消息实时推送等内容,值得一看哈!


  • 作者:Tommmmm

  • www.jianshu.com/p/ae14101989f2

架构

前后端分离:

Nginx 与 Tomcat 的关系在这篇文章,几分钟可以快速了解:

https://www.jianshu.com/p/22dcb7ef9172

补充:

  • setting.xml 文件的作用:settings.xml 是 maven 的全局配置文件。而 pom.xml 文件是所在项目的局部配置。Settings.xml 中包含类似本地仓储位置、修改远程仓储服务器、认证信息等配置。

  • maven 的作用:借助 Maven,可将 jar 包仅仅保存在 “仓库” 中,有需要该文件时,就引用该文件接口,不需要复制文件过来占用空间

注:这个 “仓库” 应该就是本地安装 maven 的目录下的 Repository 的文件夹

分布式锁

线程锁:当某个方法或代码使用锁,在同一时刻仅有一个线程执行该方法或该代码段。线程锁只在同一 JVM 中有效,因为线程锁的实现在根本上是依靠线程之间共享内存实现的。如 synchronized

进程锁:为了控制同一操作系统中多个进程访问某个共享资源。

分布式锁:当多个进程不在同一个系统中,用分布式锁控制多个进程对资源的访问。

分布式锁一般有三种实现方式:

  1. 数据库乐观锁;

  2. 基于 Redis 的分布式锁;

  3. 基于 ZooKeeper 的分布式锁。

乐观锁的实现:使用版本标识来确定读到的数据与提交时的数据是否一致。提交后修改版本标识,不一致时可以采取丢弃和再次尝试的策略。

CAS:可以阅读这篇文章:

https://www.jianshu.com/p/456bb1ea9627

分布式锁基于 Redis 的实现:(本系统锁才用的)

基本命令:

  • SETNX(SET if Not exist):当且仅当 key 不存在,将 key 的值设为 value ,并返回 1;若给定的 key 已经存在,则 SETNX 不做任何动作,并返回 0。

  • GETSET:将给定 key 的值设为 value ,并返回 key 的旧值。先根据 key 获取到旧的 value,再 set 新的 value。

  • EXPIRE 为给定 key 设置生存时间, 当 key 过期时,它会被自动删除。

加锁方式:

这里的 jedis 是 Java 对 Redis 的集成

jedis.set(String key, String value, String nxxx, String expx, int time)

错误的加锁方式 1:

如果程序在执行完 setnx() 之后突然崩溃,导致锁没有设置过期时间。那么将会发生死锁。

Long result = jedis.setnx(Key, value);if (result == 1) {// 若在这里程序突然崩溃,则无法设置过期时间,将发生死锁jedis.expire(Key, expireTime);}

错误的加锁方式 2:

分布式锁才用(Key,过期时间)的方式,如果锁存在,那么获取它的过期时间,如果锁的确已经过期了,那么获得锁,并且设置新的过期时间

错误分析:不同的客户端之间需要同步好时间。

 long expires = System.currentTimeMillis() + expireTime;String expiresStr = String.valueOf(expires);// 如果当前锁不存在,返回加锁成功if (jedis.setnx(lockKey, expiresStr) == 1) {return true;}// 如果锁存在,获取锁的过期时间String currentValueStr = jedis.get(lockKey);if (currentValueStr != null && Long.parseLong(currentValueStr) < System.currentTimeMillis()) {// 锁已过期,获取上一个锁的过期时间,并设置现在锁的过期时间String oldValueStr = jedis.getSet(lockKey, expiresStr);if (oldValueStr != null && oldValueStr.equals(currentValueStr)) {// 考虑多线程并发的情况,只有一个线程的设置值和当前值相同,它才有权利加锁return true;}}// 其他情况,一律返回加锁失败return false;

解锁:判断锁的拥有者后可以使用 jedis.del(lockKey) 来释放锁。

分布式锁基于 Zookeeper 的实现

Zookeeper 简介:Zookeeper 提供一个多层级的节点命名空间(节点称为 znode),每个节点都用一个以斜杠(/)分隔的路径表示,而且每个节点都有父节点(根节点除外)。

例如,/foo/doo 这个表示一个 znode,它的父节点为 / foo,父父节点为 /,而 / 为根节点没有父节点。

client 不论连接到哪个 Server,展示给它都是同一个视图,这是 zookeeper 最重要的性能。

Zookeeper 的核心是原子广播,这个机制保证了各个 Server 之间的同步。实现这个机制的协议叫做 Zab 协议。Zab 协议有两种模式,它们分别是恢复模式(选主)和广播模式(同步)。*当服务启动或者在领导者崩溃后,Zab 就进入了恢复模式*,当领导者被选举出来,且大多数 Server 完成了和 leader 的状态同步以后,恢复模式就结束了。状态同步保证了 leader 和 Server 具有相同的系统状态。

为了保证事务的顺序一致性,zookeeper 采用了递增的事务 id 号(zxid)来标识事务,实现中 zxid 是一个 64 位的数字。

Zookeeper 的分布式锁原理

获取分布式锁的流程:

  1. 在获取分布式锁的时候在 locker 节点 (locker 节点是 Zookeeper 的指定节点) 下创建临时顺序节点,释放锁的时候删除该临时节点。

  2. 客户端调用 createNode 方法在 locker 下创建临时顺序节点,然后调用 getChildren(“locker”) 来获取 locker 下面的所有子节点,注意此时不用设置任何 Watcher。

  3. 客户端获取到所有的子节点 path 之后,如果发现自己创建的子节点序号最小,那么就认为该客户端获取到了锁。

  4. 如果发现自己创建的节点并非 locker 所有子节点中最小的,说明自己还没有获取到锁,此时客户端需要找到比自己小的那个节点,然后对其调用 exist() 方法,同时对其注册事件监听器。

  5. 之后,让这个被关注的节点删除,则客户端的 Watcher 会收到相应通知,此时再次判断自己创建的节点是否是 locker 子节点中序号最小的,如果是则获取到了锁,如果不是则重复以上步骤继续获取到比自己小的一个节点并注册监听。

我的解释:

A 在 Locker 下创建了 Noden —> 循环 (每次获取 Locker 下的所有子节点 —> 对这些节点按节点自增号排序顺序 —> 判断自己创建的 Noden 是否是第一个节点 —> 如果是则获得了分布式锁 —> 如果不是监听上一个节点 Node_n-1 等它释放掉分布式锁。)

@ControllerAdvice 处理全局异常
Mybatis 注解方式的使用:
@insert 用注解方式写 SQL 语句

分布式系统的下的 Session

1、分布式系统:多节点,节点发送数据交互,不共享主内存,但通过网络发送消息合作。

分布式:不同功能模块的节点

集群:相同功能的节点

2、Session 与 token

服务端在 HTTP 头里设置 SessionID 而客户端将其保存在 cookie

而使用 Token 时需要手动在 HTTP 头里设置,服务器收到请求后取出 cookie 进行验证。

都是一个用户一个标志

3、分布式系统中的 Session 问题:

高并发:通过设计保证系统能够同时并行处理很多请求。

当高并发量的请求到达服务端的时候通过负载均衡的方式分发到集群中的某个服务器,这样就有可能导致同一个用户的多次请求被分发到集群的不同服务器上,就会出现取不到 session 数据的情况。

根据访问不同的 URL,负载到不同的服务器上去

三台机器,A1 部署类目,A2 部署商品,A3 部署单服务

通用方案:用 Redis 保存 Session 信息,服务器需要时都去找 Redis 要。登录时保存好 key-value,登出时让他失效

垂直扩展:IP 哈希 IP 的哈希值相同的访问同一台服务器

session 的一致性:只要用户不重启浏览器,每次 http 短连接请求,理论上服务端都能定位到 session,保持会话。

Redis 作为分布式锁

高并发:通过设计保证系统能够同时并行处理很多请求。

同步:Java 中的同步指的是通过人为的控制和调度,保证共享资源的多线程访问成为线程安全。

线程的 Block 状态:

a. 调用 join() 和 sleep() 方法,sleep() 时间结束或被打断

b.wait(),使该线程处于等待池, 直到 notify()/notifyAll():不释放资源

此外,在 runnable 状态的线程是处于被调度的线程,Thread 类中的 yield 方法可以让一个 running 状态的线程转入 runnable。

Q:为什么 wait,notify 和 notifyAll 必须与 synchronized 一起使用?Obj.wait()、Obj.notify 必须在 synchronized(Obj){…} 语句块内。

A:wait 就是说线程在获取对象锁后,主动释放对象锁,同时本线程休眠。

Q:Synchronized:

A:Synchronized 就是非公平锁,它无法保证等待的线程获取锁的顺序。

公平和非公平锁的队列都基于锁内部维护的一个双向链表,表结点 Node 的值就是每一个请求当前锁的线程。公平锁则在于每次都是依次从队首取值。

ReentrantLock 重入性:

重入锁可以看这两篇文章,都比较简单

https://www.jianshu.com/p/587a4559442b
https://www.jianshu.com/p/1c52f17efaab

Spring + Redis 缓存的两个重要注解:

  • @cacheable 只会执行一次,当标记在一个方法上时表示该方法是支持缓存的,Spring 会在其被调用后将其返回值缓存起来,以保证下次利用同样的参数来执行该方法时可以直接从缓存中获取结果。

  • @cacheput:与 @Cacheable 不同的是使用 @CachePut 标注的方法在执行前不会去检查缓存中是否存在之前执行过的结果,而是每次都会执行该方法,并将执行结果以键值对的形式存入指定的缓存中。

对数据库加锁(乐观锁 与 悲观锁)

悲观锁依赖数据库实现:

select * from account where name=”Erica” for update

这条 sql 语句锁定了 account 表中所有符合检索条件(name=”Erica”)的记录,使该记录在修改期间其它线程不得占有。

代码层加锁:

String hql ="from TUser as user where user.;
Query query = session.createQuery(hql);
query.setLockMode("user",LockMode.UPGRADE); //加锁
List userList = query.list();//执行查询,获取数据

其它

@Data 类似于自动生成了 Getter()、Setter()、ToString() 等方法。

JAVA1.8 的新特性 StreamAPI:Collectors 中提供了将流中的元素累积到汇聚结果的各种方式

List<Menu> menus=Menu.getMenus.stream().collect(Collectors.toList())

For - each 写法:

for each 语句是 java5 新增,在遍历数组、集合的时候,for each 拥有不错的性能。

public static void main(String[] args) {String[] names = {"beibei", "jingjing"};for (String name : names) {System.out.println(name);}}

for each 虽然能遍历数组或者集合,但是只能用来遍历,无法在遍历的过程中对数组或者集合进行修改。

BindingResult:一个 @Valid 的参数后必须紧挨着一个 BindingResult 参数,否则 spring 会在校验不通过时直接抛出异常。

@Data
public class OrderForm {@NotEmpty(message = "姓名必填")private String name;
}

后台:

@RequestMapping("save")  public String save( @Valid OrderForm order,BindingResult result) {  //  if(result.hasErrors()){  List<ObjectError> ls=result.getAllErrors();  for (int i = 0; i < ls.size(); i++) {  log.error("参数不正确,OrderForm={}", order);throw new SellException(………… ,result.getFeildError.getDefaultMessage())System.out.println("error:"+ls.get(i));  }  }  return "adduser";  }

result.getFeildError.getDefaultMessage() 可抛出 “姓名必填” 的异常。

4、List 转为 Map

public class Apple {private Integer id;private String name;private BigDecimal money;private Integer num;/*构造函数*/
}
List<Apple> appleList = new ArrayList<>();//存放apple对象集合
Apple apple1 =  new Apple(1,"苹果1",new BigDecimal("3.25"),10);
Apple apple12 = new Apple(1,"苹果2",new BigDecimal("1.35"),20);
Apple apple2 =  new Apple(2,"香蕉",new BigDecimal("2.89"),30);
Apple apple3 =  new Apple(3,"荔枝",new BigDecimal("9.99"),40);
appleList.add(apple1);
appleList.add(apple12);
appleList.add(apple2);
appleList.add(apple3);
Map<Integer, Apple> appleMap =
appleList.stream().collect(Collectors.toMap(Apple::getId, a -> a,(k1,k2)->k1));

5、Collection 的子类:List、Set

List:ArrayList、LinkedList 、Vector

List:有序容器,允许 null 元素,允许重复元素

Set:元素是无序的,不允许元素

最流行的是基于 HashMap 实现的 HashSet,由 [hashCode() 和 equals()]保证元素的唯一性。

可以用 set 帮助去掉 List 中的重复元素,set 的构造方法的参数可以是 List,构造后是一个去重的 set。

HashMap 的补充:它不是 Collection 下的

Map 可以使用 containsKey()/containsValue() 来检查其中是否含有某个 key/value。

HashMap 会利用对象的 hashCode 来快速找到 key。

插入过程:通过一个 hash 函数确定 Entry 的插入位置 index=hash(key),但是数组的长度有限,可能会发生 index 冲突,当发生了冲突时,会使用头插法,即为新来的 Entry 指向旧的 Entry,成为一个链表。

每次插入时依次遍历它的 index 下的单链表,如果存在 Key 一致的节点,那么直接替换,并且返回新的值。

但是单链表不会一直增加元素,当元素个数超过 8 个时,会尝试将单链表转化为红黑树存储。

为何加载因子默认为 0.75?*(0.75 开始扩容)*

答:通过源码里的 javadoc 注释看到,元素在哈希表中分布的桶频率服从参数为 0.5 的泊松分布。

源码地址:

https://github.com/923310233/wxOrder

(完)

MarkerHub文章索引:(点击阅读原文直达)

https://github.com/MarkerHub/JavaIndex

【推荐阅读】

面试问烂的 Spring AOP 原理、SpringMVC 过程

基于 token 的多平台身份认证架构设计

所谓代码生成,简单!我详细分析给你看!

好文!必须点赞

SpringBoot 微信点餐开源系统相关推荐

  1. SpringBoot 微信点餐开源系统,值得一看

    今天跟大家分享SpringBoot 微信点餐开源系统的知识. 1 架构 前后端分离: Nginx 与 Tomcat 的关系在这篇文章,几分钟可以快速了解: https://www.jianshu.co ...

  2. SpringBoot微信点餐开源系统

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 作者:Tommmmm www.jianshu.com/p/ae14 ...

  3. 收藏!开源项目SpringBoot 微信点餐开源系统

    点击上方"码农进阶之路",选择"设为星标" 回复"面经"获取面试资料 作者:Tommmmm www.jianshu.com/p/ae1410 ...

  4. 如何利用Spring Boot 微信点餐开源系统

    由于细节内容实在太多啦,所以只把部分知识点整理出来粗略的介绍,每个小节点里面都有更细化的内容! 接下来开始分享啦 架构 前后端分离: 补充: setting.xml 文件的作用:settings.xm ...

  5. Spring Boot 微信点餐开源系统

    架构 前后端分离: Nginx与Tomcat的关系在这篇文章,几分钟可以快速了解: " https://www.jianshu.com/p/22dcb7ef9172 补充: setting. ...

  6. 开源项目工时系统_SpringBoot 微信点餐开源系统!综合运用项目,值得一看!

    作者:Tommmmm来源:www.jianshu.com/p/ae14101989f2 架构 前后端分离: Nginx 与 Tomcat 的关系在这篇文章,几分钟可以快速了解: https://www ...

  7. Spring Boot 微信点餐开源系统!

    点击上方"码农突围",马上关注 这里是码农充电第一站,回复"666",获取一份专属大礼包 真爱,请设置"星标"或点个"在看&quo ...

  8. 第二课 SpringBoot微信点餐系统买家类目设计

    第二课 SpringBoot微信点餐系统买家类目设计 tags: Java 慕课网 categories: DAO层开发 service层开发 文章目录 第二课 SpringBoot微信点餐系统买家类 ...

  9. 基于Springboot微信点餐系统的开发与实现

    微信点餐数据库注意事项 商品表: 1:在企业级中,由于数据量是比较大的,所以id一般采用的是varchar,不采用int 2:凡是涉及到价格的统一采用decimal,例如本项目中单价如下: produ ...

最新文章

  1. blast程序 介绍 简介
  2. Java-Java中的线程池原理分析及使用
  3. canvas画布属性globalAlpha 和 createRadialGradient函数出现的设置问题
  4. 珍惜那些爱你的人,不要令他们失望,加油吧,我们。
  5. 编程书单:十本Python编程语言的入门书籍
  6. 【渝粤教育】国家开放大学2018年春季 建筑结构基础 参考试题
  7. 个人推动团队项目进展_推动者和关守者对开发团队的价值
  8. python3中exec_Python3
  9. android开发实战-记账本APP(二)
  10. 局域网、城域网、广域网、国际互联网(internet)
  11. html制作网页时字体怎么设置,网页制作—字体的设置
  12. EasyUI 中 Combobox里的onChange和onSelect事件的区别
  13. Tlsr8258开发-b85m_module编译无法通过
  14. 4月日本旅游签证简化办理攻略分享
  15. 很雷很有财也很搞笑的笔试答题
  16. 微信钱包里的钱居然不见了,微信支付绑卡的用户要注意这几点
  17. server2012安全_2012年重要安全提示
  18. 【Flume】【源码分析】flume中sink到hdfs,文件系统频繁产生文件,文件滚动配置不起作用?
  19. 软件外包网站TOP5
  20. 淘小宝蚂蚁聊天表情V1.0-V2.0的制作过程

热门文章

  1. c#将byte转为int_c#怎么把byte转化成int
  2. 【AI视野·今日CV 计算机视觉论文速览 第161期】Thu, 26 Sep 2019
  3. STM32最小系统设计
  4. edger和deseq2_简单使用DESeq2/EdgeR做差异分析
  5. String转换成json格式(二):org.codehaus.jettison.json.JSONObject
  6. 用计算机创造一个虚拟世界,人类世界是不是一个虚拟程序?一起来探索
  7. 纳米线传感器来了,传感芯片还会远吗
  8. 情书的写作格式及内容
  9. 7.Hive性能优化及Hive3新特性
  10. “在深圳,从工资5000到主管,我依然很焦虑”