アーキテクチャ
の分離の終了前と後:
この記事ではTomcatでnginxの関係は、数分はすぐに理解することができます:
https://www.jianshu.com/p/22dcb7ef9172
彼は加えました:
-
setting.xmlファイルの役割:settings.xmlのは、グローバル・コンフィギュレーション・ファイルをMavenのさ。pom.xmlファイルはどこローカル設定項目です。settings.xmlは、リモートストレージサーバ、認証設定情報等を変更、同様のローカルストレージ場所を含みます。
-
Mavenのの役割:援助のMaven、ジャーパッケージは唯一の「倉庫」に保存することができたときに、ファイルは、ファイル・インタフェース、スペース上のファイルをコピーする必要はありませんを参照することが必要です。
注:「倉庫は、」Mavenのインストールフォルダの下のローカルリポジトリ上のファイルディレクトリでなければなりません
分散ロック
ロックスレッド:コードは、メソッドまたはコードセグメントを実行すると同時に唯一のスレッドで、方法またはロックを使用する場合。スレッドロックの実現が基本的にあるスレッド間で達成するために、共有メモリに依存しているため、スレッド・ロックは、同じJVMでのみ有効です。このような同期として
プロセスロック:共有リソースにアクセスする複数のプロセスで同じオペレーティング・システムを制御するために。
ロックを分散:複数のプロセスが同じシステムでない場合は、複数のプロセスがリソースへの排他制御アクセスを分散して。
分散ロック3つの一般的な方法があります。
-
データベース楽観的ロック。
-
Redisのベースの分散錠。
-
分散ロックの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 的分布式锁原理
获取分布式锁的流程:
-
在获取分布式锁的时候在 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
List:ArrayList、LinkedList 、Vector
List:有序容器,允许 null 元素,允许重复元素
Set:元素是无序的,不允许元素
最も人気がHashSetのHashMapの要素の一意性を保証するために、[ハッシュコード()とequals()]によって実現基づいています。
リストは、反復する要素を削除、コンストラクタメソッドのセットパラメータはリストであってもよく、構造は重複排除のセットで助けとすることができます。
HashMapのは、コメントを追加しました:それはコレクションの下ではありません
地図は、彼らがどのキー/値が含まれているかどうかを確認するためのcontainsKey()/のcontainsValue()を使用することができます。
HashMapのハッシュコードオブジェクトはすぐにキーを見つけるために使用します。
挿入:エントリは、エントリの新しい点である第1の補間方法が使用されるハッシュ関数挿入位置インデックス=ハッシュ(キー)が、配列の限定された長さの衝突が発生した場合、インデックスの競合は、発生する可能性があり、によって決定されます古いエントリは、リンクリストになります。
順次同じキーノードが直接交換その後、存在し、新しい値を返す場合、各単鎖が、そのインデックスに挿入されるトラバース。
しかし、増加は8以上の要素の数は、赤黒ツリーに単一のストアを一覧表示しようとする単一のリスト要素、なかったでしょう。
なぜ、0.75のデフォルトの負荷係数?(0.75スタート拡張)
ソースアドレス:
https://github.com/923310233/wxOrder