国产亚洲精品福利在线无卡一,国产精久久一区二区三区,亚洲精品无码国模,精品久久久久久无码专区不卡

當(dāng)前位置: 首頁(yè) > news >正文

網(wǎng)站建設(shè)公司該怎么銷售微信推廣方法

網(wǎng)站建設(shè)公司該怎么銷售,微信推廣方法,濟(jì)南手機(jī)網(wǎng)站建設(shè)公司報(bào)價(jià),小程序項(xiàng)目一、實(shí)現(xiàn)原理 1.1 基本原理 JDK 原生的鎖可以讓不同線程之間以互斥的方式來(lái)訪問(wèn)共享資源,但如果想要在不同進(jìn)程之間以互斥的方式來(lái)訪問(wèn)共享資源,JDK 原生的鎖就無(wú)能為力了。此時(shí)可以使用 Redis 來(lái)實(shí)現(xiàn)分布式鎖。 Redis 實(shí)現(xiàn)分布式鎖的核心命令如下&am…

一、實(shí)現(xiàn)原理

1.1 基本原理

JDK 原生的鎖可以讓不同線程之間以互斥的方式來(lái)訪問(wèn)共享資源,但如果想要在不同進(jìn)程之間以互斥的方式來(lái)訪問(wèn)共享資源,JDK 原生的鎖就無(wú)能為力了。此時(shí)可以使用 Redis 來(lái)實(shí)現(xiàn)分布式鎖。

Redis 實(shí)現(xiàn)分布式鎖的核心命令如下:

SETNX key value

SETNX 命令的作用是:如果指定的 key 不存在,則創(chuàng)建并為其設(shè)置值,然后返回狀態(tài)碼 1;如果指定的 key 存在,則直接返回 0。如果返回值為 1,代表獲得該鎖;此時(shí)其他進(jìn)程再次嘗試創(chuàng)建時(shí),由于 key 已經(jīng)存在,則都會(huì)返回 0 ,代表鎖已經(jīng)被占用。

當(dāng)獲得鎖的進(jìn)程處理完成業(yè)務(wù)后,再通過(guò) del 命令將該 key 刪除,其他進(jìn)程就可以再次競(jìng)爭(zhēng)性地進(jìn)行創(chuàng)建,獲得該鎖。

通常為了避免死鎖,我們會(huì)為鎖設(shè)置一個(gè)超時(shí)時(shí)間,在 Redis 中可以通過(guò) expire 命令來(lái)進(jìn)行實(shí)現(xiàn):

EXPIRE key seconds

這里我們將兩者結(jié)合起來(lái),并使用 Jedis 客戶端來(lái)進(jìn)行實(shí)現(xiàn),其代碼如下:

Long result = jedis.setnx("lockKey", "lockValue");
if (result == 1) {// 如果此處程序被異常終止(如直接kill -9進(jìn)程),則設(shè)置超時(shí)的操作就無(wú)法進(jìn)行,該鎖就會(huì)出現(xiàn)死鎖jedis.expire("lockKey", 3);
}

上面的代碼存在原子性問(wèn)題,即 setnx + expire 操作是非原子性的,如果在設(shè)置超時(shí)時(shí)間前,程序被異常終止,則程序就會(huì)出現(xiàn)死鎖。此時(shí)可以將 SETNX 和 EXPIRE 兩個(gè)命令寫在同一個(gè) Lua 腳本中,然后通過(guò)調(diào)用 Jedis 的 eval() 方法來(lái)執(zhí)行,并由 Redis 來(lái)保證整個(gè) Lua 腳本操作的原子性。這種方式實(shí)現(xiàn)比較繁瑣,因此官方文檔中推薦了另外一種更加優(yōu)雅的實(shí)現(xiàn)方法:

1.2 官方推薦

[官方文檔]( Distributed locks with Redis) 中推薦直接使用 set 命令來(lái)進(jìn)行實(shí)現(xiàn):

SET key value [EX seconds|PX milliseconds] [NX|XX] [KEEPTTL]

