九色国产,午夜在线视频,新黄色网址,九九色综合,天天做夜夜做久久做狠狠,天天躁夜夜躁狠狠躁2021a,久久不卡一区二区三区

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
【附源碼】Spring Boot微信點餐系統(tǒng),面試可以拿來吹了

架構(gòu)

前后端分離:

部署架構(gòu):

補充:

  • setting.xml 文件的作用:settings.xml是maven的全局配置文件。而pom.xml文件是所在項目的局部配置。Settings.xml中包含類似本地倉儲位置、修改遠程倉儲服務(wù)器、認(rèn)證信息等配置。

  • maven的作用:借助Maven,可將jar包僅僅保存在“倉庫”中,有需要該文件時,就引用該文件接口,不需要復(fù)制文件過來占用空間。

注:這個“倉庫”應(yīng)該就是本地安裝maven的目錄下的Repository的文件夾

分布式鎖

線程鎖:當(dāng)某個方法或代碼使用鎖,在同一時刻僅有一個線程執(zhí)行該方法或該代碼段。線程鎖只在同一JVM中有效,因為線程鎖的實現(xiàn)在根本上是依靠線程之間共享內(nèi)存實現(xiàn)的。如synchronized

進程鎖:為了控制同一操作系統(tǒng)中多個進程訪問某個共享資源。

分布式鎖:當(dāng)多個進程不在同一個系統(tǒng)中,用分布式鎖控制多個進程對資源的訪問。

分布式鎖一般有三種實現(xiàn)方式:

  1. 數(shù)據(jù)庫樂觀鎖;

  2. 基于Redis的分布式鎖;

  3. 基于ZooKeeper的分布式鎖。

樂觀鎖的實現(xiàn):使用版本標(biāo)識來確定讀到的數(shù)據(jù)與提交時的數(shù)據(jù)是否一致。提交后修改版本標(biāo)識,不一致時可以采取丟棄和再次嘗試的策略。

分布式鎖基于Redis的實現(xiàn):(本系統(tǒng)鎖才用的)

基本命令:

  • SETNX(SET if Not exist):當(dāng)且僅當(dāng) key 不存在,將 key 的值設(shè)為 value ,并返回1;若給定的 key 已經(jīng)存在,則 SETNX 不做任何動作,并返回0。

  • GETSET:將給定 key 的值設(shè)為 value ,并返回 key 的舊值。先根據(jù)key獲取到舊的value,再set新的value。

  • EXPIRE 為給定 key 設(shè)置生存時間,當(dāng) key 過期時,它會被自動刪除。

加鎖方式:

這里的jedis是Java對Redis的集成

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

錯誤的加鎖方式1:

如果程序在執(zhí)行完setnx()之后突然崩潰,導(dǎo)致鎖沒有設(shè)置過期時間。那么將會發(fā)生死鎖。

Long result = jedis.setnx(Key, value);
    if (result == 1) {
        // 若在這里程序突然崩潰,則無法設(shè)置過期時間,將發(fā)生死鎖
        jedis.expire(Key, expireTime);
    }

錯誤的加鎖方式2:

分布式鎖才用(Key,過期時間)的方式,如果鎖存在,那么獲取它的過期時間,如果鎖的確已經(jīng)過期了,那么獲得鎖,并且設(shè)置新的過期時間

錯誤分析:不同的客戶端之間需要同步好時間。

 long expires = System.currentTimeMillis() + expireTime;
    String expiresStr = String.valueOf(expires);

    // 如果當(dāng)前鎖不存在,返回加鎖成功
    if (jedis.setnx(lockKey, expiresStr) == 1) {
        return true;
    }

    // 如果鎖存在,獲取鎖的過期時間
    String currentValueStr = jedis.get(lockKey);
    if (currentValueStr != null && Long.parseLong(currentValueStr) < System.currentTimeMillis()) {
        // 鎖已過期,獲取上一個鎖的過期時間,并設(shè)置現(xiàn)在鎖的過期時間
        String oldValueStr = jedis.getSet(lockKey, expiresStr);
        if (oldValueStr != null && oldValueStr.equals(currentValueStr)) {
            // 考慮多線程并發(fā)的情況,只有一個線程的設(shè)置值和當(dāng)前值相同,它才有權(quán)利加鎖
            return true;
        }
    }

    // 其他情況,一律返回加鎖失敗
    return false;

解鎖:判斷鎖的擁有者后可以使用 jedis.del(lockKey) 來釋放鎖。

分布式鎖基于Zookeeper的實現(xiàn)

Zookeeper簡介:Zookeeper提供一個多層級的節(jié)點命名空間(節(jié)點稱為znode),每個節(jié)點都用一個以斜杠(/)分隔的路徑表示,而且每個節(jié)點都有父節(jié)點(根節(jié)點除外)。

例如,/foo/doo這個表示一個znode,它的父節(jié)點為/foo,父父節(jié)點為/,而/為根節(jié)點沒有父節(jié)點。

