xxl-job源碼分析

xxl-job源碼分析

xxl-job

系統說明

安裝

安裝部署參考文檔:分布式任務調度平臺xxl-job

功能

定時調度、服務解耦、靈活控制跑批時間(停止、開啟、重新設定時間、手動觸發)

XXL-JOB是一個輕量級分布式任務調度平臺,其核心設計目標是開發迅速、學習簡單、輕量級、易擴展。現已開放源代碼并接入多家公司線上產品線,開箱即用

概念

執行器列表:一個執行器是一個項目

任務:一個任務是一個項目中的 JobHandler

一個xxl-job服務可以有多個執行器(項目),一個項目下可以有多個任務(JobHandler),他們是如何關聯的?

頁面操作:

  1. 在管理平臺可以新增執行器(項目)
  2. 在任務列表可以指定執行器(項目)下新增多個任務(JobHandler)

代碼操作:

  1. 項目配置中增加 xxl.job.executor.appname = "執行器名稱"
  2. 在實現類中增加 @JobHandler(value="xxl-job-demo") 注解,并繼承 IJobHandler

架構圖

1072053-20190920094456690-1804945154.png

拋出疑問

  1. 調度中心啟動過程?
  2. 執行器啟動過程?
  3. 執行器如何注冊到調度中心?
  4. 調度中心怎么調用執行器?
  5. 集群調度時如何控制一個任務在該時刻不會重復執行
  6. 集群部署應該注意什么?

系統分析

執行器依賴jar包

com.xuxueli:xxl-job-core:2.1.0

com.xuxueli:xxl-registry-client:1.0.2

com.xuxueli:xxl-rpc-core:1.4.1

調度中心啟動過程

// 1. 加載 XxlJobAdminConfig,adminConfig = this
XxlJobAdminConfig.java// 啟動過程代碼
@Component
public class XxlJobScheduler implements InitializingBean, DisposableBean {private static final Logger logger = LoggerFactory.getLogger(XxlJobScheduler.class);@Overridepublic void afterPropertiesSet() throws Exception {// init i18ninitI18n();// admin registry monitor run// 2. 啟動注冊監控器(將注冊到register表中的IP加載到group表)/ 30執行一次JobRegistryMonitorHelper.getInstance().start();// admin monitor run// 3. 啟動失敗日志監控器(失敗重試,失敗郵件發送)JobFailMonitorHelper.getInstance().start();// admin-server// 4. 初始化RPC服務initRpcProvider();// start-schedule// 5. 啟動定時任務調度器(執行任務,緩存任務)JobScheduleHelper.getInstance().start();logger.info(">>>>>>>>> init xxl-job admin success.");}......
}

執行器啟動過程

@Override
public void start() throws Exception {// init JobHandler Repository// 將執行 JobHandler 注冊到緩存中 jobHandlerRepository(ConcurrentMap)initJobHandlerRepository(applicationContext);// refresh GlueFactory// 刷新GLUEGlueFactory.refreshInstance(1);// super start// 核心啟動項super.start();
}public void start() throws Exception {// 初始化日志路徑 // private static String logBasePath = "/data/applogs/xxl-job/jobhandler";XxlJobFileAppender.initLogPath(this.logPath);// 初始化注冊中心列表 (把注冊地址放到 List)this.initAdminBizList(this.adminAddresses, this.accessToken);// 啟動日志文件清理線程 (一天清理一次)// 每天清理一次過期日志,配置參數必須大于3才有效JobLogFileCleanThread.getInstance().start((long)this.logRetentionDays);// 開啟觸發器回調線程TriggerCallbackThread.getInstance().start();// 指定端口this.port = this.port > 0 ? this.port : NetUtil.findAvailablePort(9999);// 指定IPthis.ip = this.ip != null && this.ip.trim().length() > 0 ? this.ip : IpUtil.getIp();// 初始化RPC 將執行器注冊到調度中心 30秒一次this.initRpcProvider(this.ip, this.port, this.appName, this.accessToken);
}

執行器注冊到調度中心

執行器

// 注冊執行器入口
XxlJobExecutor.java->initRpcProvider()->xxlRpcProviderFactory.start();// 開啟注冊
XxlRpcProviderFactory.java->start();// 執行注冊
ExecutorRegistryThread.java->start();
// RPC 注冊代碼
for (AdminBiz adminBiz: XxlJobExecutor.getAdminBizList()) {try {ReturnT<String> registryResult = adminBiz.registry(registryParam);if (registryResult!=null && ReturnT.SUCCESS_CODE == registryResult.getCode()) {registryResult = ReturnT.SUCCESS;logger.debug(">>>>>>>>>>> xxl-job registry success, registryParam:{}, registryResult:{}", new Object[]{registryParam, registryResult});break;} else {logger.info(">>>>>>>>>>> xxl-job registry fail, registryParam:{}, registryResult:{}", new Object[]{registryParam, registryResult});}} catch (Exception e) {logger.info(">>>>>>>>>>> xxl-job registry error, registryParam:{}", registryParam, e);}}

調度中心

// RPC 注冊服務
AdminBizImpl.java->registry();

數據庫

1072053-20190920094906422-637038043.png

1072053-20190920094851139-1515543303.png

調度中心調用執行器

/* 調度中心執行步驟 */
// 1. 調用執行器
XxlJobTrigger.java->runExecutor();// 2. 獲取執行器
XxlJobScheduler.java->getExecutorBiz();// 3. 調用
ExecutorBizImpl.java->run();/* 執行器執行步驟 */
// 1. 執行器接口
ExecutorBiz.java->run();// 2. 執行器實現
ExecutorBizImpl.java->run();// 3. 把jobInfo 從 jobThreadRepository (ConcurrentMap) 中獲取一個新線程,并開啟新線程
XxlJobExecutor.java->registJobThread();// 4. 保存到當前線程隊列
JobThread.java->pushTriggerQueue();// 5. 執行
JobThread.java->handler.execute(triggerParam.getExecutorParams());

調度中心(Admin)

實現 org.springframework.beans.factory.InitializingBean類,重寫 afterPropertiesSet 方法,在初始化bean的時候都會執行該方法

DisposableBean spring停止時執行

結束加載項