這里我們主要關(guān)注以下四個(gè)參數(shù):

  • EX?:設(shè)置超時(shí)時(shí)間,單位是秒;
  • PX?:設(shè)置超時(shí)時(shí)間,單位是毫秒;
  • NX?:當(dāng)且僅當(dāng)對(duì)應(yīng)的 Key 不存在時(shí)才進(jìn)行設(shè)置;
  • XX:當(dāng)且僅當(dāng)對(duì)應(yīng)的 Key 存在時(shí)才進(jìn)行設(shè)置。

這四個(gè)參數(shù)從 Redis 2.6.12 版本開(kāi)始支持,因?yàn)楫?dāng)前大多數(shù)在用的 Redis 都已經(jīng)高于這個(gè)版本,所以推薦直接使用該命令來(lái)實(shí)現(xiàn)分布式鎖。對(duì)應(yīng)的 Jedis 代碼如下:

jedis.set("lockKey", "lockValue", SetParams.setParams().nx().ex(3));

此時(shí)一條命令就可以完成值和超時(shí)時(shí)間的設(shè)置,并且因?yàn)橹挥幸粭l命令,因此其原子性也得到了保證。但因?yàn)橐肓顺瑫r(shí)時(shí)間來(lái)避免死鎖,同時(shí)也引出了其它兩個(gè)問(wèn)題:

  • 問(wèn)題一:當(dāng)業(yè)務(wù)處理的時(shí)間超過(guò)過(guò)期時(shí)間后(圖中進(jìn)程 A),由于鎖已經(jīng)被釋放,此時(shí)其他進(jìn)程就可以獲得該鎖(圖中進(jìn)程 B),這意味著有兩個(gè)進(jìn)程(A 和 B)同時(shí)進(jìn)入了臨界區(qū),此時(shí)分布式鎖就失效了;
  • 問(wèn)題二:如上圖所示,當(dāng)進(jìn)程 A 業(yè)務(wù)處理完成后,此時(shí)刪除的是進(jìn)程 B 的鎖,進(jìn)而導(dǎo)致分布式鎖又一次失效,讓進(jìn)程 B 和 進(jìn)程 C 同時(shí)進(jìn)入了臨界區(qū)。

針對(duì)問(wèn)題二,我們可以在創(chuàng)建鎖時(shí)為其指定一個(gè)唯一的標(biāo)識(shí)作為 Key 的 Value,這里假設(shè)我們采用 UUID + 線程ID 來(lái)作為唯一標(biāo)識(shí):

String identifier = UUID.randomUUID() + ":" + Thread.currentThread().getId();
jedis.set("LockKey", identifier, SetParams.setParams().nx().ex(3));

然后在刪除鎖前,先將該唯一標(biāo)識(shí)與鎖的 Value 值進(jìn)行比較,如果不相等,證明該鎖不屬于當(dāng)前的操作對(duì)象,此時(shí)不執(zhí)行刪除操作。為保證判斷操作和刪除操作整體的原子性,這里需要使用 Lua 腳本來(lái)執(zhí)行:

if redis.call("get",KEYS[1]) == ARGV[1] thenreturn redis.call("del",KEYS[1])
elsereturn 0
end

這段腳本的意思是如果 value 的值與給定的值相同,則執(zhí)行刪除命令,否則直接返回狀態(tài)碼 0 。對(duì)應(yīng)使用 Jedis 實(shí)現(xiàn)的代碼如下:

