SpringBoot open source micro-channel ordering system! Comprehensive use projects, worth a visit!

Architecture
before and after the end of the separation:
Here Insert Picture Description
Here Insert Picture Description

Here Insert Picture Description

Nginx relationship with Tomcat in this article, a few minutes can quickly understand:

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

supplement:

  • The role of setting.xml files: settings.xml is maven global configuration file. The pom.xml file is where the local configuration items. Settings.xml contains similar local storage location, modify the remote storage server, the authentication configuration information and the like.

  • The role of the maven: when the aid Maven, jar package can be saved only in the "warehouse", there is need that file, you reference the file interface, no need to copy files over space.

Note: The "warehouse" should be the file directory on the local Repository under the maven installation folder

Distributed Lock

Thread lock : When the code uses a method or lock, at the same time only one thread executing the method or the code segments. Thread lock is only valid in the same JVM, because the realization of thread lock is fundamentally rely on shared memory to achieve between threads. Such as synchronized

Process lock : In order to control the same operating system in multiple processes to access a shared resource.

Distributed Lock : When multiple processes are not the same system, with multiple processes distributed lock control access to resources.

Distributed Lock There are three general ways:

  • Database optimistic locking;

  • Redis-based distributed lock;

  • Based on a distributed lock ZooKeeper.

Achieve optimistic locking : when the data submitted by using the version identifier to determine whether the read data is consistent. After submitting a modified version of the identification can take and try again discard policy is inconsistent.

CAS : you can read this article:

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

Distributed Lock achieve this :( Redis-based system lock only used)

Here Insert Picture Description
Basic commands:

  • 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 的分布式锁原理

获取分布式锁的流程:

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

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

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

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

  • 之后,让这个被关注的节点删除,则客户端的 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

Here Insert Picture Description
List:ArrayList、LinkedList 、Vector

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

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

The most popular is based HashSet HashMap implemented by [hashCode () and equals ()] to ensure the uniqueness of the element.
List can be set to help remove the repeating elements, the set parameter to the constructor method may be List, the structure is a set of deduplication.
HashMap added: It is not under the Collection

Map can be used containsKey () / containsValue () to check whether they contain which a key / value.

HashMap hashCode object will use to quickly find the key.

Insertion: Entry is determined by a hash function insertion position index = hash (key), but the limited length of the array, index conflict may occur, when a collision occurs, the first interpolation method will be used, that is, the new point of Entry old Entry, become a linked list.

Sequentially traversing each single chain is inserted in its index, if the same Key node exists, then a direct replacement, and returns the new value.

But the increase would not have been a single list element, when the number of elements over 8, will try to list a single store into a red-black tree.

Why default load factor of 0.75? (0.75 start expansion)
Source Address:

https://github.com/923310233/wxOrder

Published 65 original articles · won praise 4 · views 20000 +

Guess you like

Origin blog.csdn.net/qq_20282955/article/details/104231329