摘要:由于標(biāo)題長(zhǎng)度限制,原題是這樣某系統(tǒng)萬,每十分鐘統(tǒng)計(jì)一下請(qǐng)求次數(shù)最多的個(gè)。請(qǐng)求寫到日志的話,其實(shí)就是超大文件中統(tǒng)計(jì)問題。
由于標(biāo)題長(zhǎng)度限制,原題是這樣:某系統(tǒng)QPS100萬,每十分鐘統(tǒng)計(jì)一下請(qǐng)求次數(shù)最多的100個(gè)IP。ip請(qǐng)求寫到日志的話,其實(shí)就是超大文件中統(tǒng)計(jì)top k問題。10分鐘6億條記錄,大約是10G級(jí)別,所以對(duì)于一般單機(jī)處理來講不能一次性加載到內(nèi)存計(jì)算。所以分治算法是處理這類問題的基本思想。
思路前面說了分治思想。那么具體如何分解問題呢。
思路就是把大文件分割成多個(gè)可以內(nèi)存處理的小文件,對(duì)每個(gè)小文件統(tǒng)計(jì)top k問題,最后再對(duì)所有統(tǒng)計(jì)結(jié)果合并得到最終的top k。
注意,這里的分割并不是隨意分割的,那樣最終結(jié)果顯然是不對(duì)的,必須保證相同的ip記錄都分割到同一個(gè)文件。那么hash算法最合適不過了,可以把相同的ip哈希到同一文件。
關(guān)于top k問題,效率高的解法是使用構(gòu)造最小堆或者借助快速排序的思想,復(fù)雜度為O(nlogk)。這里更適合用最小堆,具體來說,就是先利用前k個(gè)數(shù)據(jù)構(gòu)建一個(gè)固定大小k的最小堆,對(duì)之后的數(shù)據(jù),小于堆頂不做處理,大于則替換堆頂并調(diào)整。這樣,對(duì)每個(gè)文件順序處理完之后就得到最終結(jié)果,而不需要保留每個(gè)文件的top k再歸并。
實(shí)現(xiàn)博主偷懶,借助TreeSet代替最小堆來維護(hù)top k數(shù)據(jù),TreeSet的話底層是借助紅黑樹排序,比最小堆復(fù)雜些,實(shí)際上對(duì)每個(gè)小文件用紅黑樹全排序再截取前k個(gè)。復(fù)雜度O(nlogm),這里m是每個(gè)小文件中的數(shù)量, m>>k。再有時(shí)間的話再用最小堆優(yōu)化一下,復(fù)雜度應(yīng)為O(nlogk)。
ps:已實(shí)現(xiàn)最小堆版本,見實(shí)現(xiàn)2,并做了對(duì)比實(shí)驗(yàn)
定時(shí)任務(wù)使用quartz實(shí)現(xiàn)。
下面是代碼。
IP類,封裝ip計(jì)數(shù),使用TreeSet存放須實(shí)現(xiàn)comparable接口。注意這里重寫compare方法不要return 0,否則會(huì)被TreeSet視為相同對(duì)象而放不進(jìn)去。這個(gè)可以看一下TreeSet的實(shí)現(xiàn),它實(shí)際上內(nèi)部還是一個(gè)TreeMap,只是把對(duì)象作為key,而value沒有使用。add添加元素時(shí),會(huì)調(diào)用TreeMap的put方法,put內(nèi)部又會(huì)調(diào)用compare方法,如果compare返回結(jié)果為0,只是重新setValue,對(duì)TreeSet相當(dāng)于什么也沒做。
package com.hellolvs; import org.apache.commons.lang3.builder.ToStringBuilder; /** * IP計(jì)數(shù)POJO * * @author lvs * @date 2017/12/08. */ public class IP implements Comparable{ private String ip; private int count; public IP() { } public IP(String ip, int count) { this.ip = ip; this.count = count; } public String getIp() { return ip; } public void setIp(String ip) { this.ip = ip; } public int getCount() { return count; } public void setCount(int count) { this.count = count; } @Override public int compareTo(IP o) { return o.count < this.count ? -1 : 1; } @Override public String toString() { return ToStringBuilder.reflectionToString(this); } }
IPCountJob類,定時(shí)統(tǒng)計(jì)日志文件中top k個(gè)ip。
注意其中的分割文件,這里的分割需要對(duì)文件邊讀邊寫,不能一次性讀入內(nèi)存再分割。guava io的readLines是直接裝入內(nèi)存的,所以不能用。可以使用java原生的io類,或使用commons io的LineIterator更優(yōu)雅一些。
package com.hellolvs; import com.google.common.base.Charsets; import com.google.common.base.Objects; import com.google.common.base.StandardSystemProperty; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.common.io.Files; import com.google.common.io.LineProcessor; import org.apache.commons.io.FileUtils; import org.apache.commons.io.LineIterator; import org.quartz.Job; import org.quartz.JobExecutionContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.nio.charset.Charset; import java.security.SecureRandom; import java.util.HashMap; import java.util.Map; import java.util.TreeSet; import java.util.concurrent.atomic.AtomicInteger; /** * 定時(shí)Job,每十分鐘統(tǒng)計(jì)請(qǐng)求次數(shù)前k的ip * * @author lvs * @date 2017/12/08. */ public class IPCountJob implements Job { private static final Logger LOG = LoggerFactory.getLogger(IPCountJob.class); private static final String LINE_SEPARATOR = StandardSystemProperty.LINE_SEPARATOR.value(); private static final Charset UTF_8 = Charsets.UTF_8; private static final String INPUT_PATH = "/home/lvs/logs/ip.log"; private static final String OUTPUT_PATH = "/home/lvs/logs/split/"; private static final int SPLIT_NUM = 1024; private static final int TOP_K = 100; /** * 利用TreeSet存儲(chǔ)請(qǐng)求次數(shù)前k的IP */ private TreeSetresultSet = Sets.newTreeSet(); /** * 分割文件用,保存每個(gè)文件的寫入流對(duì)象 */ private final Map bufferMap = Maps.newHashMapWithExpectedSize(SPLIT_NUM); /** * 定時(shí)任務(wù),每十分鐘統(tǒng)計(jì)請(qǐng)求次數(shù)前k的IP */ @Override public void execute(JobExecutionContext jobExecutionContext) { // 捕獲異常,防止定時(shí)任務(wù)中斷 try { execute(); } catch (Exception e) { LOG.error("定時(shí)任務(wù)出錯(cuò):{}", e.getMessage(), e); } } /** * 統(tǒng)計(jì)大文件中請(qǐng)求次數(shù)前k的IP * * @throws IOException I/O error */ public void execute() throws IOException { // 這里應(yīng)該每10分鐘獲取當(dāng)前輪替日志文件路徑,此處用常量路徑模擬 File ipLogFile = new File(INPUT_PATH); splitLog(ipLogFile, SPLIT_NUM); File logSplits = new File(OUTPUT_PATH); for (File logSplit : logSplits.listFiles()) { countTopK(logSplit, TOP_K); } LOG.info("結(jié)果集:{}", resultSet.size()); for (IP ip : resultSet) { LOG.info("{}", ip); } } /** * 生成模擬日志文件 * * @param logNum 生成日志條數(shù) * @throws IOException I/O error */ public static void generateLog(long logNum) throws IOException { /* 創(chuàng)建文件 */ File log = new File(INPUT_PATH); File parentDir = log.getParentFile(); if (!parentDir.exists()) { parentDir.mkdirs(); } log.createNewFile(); /* 生成隨機(jī)ip寫入文件 */ SecureRandom random = new SecureRandom(); try (BufferedWriter bw = new BufferedWriter(new FileWriter(log))) { for (int i = 0; i < logNum; i++) { StringBuilder sb = new StringBuilder(); sb.append("192.").append(random.nextInt(255)).append(".").append(random.nextInt(255)).append(".") .append(random.nextInt(255)).append(LINE_SEPARATOR); bw.write(sb.toString()); } bw.flush(); } } /** * 分割日志文件 * * @param logFile 待分割文件 * @param fileNum 分割文件數(shù)量 * @throws IOException I/O error */ private void splitLog(File logFile, int fileNum) throws IOException { /* 為每個(gè)分割文件創(chuàng)建寫入流對(duì)象 */ for (int i = 0; i < fileNum; i++) { File file = new File(OUTPUT_PATH + i); File parentDir = file.getParentFile(); if (!parentDir.exists()) { parentDir.mkdirs(); } bufferMap.put(i, new BufferedWriter(new FileWriter(file))); } /* 根據(jù)ip的hashcode將數(shù)據(jù)分割到不同文件中 */ LineIterator it = null; try { it = FileUtils.lineIterator(logFile, "UTF-8"); while (it.hasNext()) { String ip = it.nextLine(); int hashCode = Objects.hashCode(ip); hashCode = hashCode < 0 ? -hashCode : hashCode; BufferedWriter writer = bufferMap.get(hashCode % fileNum); writer.write(ip + LINE_SEPARATOR); } } finally { /* 釋放資源 */ LineIterator.closeQuietly(it); for (Map.Entry buffer : bufferMap.entrySet()) { BufferedWriter writer = buffer.getValue(); writer.flush(); writer.close(); } bufferMap.clear(); } } /** * 統(tǒng)計(jì)請(qǐng)求次數(shù)前k的IP * * @param logSplit 當(dāng)前分割文件 * @param k top k * @throws IOException I/O error */ private void countTopK(File logSplit, int k) throws IOException { /* 讀取文件對(duì)ip計(jì)數(shù) */ HashMap ipCountMap = Files.readLines(logSplit, UTF_8, new LineProcessor >() { private HashMap ipCountMap = Maps.newHashMap(); @Override public boolean processLine(String line) throws IOException { AtomicInteger ipCount = ipCountMap.get(line.trim()); if (ipCount != null) { ipCount.getAndIncrement(); } else { ipCountMap.put(line.trim(), new AtomicInteger(1)); } return true; } @Override public HashMap getResult() { return ipCountMap; } }); /* 統(tǒng)計(jì)結(jié)果添加到TreeSet */ for (Map.Entry entry : ipCountMap.entrySet()) { resultSet.add(new IP(entry.getKey(), entry.getValue().get())); } /* TreeSet只保留前k個(gè)ip */ TreeSet temp = Sets.newTreeSet(); int i = 0; for (IP o : resultSet) { temp.add(o); i++; if (i >= k) { break; } } resultSet = temp; } /** * 返回統(tǒng)計(jì)結(jié)果 * * @return 結(jié)果集合 */ public TreeSet getResult() { return resultSet; } }
Main,定時(shí)任務(wù)啟動(dòng)
package com.hellolvs; import org.quartz.JobBuilder; import org.quartz.JobDetail; import org.quartz.Scheduler; import org.quartz.SimpleScheduleBuilder; import org.quartz.Trigger; import org.quartz.TriggerBuilder; import org.quartz.impl.StdSchedulerFactory; /** * 定時(shí)任務(wù)啟動(dòng)器 * * @author lvs * @date 2017/12/11. */ public class Main { public static void main(String[] args) throws Exception { // 生成模擬日志文件 IPCountJob.generateLog(600000000); JobDetail job = JobBuilder.newJob(IPCountJob.class) .withIdentity("ipCountJob", "group1").build(); Trigger trigger = TriggerBuilder .newTrigger() .withIdentity("ipCountTrigger", "group1") .withSchedule( SimpleScheduleBuilder.simpleSchedule() .withIntervalInMinutes(10).repeatForever()) .build(); Scheduler scheduler = new StdSchedulerFactory().getScheduler(); scheduler.start(); scheduler.scheduleJob(job, trigger); } }實(shí)現(xiàn)2
IP類
package com.hellolvs; import org.apache.commons.lang3.builder.ToStringBuilder; /** * IP計(jì)數(shù)POJO * * @author lvs * @date 2017/12/08. */ public class IP implements Comparable{ private String ip; private int count; public IP() { } public IP(String ip, int count) { this.ip = ip; this.count = count; } public String getIp() { return ip; } public void setIp(String ip) { this.ip = ip; } public int getCount() { return count; } public void setCount(int count) { this.count = count; } @Override public int compareTo(IP o) { return Integer.compare(this.count, o.count); } @Override public String toString() { return ToStringBuilder.reflectionToString(this); } }
IPCountJob類,最小堆版本統(tǒng)計(jì)top k
package com.hellolvs; import com.google.common.base.Charsets; import com.google.common.base.Objects; import com.google.common.base.StandardSystemProperty; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.io.Files; import com.google.common.io.LineProcessor; import org.apache.commons.io.FileUtils; import org.apache.commons.io.LineIterator; import org.quartz.Job; import org.quartz.JobExecutionContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.nio.charset.Charset; import java.security.SecureRandom; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; /** * 定時(shí)Job,每十分鐘統(tǒng)計(jì)請(qǐng)求次數(shù)前k的ip * * @author lvs * @date 2017/12/08. */ public class IPCountJob implements Job { private static final Logger LOG = LoggerFactory.getLogger(IPCountJob.class); private static final String LINE_SEPARATOR = StandardSystemProperty.LINE_SEPARATOR.value(); private static final Charset UTF_8 = Charsets.UTF_8; private static final String INPUT_PATH = "/home/lvs/logs/ip.log"; private static final String OUTPUT_PATH = "/home/lvs/logs/split/"; private static final int SPLIT_NUM = 1024; private static final int TOP_K = 100; /** * 利用最小堆結(jié)構(gòu)存儲(chǔ)請(qǐng)求次數(shù)前k的IP */ private Listresult = Lists.newArrayListWithExpectedSize(TOP_K); /** * 分割文件用,保存每個(gè)文件的寫入流對(duì)象 */ private final Map bufferMap = Maps.newHashMapWithExpectedSize(SPLIT_NUM); /** * 定時(shí)任務(wù),每十分鐘統(tǒng)計(jì)請(qǐng)求次數(shù)前k的IP */ @Override public void execute(JobExecutionContext jobExecutionContext) { // 捕獲異常,防止定時(shí)任務(wù)中斷 try { execute(); } catch (Exception e) { LOG.error("定時(shí)任務(wù)出錯(cuò):{}", e.getMessage(), e); } } /** * 統(tǒng)計(jì)大文件中請(qǐng)求次數(shù)前k的IP * * @throws IOException I/O error */ public void execute() throws IOException { // 這里應(yīng)該每10分鐘獲取當(dāng)前輪替日志文件路徑,此處用常量路徑模擬 File ipLogFile = new File(INPUT_PATH); splitLog(ipLogFile, SPLIT_NUM); File logSplits = new File(OUTPUT_PATH); for (File logSplit : logSplits.listFiles()) { countTopK(logSplit, TOP_K); } MinHeap.sort(result); LOG.info("結(jié)果集:{}", result.size()); for (int i = result.size() - 1; i >= 0; i--) { LOG.info("{}", result.get(i)); } } /** * 生成模擬日志文件 * * @param logNum 生成日志條數(shù) * @throws IOException I/O error */ public static void generateLog(long logNum) throws IOException { /* 創(chuàng)建文件 */ File log = new File(INPUT_PATH); File parentDir = log.getParentFile(); if (!parentDir.exists()) { parentDir.mkdirs(); } log.createNewFile(); /* 生成隨機(jī)ip寫入文件 */ SecureRandom random = new SecureRandom(); try (BufferedWriter bw = new BufferedWriter(new FileWriter(log))) { for (int i = 0; i < logNum; i++) { StringBuilder sb = new StringBuilder(); sb.append("192.").append(random.nextInt(255)).append(".").append(random.nextInt(255)).append(".") .append(random.nextInt(255)).append(LINE_SEPARATOR); bw.write(sb.toString()); } bw.flush(); } } /** * 分割日志文件 * * @param logFile 待分割文件 * @param fileNum 分割文件數(shù)量 * @throws IOException I/O error */ private void splitLog(File logFile, int fileNum) throws IOException { /* 為每個(gè)分割文件創(chuàng)建寫入流對(duì)象 */ for (int i = 0; i < fileNum; i++) { File file = new File(OUTPUT_PATH + i); File parentDir = file.getParentFile(); if (!parentDir.exists()) { parentDir.mkdirs(); } bufferMap.put(i, new BufferedWriter(new FileWriter(file))); } /* 根據(jù)ip的hashcode將數(shù)據(jù)分割到不同文件中 */ LineIterator it = null; try { it = FileUtils.lineIterator(logFile, "UTF-8"); while (it.hasNext()) { String ip = it.nextLine(); int hashCode = Objects.hashCode(ip); hashCode = hashCode < 0 ? -hashCode : hashCode; BufferedWriter writer = bufferMap.get(hashCode % fileNum); writer.write(ip + LINE_SEPARATOR); } } finally { /* 釋放資源 */ LineIterator.closeQuietly(it); for (Map.Entry buffer : bufferMap.entrySet()) { BufferedWriter writer = buffer.getValue(); writer.flush(); writer.close(); } bufferMap.clear(); } } /** * 統(tǒng)計(jì)請(qǐng)求次數(shù)前k的IP * * @param logSplit 當(dāng)前分割文件 * @param k top k * @throws IOException I/O error */ private void countTopK(File logSplit, int k) throws IOException { /* 讀取文件對(duì)ip計(jì)數(shù) */ HashMap ipCountMap = Files.readLines(logSplit, UTF_8, new LineProcessor >() { private HashMap ipCountMap = Maps.newHashMap(); @Override public boolean processLine(String line) throws IOException { AtomicInteger ipCount = ipCountMap.get(line.trim()); if (ipCount != null) { ipCount.getAndIncrement(); } else { ipCountMap.put(line.trim(), new AtomicInteger(1)); } return true; } @Override public HashMap getResult() { return ipCountMap; } }); /* 前k條數(shù)據(jù)用來構(gòu)建初始最小堆,之后的數(shù)據(jù)比堆頂大則替換堆頂并調(diào)堆 */ for (Map.Entry entry : ipCountMap.entrySet()) { IP ip = new IP(entry.getKey(), entry.getValue().get()); if (result.size() != k) { result.add(ip); if (result.size() == k) { MinHeap.initMinHeap(result); } } else { if (ip.compareTo(result.get(0)) > 0) { result.set(0, ip); MinHeap.adjust(result, 0, k); } } } } /** * 返回統(tǒng)計(jì)結(jié)果 * * @return 結(jié)果集合 */ public List getResult() { return result; } }
MinHeap類,最小堆工具
package com.hellolvs; import java.util.List; /** * 最小堆 * * @author lvs * @date 2017-12-12 */ public class MinHeap { /** * 對(duì)最小堆排序 * * @param list 已經(jīng)為最小堆結(jié)構(gòu)的列表 * @param元素須實(shí)現(xiàn)Comparable接口 */ public static > void sort(List list) { for (int i = list.size() - 1; i > 0; i--) { swap(list, 0, i); adjust(list, 0, i); } } /** * 初始化最小堆 * * @param list 待初始化為最小堆的列表 * @param 元素須實(shí)現(xiàn)Comparable接口 */ public static > void initMinHeap(List list) { /* 從最后一個(gè)非葉節(jié)點(diǎn)開始至根節(jié)點(diǎn)依次調(diào)整 */ for (int i = list.size() / 2 - 1; i >= 0; i--) { adjust(list, i, list.size()); } } /** * 調(diào)堆 * * @param list 當(dāng)前堆 * @param 元素須實(shí)現(xiàn)Comparable接口 * @param cur 待調(diào)整位置 * @param length 當(dāng)前堆大小 */ public static > void adjust(List list, int cur, int length) { T tmp = list.get(cur); for (int i = 2 * cur + 1; i < length; i = 2 * i + 1) { if (i + 1 < length && list.get(i).compareTo(list.get(i + 1)) > 0) { i++; // i指向孩子節(jié)點(diǎn)中最小的節(jié)點(diǎn) } if (tmp.compareTo(list.get(i)) > 0) { list.set(cur, list.get(i)); // 最小孩子節(jié)點(diǎn)調(diào)整到其父節(jié)點(diǎn) cur = i; // 當(dāng)前節(jié)點(diǎn)置為最小孩子節(jié)點(diǎn),繼續(xù)調(diào)整 } else { break; // 沒有調(diào)整時(shí)退出循環(huán) } } list.set(cur, tmp); // 被調(diào)整節(jié)點(diǎn)最終存放位置 } /** * 交換List中的元素 * * @param list 待交換列表 * @param i 第一個(gè)元素位置 * @param j 第二個(gè)元素位置 */ private static > void swap(List list, int i, int j) { T tmp = list.get(i); list.set(i, list.get(j)); list.set(j, tmp); } }
Main類,無改動(dòng)
package com.hellolvs; import org.quartz.JobBuilder; import org.quartz.JobDetail; import org.quartz.Scheduler; import org.quartz.SimpleScheduleBuilder; import org.quartz.Trigger; import org.quartz.TriggerBuilder; import org.quartz.impl.StdSchedulerFactory; /** * 定時(shí)任務(wù)啟動(dòng)器 * * @author lvs * @date 2017/12/11. */ public class Main { public static void main(String[] args) throws Exception { // 生成模擬日志文件 IPCountJob.generateLog(600000000); JobDetail job = JobBuilder.newJob(IPCountJob.class) .withIdentity("ipCountJob", "group1").build(); Trigger trigger = TriggerBuilder .newTrigger() .withIdentity("ipCountTrigger", "group1") .withSchedule( SimpleScheduleBuilder.simpleSchedule() .withIntervalInMinutes(10).repeatForever()) .build(); Scheduler scheduler = new StdSchedulerFactory().getScheduler(); scheduler.start(); scheduler.scheduleJob(job, trigger); } }附
附一下pom文件:
對(duì)比實(shí)驗(yàn)4.0.0 com.hellolvs ipCount 1.0.0 jar 20.0 3.1 2.4 2.6 2.1.7 1.7.5 1.0.13 4.10 1.8 UTF-8 com.google.guava guava ${guava.version} org.apache.commons commons-lang3 ${commons-lang3.version} commons-io commons-io ${commons-io.version} joda-time joda-time ${joda-time.version} org.quartz-scheduler quartz ${org.quartz-scheduler.version} org.slf4j slf4j-api ${org.slf4j.version} ch.qos.logback logback-classic ${logback.version} runtime ch.qos.logback logback-core ${logback.version} runtime junit junit-dep ${junit.version} test com.google.guava guava org.apache.commons commons-lang3 commons-io commons-io joda-time joda-time org.quartz-scheduler quartz org.slf4j slf4j-api ch.qos.logback logback-classic ch.qos.logback logback-core junit junit-dep ROOT org.apache.maven.plugins maven-compiler-plugin ${java.version} ${project.build.sourceEncoding}
生成了6億條數(shù)據(jù)的日志。
TreeSet版本:生成6億條日志時(shí)間:521582 分割文件時(shí)間:173219 分割后統(tǒng)計(jì)top k時(shí)間:195037 定時(shí)任務(wù)執(zhí)行時(shí)間:368294
注:定時(shí)任務(wù)執(zhí)行時(shí)間指的是對(duì)大文件的總統(tǒng)計(jì)時(shí)間,主要是分割文件+分割后統(tǒng)計(jì)top k。
cpu和堆使用情況:
可以看到堆變化明顯分為三階段:對(duì)應(yīng)了生成日志、分割日志、分割后統(tǒng)計(jì)top k。
最小堆版本:生成6億條日志時(shí)間:513840 分割文件時(shí)間:148861 分割后統(tǒng)計(jì)top k時(shí)間:190966 定時(shí)任務(wù)執(zhí)行時(shí)間:339870
cpu和堆使用情況:
總結(jié):
生成日志和分割文件是沒有改動(dòng)的,運(yùn)行時(shí)間不一樣,可能有一定誤差。
倒是兩個(gè)版本統(tǒng)計(jì)top k時(shí)間沒有明顯的變化,按上面分析O(nlogm)和O(nlogk)應(yīng)該有比較明顯的差距才對(duì),這里n=600000000,m約600000,k=100,各位可以幫忙分析一下效率差距不大的原因。
不過可以看到堆內(nèi)存使用明顯降低了約100MB,因?yàn)門reeSet需要添加m個(gè)元素再截取k個(gè),而MinHeap只需要添加k個(gè)元素。
個(gè)人博客:www.hellolvs.com
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/70785.html
摘要:自己的小網(wǎng)站跑在阿里云的上面偶爾也去分析分析自己網(wǎng)站服務(wù)器日志看看網(wǎng)站的訪問量。然后統(tǒng)計(jì)最終返回的數(shù)字就是當(dāng)前所有端口的已建立連接的總數(shù)。 自己的小網(wǎng)站跑在阿里云的ECS上面,偶爾也去分析分析自己網(wǎng)站服務(wù)器日志,看看網(wǎng)站的訪問量。看看有沒有黑闊搞破壞!于是收集,整理一些服務(wù)器日志分析命令,大家可以試試! 1、查看有多少個(gè)IP訪問: awk {print $1} log_file|sor...
摘要:今年的無論是常態(tài)全鏈路壓測(cè)或者是雙十一當(dāng)天,面臨的主要問題是如何保障自身系統(tǒng)在海量數(shù)據(jù)沖擊下的穩(wěn)定性,以及如何更快的展現(xiàn)各個(gè)系統(tǒng)的狀態(tài)及更好的幫助開發(fā)同學(xué)發(fā)現(xiàn)及定位問題。在整個(gè)雙十一備戰(zhàn)過程中,遇到并解決了很多疑難雜癥。 摘要: EagleEye作為阿里集團(tuán)老牌的鏈路跟蹤系統(tǒng),其自身業(yè)務(wù)雖不在交易鏈路上,但卻監(jiān)控著全集團(tuán)的鏈路狀態(tài),特別是在中間件的遠(yuǎn)程調(diào)用上,覆蓋了集團(tuán)絕大部分的場(chǎng)景,...
摘要:自己的小網(wǎng)站跑在阿里云的上面偶爾也去分析分析自己網(wǎng)站服務(wù)器日志看看網(wǎng)站的訪問量。表示能夠處理個(gè)并發(fā)請(qǐng)求,這個(gè)值可根據(jù)負(fù)載情況自動(dòng)調(diào)整。最終返回的數(shù)字就是當(dāng)前所有端口的請(qǐng)求總數(shù)。 自己的小網(wǎng)站跑在阿里云的ECS上面,偶爾也去分析分析自己網(wǎng)站服務(wù)器日志,看看網(wǎng)站的訪問量。看看有沒有黑闊搞破壞!于是收集,整理一些服務(wù)器日志分析命令,大家可以試試! 1、查看有多少個(gè)IP訪問: awk {pr...
閱讀 929·2021-11-22 12:09
閱讀 3704·2021-09-27 13:36
閱讀 1390·2021-08-20 09:37
閱讀 4007·2019-12-27 12:22
閱讀 2352·2019-08-30 15:55
閱讀 2357·2019-08-30 13:16
閱讀 2817·2019-08-26 17:06
閱讀 3433·2019-08-23 18:32