String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
jedis.eval(script, Collections.singletonList("LockKey"),  // keys的集合Collections.singletonList(identifier)  // args的集合);

接著再看問(wèn)題一,問(wèn)題一最簡(jiǎn)單的解決方法是:你可以估計(jì)業(yè)務(wù)的最大處理時(shí)間,然后保證設(shè)置的過(guò)期時(shí)間大于最大處理時(shí)間。但是由于業(yè)務(wù)會(huì)面臨各種復(fù)雜的情況,因此可能無(wú)法保證業(yè)務(wù)每一次都能在規(guī)定的過(guò)期時(shí)間內(nèi)處理完成,此時(shí)可以使用延長(zhǎng)鎖時(shí)效的策略。

1.3 延長(zhǎng)鎖時(shí)效

延長(zhǎng)鎖時(shí)效的方案如下:假設(shè)鎖超時(shí)時(shí)間是 30 秒,此時(shí)程序需要每隔一段時(shí)間去掃描一下該鎖是否還存在,掃描時(shí)間需要小于超時(shí)時(shí)間,通??梢栽O(shè)置為超時(shí)時(shí)間的 1/3,在這里也就是 10 秒掃描一次。如果鎖還存在,則重置其超時(shí)時(shí)間恢復(fù)到 30 秒。通過(guò)這種方案,只要業(yè)務(wù)還沒(méi)有處理完成,鎖就會(huì)一直有效;而當(dāng)業(yè)務(wù)一旦處理完成,程序也會(huì)馬上刪除該鎖。

Redis 的 Java 客戶端 Redisson 提供的分布式鎖就支持類似的延長(zhǎng)鎖時(shí)效的策略,稱為 WatchDog,直譯過(guò)來(lái)就是 “看門狗” 機(jī)制。

以上討論的都是單機(jī)環(huán)境下的 Redis 分布式鎖,而想要保證 Redis 分布式鎖是高可用,首先 Redis 得是高可用的,Redis 的高可用模式主要有兩種:哨兵模式和集群模式。以下分別進(jìn)行討論:

二、哨兵模式與分布式鎖

哨兵模式是主從模式的升級(jí)版,能夠在故障發(fā)生時(shí)自動(dòng)進(jìn)行故障切換,選舉出新的主節(jié)點(diǎn)。但由于 Redis 的復(fù)制機(jī)制是異步的,因此在哨兵模式下實(shí)現(xiàn)的分布式鎖是不可靠的,原因如下:

  • 由于主從之間的復(fù)制操作是異步的,當(dāng)主節(jié)點(diǎn)上創(chuàng)建好鎖后,此時(shí)從節(jié)點(diǎn)上的鎖可能尚未創(chuàng)建。而如果此時(shí)主節(jié)點(diǎn)發(fā)生了宕機(jī),從節(jié)點(diǎn)上將不會(huì)創(chuàng)建該分布式鎖;
  • 從節(jié)點(diǎn)晉升為主節(jié)點(diǎn)后,其他進(jìn)程(或線程)仍然可以在該新主節(jié)點(diǎn)創(chuàng)建分布式鎖,此時(shí)就存在多個(gè)進(jìn)程(或線程)同時(shí)進(jìn)入了臨界區(qū),分布式鎖就失效了。

因此在哨兵模式下,無(wú)法避免鎖失效的問(wèn)題。因此想要實(shí)現(xiàn)高可用的分布式鎖,可以采取 Redis 的另一個(gè)高可用方案 —— Redis 集群模式。

三、集群模式與分布式鎖

3.1 RedLock 方案

想要在集群模式下實(shí)現(xiàn)分布式鎖,Redis 提供了一種稱為 RedLock 的方案,假設(shè)我們有 N 個(gè) Redis 實(shí)例,此時(shí)客戶端的執(zhí)行過(guò)程如下:

  • 以毫秒為單位記錄當(dāng)前的時(shí)間,作為開(kāi)始時(shí)間;
  • 接著采用和單機(jī)版相同的方式,依次嘗試在每個(gè)實(shí)例上創(chuàng)建鎖。為了避免客戶端長(zhǎng)時(shí)間與某個(gè)故障的 Redis 節(jié)點(diǎn)通訊而導(dǎo)致阻塞,這里采用快速輪詢的方式:假設(shè)創(chuàng)建鎖時(shí)設(shè)置的超時(shí)時(shí)間為 10 秒,則訪問(wèn)每個(gè) Redis 實(shí)例的超時(shí)時(shí)間可能在 5 到 50 毫秒之間,如果在這個(gè)時(shí)間內(nèi)還沒(méi)有建立通信,則嘗試連接下一個(gè)實(shí)例;
  • 如果在至少 N/2+1 個(gè)實(shí)例上都成功創(chuàng)建了鎖。并且 當(dāng)前時(shí)間 - 開(kāi)始時(shí)間 < 鎖的超時(shí)時(shí)間 ,則認(rèn)為已經(jīng)獲取了鎖,鎖的有效時(shí)間等于 超時(shí)時(shí)間 - 花費(fèi)時(shí)間(如果考慮不同 Redis 實(shí)例所在服務(wù)器的時(shí)鐘漂移,則還需要減去時(shí)鐘漂移);
  • 如果少于 N/2+1 個(gè)實(shí)例,則認(rèn)為創(chuàng)建分布式鎖失敗,此時(shí)需要?jiǎng)h除這些實(shí)例上已創(chuàng)建的鎖,以便其他客戶端進(jìn)行創(chuàng)建。
  • 該客戶端在失敗后,可以等待一個(gè)隨機(jī)的時(shí)間后,再次進(jìn)行重試。

