一、Hadoop的3個核心組件:
分佈式文件系統:HDFS ——實現將文件分佈式存儲在很多的服務器上
分佈式運算編程框架:MapReduce ——實現在很多機器上分佈式並行運算
分佈式資源調度平臺:YARN ——幫用戶調度大量的MapReduce程序,併合理分配運算資源
二、HDFS整體運行機制
HDFS:分佈式文件系統
2.1 HDFS有着文件系統共同的特徵
- 有目錄結構,頂層目錄是: /
- 系統中存放的就是文件
- 系統可以提供對文件的:創建、刪除、修改、查看、移動等功能
如:查看根目錄文件
hadoop fs -ls /
2.2 HDFS跟普通的單機文件系統有區別
- 單機文件系統中存放的文件,是在一臺機器的操作系統中
- hdfs的文件系統會橫跨N多的機器
- 單機文件系統中存放的文件,是在一臺機器的磁盤上
- HDFS文件系統中存放的文件,是落在n多機器的本地單機文件系統中(HDFS是一個基於Linux本地文件系統之上的文件系統)
2.3 hdfs的工作機制與圖解:
- 客戶把一個文件存入HDFS,其實HDFS會把這個文件切塊後,分散存儲在N臺Linux機器系統中(負責存儲文件塊的角色:data node)<準確來說:切塊的行爲是由客戶端決定的>
- 一旦文件被切塊存儲,那麼,HDFS中就必須有一個機制,來記錄用戶的每一個文件的切塊信息,及每一塊的具體存儲機器(負責記錄塊信息的角色是:name node)
- 爲了保證數據的安全性,hdfs可以將每一個文件塊在集羣中存放多個副本(到底存幾個副本,是由當時存入該文件的客戶端指定的)
綜述:一個hdfs系統,由一臺運行了namenode的服務器,和N臺運行了datanode的服務器組成!
三、安裝HDFS集羣的具體步驟:
一、首先需要準備N臺linux服務器
學習階段,用虛擬機即可!
先準備4臺虛擬機:1個namenode節點 + 3 個datanode 節點
二、修改各臺機器的主機名和ip地址
主機名:hdp-01 對應的ip地址:192.168.33.61
主機名:hdp-02 對應的ip地址:192.168.33.62
主機名:hdp-03 對應的ip地址:192.168.33.63
主機名:hdp-04 對應的ip地址:192.168.33.64
三、從windows中用CRT軟件進行遠程連接
在windows中將各臺linux機器的主機名配置到的windows的本地域名映射文件中:
c:/windows/system32/drivers/etc/hosts
192.168.33.61 hdp-01
192.168.33.62 hdp-02
192.168.33.63 hdp-03
192.168.33.64 hdp-04
四、配置Linux服務器的基礎軟件環境
關閉防火牆
CentOS6:
service iptables stop #關閉防火牆
chkconfig iptables off #關閉防火牆自啓
CentOS7:
firewall-cmd --state #查看默認防火牆狀態(關閉後顯示notrunning,開啓後顯示running)
systemctl stop firewalld.service #停止firewall
systemctl disable firewalld.service #禁止firewall開機啓動
1、安裝jdk:(hadoop體系中的各軟件都是java開發的)
2、利用alt+p 打開sftp窗口,然後將jdk壓縮包拖入sftp窗口
3、然後在linux中將jdk壓縮包解壓到/root/apps下
4、配置環境變量:
vi /etc/profile
在文件的最後,加入:
export JAVA_HOME=/root/apps/jdk1.8.0_60
export PATH=$PATH:$JAVA_HOME/bin
5、修改完成後,記得執行source /etc/profile使配置生效
6、檢驗:在任意目錄下輸入命令:java -version 看是否成功執行
7、將安裝好的jdk目錄用scp命令拷貝到其他機器
scp -r /root/apps hdp-02:/root
scp -r /root/apps hdp-03:/root
scp -r /root/apps hdp-04:/root
8、將/etc/profile配置文件也用scp命令拷貝到其他機器並分別執行source命令
scp -r /etc/profile hdp-02:/etc/profile
scp -r /etc/profile hdp-03:/etc/profile
scp -r /etc/profile hdp-04:/etc/profile
9、集羣內主機的域名映射配置,在hdp-01上,vi /etc/hosts
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.33.61 hdp-01
192.168.33.62 hdp-02
192.168.33.63 hdp-03
192.168.33.64 hdp-04
然後,將hosts文件拷貝到集羣中的所有其他機器上
scp /etc/hosts hdp-02:/etc/
scp /etc/hosts hdp-03:/etc/
scp /etc/hosts hdp-04:/etc/
五、正式安裝HDFS集羣
1、上傳hadoop安裝包到hdp-01
https://hadoop.apache.org/releases.html
2、修改配置文件
核心配置參數:
1) 配置JAVA環境
2) 指定Hadoop的默認文件系統爲:HDFS
3) 指定HDFS的namenode節點爲哪臺機器
4) 指定namenode軟件存儲元數據的本地目錄
5) 指定datanode軟件存放文件塊的本地目錄
6) 指定哪些機器爲datanode
3、hadoop的配置文件在:/root/apps/hadoop安裝目錄/etc/hadoop/
(1)修改hadoop-env.sh 【配置JAVA_HOME 用於Hadoop環境 】
export JAVA_HOME=/root/apps/jdk1.8.0_60
(2)修改core-site.xml 【設置默認的文件系統、設置namenode爲hdp-01機器】
<configuration>
<property>
<name>fs.defaultFS</name>
<value>hdfs://hdp-01:9000</value>
</property>
</configuration>
(3) 修改hdfs-site.xml 【指定namenode、datanode軟件存儲元數據的本地目錄】
<configuration>
<property>
<name>dfs.namenode.name.dir</name>
<value>/root/hdpdata/name/</value>
</property>
<property>
<name>dfs.datanode.data.dir</name>
<value>/root/hdpdata/data</value>
</property>
<property>
<name>dfs.namenode.secondary.http-address</name>
<value>hdp-02:50090</value>
</property>
</configuration>
(4) 拷貝整個hadoop安裝目錄到其他機器
scp -r /root/apps/hadoop-2.8.1 hdp-02:/root/apps/
scp -r /root/apps/hadoop-2.8.1 hdp-03:/root/apps/
scp -r /root/apps/hadoop-2.8.1 hdp-04:/root/apps/
(5) 啓動HDFS
所謂的啓動HDFS,就是在對的機器上啓動對的軟件,要運行hadoop的命令,需要在linux環境中配置HADOOP_HOME和PATH環境變量,vi /etc/profile
export JAVA_HOME=/root/apps/jdk1.8.0_60
export HADOOP_HOME=/root/apps/hadoop-2.8.1
export PATH=$PATH:$JAVA_HOME/bin:$HADOOP_HOME/bin:$HADOOP_HOME/sbin
首先,初始化namenode的元數據目錄
要在hdp-01上執行hadoop的一個命令來初始化namenode的元數據存儲目錄
hadoop namenode -format
- 創建一個全新的元數據存儲目錄
- 生成記錄元數據的文件fsimage
- 生成集羣的相關標識:如:集羣id——clusterID
然後,啓動namenode進程(在hdp-01上)
hadoop-daemon.sh start namenode
啓動完後,首先用jps查看一下namenode的進程是否存在
然後,在windows中用瀏覽器訪問namenode提供的web端口:50070
http://hdp-01:50070
然後,啓動衆datanode們(在任意地方)
hadoop-daemon.sh start datanode
6) 用自動批量啓動腳本來啓動HDFS【一個個啓動datanode不累嗎?】
- 先配置hdp-01到集羣中所有機器(包含自己)的免密登陸 輸入命令 :
ssh-keygen #【一直回車】 ssh-copy-id hdp-01 #輸入密碼 ssh-copy-id hdp-02 #輸入密碼 ssh-copy-id hdp-03 #輸入密碼 ssh-copy-id hdp-04 #輸入密碼
- 配完免密後,可以執行一次 ssh 0.0.0.0
- 修改hadoop安裝目錄中/etc/hadoop/slaves(可以通過配置文件知道namenode,但無法知道datanode,所以把需要啓動datanode進程的節點列入)
hdp-01 hdp-02 hdp-03 hdp-04
- 在hdp-01上用腳本:start-dfs.sh來自動啓動整個集羣【這個腳本位於sbin/目錄下】
- 如果要停止,則用腳本:stop-dfs.sh
四、hdfs的客戶端操作
客戶端的理解:
HDFS的客戶端有多種形式:
- 網頁形式
- 命令行形式
- 客戶端在哪裏運行,沒有約束,只要運行客戶端的機器能夠跟HDFS集羣聯網
文件的切塊大小和存儲的副本數量,都是由客戶端決定!所謂的由客戶端決定,是通過配置參數來定的
hdfs的客戶端會讀以下兩個參數,來決定切塊大小、副本數量:
切塊大小的參數:dfs.blocksize
副本數量的參數:dfs.replication
五、HDFS客戶端的常用操作命令
查看hdfs的根目錄
hadoop fs -ls /
上傳文件到hdfs中【將根目錄下的本地文件放入HDFS的/aaa目錄下】
hadoop fs -put /本地文件 /aaa
copyFromLocal等價於put
hadoop fs -copyFromLocal /本地文件 /hdfs路徑
跟copyFromLocal的區別是:從本地移動到hdfs中
hadoop fs -moveFromLocal /本地文件 /hdfs路徑
移動HDFS中的文件(與moveFromLocal的區別是mv僅僅是HDFS文件夾的內部移動)
hadoop fs -mv /hdfs的路徑1 /hdfs的另一個路徑2
下載文件到客戶端本地磁盤
hadoop fs -get /hdfs中的路徑 /本地磁盤目錄
hadoop fs -copyToLocal /hdfs中的路徑 /本地磁盤路徑 ## 跟get等價
hadoop fs -moveToLocal /hdfs路徑 /本地路徑 ## 從hdfs中移動【刪除】到本地
在HDFS中創建文件夾
hadoop fs -mkdir -p /aaa/xxx
複製HDFS中的文件到HDFS的另一個目錄
hadoop fs -cp /hdfs路徑_1 /hdfs路徑_2
刪除HDFS中的文件或文件夾
hadoop fs -rm -r /aaa
查看HDFS中的文本文件內容
hadoop fs -cat /demo.txt
hadoop fs -tail -f /demo.txt
修改文件的權限
hadoop fs -chown user:group /aaa
hadoop fs -chmod 700 /aaa
追加內容到已存在的文件
hadoop fs -appendToFile /本地文件 /hdfs中的文件
六、HDFS的JAVA客戶端API使用
修改配置參考http://hadoop.apache.org/docs/stable/hadoop-project-dist/hadoop-hdfs/hdfs-default.xml
1、導入jar包
2、書寫代碼
所包含的方法有:
0、上傳一個文件到HDFS中 fs.copyFromLocalFile(...);
1、從HDFS中下載文件到客戶端本地磁盤 fs.copyToLocalFile(...);
2、在HDFS內部移動文件\修改名稱 fs.rename(...);
3、在HDFS中創建文件夾 fs.mkdirs(...);
4、在HDFS中刪除文件或文件夾 fs.delete(...);
5、查詢HDFS指定目錄下的文件信息 fs.listFiles(new Path("/"), true); //從返回的迭代器中取信息
6、查詢HDFS指定目錄下的文件和文件夾信息 fs.listStatus(new Path("/"));
7、讀取HDFS中的文件的內容
8、讀取HDFS中文件的指定偏移量範圍的內容
9、往HDFS中的文件寫內容
package cn.itcats.hdfs;
public class HdfsClientDemo {
public static void main(String[] args) throws Exception {
/**
* Configuration參數對象的機制: 構造時,會加載jar包中的默認配置 xx-default.xml 再加載
* 用戶配置xx-site.xml ,覆蓋掉默認參數 構造完成之後,還可以conf.set("p","v"),會再次覆蓋用戶配置文件中的參數值
*/
// new Configuration()會從項目的classpath中加載core-default.xml hdfs-default.xml
// core-site.xml hdfs-site.xml等文件
Configuration conf = new Configuration();
// 指定本客戶端上傳文件到hdfs時需要保存的副本數爲:2
conf.set("dfs.replication", "2");
// 指定本客戶端上傳文件到hdfs時切塊的規格大小:64M
conf.set("dfs.blocksize", "64m");
// 構造一個訪問指定HDFS系統的客戶端對象:
// 參數1:——HDFS系統的URI,參數2:——客戶端要特別指定的參數,參數3:客戶端的身份(用戶名)
FileSystem fs = FileSystem.get(new URI("hdfs://hdp-01:9000/"), conf, "root");
// 上傳一個文件到HDFS中
fs.copyFromLocalFile(new Path("D:/install-pkgs/hbase-1.2.1-bin.tar.gz"), new Path("/aaa/"));
fs.close();
}
FileSystem fs = null;
@Before
public void init() throws Exception {
Configuration conf = new Configuration();
conf.set("dfs.replication", "2");
conf.set("dfs.blocksize", "64m");
fs = FileSystem.get(new URI("hdfs://hdp-01:9000/"), conf, "root");
}
/**
* 從HDFS中下載文件到客戶端本地磁盤
*
* @throws IOException
* @throws IllegalArgumentException
*/
@Test
public void testGet() throws IllegalArgumentException, IOException {
fs.copyToLocalFile(new Path("/hdp20-05.txt"), new Path("f:/"));
fs.close();
}
/**
* 在hdfs內部移動文件\修改名稱
*/
@Test
public void testRename() throws Exception {
fs.rename(new Path("/install.log"), new Path("/aaa/in.log"));
fs.close();
}
/**
* 在hdfs中創建文件夾
*/
@Test
public void testMkdir() throws Exception {
fs.mkdirs(new Path("/xx/yy/zz"));
fs.close();
}
/**
* 在hdfs中刪除文件或文件夾
*/
@Test
public void testRm() throws Exception {
fs.delete(new Path("/aaa"), true);
fs.close();
}
/**
* 查詢hdfs指定目錄下的文件信息
*/
@Test
public void testLs() throws Exception {
// 只查詢文件的信息,不返回文件夾的信息
RemoteIterator<LocatedFileStatus> iter = fs.listFiles(new Path("/"), true);
while (iter.hasNext()) {
LocatedFileStatus status = iter.next();
System.out.println("文件全路徑:" + status.getPath());
System.out.println("塊大小:" + status.getBlockSize());
System.out.println("文件長度:" + status.getLen());
System.out.println("副本數量:" + status.getReplication());
System.out.println("塊信息:" + Arrays.toString(status.getBlockLocations()));
System.out.println("--------------------------------");
}
fs.close();
}
/**
* 查詢hdfs指定目錄下的文件和文件夾信息
*/
@Test
public void testLs2() throws Exception {
FileStatus[] listStatus = fs.listStatus(new Path("/"));
for (FileStatus status : listStatus) {
System.out.println("文件全路徑:" + status.getPath());
System.out.println(status.isDirectory() ? "這是文件夾" : "這是文件");
System.out.println("塊大小:" + status.getBlockSize());
System.out.println("文件長度:" + status.getLen());
System.out.println("副本數量:" + status.getReplication());
System.out.println("--------------------------------");
}
fs.close();
}
/**
* 讀取hdfs中的文件的內容
*
* @throws IOException
* @throws IllegalArgumentException
*/
@Test
public void testReadData() throws IllegalArgumentException, IOException {
FSDataInputStream in = fs.open(new Path("/test.txt"));
BufferedReader br = new BufferedReader(new InputStreamReader(in, "utf-8"));
String line = null;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
br.close();
in.close();
fs.close();
}
/**
* 讀取hdfs中文件的指定偏移量範圍的內容
*
*
* 思考:用本例中的知識,實現讀取一個文本文件中的指定BLOCK塊中的所有數據
*
* @throws IOException
* @throws IllegalArgumentException
*/
@Test
public void testRandomReadData() throws IllegalArgumentException, IOException {
FSDataInputStream in = fs.open(new Path("/xx.dat"));
// 將讀取的起始位置進行指定
in.seek(12);
// 讀16個字節
byte[] buf = new byte[16];
in.read(buf);
System.out.println(new String(buf));
in.close();
fs.close();
}
/**
* 往hdfs中的文件寫內容
*
* @throws IOException
* @throws IllegalArgumentException
*/
@Test
public void testWriteData() throws IllegalArgumentException, IOException {
FSDataOutputStream out = fs.create(new Path("/zz.jpg"), false);
// D:\images\006l0mbogy1fhehjb6ikoj30ku0ku76b.jpg
FileInputStream in = new FileInputStream("D:/images/006l0mbogy1fhehjb6ikoj30ku0ku76b.jpg");
byte[] buf = new byte[1024];
int read = 0;
while ((read = in.read(buf)) != -1) {
out.write(buf,0,read);
}
in.close();
out.close();
fs.close();
}
}
core-default.xml在hadoop-common包下
hdfs-default.xml中hadoop-hdfs包下
既然在new Configuration()時會自動從項目的classpath中加載解析core-default.xml、hdfs-default.xml、core-site.xml、hdfs-site.xml等文件,那麼我們也可以直接通過修改配置文件的方式修改配置【在src下創建同名文件,如hdfs-default.xml,注意配置文件的格式】,如:
<configuration>
<property>
<name>dfs.replication</name>
<value>4</value>
</property>
<property>
<name>dfs.blocksize</name>
<value>16m</value>
</property>
</configuration>
如果即寫了配置文件,又手動創建了Configuration類,並set爲屬性賦值,那麼最終是配置文件生效還是程序手動set賦值生效呢?
答案是:set手動賦值生效,剛纔已經解釋過了,先讀取配置文件,後經過程序代碼set賦值,set起了覆蓋作用。
七、項目實戰【日誌採集系統】
日誌採集系統架構圖
7.1 需求描述:
在業務系統的服務器上,業務程序會不斷生成業務日誌(比如網站的頁面訪問日誌)
業務日誌是用log4j生成的,會不斷地切出日誌文件
需要定期(比如每小時)從業務服務器上的日誌目錄中,探測需要採集的日誌文件(access.log不能採),發往HDFS
注意點:
(1)業務服務器可能有多臺(hdfs上的文件名不能直接用日誌服務器上的文件名)
(2)當天採集到的日誌要放在hdfs的當天目錄中
(3)採集完成的日誌文件,需要移動到到日誌服務器的一個備份目錄中
(4)定期檢查(一小時檢查一次)備份目錄,將備份時長超出24小時的日誌文件清除
7.2 設計規劃
1、流程
啓動一個定時任務:
————定時探測日誌源目錄
————獲取需要採集的文件
————移動這些文件到一個待上傳目錄
————遍歷帶上傳目錄中各文件,逐一傳輸到HDFS的目標路徑,同時將傳輸完成的文件移動到備份目錄
啓動一個定時任務:
————探測備份目錄中的備份數據,檢查是否已超出最長備份時長,如果超出,則刪除
2、規劃各種路徑
日誌源路徑:d:/logs/accesslog/
待上傳臨時目錄: d:/logs/toupload/
備份目錄: d:/logs/backup/日期/
HDFS存儲路徑: /logs/日期
HDFS中的文件的前綴:access_log_
HDFS中的文件的後綴: .log
代碼如下:
public class LogCollectApp {
public static void main(String[] args) {
Timer timer = new Timer();
//開啓定時任務,延遲0ms,每隔1分鐘採集一次
timer.schedule(new CollectLogTask(), 0, 60 * 60 * 1000L);
//探測備份目錄中的備份數據,檢查是否已超出最長備份時長,如果超出,則刪除
timer.schedule(new BackupCleanTask(), 0, 60 * 60 * 1000L);
}
}
public class CollectLogTask extends TimerTask {
/**
* ————定時探測日誌源目錄
* ————獲取需要採集的文件
* ————移動這些文件到一個待上傳目錄
* ————遍歷帶上傳目錄中各文件,逐一傳輸到HDFS的目標路徑,同時將傳輸完成的文件移動到備份目錄
* <p>
* 日誌源路徑:/Users/fatah/Desktop/logs/accesslog/
* 待上傳臨時目錄: /Users/fatah/Desktop/logs/toupload/
* 備份目錄: /Users/fatah/Desktop/logs/backup/日期/
* <p>
* HDFS存儲路徑: /logs/日期
* HDFS中的文件的前綴:access_log_
* HDFS中的文件的後綴: .log
*/
public void run() {
//構造一個Log4j日誌對象
Logger logger = Logger.getLogger("logRollingFile");
try {
//創建一個PropertyHolder對象(單例存在)
Properties props = PropertyHolderLazy.getProps();
//獲取本次時間採集的日期(格式化日期)
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd-HH");
String day = sdf.format(new Date());
File srcDir = new File(props.getProperty(Constants.LOG_SOURCE_DIR));
//過濾名字爲access.log的日誌文件 因爲使用滾動的日誌輸出 最新的日誌爲access.log 我們只需要access.log.x
File[] listFiles = srcDir.listFiles(new FilenameFilter() {
public boolean accept(File dir, String name) {
if (name.startsWith(props.getProperty(Constants.LOG_LEGAL_PREFIX)))
return true;
return false;
}
});
//記錄日誌
logger.info("探測到如下文件需要採集" + Arrays.toString(listFiles));
//移動這些文件到一個待上傳目錄
File toUploadDir = new File(props.getProperty(Constants.LOG_TOUPLOAD_DIR));
for (File file : listFiles) {
FileUtils.moveFileToDirectory(file, toUploadDir, true);
}
//記錄日誌
logger.info("如下文件移動到了待上傳目錄" + toUploadDir.getAbsolutePath());
//創建一個HDFS客戶端對象
//遍歷帶上傳目錄中各文件,逐一傳輸到HDFS的目標路徑(主要更名),同時將傳輸完成的文件移動到備份目錄
FileSystem fs = FileSystem.get(new URI(props.getProperty(Constants.HDFS_URI)), new Configuration(), props.getProperty(Constants.HDFS_USERNAME));
File[] toUploadFiles = toUploadDir.listFiles();
//檢查HDFS中的日期目錄是否存在,如果不存在則創建
Path hdfsDestPath = new Path(props.getProperty(Constants.HDFS_DEST_BASE_DIR) + day);
if (!fs.exists(hdfsDestPath))
fs.mkdirs(hdfsDestPath);
//檢查本地的備份目錄是否存在,如果不存在則創建
File backupDir = new File(props.getProperty(Constants.LOG_BACKUP_BASE_DIR) + day + "/");
if (!backupDir.exists())
backupDir.mkdirs();
for (File file : toUploadFiles) {
//傳輸文件到HDFS並改名
Path destPath = new Path(props.getProperty(Constants.HDFS_DEST_BASE_DIR) + day + props.getProperty(Constants.HDFS_FILE_PREFIX) + UUID.randomUUID() + props.getProperty(Constants.HDFS_FILE_SUFFIX));
fs.copyFromLocalFile(new Path(file.getAbsolutePath()), destPath);
//記錄日誌
logger.info("文件傳輸到HDFS完成" + file.getAbsolutePath() + " >>> " + destPath);
//將傳輸完成的文件移動到備份目錄
FileUtils.copyFileToDirectory(file, backupDir);
//記錄日誌
logger.info("文件備份完成" + file.getAbsolutePath() + " >>> " + backupDir);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
//探測備份目錄中的備份數據,檢查是否已超出最長備份時長,如果超出,則刪除
public class BackupCleanTask extends TimerTask {
public void run() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd-HH");
long now = new Date().getTime();
try {
Properties props = PropertyHolderLazy.getProps();
//探測本地的備份目錄
File backupBaseDir = new File(props.getProperty(Constants.LOG_BACKUP_BASE_DIR));
File[] dayBackDir = backupBaseDir.listFiles();
//判斷備份的子目錄日期是否超過24小時
for (File dir : dayBackDir) {
long time = sdf.parse(dir.getName()).getTime();
if (now - time > Long.parseLong(props.getProperty(Constants.LOG_BACKUP_TIMEOUT)) * 60 * 60 * 1000L) {
FileUtils.deleteDirectory(dir);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public interface Constants {
public static final String LOG_SOURCE_DIR = "LOG_SOURCE_DIR";
public static final String LOG_TOUPLOAD_DIR = "LOG_TOUPLOAD_DIR";
public static final String LOG_BACKUP_BASE_DIR = "LOG_BACKUP_BASE_DIR";
public static final String LOG_BACKUP_TIMEOUT = "LOG_BACKUP_TIMEOUT";
public static final String LOG_LEGAL_PREFIX = "LOG_LEGAL_PREFIX";
public static final String HDFS_URI = "HDFS_URI";
public static final String HDFS_DEST_BASE_DIR = "HDFS_DEST_BASE_DIR";
public static final String HDFS_FILE_PREFIX = "HDFS_FILE_PREFIX";
public static final String HDFS_FILE_SUFFIX = "HDFS_FILE_SUFFIX";
public static final String HDFS_USERNAME = "HDFS_USERNAME";
}
/**
* 使用單例模式加載讀取配置文件(餓漢式)
*/
public class PropertyHolderEager {
private static Properties pro = new Properties();
static {
try{
pro.load(PropertyHolderEager.class.getResourceAsStream("collect.properties"));
}catch (Exception e){}
}
private PropertyHolderEager() {
}
public static Properties getProps() {
return pro;
}
}
/**
* 使用懶漢式讀取collect.properties配置文件(注意線程安全問題)
*/
public class PropertyHolderLazy {
private static volatile Properties pro = null;
private PropertyHolderLazy() {
}
public static Properties getProps() throws IOException{
if (pro == null) {
synchronized (PropertyHolderLazy.class) {
if (pro == null) {
pro = new Properties();
pro.load(PropertyHolderLazy.class.getResourceAsStream("collect.properties"));
}
}
}
return pro;
}
}
在classpath下創建:
collect.properties
LOG_SOURCE_DIR=/Users/fatah/Desktop/logs/accesslog/
LOG_TOUPLOAD_DIR=/Users/fatah/Desktop/logs/toupload/
LOG_BACKUP_BASE_DIR=/Users/fatah/Desktop/logs/backup/
LOG_BACKUP_TIMEOUT=24
LOG_LEGAL_PREFIX=access.log.
HDFS_URI=hdfs://hdp-01:9000/
HDFS_DEST_BASE_DIR=/logs/
HDFS_FILE_PREFIX=access_log_
HDFS_FILE_SUFFIX=.log
HDFS_USERNAME=root
log4j.properties
### 設置###
#log4j.rootLogger=debug,stdout,genlog
log4j.rootLogger=INFO,logRollingFile,stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=[%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n
###
log4j.logger.logRollingFile= ERROR,test1
log4j.appender.test1 = org.apache.log4j.RollingFileAppender
log4j.appender.test1.layout = org.apache.log4j.PatternLayout
log4j.appender.test1.layout.ConversionPattern =%d{yyyy-MMM-dd HH:mm:ss}-[TS] %p %t %c - %m%n
log4j.appender.test1.Threshold = DEBUG
log4j.appender.test1.ImmediateFlush = TRUE
log4j.appender.test1.Append = TRUE
log4j.appender.test1.File = /Users/fatah/Desktop/logs/collect/collect.log
log4j.appender.test1.MaxFileSize = 102400KB
log4j.appender.test1.MaxBackupIndex = 200
### log4j.appender.test1.Encoding = UTF-8