client不論連接到哪個Server,展示給它都是同一個視圖,這是zookeeper最重要的性能。

Zookeeper 的核心是原子廣播,這個機制保證了各個Server之間的同步。實現(xiàn)這個機制的協(xié)議叫做Zab協(xié)議。Zab協(xié)議有兩種模式,它們分別是恢復(fù)模式(選主)和廣播模式(同步)。當(dāng)服務(wù)啟動或者在領(lǐng)導(dǎo)者崩潰后,Zab就進入了恢復(fù)模式,當(dāng)領(lǐng)導(dǎo)者被選舉出來,且大多數(shù)Server完成了和 leader的狀態(tài)同步以后,恢復(fù)模式就結(jié)束了。狀態(tài)同步保證了leader和Server具有相同的系統(tǒng)狀態(tài)。

為了保證事務(wù)的順序一致性,zookeeper采用了遞增的事務(wù)id號(zxid)來標(biāo)識事務(wù),實現(xiàn)中zxid是一個64位的數(shù)字。

Zookeeper的分布式鎖原理

獲取分布式鎖的流程:

  1. 在獲取分布式鎖的時候在locker節(jié)點(locker節(jié)點是Zookeeper的指定節(jié)點)下創(chuàng)建臨時順序節(jié)點,釋放鎖的時候刪除該臨時節(jié)點。

  2. 客戶端調(diào)用createNode方法在locker下創(chuàng)建臨時順序節(jié)點,然后調(diào)用getChildren(“l(fā)ocker”)來獲取locker下面的所有子節(jié)點,注意此時不用設(shè)置任何Watcher。

  3. 客戶端獲取到所有的子節(jié)點path之后,如果發(fā)現(xiàn)自己創(chuàng)建的子節(jié)點序號最小,那么就認(rèn)為該客戶端獲取到了鎖。

  4. 如果發(fā)現(xiàn)自己創(chuàng)建的節(jié)點并非locker所有子節(jié)點中最小的,說明自己還沒有獲取到鎖,此時客戶端需要找到比自己小的那個節(jié)點,然后對其調(diào)用exist()方法,同時對其注冊事件監(jiān)聽器。

  5. 之后,讓這個被關(guān)注的節(jié)點刪除,則客戶端的Watcher會收到相應(yīng)通知,此時再次判斷自己創(chuàng)建的節(jié)點是否是locker子節(jié)點中序號最小的,如果是則獲取到了鎖,如果不是則重復(fù)以上步驟繼續(xù)獲取到比自己小的一個節(jié)點并注冊監(jiān)聽。

我的解釋:

A在Locker下創(chuàng)建了Node_n —>循環(huán) ( 每次獲取Locker下的所有子節(jié)點 —> 對這些節(jié)點按節(jié)點自增號排序順序 —> 判斷自己創(chuàng)建的Node_n是否是第一個節(jié)點 —> 如果是則獲得了分布式鎖 —> 如果不是監(jiān)聽上一個節(jié)點Node_n-1 等它釋放掉分布式鎖。)

@ControllerAdvice處理全局異常
Mybatis注解方式的使用:
@insert 用注解方式寫SQL語句

分布式系統(tǒng)的下的Session

1、分布式系統(tǒng):多節(jié)點,節(jié)點發(fā)送數(shù)據(jù)交互,不共享主內(nèi)存,但通過網(wǎng)絡(luò)發(fā)送消息合作。

分布式:不同功能模塊的節(jié)點

集群:相同功能的節(jié)點

2、Session 與token

服務(wù)端在HTTP頭里設(shè)置SessionID而客戶端將其保存在cookie

而使用Token時需要手動在HTTP頭里設(shè)置,服務(wù)器收到請求后取出cookie進行驗證。

都是一個用戶一個標(biāo)志

3、分布式系統(tǒng)中的Session問題:

高并發(fā):通過設(shè)計保證系統(tǒng)能夠同時并行處理很多請求。

當(dāng)高并發(fā)量的請求到達服務(wù)端的時候通過負(fù)載均衡的方式分發(fā)到集群中的某個服務(wù)器,這樣就有可能導(dǎo)致同一個用戶的多次請求被分發(fā)到集群的不同服務(wù)器上,就會出現(xiàn)取不到session數(shù)據(jù)的情況。

根據(jù)訪問不同的URL,負(fù)載到不同的服務(wù)器上去

三臺機器,A1部署類目,A2部署商品,A3部署單服務(wù)

通用方案:用Redis保存Session信息,服務(wù)器需要時都去找Redis要。登錄時保存好key-value,登出時讓他失效

垂直擴展:IP哈希 IP的哈希值相同的訪問同一臺服務(wù)器

session的一致性:只要用戶不重啟瀏覽器,每次http短連接請求,理論上服務(wù)端都能定位到session,保持會話。

Redis作為分布式鎖