以上就是 RedLock 的實(shí)現(xiàn)方案,可以看到主要是由客戶端來(lái)實(shí)現(xiàn)的,并不真正涉及到 Redis 集群相關(guān)的功能。因此這里的 N 個(gè) Redis 實(shí)例并不要求是一個(gè)真正的 Redis 集群,它們彼此之間可以是完全獨(dú)立的,但由于只需要半數(shù)節(jié)點(diǎn)獲得鎖就能真正獲得鎖,因此其仍然具備容錯(cuò)性和高可用性。后面使用 Redisson 來(lái)演示 RedLock 時(shí)會(huì)再次驗(yàn)證這一點(diǎn)。

3.2 低延遲通訊

另外實(shí)現(xiàn) RedLock 方案的客戶端與所有 Redis 實(shí)例進(jìn)行通訊時(shí),必須要保證低延遲,而且最好能使用多路復(fù)用技術(shù)來(lái)保證一次性將 SET 命令發(fā)送到所有 Redis 節(jié)點(diǎn)上,并獲取到對(duì)應(yīng)的執(zhí)行結(jié)果。如果網(wǎng)絡(luò)延遲較高,假設(shè)客戶端 A 和 B 都在嘗試創(chuàng)建鎖:

SET key 隨機(jī)數(shù)A EX 3 NX  #A客戶端
SET key 隨機(jī)數(shù)B EX 3 NX  #B客戶端

此時(shí)可能客戶端 A 在一半節(jié)點(diǎn)上創(chuàng)建了鎖,而客戶端 B 在另外一半節(jié)點(diǎn)上創(chuàng)建了鎖,那么兩個(gè)客戶端都將無(wú)法獲取到鎖。如果并發(fā)很高,則可能存在多個(gè)客戶端分別在部分節(jié)點(diǎn)上創(chuàng)建了鎖,而沒(méi)有一個(gè)客戶端的數(shù)量超過(guò) N/2+1。這也就是上面過(guò)程的最后一步中,強(qiáng)調(diào)一旦客戶端失敗后,需要等待一個(gè)隨機(jī)時(shí)間后再進(jìn)行重試的原因,如果是一個(gè)固定時(shí)間,則所有失敗的客戶端又同時(shí)發(fā)起重試,情況就還是一樣。

因此最佳的實(shí)現(xiàn)就是客戶端的 SET 命令能幾乎同時(shí)到達(dá)所有節(jié)點(diǎn),并幾乎同時(shí)接受到所有執(zhí)行結(jié)果。 想要保證這一點(diǎn),低延遲的網(wǎng)絡(luò)通信極為關(guān)鍵,下文介紹的 Redisson 就采用 Netty 框架來(lái)保證這一功能的實(shí)現(xiàn)。

3.3 持久化與高可用

為了保證高可用,所有 Redis 節(jié)點(diǎn)還需要開(kāi)啟持久化。假設(shè)不開(kāi)啟持久化,假設(shè)進(jìn)程 A 獲得鎖后正在處理業(yè)務(wù)邏輯,此時(shí)節(jié)點(diǎn)宕機(jī)重啟,因?yàn)殒i數(shù)據(jù)丟失了,其他進(jìn)程便可以再次創(chuàng)建該鎖,因此所有 Redis 節(jié)點(diǎn)都需要開(kāi)啟 AOF 的持久化方式。

