Cluster模式潜在问题及解决方案、Web服务综合解决方案
會(huì)不斷更新!沖沖沖!跳轉(zhuǎn)連接
https://blog.csdn.net/qq_35349982/category_10317485.html
Cluster模式潛在問(wèn)題及解決方案、Web服務(wù)綜合解決方案
1.一致性的哈希算法
1.1分布式與集群
分布式和集群是不?樣的,分布式?定是集群,但是集群不?定是分布式
1.2Hash算法
1.1 順序查找法
list:List[1,5,7,6,3,4,8] // 通過(guò)循環(huán)判斷來(lái)實(shí)現(xiàn) for(int element: list) { if(element == n) { 如果相等,說(shuō)明n存在于數(shù)據(jù)集中 }}1.2?分查找法
1.3 直接尋址法
定義?個(gè)數(shù)組,數(shù)組?度?于等于數(shù)據(jù)集?度,此處?度為9,數(shù)據(jù)1就存儲(chǔ)在下標(biāo)為1的位置,3就存儲(chǔ)在下標(biāo)為3的元素位置,,,依次類推。
這個(gè)時(shí)候,我想看下5存在與否,只需要判斷l(xiāng)ist.get(5) array[5] 是否為空,如果為空,代表5不存在于數(shù)據(jù)集,如果不為空代表5在數(shù)據(jù)集當(dāng)中,通過(guò)?次查找就達(dá)到了?的,時(shí)間復(fù)雜度為O(1)。這種?式叫做“直接尋址法”:
1.4 開(kāi)放尋址法 | 拉鏈法
1.3?致性Hash算法
?先有?條直線,直線開(kāi)頭和結(jié)尾分別定為為1和2的32次?減1,這相當(dāng)于?個(gè)地址,對(duì)于這樣?條線,彎過(guò)來(lái)構(gòu)成?個(gè)圓環(huán)形成閉環(huán),這樣的?個(gè)圓環(huán)稱為hash環(huán)。
紅節(jié)點(diǎn) —服務(wù)器節(jié)點(diǎn)
綠人像 —請(qǐng)求訪問(wèn)
每?臺(tái)服務(wù)器負(fù)責(zé)?段,?致性哈希算法對(duì)于節(jié)點(diǎn)的增減都只需重定位環(huán)空間中的??部分?jǐn)?shù)據(jù),具有較好的容錯(cuò)性和可擴(kuò)展性。
但是,?致性哈希算法在服務(wù)節(jié)點(diǎn)太少時(shí),容易因?yàn)楣?jié)點(diǎn)分部不均勻?造成數(shù)據(jù)傾斜問(wèn)題。
例如系統(tǒng)中只有兩臺(tái)服務(wù)器,其環(huán)分布如下,節(jié)點(diǎn)2只能負(fù)責(zé)?常?的?段,?量的客戶端請(qǐng)求落在了節(jié)點(diǎn)1上,這就是數(shù)據(jù)(請(qǐng)求)傾斜問(wèn)題
解決方案:?致性哈希算法引?了虛擬節(jié)點(diǎn)機(jī)制,即對(duì)每?個(gè)服務(wù)節(jié)點(diǎn)計(jì)算多個(gè)哈希,每個(gè)計(jì)算結(jié)果位置都放置?個(gè)此服務(wù)節(jié)點(diǎn),稱為虛擬節(jié)點(diǎn)。
1.4手寫(xiě)Hash算法
1.流程口述
2.代碼
import java.util.SortedMap; import java.util.TreeMap; import java.util.UUID;public class ConsistentHashWithVirtual {public static void main(String[] args) {//step1 初始化:把服務(wù)器節(jié)點(diǎn)IP的哈希值對(duì)應(yīng)到哈希環(huán)上// 定義服務(wù)器ipString[] tomcatServers = new String[]{"123.111.0.0","123.101.3.1","111.20.35.2","123.98.26.3"};SortedMap<Integer,String> hashServerMap = new TreeMap<>();// 定義針對(duì)每個(gè)真實(shí)服務(wù)器虛擬出來(lái)幾個(gè)節(jié)點(diǎn)int virtaulCount = 3;for(String tomcatServer: tomcatServers) {// 求出每一個(gè)ip的hash值,對(duì)應(yīng)到hash環(huán)上,存儲(chǔ)hash值與ip的對(duì)應(yīng)關(guān)系int serverHash = Math.abs(tomcatServer.hashCode());// 存儲(chǔ)hash值與ip的對(duì)應(yīng)關(guān)系hashServerMap.put(serverHash,tomcatServer);// 處理虛擬節(jié)點(diǎn)for(int i = 0; i < virtaulCount; i++) {int virtualHash = Math.abs((tomcatServer + "#" + i).hashCode());hashServerMap.put(virtualHash,"----由虛擬節(jié)點(diǎn)"+ i + "映射過(guò)來(lái)的請(qǐng)求:"+ tomcatServer);}}//step2 針對(duì)客戶端IP求出hash值// 定義客戶端IPString[] clients = new String[]{"10.78.12.3","113.25.63.1","126.12.3.8"};for(String client : clients) {int clientHash = Math.abs(client.hashCode());//step3 針對(duì)客戶端,找到能夠處理當(dāng)前客戶端請(qǐng)求的服務(wù)器(哈希環(huán)上順時(shí)針最近)// 根據(jù)客戶端ip的哈希值去找出哪一個(gè)服務(wù)器節(jié)點(diǎn)能夠處理()SortedMap<Integer, String> integerStringSortedMap = hashServerMap.tailMap(clientHash);if(integerStringSortedMap.isEmpty()) {// 取哈希環(huán)上的順時(shí)針第一臺(tái)服務(wù)器Integer firstKey = hashServerMap.firstKey();System.out.println("==========>>>>客戶端:" + client + " 被路由到服務(wù)器:" + hashServerMap.get(firstKey));}else{Integer firstKey = integerStringSortedMap.firstKey();System.out.println("==========>>>>客戶端:" + client + " 被路由到服務(wù)器:" + hashServerMap.get(firstKey));}}}}1.5nginx的負(fù)載均衡
1.ip_hash的問(wèn)題
Nginx的IP_hash策略可以在客戶端ip不變的情況下,將其發(fā)出的請(qǐng)求始終路由到同?個(gè)?標(biāo)服務(wù)器上,實(shí)現(xiàn)會(huì)話粘滯,避免處理session共享問(wèn)題
如果沒(méi)有IP_hash策略,那么如何實(shí)現(xiàn)會(huì)話粘滯?
可以維護(hù)?張映射表,存儲(chǔ)客戶端IP或者sessionid與具體?標(biāo)服務(wù)器的映射關(guān)系
<ip,tomcat1>
缺點(diǎn)
1)那么,在客戶端很多的情況下,映射表?常?,浪費(fèi)內(nèi)存空間
2)客戶端上下線,?標(biāo)服務(wù)器上下線,都會(huì)導(dǎo)致重新維護(hù)映射表,映射表維護(hù)成本很?
2.ngx_http_upstream_consistent_hash模塊
ngx_http_upstream_consistent_hash 模塊是?個(gè)負(fù)載均衡器,使??個(gè)內(nèi)部?致性hash算法來(lái)選擇合適的后端節(jié)點(diǎn)。
下載地址: https://github.com/replay/ngx_http_consistent_hash
配置策略
consistent_hash $remote_addr:可以根據(jù)客戶端ip映射 consistent_hash $request_uri:根據(jù)客戶端請(qǐng)求的uri映射 consistent_hash $args:根據(jù)客戶端攜帶的參數(shù)進(jìn)?映安裝模塊
./configure —add-module=/root/ngx_http_consistent_hash-master make make install #負(fù)載均衡策略 upstream myServer{ #每個(gè)請(qǐng)求按照ip的hash結(jié)果分配,每?個(gè)客戶端的請(qǐng)求會(huì)固定分配到同?個(gè)?標(biāo)服務(wù)器處理,可以解決session問(wèn)題consistent_hash $request_uri;#weight代表權(quán)重,默認(rèn)每?個(gè)負(fù)載的服務(wù)器都為1,權(quán)重越?那么被分配的請(qǐng)求越多(?于服務(wù)器性能不均衡的場(chǎng)景)server 47.95.1.96:8080 weight=1;server 47.95.1.96:8081 weight=2; }proxy_pass http://myServer/;2.時(shí)鐘同步問(wèn)題
1.場(chǎng)景
一個(gè)電商系統(tǒng),三個(gè)服務(wù)器,三個(gè)訂單,因?yàn)闀r(shí)鐘不同步,造成三個(gè)系統(tǒng)的`訂單時(shí)間`不一致2.方案
2.1訪問(wèn)授時(shí)中心
#使? ntpdate ?絡(luò)時(shí)間同步命令ntpdate -u ntp.api.bz #從?個(gè)時(shí)間服務(wù)器同步時(shí)間2.2一個(gè)節(jié)點(diǎn)訪問(wèn),其他節(jié)點(diǎn)同步
?個(gè)服務(wù)器節(jié)點(diǎn)可以訪問(wèn)互聯(lián)?或者所有節(jié)點(diǎn)都不能夠訪問(wèn)互聯(lián)?
一個(gè)節(jié)點(diǎn)訪問(wèn),其他節(jié)點(diǎn)同步
##修改/etc/ntp.conf?件
#1.先讓一個(gè)節(jié)點(diǎn)同步授時(shí)中心 ntpdate -u ntp.api.bz #1、如果有 restrict default ignore,注釋掉它 #2、添加如下??內(nèi)容restrict 172.17.0.0 mask 255.255.255.0 nomodify notrap # 放開(kāi)局域?同步功能,172.17.0.0是你的局域??段server 127.127.1.0 # local clockfudge 127.127.1.0 stratum 10 #選擇一個(gè)主節(jié)點(diǎn) #3、重啟?效并配置ntpd服務(wù)開(kāi)機(jī)?啟動(dòng)service ntpd restartchkconfig ntpd on##其他節(jié)點(diǎn)同步ntpdate 172.17.0.173.分布式ID解決?案
3.1 UUID
UUID 是指Universally Unique Identififier,翻譯為中?是通?唯?識(shí)別碼
public class MyTest {public static void main(String[] args) {System.out.println(java.util.UUID.randomUUID().toString());} }3.2 獨(dú)?數(shù)據(jù)庫(kù)的?增ID
場(chǎng)景:分表后兩張表公用一個(gè)獨(dú)立表的ID
創(chuàng)建一張表
-- ---------------------------- -- Table structure for DISTRIBUTE_ID -- ---------------------------- DROP TABLE IF EXISTS `DISTRIBUTE_ID`; CREATE TABLE `DISTRIBUTE_ID` (`id` bigint(32) NOT NULL AUTO_INCREMENT COMMENT '主鍵',`createtime` datetime DEFAULT NULL,PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; insert into DISTRIBUTE_ID(createtime) values(NOW()); select LAST_INSERT_ID();缺點(diǎn):
需要代碼連接到數(shù)據(jù)庫(kù)才能獲取到id,性能?法保障,
mysql數(shù)據(jù)庫(kù)實(shí)例掛掉了,那么就?法獲取分布式id了。
3.3 SnowFlake 雪花算法
3.4 借助Redis的Incr命令獲取全局唯?ID
安裝redis后
源碼:
public Long incr(String key) {this.checkIsInMultiOrPipeline();//獲取鏈接this.client.incr(key); //獲取keyreturn this.client.getIntegerReply();} //======================================= //讀取 public long readLongCrLf() {byte[] buf = this.buf;this.ensureFill();boolean isNeg = buf[this.count] == 45;if (isNeg) {++this.count;}long value = 0L;while(true) {this.ensureFill();int b = buf[this.count++];if (b == 13) {this.ensureFill();if (buf[this.count++] != 10) {throw new JedisConnectionException("Unexpected character!");} else {return isNeg ? -value : value;}}value = value * 10L + (long)b - 48L;}}4.分布式任務(wù)調(diào)度
4.1定時(shí)任務(wù)場(chǎng)景
- 訂單審核,出庫(kù)
- 訂單支付退款
- 禮券同步
- 物流信息推送
- 短信推送
- 日志監(jiān)控
4.2什么是分布式任務(wù)調(diào)度
4.3定時(shí)任務(wù)與消息隊(duì)列的區(qū)別
-
共同點(diǎn)
-
異步處理
比如 注冊(cè),下單時(shí)間
-
應(yīng)用解耦
不管定時(shí)任務(wù)作業(yè)還是MQ 都可以實(shí)現(xiàn)應(yīng)用解耦
-
流量削峰
任何作業(yè)和MQ都可以用來(lái)抗流量
-
不同點(diǎn)
- 定時(shí)任務(wù)作業(yè)時(shí)時(shí)間驅(qū)動(dòng),MQ是時(shí)間驅(qū)動(dòng)
- 時(shí)間驅(qū)動(dòng)是不可替代的,比如說(shuō)金融系統(tǒng)是每日的利息結(jié)算,不是來(lái)一條做一條,還是批量時(shí)間去處理
- 定時(shí)任務(wù)作業(yè)更傾向于批處理,MQ傾向于逐條處理
4.4 Quartz定時(shí)
<!--任務(wù)調(diào)度框架quartz--><!-- https://mvnrepository.com/artifact/org.quartz-scheduler/quartz --><dependency><groupId>org.quartz-scheduler</groupId><artifactId>quartz</artifactId><version>2.3.2</version></dependency> public class QuartzMan {// 1、創(chuàng)建任務(wù)調(diào)度器(好比公交調(diào)度站)public static Scheduler createScheduler() throws SchedulerException {SchedulerFactory schedulerFactory = new StdSchedulerFactory();Scheduler scheduler = schedulerFactory.getScheduler();return scheduler;}// 2、創(chuàng)建一個(gè)任務(wù)(好比某一個(gè)公交車(chē)的出行)public static JobDetail createJob() {JobBuilder jobBuilder = JobBuilder.newJob(DemoJob.class); // TODO 自定義任務(wù)類jobBuilder.withIdentity("jobName","myJob");JobDetail jobDetail = jobBuilder.build();return jobDetail;}/*** 3、創(chuàng)建作業(yè)任務(wù)時(shí)間觸發(fā)器(類似于公交車(chē)出車(chē)時(shí)間表)* cron表達(dá)式由七個(gè)位置組成,空格分隔* 1、Seconds(秒) 0~59* 2、Minutes(分) 0~59* 3、Hours(小時(shí)) 0~23* 4、Day of Month(天)1~31,注意有的月份不足31天* 5、Month(月) 0~11,或者 JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC* 6、Day of Week(周) 1~7,1=SUN或者 SUN,MON,TUE,WEB,THU,FRI,SAT* 7、Year(年)1970~2099 可選項(xiàng)*示例:* 0 0 11 * * ? 每天的11點(diǎn)觸發(fā)執(zhí)行一次* 0 30 10 1 * ? 每月1號(hào)上午10點(diǎn)半觸發(fā)執(zhí)行一次*/public static Trigger createTrigger() {// 創(chuàng)建時(shí)間觸發(fā)器CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity("triggerName","myTrigger").startNow().withSchedule(CronScheduleBuilder.cronSchedule("*/2 * * * * ?")).build();return cronTrigger;}/*** main函數(shù)中開(kāi)啟定時(shí)任務(wù)* @param args*/public static void main(String[] args) throws SchedulerException {// 1、創(chuàng)建任務(wù)調(diào)度器(好比公交調(diào)度站)Scheduler scheduler = QuartzMan.createScheduler();// 2、創(chuàng)建一個(gè)任務(wù)(好比某一個(gè)公交車(chē)的出行)JobDetail job = QuartzMan.createJob();// 3、創(chuàng)建任務(wù)的時(shí)間觸發(fā)器(好比這個(gè)公交車(chē)的出行時(shí)間表)Trigger trigger = QuartzMan.createTrigger();// 4、使用任務(wù)調(diào)度器根據(jù)時(shí)間觸發(fā)器執(zhí)行我們的任務(wù)scheduler.scheduleJob(job,trigger);scheduler.start();} }4.5 Elastic-job
github地址:https://github.com/elasticjob
1.主要功能
- 分布式環(huán)境下,能夠避免同一個(gè)任務(wù)多實(shí)例重復(fù)執(zhí)行
- 彈性擴(kuò)容縮容,當(dāng)集群中增加某一個(gè)實(shí)例,它能被選舉且執(zhí)行任務(wù),減少一個(gè)實(shí)例,任務(wù)自動(dòng)分配給其他實(shí)例
- 任務(wù)失敗時(shí),轉(zhuǎn)移給其他實(shí)例
- 自動(dòng)記錄錯(cuò)過(guò)執(zhí)行的作業(yè)
- 支持并行調(diào)度,支持任務(wù)分片
- 作業(yè)分片后,能后保證同一分片在分布式環(huán)境中僅執(zhí)行一個(gè)
2.代碼執(zhí)行實(shí)例
- 分片 ====>shardingItemParameters(“0=bachelor,1=master,2=doctor”)
- 把一個(gè)任務(wù)分成多個(gè)task,每一個(gè)task交給一個(gè)具體的一個(gè)機(jī)器實(shí)例去處理
- Strategy策略定義這些分片項(xiàng)怎么去分配到機(jī)器,默認(rèn)是平均分,分片和作業(yè)是通過(guò)一個(gè)注冊(cè)中心協(xié)調(diào)的
- 擴(kuò)容
分布式的理解
5.Session的共享
5.1 Session問(wèn)題原因分析
Http是無(wú)狀態(tài)協(xié)議
兩種用于保持Http狀態(tài)的技術(shù),那就是Cookie和Session
JSESSIONID是一個(gè)Cookie,Servlet容器(tomcat,jetty)用來(lái)記錄用戶session
5.2 Session一致性的解決方案
同?個(gè)客戶端IP的請(qǐng)求都會(huì)被路由到同?個(gè)?標(biāo)服務(wù)器,也叫做會(huì)話粘滯
- 優(yōu)點(diǎn):
配置簡(jiǎn)單,不?侵應(yīng)?,不需要額外修改代碼
- 缺點(diǎn):
服務(wù)器重啟Session丟失
存在單點(diǎn)負(fù)載?的?險(xiǎn)
單點(diǎn)故障問(wèn)題
Session復(fù)制(不推薦)多個(gè)tomcat之間通過(guò)修改配置?件,達(dá)到Session之間的復(fù)制
Session共享(交代redis)
優(yōu)點(diǎn):
- 能適應(yīng)各種負(fù)載均衡策略
- 服務(wù)器重啟或者當(dāng)即不會(huì)造成Session丟失
- 擴(kuò)展能力強(qiáng)
- 適合大集群數(shù)量使用
缺點(diǎn)
- 對(duì)應(yīng)用有侵入性
5.3代碼實(shí)現(xiàn)(Session共享)
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><dependency><groupId>org.springframework.session</groupId><artifactId>spring-session-data-redis</artifactId></dependency> spring.redis.database=0 spring.redis.host=127.0.0.1 spring.redis.port=6379 @SpringBootApplication @EnableCaching @EnableRedisHttpSession //加這個(gè)注解 public class LoginApplication extends SpringBootServletInitializer {@Overrideprotected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {return builder.sources(LoginApplication.class);}public static void main(String[] args) {SpringApplication.run(LoginApplication.class, args);}}5.4源碼刨析
附錄
1.wrapper的概念
實(shí)現(xiàn)Wrapper的類需要:
單詞
Cluster 群,集群
strict 嚴(yán)格
stratum 層
ensure 保證
Coordinator協(xié)調(diào),班主任
elastic 彈性的
總結(jié)
以上是生活随笔為你收集整理的Cluster模式潜在问题及解决方案、Web服务综合解决方案的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: dubbo笔记+源码刨析
- 下一篇: springMvc源码刨析笔记