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

37次阅读

共计 6935 个字符,预计需要花费 18 分钟才能阅读完成。

因为细节内容切实太多啦,所以只把局部知识点整理出来粗略的介绍,每个小节点外面都有更细化的内容!

接下来开始分享啦

架构

前后端拆散:

补充:

  • setting.xml 文件的作用:settings.xml 是 maven 的全局配置文件。而 pom.xml 文件是所在我的项目的部分配置。Settings.xml 中蕴含相似本地仓储地位、批改近程仓储服务器、认证信息等配置。
  • maven 的作用:借助 Maven,可将 jar 包仅仅保留在“仓库”中 ,有须要该文件时,就援用该文件接口, 不须要复制文件过去占用空间

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

分布式锁

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

过程锁:为了管制同一操作系统中多个过程拜访某个共享资源。

分布式锁:当多个过程不在同一个零碎中,用分布式锁管制多个过程对资源的拜访。

分布式锁个别有三种实现形式:

  1. 数据库乐观锁;
  2. 基于 Redis 的分布式锁;
  3. 基于 ZooKeeper 的分布式锁。

乐观锁的实现:应用版本标识来确定读到的数据与提交时的数据是否统一。提交后批改版本标识,不统一时能够采取抛弃和再次尝试的策略。

分布式锁基于 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,过期工夫)的形式,如果锁存在,那么获取它的过期工夫,如果锁确实曾经过期了,那么取得锁,并且设置新的过期工夫

谬误剖析:不同的客户端之间须要同步好工夫。

 //+V:BGM7756,收费支付材料 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; //+V:BGM7756,收费支付材料

中场休息时间!最近整顿了一份 Java 外围知识点。笼罩了 springboot、JVM、锁、并发、Java 反射、Spring

原理、微服务、Zookeeper、数据库、数据结构等大量知识点。

如果须要收费获取到这个【材料】文档的话增加微信:BGM7756

解锁:判断锁的拥有者后能够应用 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 下创立了 Node_n —> 循环 (每次获取 Locker 下的所有子节点 —> 对这些节点按节点自增号排序程序 —> 判断本人创立的 Node_n 是否是第一个节点 —> 如果是则取得了分布式锁 —> 如果不是监听上一个节点 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 知音公众号回复“多线程聚合”)

同步: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/587…

https://www.jianshu.com/p/1c5…

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.name='Erica'";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 会在校验不通过时间接抛出异样。

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

后盾:

 //+V:BGM7756,收费支付材料 @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"; } //+V:BGM7756,收费支付材料

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

4、List 转为 Map

 //+V:BGM7756,收费支付材料 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)); //+V:BGM7756,收费支付材料

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 的泊松散布。

总结

通过下面的介绍,置信大家对 springboot 微信点餐开源零碎方面有了进一步的意识,心愿可能帮忙到大家!

因为细节内容切实太多啦,所以只把局部知识点整理出来粗略的介绍

最近整顿了一份 Java 外围知识点。笼罩了 springboot、JVM、锁、并发、Java 反射、Spring
原理、微服务、Zookeeper、数据库、数据结构等大量知识点。

如果须要收费获取到这个【材料】文档增加微信:BGM7756

正文完
 0