高并發(fā):通過設(shè)計保證系統(tǒng)能夠同時并行處理很多請求。

同步:Java中的同步指的是通過人為的控制和調(diào)度,保證共享資源的多線程訪問成為線程安全。

線程的Block狀態(tài):

a.調(diào)用join()和sleep()方法,sleep()時間結(jié)束或被打斷

b.wait(),使該線程處于等待池,直到notify()/notifyAll():不釋放資源

此外,在runnable狀態(tài)的線程是處于被調(diào)度的線程,Thread類中的yield方法可以讓一個running狀態(tài)的線程轉(zhuǎn)入runnable。

Q:為什么wait,notify和notifyAll必須與synchronized一起使用?Obj.wait()、Obj.notify必須在synchronized(Obj){…}語句塊內(nèi)。

A:wait就是說線程在獲取對象鎖后,主動釋放對象鎖,同時本線程休眠。

Q:Synchronized:

A:Synchronized就是非公平鎖,它無法保證等待的線程獲取鎖的順序。

公平和非公平鎖的隊列都基于鎖內(nèi)部維護的一個雙向鏈表,表結(jié)點Node的值就是每一個請求當(dāng)前鎖的線程。公平鎖則在于每次都是依次從隊首取值。

ReentrantLock重入性:

Spring + Redis緩存的兩個重要注解:

  • @cacheable 只會執(zhí)行一次,當(dāng)標(biāo)記在一個方法上時表示該方法是支持緩存的,Spring會在其被調(diào)用后將其返回值緩存起來,以保證下次利用同樣的參數(shù)來執(zhí)行該方法時可以直接從緩存中獲取結(jié)果。

  • @cacheput:與@Cacheable不同的是使用@CachePut標(biāo)注的方法在執(zhí)行前不會去檢查緩存中是否存在之前執(zhí)行過的結(jié)果,而是每次都會執(zhí)行該方法,并將執(zhí)行結(jié)果以鍵值對的形式存入指定的緩存中。

對數(shù)據(jù)庫加鎖(樂觀鎖 與 悲觀鎖)

悲觀鎖依賴數(shù)據(jù)庫實現(xiàn):

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();//執(zhí)行查詢,獲取數(shù)據(jù)

其它

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

JAVA1.8的新特性StreamAPI:Collectors中提供了將流中的元素累積到匯聚結(jié)果的各種方式

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

For - each 寫法:

for each語句是java5新增,在遍歷數(shù)組、集合的時候,for each擁有不錯的性能。

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

for each雖然能遍歷數(shù)組或者集合,但是只能用來遍歷,無法在遍歷的過程中對數(shù)組或者集合進行修改。

BindingResult:一個@Valid的參數(shù)后必須緊挨著一個BindingResult 參數(shù),否則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("參數(shù)不正確,OrderForm={}", order);
                throw new SellException(
                 ………… ,
             result.getFeildError.getDefaultMessage()
              )
                System.out.println("error:"+ls.get(i));  
            }  
        }  
        return "adduser";  
    }

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

4、List轉(zhuǎn)為Map

public class Apple {
    private Integer id;
    private String name;
    private BigDecimal money;
    private Integer num;
   /*構(gòu)造函數(shù)*/
}
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元素,允許重復(fù)元素

Set:元素是無序的,不允許元素

最流行的是基于 HashMap 實現(xiàn)的 HashSet,由hashCode()和equals()保證元素的唯一性。

可以用set幫助去掉List中的重復(fù)元素,set的構(gòu)造方法的參數(shù)可以是List,構(gòu)造后是一個去重的set。

HashMap的補充:它不是Collection下的

Map可以使用containsKey()/containsValue()來檢查其中是否含有某個key/value。

HashMap會利用對象的hashCode來快速找到key。

插入過程:通過一個hash函數(shù)確定Entry的插入位置index=hash(key),但是數(shù)組的長度有限,可能會發(fā)生index沖突,當(dāng)發(fā)生了沖突時,會使用頭插法,即為新來的Entry指向舊的Entry,成為一個鏈表。

每次插入時依次遍歷它的index下的單鏈表,如果存在Key一致的節(jié)點,那么直接替換,并且返回新的值。

但是單鏈表不會一直增加元素,當(dāng)元素個數(shù)超過8個時,會嘗試將單鏈表轉(zhuǎn)化為紅黑樹存儲。

為何加載因子默認(rèn)為0.75?(0.75開始擴容)

答:通過源碼里的javadoc注釋看到,元素在哈希表中分布的桶頻率服從參數(shù)為0.5的泊松分布。

源碼地址:https://github.com/923310233/wxOrder

本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊舉報。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
分布式鎖如何實現(xiàn)
分布式鎖的實現(xiàn)
最全分布式鎖設(shè)計方案
分布式鎖
淺談分布式鎖與樂觀鎖
Redis分布式鎖解決并發(fā)超賣問題
更多類似文章 >>
生活服務(wù)
熱點新聞
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服