AOF 默認(rèn)的同步機(jī)制為 everysec,即每秒進(jìn)程一次持久化,此時(shí)能夠兼顧性能與數(shù)據(jù)安全,發(fā)生意外宕機(jī)的時(shí),最多會(huì)丟失一秒的數(shù)據(jù)。但如果碰巧就是在這一秒的時(shí)間內(nèi)進(jìn)程 A 創(chuàng)建了鎖,并由于宕機(jī)而導(dǎo)致數(shù)據(jù)丟失。此時(shí)其他進(jìn)程還可以創(chuàng)建該鎖,鎖的互斥性也就失效了。想要解決這個(gè)問(wèn)題有兩種方式:

  • 方式一:修改 Redis.conf 中 appendfsync 的值為 always,即每次命令后都進(jìn)行持久化,此時(shí)會(huì)降低 Redis 性能,進(jìn)而也會(huì)降低分布式鎖的性能,但鎖的互斥性得到了絕對(duì)的保證;
  • 方式二:一旦節(jié)點(diǎn)宕機(jī)了,需要等到鎖的超時(shí)時(shí)間過(guò)了之后才進(jìn)行重啟,此時(shí)相當(dāng)于原有鎖自然失效(但你首先需要保證業(yè)務(wù)能在設(shè)定的超時(shí)時(shí)間內(nèi)完成),這種方案也稱為延時(shí)重啟。

四、Redisson

Redisson 是 Redis 的 Java 客戶端,它提供了各種的 Redis 分布式鎖的實(shí)現(xiàn),如可重入鎖、公平鎖、RedLock、讀寫鎖等等,并且在實(shí)現(xiàn)上考慮得也更加全面,適用于生產(chǎn)環(huán)境下使用。

4.1 分布式鎖

使用 Redisson 來(lái)創(chuàng)建單機(jī)版本分布式鎖非常簡(jiǎn)單,示例如下:

// 1.創(chuàng)建RedissonClient,如果與spring集成,可以將RedissonClient聲明為Bean,在使用時(shí)注入即可
Config config = new Config();
config.useSingleServer().setAddress("redis://192.168.0.100:6379");
RedissonClient redissonClient = Redisson.create(config);// 2.創(chuàng)建鎖實(shí)例
RLock lock = redissonClient.getLock("myLock");
try {//3.嘗試獲取分布式鎖,第一個(gè)參數(shù)為等待時(shí)間,第二個(gè)參數(shù)為鎖過(guò)期時(shí)間boolean isLock = lock.tryLock(10, 30, TimeUnit.SECONDS);if (isLock) {// 4.模擬業(yè)務(wù)處理System.out.println("處理業(yè)務(wù)邏輯");Thread.sleep(20 * 1000);}
} catch (Exception e) {e.printStackTrace();
} finally {//5.釋放鎖lock.unlock();
}
redissonClient.shutdown();

此時(shí)對(duì)應(yīng)在 Redis 中的數(shù)據(jù)結(jié)構(gòu)如下:

可以看到 key 就是代碼中設(shè)置的鎖名,而 value 值的類型是 hash,其中鍵 9280e909-c86b-43ec-b11d-6e5a7745e2e9:13 的格式為 UUID + 線程ID ;鍵對(duì)應(yīng)的值為 1,代表加鎖的次數(shù)。之所以要采用 hash 這種格式,主要是因?yàn)?Redisson 創(chuàng)建的鎖是具有重入性的,即你可以多次進(jìn)行加鎖:

boolean isLock1 = lock.tryLock(0, 30, TimeUnit.SECONDS);
boolean isLock2 = lock.tryLock(0, 30, TimeUnit.SECONDS);

此時(shí)對(duì)應(yīng)的值就會(huì)變成 2,代表加了兩次鎖:

當(dāng)然和其他重入鎖一樣,需要保證解鎖的次數(shù)和加鎖的次數(shù)一樣,才能完全解鎖:

lock.unlock();
lock.unlock();

4.2 RedLock