  1. 停止定時任務調度器(中斷scheduleThread,中斷ringThread)
  2. 停止觸發線程池(JobTriggerPoolHelper)
  3. 停止注冊監控器(registryThread)
  4. 停止失敗日志監控器(monitorThread)
  5. 停止RPC服務(stopRpcProvider)

手動執行方式

JobInfoController.java

@RequestMapping("/trigger")
@ResponseBody
//@PermissionLimit(limit = false)
public ReturnT<String> triggerJob(int id, String executorParam) {// force cover job paramif (executorParam == null) {executorParam = "";}JobTriggerPoolHelper.trigger(id, TriggerTypeEnum.MANUAL, -1, null, executorParam);return ReturnT.SUCCESS;
}

定時調度策略

調度策略執行圖

1072053-20190920094826086-2070926333.png

調度策略源碼

JobScheduleHelper.java->start();

路由策略

第一個

固定選擇第一個機器

ExecutorRouteFirst.java->route();
最后一個

固定選擇最后一個機器

ExecutorRouteLast.java->route();
輪詢

隨機選擇在線的機器

ExecutorRouteRound.java->route();private static int count(int jobId) {// cache clearif (System.currentTimeMillis() > CACHE_VALID_TIME) {routeCountEachJob.clear();CACHE_VALID_TIME = System.currentTimeMillis() + 1000*60*60*24;}// count++Integer count = routeCountEachJob.get(jobId);count = (count==null || count>1000000)?(new Random().nextInt(100)):++count;  // 初始化時主動Random一次,緩解首次壓力routeCountEachJob.put(jobId, count);return count;
}
隨機

隨機獲取地址列表中的一個

ExecutorRouteRandom.java->route();
一致性HASH

一個job通過hash算法固定使用一臺機器,且所有任務均勻散列在不同機器

ExecutorRouteConsistentHash.java->route();public String hashJob(int jobId, List<String> addressList) {// ------A1------A2-------A3------// -----------J1------------------TreeMap<Long, String> addressRing = new TreeMap<Long, String>();for (String address: addressList) {for (int i = 0; i < VIRTUAL_NODE_NUM; i++) {long addressHash = hash("SHARD-" + address + "-NODE-" + i);addressRing.put(addressHash, address);}}long jobHash = hash(String.valueOf(jobId));// 取出鍵值 >= jobHashSortedMap<Long, String> lastRing = addressRing.tailMap(jobHash);if (!lastRing.isEmpty()) {return lastRing.get(lastRing.firstKey());}return addressRing.firstEntry().getValue();
}
最不經常使用

使用頻率最低的機器優先被選舉
把地址列表加入到內存中,等下次執行時剔除無效的地址,判斷地址列表中執行次數最少的地址取出
頻率、次數

ExecutorRouteLFU.java->route();public String route(int jobId, List<String> addressList) {// cache clearif (System.currentTimeMillis() > CACHE_VALID_TIME) {jobLfuMap.clear();CACHE_VALID_TIME = System.currentTimeMillis() + 1000*60*60*24;}// lfu item initHashMap<String, Integer> lfuItemMap = jobLfuMap.get(jobId);     // Key排序可以用TreeMap+構造入參Compare;Value排序暫時只能通過ArrayList;if (lfuItemMap == null) {lfuItemMap = new HashMap<String, Integer>();jobLfuMap.putIfAbsent(jobId, lfuItemMap);   // 避免重復覆蓋}// put newfor (String address: addressList) {if (!lfuItemMap.containsKey(address) || lfuItemMap.get(address) >1000000 ) {// 0-n隨機數,包括0不包括nlfuItemMap.put(address, new Random().nextInt(addressList.size()));  // 初始化時主動Random一次,緩解首次壓力}}// remove oldList<String> delKeys = new ArrayList<>();for (String existKey: lfuItemMap.keySet()) {if (!addressList.contains(existKey)) {delKeys.add(existKey);}}if (delKeys.size() > 0) {for (String delKey: delKeys) {lfuItemMap.remove(delKey);}}/*********************** 優化 START ***********************/// 優化  remove old部分Iterator<String> iterable = lfuItemMap.keySet().iterator();while (iterable.hasNext()) {String address = iterable.next();if (!addressList.contains(address)) {iterable.remove();}}/*********************** 優化 START ***********************/// load least userd count address// 從小到大排序List<Map.Entry<String, Integer>> lfuItemList = new ArrayList<Map.Entry<String, Integer>>(lfuItemMap.entrySet());Collections.sort(lfuItemList, new Comparator<Map.Entry<String, Integer>>() {@Overridepublic int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {return o1.getValue().compareTo(o2.getValue());}});Map.Entry<String, Integer> addressItem = lfuItemList.get(0);String minAddress = addressItem.getKey();addressItem.setValue(addressItem.getValue() + 1);return addressItem.getKey();
}
最近最久未使用

最久未使用的機器優先被選舉
用鏈表的方式存儲地址,第一個地址使用后下次該任務過來使用第二個地址,依次類推(PS:有點類似輪詢策略)
與輪詢策略的區別:

  1. 輪詢策略是第一次隨機找一臺機器執行,后續執行會將索引加1取余
  2. 輪詢策略依賴 addressList 的順序,如果這個順序變了,索引到下一次的機器可能不是期望的順序
  3. LRU算法第一次執行會把所有地址加載進來并緩存,從第一個地址開始執行,即使 addressList 地址順序變了也不影響
    次數
ExecutorRouteLRU.java->route();public String route(int jobId, List<String> addressList) {// cache clearif (System.currentTimeMillis() > CACHE_VALID_TIME) {jobLRUMap.clear();CACHE_VALID_TIME = System.currentTimeMillis() + 1000*60*60*24;}// init lruLinkedHashMap<String, String> lruItem = jobLRUMap.get(jobId);if (lruItem == null) {/*** LinkedHashMap*      a、accessOrder:ture=訪問順序排序(get/put時排序);false=插入順序排期;*      b、removeEldestEntry:新增元素時將會調用,返回true時會刪除最老元素;可封裝LinkedHashMap并重寫該方法,比如定義最大容量,超出是返回true即可實現固定長度的LRU算法;*/lruItem = new LinkedHashMap<String, String>(16, 0.75f, true);jobLRUMap.putIfAbsent(jobId, lruItem);}/*********************** 舉個例子 START ***********************/// 如果accessOrder為true的話,則會把訪問過的元素放在鏈表后面,放置順序是訪問的順序 // 如果accessOrder為flase的話,則按插入順序來遍歷LinkedHashMap<String, String> lruItem = new LinkedHashMap<String, String>(16, 0.75f, true);jobLRUMap.putIfAbsent(1, lruItem);lruItem.put("192.168.0.1", "192.168.0.1");lruItem.put("192.168.0.2", "192.168.0.2");lruItem.put("192.168.0.3", "192.168.0.3");String eldestKey = lruItem.entrySet().iterator().next().getKey();String eldestValue = lruItem.get(eldestKey);System.out.println(eldestValue + ": " + lruItem);eldestKey = lruItem.entrySet().iterator().next().getKey();eldestValue = lruItem.get(eldestKey);System.out.println(eldestValue + ": " + lruItem);// 輸出結果:192.168.0.1: {192.168.0.2=192.168.0.2, 192.168.0.3=192.168.0.3, 192.168.0.1=192.168.0.1}
192.168.0.2: {192.168.0.3=192.168.0.3, 192.168.0.1=192.168.0.1, 192.168.0.2=192.168.0.2}/*********************** 舉個例子 END ***********************/// put newfor (String address: addressList) {if (!lruItem.containsKey(address)) {lruItem.put(address, address);}}// remove oldList<String> delKeys = new ArrayList<>();for (String existKey: lruItem.keySet()) {if (!addressList.contains(existKey)) {delKeys.add(existKey);}}if (delKeys.size() > 0) {for (String delKey: delKeys) {lruItem.remove(delKey);}}// loadString eldestKey = lruItem.entrySet().iterator().next().getKey();String eldestValue = lruItem.get(eldestKey);return eldestValue;
}
故障轉移

按照順序依次進行心跳檢測,第一個心跳檢測成功的機器選定為目標執行器并發起調度

ExecutorRouteFailover.java->route();
忙碌轉移

按照順序依次進行空閑檢測,第一個空閑檢測成功的機器選定為目標執行器并發起調度

ExecutorRouteBusyover.java->route();
分片廣播

廣播觸發對應集群中所有機器執行一次任務,同時傳遞分片參數;可根據分片參數開發分片任務

阻塞處理策略

為了解決執行線程因并發問題、執行效率慢、任務多等原因而做的一種線程處理機制,主要包括 串行、丟棄后續調度、覆蓋之前調度,一般常用策略是串行機制

ExecutorBlockStrategyEnum.javaSERIAL_EXECUTION("Serial execution"), // 串行
DISCARD_LATER("Discard Later"), // 丟棄后續調度
COVER_EARLY("Cover Early"); // 覆蓋之前調度ExecutorBizImpl.java->run();// executor block strategy
if (jobThread != null) {ExecutorBlockStrategyEnum blockStrategy = ExecutorBlockStrategyEnum.match(triggerParam.getExecutorBlockStrategy(), null);if (ExecutorBlockStrategyEnum.DISCARD_LATER == blockStrategy) {// discard when runningif (jobThread.isRunningOrHasQueue()) {return new ReturnT<String>(ReturnT.FAIL_CODE, "block strategy effect:"+ExecutorBlockStrategyEnum.DISCARD_LATER.getTitle());}} else if (ExecutorBlockStrategyEnum.COVER_EARLY == blockStrategy) {// kill running jobThreadif (jobThread.isRunningOrHasQueue()) {removeOldReason = "block strategy effect:" + ExecutorBlockStrategyEnum.COVER_EARLY.getTitle();jobThread = null;}} else {// just queue trigger}
}
單機串行

對當前線程不做任何處理,并在當前線程的隊列里增加一個執行任務

丟棄后續調度

如果當前線程阻塞,后續任務不再執行,直接返回失敗

覆蓋之前調度

創建一個移除原因,新建一個線程去執行后續任務

運行模式

ExecutorBizImpl.java->run();
BEAN

java里的bean對象

GLUE(Java)

利用java的反射機制,通過代碼字符串生成實體類

IJobHandler originJobHandler = GlueFactory.getInstance().loadNewInstance(triggerParam.getGlueSource());GroovyClassLoader
GLUE(Shell Python PHP Nodejs PowerShell)

按照文件命名規則創建一個執行腳本文件和一個日志輸出文件,通過腳本執行器執行

失敗重試次數

任務失敗后記錄到 xxl_job_log 中,由失敗監控線程查詢處理失敗的任務且失敗次數大于0,繼續執行

任務超時時間

把超時時間給 triggerParam 觸發參數,在調用執行器的任務時超時時間,有點類似HttpClient的超時時間

執行器(Exector)

  1. 注冊自己的機器地址

  2. 注冊項目中的 JobHandler

  3. 提供被調度中心調用的接口

    public interface ExecutorBiz {/*** 供調度中心檢測機器是否存活** beat* @return*/public ReturnT<String> beat();/*** 供調度中心檢測機器是否空閑** @param jobId* @return*/public ReturnT<String> idleBeat(int jobId);/*** kill* @param jobId* @return*/public ReturnT<String> kill(int jobId);/*** log* @param logDateTim* @param logId* @param fromLineNum* @return*/public ReturnT<LogResult> log(long logDateTim, long logId, int fromLineNum);/*** 執行觸發器* * @param triggerParam* @return*/public ReturnT<String> run(TriggerParam triggerParam);}

總結

1072053-20190920094739862-1959701255.png

學到了什么

  1. 算法(LFU、LRU、輪詢等)
  2. JDK動態代理對象(詳細研究)
  3. 用到了Netty(詳細研究)
  4. FutureTask
  5. GroovyClassLoader
posted on 2019-09-20 09:43?小猴子先生 閱讀(...) 評論(...) 編輯 收藏

轉載于:https://www.cnblogs.com/guoyinli/p/11555035.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/276694.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/276694.shtml
英文地址,請注明出處:http://en.pswp.cn/news/276694.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

定制jQuery File Upload為微博式單文件上傳

原文鏈接&#xff1a;http://avnpc.com/pages/single-file-upload-component-by-jquery-file-upload jQuery File Upload是一個非常優秀的上傳組件&#xff0c;主要使用了XHR作為上傳方式&#xff0c;并且利用了相當多的現代瀏覽器功能&#xff0c;所以可以實現諸如批量上傳、超…

vb趣味編程彈球小游戲_最好玩的微信小游戲集合,總有一款是你沒玩過的

大家好&#xff0c;這里是小雅龍生活趣味時間&#xff0c;自從17年微信推出小游戲程序以來&#xff0c;微信小游戲行業可謂是炙手可熱&#xff0c;知道2019年不斷有許許多多的微信小游戲如雨后春筍般的生根發芽。下面就由我帶大家來看看今年最好玩&#xff0c;最受歡迎的微信小…

開發MOSS2007 Masterpage的一些經驗

一直在做MOSS平臺的Masterpage開發,碰到很多的問題,總結了一些經驗,特此記錄: masterpage的所有的ContentPlaceholder詳細解釋見以下網址:http://www.cnblogs.com/WinYoung/archive/2007/06/25/791766.html 1.如果應用masterpage以后IE狀態欄出現""網頁指令碼錯誤訊息…

Golang——垃圾回收GC(2)

1 垃圾回收中的重要概念 1.1 定義 In computer science, garbage collection (GC) is a form of automatic memory management. The garbage collector, or just collector, attempts to reclaim garbage, or memory occupied by objects that are no longer in use by the pro…

java gui框架_推薦!程序員整理的Java資源大全

構建這里搜集了用來構建應用程序的工具。Apache Maven&#xff1a;Maven使用聲明進行構建并進行依賴管理&#xff0c;偏向于使用約定而不是配置進行構建。Maven優于Apache Ant。后者采用了一種過程化的方式進行配置&#xff0c;所以維護起來相當困難。Gradle&#xff1a;Gradle…

帆軟報表(finereport)控件背景色更改

setTimeout(function() {$(.fr-trigger-btn-up).css({"background-color": "#003399" });}, 100); 轉載于:https://www.cnblogs.com/Williamls/p/11571586.html

開心網分析,師從“中國緣”

作者&#xff1a;麥田   一&#xff0c;師從“中國緣” 開心網從08年“爆發”之后&#xff0c;網上出現很多評論文章。幾乎100%的評論文章都談到了開心網“不可思議”的爆發增長速度&#xff0c;比如幾個月就進入了alexa前500等等。但是&#xff0c;幾乎沒有一篇文章提到“開心…

HTML5+CSS3+JQuery1.9 輸入框切換和Div失焦模擬

Div失焦原理&#xff1a;判斷document點擊對象是否在Div容器以內&#xff0c;否則觸發事件 需要腳本&#xff1a;jquery-1.9.1.js 下載地址&#xff1a;http://download.csdn.net/detail/dmtnewtons/5807757 <!DOCTYPE> <html> <head> <meta http-equ…

資本冬天已至,開發者卻可以著眼未來

云&#xff0c;在國內外都已成為軟件開發者的首選服務。縱觀歷史&#xff0c;在云計算發展的這些年里&#xff0c;不管云上做了多少產品和服務&#xff0c;其實都離不開云最本質的價值體系&#xff1a;自服務、高彈性、按需提供、免運維&#xff0c;這些特性也讓云服務天然成為…

mybatis 大于_酸爽!IDEA 中這么玩 MyBatis,讓編碼速度飛起!

作者&#xff1a;Orsoncnblogs.com/java-class/p/6237564.html1. 搭建 MyBatis Generator 插件環境a. 添加插件依賴 pom.xmlb. 配置文件 generatorConfig.xmlc. 數據庫配置文件 jdbc.propertiesd. 配置插件啟動項2.項目實戰a. 比如在一個項目 我們要刪除某個小組下某個用戶的信…

Java的三種代理模式完整源碼分析

Java的三種代理模式&完整源碼分析 Java的三種代理模式&完整源碼分析 參考資料&#xff1a; 博客園-Java的三種代理模式 簡書-JDK動態代理-超詳細源碼分析 [博客園-WeakCache緩存的實現機制](https://www.cnblogs.com/liuyun1995/p/8144676.html) 靜態代理 靜態代理在使…

scatter函數_matplotlib.pyplot常用函數scatter講解大全(三)

前言這篇文章再來總結一個常用畫圖函數scatter-散點圖。參數常用參數示例import matplotlib.pyplot as plt import numpy as np#導入需要的包 datanp.random.multivariate_normal([0,1],[[1,0],[0,1]],200)#準備數據&#xff0c;二維正態分布plt.rcParams["axes.unicode_m…

如何徹底卸載MySQL

本文摘自&#xff1a;http://www.heiqu.com/show-64764-1.html 內容為&#xff1a; 由于安裝MySQL的時候&#xff0c;疏忽沒有選擇底層編碼方式&#xff0c;采用默認的ASCII的編碼格式&#xff0c;于是接二連三的中文轉換問題隨之而來&#xff0c;就想卸載了重新安裝MYSQL&…

vue-cli項目模板的一些思考

之前有個想法&#xff0c;就是要利用vue寫一套ui。然后當時也沒有搞清楚到底怎么寫。 幾經周轉吧&#xff0c;通過付費的方式在gitbook上面找到了答案。 找到答案之后再看我們正在開發的項目&#xff0c;看伙伴寫的代碼&#xff0c;突然發現完全可以按照寫ui組件庫的方式調整目…

flex基于svn協同開發

想做一個游戲&#xff0c;正好有人陪我做。于是想到用flex來協同開發。本來是想使用cvs&#xff0c;可是結果搗鼓了半天&#xff0c;也沒個結果——估計是最近沒怎么看電影&#xff0c;IQ降下來了。于是改用svn。 參考資料&#xff1a;http://www.flashmagazine.com/tutorials/…

cookie與session詳解

session與cookie是什么?session與cookie屬于一種會話控制技術.常用在身份識別&#xff0c;登錄驗證&#xff0c;數據傳輸等.舉個例子&#xff0c;就像我們去超市買東西結賬的時候&#xff0c;我們要拿出我們的會員卡才會獲取優惠.這時候&#xff0c;我們怎么識別這個會員卡真實…

c++萬能頭文件_初學Python,與C對比

?背景學了一學年的C的基礎&#xff0c;下學年開課Python&#xff0c;現在正在自學中...C也不是不學了&#xff0c;而是之前買了一本《CPrimer》在學校里&#xff0c;就準備先學一下Python&#xff0c;下學期利用自由時間接著學習C。這里分析了一下二者的優缺點&#xff0c;供大…

listen(int fd, int backlog)中的backlog含義

1. listen(int fd, int backlog)中的backlog不能限制連接數量??? http://bbs.chinaunix.net/viewthread.php?tid870564 backlog應該是未完成3次握手連接和已完成3次握手而未被accept的兩對列之和.不知道我說的對不? 如果要控制連接數量,是不是要自己編碼控制...下面的可以…

本地無法啟動MySQL服務,報的錯誤:1067,進程意外終止---解決

原文鏈接&#xff1a;http://blog.csdn.net/shenhonglei1234/article/details/5928873 在本地計算機無法啟動MYSQL服務錯誤1067進程意外終止 這種情況一般是my.ini文件配置出錯了 首先找到這個文件&#xff1a; 默認安裝路徑 C:/Program Files/MySQL/MySQL Server 5.1/my.ini …

一篇文章助你理解Python3中字符串編碼問題

前幾天給大家介紹了unicode編碼和utf-8編碼的理論知識&#xff0c;以及Python2中字符串編碼問題&#xff0c;沒來得及上車的小伙伴們可以戳這篇文章&#xff1a;淺談unicode編碼和utf-8編碼的關系和一篇文章助你理解Python2中字符串編碼問題。下面在Python3環境中進行代碼演示&…