Redisson 也實(shí)現(xiàn)了 Redis 官方推薦的 RedLock 方案,這里我們啟動(dòng)三個(gè) Redis 實(shí)例進(jìn)行演示,它們彼此之間可以是完全獨(dú)立的,并不需要進(jìn)行集群的相關(guān)配置:

$ ./redis-server ../redis.conf
$ ./redis-server ../redis.conf --port 6380
$ ./redis-server ../redis.conf --port 6381

對(duì)應(yīng)的代碼示例如下:

// 1.創(chuàng)建RedissonClient
Config config01 = new Config();
config01.useSingleServer().setAddress("redis://192.168.0.100:6379");
RedissonClient redissonClient01 = Redisson.create(config01);
Config config02 = new Config();
config02.useSingleServer().setAddress("redis://192.168.0.100:6380");
RedissonClient redissonClient02 = Redisson.create(config02);
Config config03 = new Config();
config03.useSingleServer().setAddress("redis://192.168.0.100:6381");
RedissonClient redissonClient03 = Redisson.create(config03);// 2.創(chuàng)建鎖實(shí)例
String lockName = "myLock";
RLock lock01 = redissonClient01.getLock(lockName);
RLock lock02 = redissonClient02.getLock(lockName);
RLock lock03 = redissonClient03.getLock(lockName);// 3. 創(chuàng)建 RedissonRedLock
RedissonRedLock redLock = new RedissonRedLock(lock01, lock02, lock03);try {boolean isLock = redLock.tryLock(10, 300, TimeUnit.SECONDS);if (isLock) {// 4.模擬業(yè)務(wù)處理System.out.println("處理業(yè)務(wù)邏輯");Thread.sleep(200 * 1000);}
} catch (Exception e) {e.printStackTrace();
} finally {//5.釋放鎖redLock.unlock();
}redissonClient01.shutdown();
redissonClient02.shutdown();
redissonClient03.shutdown();

此時(shí)每個(gè) Redis 實(shí)例上鎖的情況如下:

可以看到每個(gè)實(shí)例上都獲得了鎖。

4.3 延長(zhǎng)鎖時(shí)效

最后,介紹一下 Redisson 的 WatchDog 機(jī)制,它可以用來(lái)延長(zhǎng)鎖時(shí)效,示例如下:

Config config = new Config();
// 1.設(shè)置WatchdogTimeout
config.setLockWatchdogTimeout(30 * 1000);
config.useSingleServer().setAddress("redis://192.168.0.100:6379");
RedissonClient redissonClient = Redisson.create(config);// 2.創(chuàng)建鎖實(shí)例
RLock lock = redissonClient.getLock("myLock");
try {//3.嘗試獲取分布式鎖,第一個(gè)參數(shù)為等待時(shí)間boolean isLock = lock.tryLock(0, TimeUnit.SECONDS);if (isLock) {// 4.模擬業(yè)務(wù)處理System.out.println("處理業(yè)務(wù)邏輯");Thread.sleep(60 * 1000);System.out.println("鎖剩余的生存時(shí)間:" + lock.remainTimeToLive());}
} catch (Exception e) {e.printStackTrace();
} finally {//5.釋放鎖lock.unlock();
}
redissonClient.shutdown();

首先 Redisson 的 WatchDog 機(jī)制只會(huì)對(duì)那些沒(méi)有設(shè)置鎖超時(shí)時(shí)間的鎖生效,所以我們這里調(diào)用的是兩個(gè)參數(shù)的 tryLock() 方法:

boolean tryLock(long time, TimeUnit unit) throws InterruptedException;

而不是包含超時(shí)時(shí)間的三個(gè)參數(shù)的 tryLock() 方法:

boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException;
復(fù)制代碼

其次我們通過(guò) config.setLockWatchdogTimeout(30 * 1000) 將 lockWatchdogTimeout 的值設(shè)置為 30000 毫秒(默認(rèn)值也是 30000 毫秒)。此時(shí) Redisson 的 WatchDog 機(jī)制會(huì)以 lockWatchdogTimeout 的 1/3 時(shí)長(zhǎng)為周期(在這里就是 10 秒)對(duì)所有未設(shè)置超時(shí)時(shí)間的鎖進(jìn)行檢查,如果業(yè)務(wù)尚未處理完成(也就是鎖還沒(méi)有被程序主動(dòng)刪除),Redisson 就會(huì)將鎖的超時(shí)時(shí)間重置為 lockWatchdogTimeout 指定的值(在這里就是設(shè)置的 30 秒),直到鎖被程序主動(dòng)刪除位置。因此在上面的例子中可以看到,不論將模擬業(yè)務(wù)的睡眠時(shí)間設(shè)置為多長(zhǎng),其鎖都會(huì)存在一定的剩余生存時(shí)間,直至業(yè)務(wù)處理完成。

反之,如果明確的指定了鎖的超時(shí)時(shí)間 leaseTime,則以 leaseTime 的時(shí)間為準(zhǔn),因?yàn)?WatchDog 機(jī)制對(duì)明確指定超時(shí)時(shí)間的鎖不會(huì)生效。

http://m.aloenet.com.cn/news/36001.html

相關(guān)文章:

  • 上海企業(yè)招聘信息發(fā)布平臺(tái)長(zhǎng)沙seo優(yōu)化推薦
  • 網(wǎng)站建設(shè)原因分析win7系統(tǒng)優(yōu)化軟件
  • 中國(guó)新聞社招聘2023年褲子seo關(guān)鍵詞
  • 中國(guó)最近軍事新聞視頻桂林網(wǎng)站優(yōu)化
  • 網(wǎng)站推廣解釋中國(guó)有幾個(gè)搜索引擎
  • 網(wǎng)站廣告輪播代碼運(yùn)營(yíng)是做什么的
  • 邀請(qǐng)注冊(cè)推廣賺錢seo深圳優(yōu)化
  • 如何在記事本中做網(wǎng)站鏈接長(zhǎng)沙自動(dòng)seo
  • 黃石建設(shè)信息網(wǎng)站國(guó)內(nèi)網(wǎng)絡(luò)銷售平臺(tái)有哪些
  • 公司網(wǎng)站建設(shè)開(kāi)發(fā)濟(jì)南興田德潤(rùn)優(yōu)惠嗎推廣平臺(tái)排行榜app
  • 行業(yè)網(wǎng)站建設(shè)費(fèi)用百度seo推廣軟件
  • 公司 做網(wǎng)站推廣信息發(fā)布平臺(tái)
  • 做網(wǎng)站是什么專業(yè)什么工作百度后臺(tái)推廣登錄
  • 做pc端網(wǎng)站要成本么廣告推廣軟件
  • wordpress loading優(yōu)化
  • wordpress手機(jī)版怎么注冊(cè)seo站
  • 做設(shè)計(jì)排版除了昵圖網(wǎng)還有什么網(wǎng)站中國(guó)新冠疫情最新消息
  • 專業(yè)做網(wǎng)站杭州網(wǎng)站推廣平臺(tái)
  • 東營(yíng)本地網(wǎng)站制作公司品牌策劃與推廣方案
  • ios wordpress 編輯器淄博seo網(wǎng)站推廣
  • 小城鎮(zhèn)建設(shè)的網(wǎng)站谷歌瀏覽器 官網(wǎng)下載
  • 做網(wǎng)站有前途云南seo網(wǎng)絡(luò)優(yōu)化師
  • 學(xué)生做的網(wǎng)站需要備案seo課程多少錢
  • 建設(shè)網(wǎng)站經(jīng)營(yíng)范圍怎么在百度上添加自己的店鋪地址
  • 網(wǎng)站制作 信科網(wǎng)絡(luò)第三方推廣平臺(tái)
  • 打字建站寶愛(ài)站官網(wǎng)
  • wordpress阿里百秀全達(dá)seo
  • i深建官方網(wǎng)站怎么做免費(fèi)的網(wǎng)站推廣
  • 織夢(mèng) 網(wǎng)站欄目管理 很慢小紅書軟文案例
  • 網(wǎng)頁(yè)設(shè)計(jì)網(wǎng)站建設(shè)的基本流程關(guān)鍵詞工具有哪些