2008年10月31日 #
數據庫鏈接 常見的問題:
1. 數據庫意外重啟后,原先的數據庫連接池能自動廢棄老的無用的鏈接,建立新的數據庫鏈接
2. 網絡異常中斷后,原先的建立的tcp鏈接,應該能進行自動切換。比如網站演習中的交換機重啟會導致網絡瞬斷
3. 分布式數據庫中間件,比如cobar會定時的將空閑鏈接異常關閉,客戶端會出現半開的空閑鏈接。
大致思考解決思路:
1. sql心跳檢查(主動式)
2. 拿鏈接嘗試一下,發現處理失敗丟棄鏈接,探雷的請求會失敗幾個 (犧牲小我,完成大我的精神)
3. 設置合理的空閑鏈接的超時時間,避免半開鏈接(懶模式,解決半開鏈接)
下面我們來看看,在dbcp中是如何實現。
sql心跳檢查
sql validate配置
<property name="testWhileIdle"><value>true</value></property>
<property name="testOnBorrow"><value>false</value></property>
<property name="testOnReturn"><value>false</value></property>
<property name="validationQuery"><value>select sysdate from dual</value></property>
<property name="validationQueryTimeout"><value>1</value></property>
<property name="timeBetweenEvictionRunsMillis"><value>30000</value></property>
<property name="numTestsPerEvictionRun"><value>16</value></property>
參數說明
dbcp是采用了commons-pool做為其連接池管理,testOnBorrow,testOnReturn, testWhileIdle是pool是提供的幾種校驗機制,通過外部鉤子的方式回調dbcp的相關數據庫鏈接(validationQuery)校驗, dbcp相關外部鉤子類:PoolableConnectionFactory,繼承于common-pool PoolableObjectFactory , dbcp通過GenericObjectPool這一入口,進行連接池的borrow,return處理。
具體參數描述:
1. testOnBorrow : 顧明思義,就是在進行borrowObject進行處理時,對拿到的connection進行validateObject校驗
2. testOnReturn : 顧明思義,就是在進行returnObject對返回的connection進行validateObject校驗,個人覺得對數據庫連接池的管理意義不大
3. testWhileIdle : 關注的重點,GenericObjectPool中針對pool管理,起了一個異步Evict的TimerTask定時線程進行控制(可通過設置參數 timeBetweenEvictionRunsMillis>0),定時對線程池中的鏈接進行validateObject校驗,對無效的鏈接進行關閉后,會調用ensureMinIdle,適當建立鏈接保證最小的minIdle連接數。
4. timeBetweenEvictionRunsMillis,設置的Evict線程的時間,單位ms,大于0才會開啟evict檢查線程
5. validateQuery, 代表檢查的sql
6. validateQueryTimeout, 代表在執行檢查時,通過statement設置,statement.setQueryTimeout(validationQueryTimeout)
7. numTestsPerEvictionRun,代表每次檢查鏈接的數量,建議設置和maxActive一樣大,這樣每次可以有效檢查所有的鏈接.
Sql心跳檢查幾點思考:
1.性能問題。
目前網站的應用大部分的瓶頸還是在I/O這一塊,大部分的I/O還是在數據庫的這一層面上,每一個請求可能會調用10來次SQL查詢,如果不走事務,一個請求會重復獲取鏈接,如果每次獲取鏈接,比如在testOnBorrow都進行validateObject,性能開銷不是很能接受,可以假定一次SQL操作消毫0.5~1ms(一般走了網絡請求基本就這數)
2.成本和收益
網站異常數據庫重啟,網絡異常斷開的頻率是非常低的,一般也就在數據庫升級,演習維護時才會進行,而且一般也是選在晚上,訪問量相對比較低的請求,而且一般會有人員值班關注,所以異步的validateObject是可以接受,但一個前提需要確保能保證在一個合理的時間段內,數據庫能完成自動重聯。
請求探雷
相關配置
dbcp自身默認支持,不需要配置
原理描述
common-pools通過borrowObject , returnObject完成連接的獲取和釋放,正常的情況是一次請求中borrow和return是一對的,有借就有還。
但在準備returnObject時,dbcp會做一件事,就是看看這個object是否已經是壞了的,如果壞了就直接丟了,就直接給丟棄了。
代碼層面:
1. 在dbcp中PoolingDataSource(實現DataSource接口)調用 PoolableConnection(dbcp connnection相關的pool delegate操作)進行相應關閉時,會檢查_conn.isClosed(),針對DataSource如果isClosed返回為 true的則不調用returnObject,直接丟棄了鏈接。
2. _conn.isClosed()是否保險,從jdk的api描述中: A connection is closed if the method close has been called on it or if certain fatal errors have occurred. 里面提供兩種情況,一種就是被調用了closed方法,另一種就是出現一些異常,說的比較含糊。
空閑鏈接檢查
相關配置
<property name="minEvictableIdleTimeMillis"><value>18000000</value></property>
<property name="removeAbandoned"><value>true</value></property>
<property name="removeAbandonedTimeout"><value>180</value></property>
參數說明
1.minEvictableIdleTimeMillis dbcp默認是30分,需要開啟異步線程Evict,否則不生效。原理很簡單,就是通過一個異步線程,每次檢查connnection上一次使用的時間戳,看看是否已經超過這個timeout時間設置。
2. removeAbandoned , removeAbandonedTimeout,主要是用于在出現鏈接緊張時候,會掃描一些鏈接未超過removeAbandonedTimeout時間還未被釋放,會主動的關閉該鏈接。
適用情況
1. 我們使用的cobar后端會有定時關閉空閑鏈接的操作,默認的空閑鏈接timeout時間為1小時,和其他oracle , mysql各不相同,所以設置好這個空閑鏈接的timeout時間還是挺重要.
2. 一般會是幾種情況出現需要removeAbandoned:
* 代碼未在finally釋放connection , 不過我們都用sqlmapClientTemplate,底層都有鏈接釋放的過程
* 遇到數據庫死鎖。以前遇到過后端存儲過程做了鎖表操作,導致前臺集群中連接池全都被block住,后續的業務處理因為拿不到鏈接所有都處理失敗了。
聊聊c3p0配置
還有我們配置的c3p0所謂的自動重連的3個參數,
<prop key="acquireRetryAttempts">30</prop>
<prop key="acquireRetryDelay">1000</prop>
<prop key="breakAfterAcquireFailure">false</prop>
個人覺得就是一個誤導,這幾個配置只是在從連接池獲取鏈接時,獲取失敗多嘗試幾次,因為我們從pool從獲取鏈接最多只會等待固定timeout時間。
如果要達到自動重連的效果,必須要c3p0支持請求探雷或者是sql心跳檢查功能,能自動的剔除無效的鏈接。
可見c3p0官方文檔描述:http://www.mchange.com/projects/c3p0/index.html#configuring_recovery
最后:
Dbcp將是我們以后數據庫驅動選擇的趨勢,最后我們如何選擇如何自動重連,這個也得根據我們的應用場景而定。比如只讀的web系統,后臺業務系統,任務系統可能處理方式就不同。
只讀Web系統:可采取請求探雷的策略,也就失敗連接池個數的請求,失敗了頁面刷新一次就好。
后臺業務系統:一般業務都涉及數據庫的寫操作,很多數據不可重入,一次處理失敗后就只能靠手工干預處理。這時候得考慮是否需要使用sql心跳檢查,比如testOnBorrow或者testWhileIdle.
1. 下載rsync (http://rsync.samba.org/)
安裝:
./configure
make
make install
2. 開啟rsync服務,修改/etc/xinetd.d/rsync
disable = no # replace <yes>
重啟xinetd 服務
service xinetd restart
3. 配置server端,/etc/rsyncd.conf
# touch rsyncd.conf
# vi rsyncd.conf
uid = ljh #表示以什么用戶運行,注意必須確保該用戶有對模塊的讀寫權限
gid = ljh
use chroot = false
max connectionts = 6
read only = no
pid file = /home/ljh/server/rsync/rsynnd.pid
lock file = /home/ljh/server/rsync/rsyncd.lock
log file = /home/ljh/server/rsync/rsyncd.log
[test]
comment = test
path = /home/ljh/server/rsync/data/test
ignore error
list = true
#auth users = ljh
#secrets file = /home/ljh/server/rsync/passwd/rsyncd.passwd













































4. 客戶端配置
訪問remote rsync列表
rsync rsync://10.0.64.162/test
簡單的執行同步命令
sync -auv --delete --password-file=/home/admin2/soft/rsync/passwd/rsyncd.passwd ~/rysnc/* ljh@10.0.64.162::test
比較實際的例子:
echo "hello" > /tmp/password.txt ;chmod 600 /tmp/password.txt
cp /home/ewalletbops/fatrix/crm/* /home/ewalletbops/fatrix/putxml/search
rsync -azv /home/ewalletbops/bops-daemon/bin/adxml/search/ /home/ewalletbops/fatrix/putxml/search
rsync -auv --delete --password-file=/tmp/password.txt /home/ewalletbops/fatrix/putxml/search yangzhen@127.0.0.1::everest/adxml
rm /tmp/password.txt































































Tip1
1.在 JAVA_HOME/jre/lib/fonts/ 下建立個目錄 fallback
2.在 fallback 里弄個中文字體最簡單ln一下就好了
比如:
ln -s /usr/share/fonts/truetype/arphic/uming.ttf $JAVA_HOME/jre/lib/fonts/fallback/
Tip2
問題描述:Java 應用程序的中文無法顯示,呈現方塊狀。
原因分析:Java 應用程序無法找到可供顯示中文的字體。
解決方案:首先,確保系統里安裝了 JDK 1.5.0_06,如果安裝的是 JRE 1.5.0_06,那么卸掉 JRE,再安裝 JDK。然后下載 fireflysung 1.3.0, 解壓后將其中的 ttf 文件丟到系統字體目錄/usr/share/fonts,再用 fc-cache -f -v 跑一遍,讓系統知道這個字體。最后,就是轉到 JDK 安裝目錄的jre/lib/fonts 中,使用下面的命令來完成。
mkdir fallback cd fallback ln -s /usr/share/fonts/fireflysung.ttf mkfontdir mkfontscale
一、UNIX下關于文件權限的表示方法和解析
SUID 是 Set User ID, SGID 是 Set Group ID的意思。
UNIX下可以用ls -l 命令來看到文件的權限。用ls命令所得到的表示法的格式是類似這樣的:-rwxr-xr-x 。下面解析一下格式所表示的意思。這種表示方法一共有十位:
9 8 7 6 5 4 3 2 1 0
- r w x r - x r - x
第9位表示文件類型,可以為p、d、l、s、c、b和-:
p表示命名管道文件
d表示目錄文件
l表示符號連接文件
-表示普通文件
s表示socket文件
c表示字符設備文件
b表示塊設備文件
第8-6位、5-3位、2-0位分別表示文件所有者的權限,同組用戶的權限,其他用戶的權限,其形式為rwx:
r表示可讀,可以讀出文件的內容
w表示可寫,可以修改文件的內容
x表示可執行,可運行這個程序
沒有權限的位置用-表示
例子:
ls -l myfile顯示為:
rwxr-x-- 1 foo staff 7734 Apr 05 17:07 myfile
表示文件myfile是普通文件,文件的所有者是foo用戶,而foo用戶屬于staff組,文件只有1個硬連接,長度是7734個字節,最后修改時間4月5日17:07。
所有者foo對文件有讀寫執行權限,staff組的成員對文件有讀和執行權限,其他的用戶對這個文件沒有權限。
如果一個文件被設置了SUID或SGID位,會分別表現在所有者或同組用戶的權限的可執行位上。例如:
1、-rwsr-xr-x 表示SUID和所有者權限中可執行位被設置
2、rwSrr- 表示SUID被設置,但所有者權限中可執行位沒有被設置
3、-rwxr-sr-x 表示SGID和同組用戶權限中可執行位被設置
4、rw-r-Sr- 表示SGID被設置,但同組用戶權限中可執行位沒有被社
其實在UNIX的實現中,文件權限用12個二進制位表示,如果該位置上的值是
1,表示有相應的權限:
11 10 9 8 7 6 5 4 3 2 1 0
S G T r w x r w x r w x
第11位為SUID位,第10位為SGID位,第9位為sticky位,第8-0位對應于上面的三組rwx位。
11 10 9 8 7 6 5 4 3 2 1 0
上面的-rwsr-xr-x的值為: 1 0 0 1 1 1 1 0 1 1 0 1
rw-r-Sr-的值為: 0 1 0 1 1 0 1 0 0 1 0 0
給文件加SUID和SUID的命令如下:
chmod u+s filename 設置SUID位
chmod u-s filename 去掉SUID設置
chmod g+s filename 設置SGID位
chmod g-s filename 去掉SGID設置
另外一種方法是chmod命令用八進制表示方法的設置。如果明白了前面的12位權限表示法也很簡單。
二、SUID和SGID的詳細解析
由于SUID和SGID是在執行程序(程序的可執行位被設置)時起作用,而可執行位只對普通文件和目錄文件有意義,所以設置其他種類文件的SUID和SGID位是沒有多大意義的。
首先講普通文件的SUID和SGID的作用。例子:
如果普通文件myfile是屬于foo用戶的,是可執行的,現在沒設SUID位,ls命令顯示如下:
-rwxr-xr-x 1 foo staff 7734 Apr 05 17:07 myfile任何用戶都可以執行這個程序。UNIX的內核是根據什么來確定一個進程對資源的訪問權限的呢?是這個進程的運行用戶的(有效)ID,包括 user id和group id。用戶可以用id命令來查到自己的或其他用戶的user id和group id。
除了一般的user id 和group id外,還有兩個稱之為effective 的id,就是有效id,上面的四個id表示為:uid,gid,euid,egid。內核主要是根據euid和egid來確定進程對資源的訪問權限。
一個進程如果沒有SUID或SGID位,則euid=uid egid=gid,分別是運行這個程序的用戶的uid和gid。例如kevin用戶的uid和gid分別為204和202,foo用戶的uid和gid為 200,201,kevin運行myfile程序形成的進程的euid=uid=204,egid=gid=202,內核根據這些值來判斷進程對資源訪問 的限制,其實就是kevin用戶對資源訪問的權限,和foo沒關系。
如果一個程序設置了SUID,則euid和egid變成被運行的程序的所有者的uid和gid,例如kevin用戶運行myfile,euid=200,egid=201,uid=204,gid=202,則這個進程具有它的屬主foo的資源訪問權限。
SUID的作用就是這樣:讓本來沒有相應權限的用戶運行這個程序時,可以訪問他沒有權限訪問的資源。passwd就是一個很鮮明的例子。
SUID的優先級比SGID高,當一個可執行程序設置了SUID,則SGID會自動變成相應的egid。
下面討論一個例子:
UNIX系統有一個/dev/kmem的設備文件,是一個字符設備文件,里面存儲了核心程序要訪問的數據,包括用戶的口令。所以這個文件不能給一般的用戶讀寫,權限設為:cr-r---- 1 root system 2, 1 May 25 1998 kmem
但ps等程序要讀這個文件,而ps的權限設置如下:
-r-xr-sr-x 1 bin system 59346 Apr 05 1998 ps
這是一個設置了SGID的程序,而ps的用戶是bin,不是root,所以不能設置SUID來訪問kmem,但大家注意了,bin和root 都屬于system組,而且ps設置了SGID,一般用戶執行ps,就會獲得system組用戶的權限,而文件kmem的同組用戶的權限是可讀,所以一般 用戶執行ps就沒問題了。但有些人說,為什么不把ps程序設置為root用戶的程序,然后設置SUID位,不也行嗎?這的確可以解決問題,但實際中為什么 不這樣做呢?因為SGID的風險比SUID小得多,所以出于系統安全的考慮,應該盡量用SGID代替SUID的程序,如果可能的話。下面來說明一下 SGID對目錄的影響。SUID對目錄沒有影響。如果一個目錄設置了SGID位,那么如果任何一個用戶對這個目錄有寫權限的話,他在這個目錄所建立的文件 的組都會自動轉為這個目錄的屬主所在的組,而文件所有者不變,還是屬于建立這個文件的用戶。
三、關于SUID和SGID的編程
和SUID和SGID編程比較密切相關的有以下的頭文件和函數:
#include
#include
uid_t getuid(void);
uid_t geteuid(void);
gid_t getgid (void);
gid_t getegid (void);
int setuid (uid_t UID);
int setruid (uid_t RUID);
int seteuid (uid_t EUID);
int setreuid (uid_t RUID,uid_t EUID);
int setgid (gid_t GID);
int setrgid (gid_t RGID);
int setegid (git_t EGID);
int setregid (gid_t RGID, gid_t EGID);
具體這些函數的說明在這里就不詳細列出來了,要用到的可以用man查。
SUID/SGID :
假如你有文件a.txt
#ls -l a.txt
-rwxrwxrwx
#chmod 4777 a.txt
-rwsrwxrwx ======>注意s位置
#chmod 2777 a.txt
-rwxrwsrwx ======>注意s位置
#chmod 7777 a.txt
-rwsrwxswt ======>出現了t,t的作用在內存中盡量保存a.txt,節省系統再加載的時間.
現在再看前面設置 SUID/SGID作用:
#cd /sbin
#./lsusb
...
#su aaa(普通用戶)
$./lsusb
...
是不是現在顯示出錯?
$su
#chmod 4755 lsusb
#su aaa
$./lsusb
... 現在明白了嗎?本來是只有root用戶才能執行的命令,加了SUID后,普通用戶就可以像root一樣的用,權限提升了。上面是對于文件來說的,對于目錄也差不多!
目錄的S屬性使得在該目錄下創建的任何文件及子目錄屬于該目錄所擁有的組,目錄的T屬性使得該目錄的所有者及root才能刪除該目錄。還有對 于s與S,設置SUID/SGID需要有運行權限,否則用ls -l后就會看到S,證明你所設置的SUID/SGID沒有起作用。
Why we need suid,how do we use suid?
r -- 讀訪問
w -- 寫訪問
x -- 執行許可
s -- SUID/SGID
t -- sticky位
那么 suid/sgid是做什么的? 為什么會有suid位呢?
要想明白這個,先讓我們看個問題:如果讓每個用戶更改自己的密碼?
用戶修改密碼,是通過運行命令passwd來實現的。最終必須要修改/etc/passwd文件,而passwd的文件的屬性是:
#ls -l /etc/passwd
rw-rr- 1 root root 2520 Jul 12 18:25 passwd
我們可以看到passwd文件只有對于root用戶是可寫的,而對于所有的他用戶來說都是沒有寫權限的。 那么一個普通的用戶如何能夠通過運行passwd命令修改這個passwd文件呢?
為了解決這個問題,SUID/SGID便應運而生。而且AT&T對它申請了專利。 呵呵。
SUID和SGID是如何解決這個問題呢?
首先,我們要知道一點:進程在運行的時候,有一些屬性,其中包括 實際用戶ID,實際組ID,有效用戶ID,有效組ID等。 實際用戶ID和實際組ID標識我們是誰,誰在運行這個程序,一般這2個字段在登陸時決定,在一個登陸會話期間, 這些值基本上不改變。
而有效用戶ID和有效組ID則決定了進程在運行時的權限。內核在決定進程是否有文件存取權限時,是采用了進程的有效用戶ID來進行判斷的。
知道了這點,我們來看看SUID的解決途徑:
當一個程序設置了為SUID位時,內核就知道了運行這個程序的時候,應該認為是文件的所有者在運行這個程序。即該程序運行的時候,有效用戶ID是該程序的所有者。舉個例子:
[root@sgrid5 bin]# ls -l passwd
-r-s-s-x 1 root root 16336 Feb 14 2003 passwd
雖然你以test登陸系統,但是當你輸入passwd命令來更改密碼的時候,由于passwd設置了SUID位,因此雖然進程的實際用戶ID 是test對應的ID,但是進程的有效用戶ID則是passwd文件的所有者root的ID,因此可以修改/etc/passwd文件。
讓我們看另外一個例子。
ping命令應用廣泛,可以測試網絡是否連接正常。ping在運行中是采用了ICMP協議,需要發送ICMP報文。但是只有root用戶才能建立ICMP報文,如何解決這個問題呢?同樣,也是通過SUID位來解決。
[root@sgrid5 bin]# ls -l /bin/ping
-rwsr-sr-x 1 root root 28628 Jan 25 2003 /bin/ping
我們可以測試一下,如果去掉ping的SUID位,再用普通用戶去運行命令,看會怎么樣。
[root@sgrid5 bin]#chmod u-s /bin/ping
[root@sgrid5 bin]# ls -l ping
-rwxr-xr-x 1 root root 28628 Jan 25 2003 ping
[root@sgrid5 bin]#su test
[test@sgrid5 bin]$ ping byhh.net
ping: icmp open socket: Operation not permitted
SUID雖然很好了解決了一些問題,但是同時也會帶來一些安全隱患。
因為設置了 SUID 位的程序如果被攻擊(通過緩沖區溢出等方面),那么hacker就可以拿到root權限。
因此在安全方面特別要注意那些設置了SUID的程序。
通過以下的命令可以找到系統上所有的設置了suid的文件:
[root@sgrid5 /]# find / -perm -04000 -type f -ls
對于這里為什么是4000,大家可以看一下前面的st_mode的各bit的意義就明白了。
在這些設置了suid的程序里,如果用不上的,就最好取消該程序的suid位。
總結:
1.Set UID:當文件系統的"所有者權限組合"的可執行位被s(即rws------)取代時,構成特殊權限規定Set UID,簡稱SUID。僅對系統中的二進制可執行文件設置有效,而且不可對Shell Script施加設置。
2.Set GID:當所有者所在的用戶組(group)的權限組合中可執行位被s所取代時(例如--
3.Sticky Bit:當文件系統"其他(others)"的權限組合中可執行位被t所取代時(例如------rwt),便構成Sticky Bit的權限設置。它只對目錄有效。
SUID和SGID,主要作用是用于當非某個文件的所有者(或組)執行(或操作目錄)文件時,可以暫時獲得該文件所有者的權限。
SBIT的作用在于訪問控制,當它對某個目錄設置此屬性后,該目錄下的所有文件,即使其它人有w屬性,都不得對其更名、移動、刪除。
設置方法:
如果你已經掌握了用(八進制)數字來表示權限的規則,再結合chmod命令進行設置就很簡單了。以下是SUID/SGID/Sticky Bit約定對應的八進制數值:
SUID = 4
SGID = 2
SBIT = 1
設置時我們把表示特殊權限的數字放在其他三位數字權限的前面。
這里指出了jar包的典型的目錄結構。簡單翻譯:
META-INF目錄中的下列文件和目錄獲得Java 2平臺的認可與解釋,用來配置應用程序、擴展程序、類加載器和服務:
• MANIFEST.MF:清單文件,用來定義與擴展和數據包相關的數據。
• INDEX.LIST:這個文件由JAR工具的新“-i”選項生成,其中包含在一個應用程序或擴展中定義的數據包的地址信息。它是JarIndex的一部分,被類加載器用來加速類加載過程。
• x.SF:JAR文件的簽名文件。x代表基礎文件名。
• x.DSA:這個簽名塊文件與同名基礎簽名文件有關。此文件存儲對應簽名文件的數字簽名。
• services/:這個目錄存儲所有服務提供程序配置文件。
介紹:
在META-INF/services目錄下保存的是service provider的配置文件。 服務在應用中會是一個接口(更多的是抽象類)。
一個類服務器提供者實現了一個服務類。這類的服務提供類可以以擴展的形式發布到平臺上。所以,jar文件引入了擴展目錄,同樣你也可以將服務提供者加入classpath提供訪問。
服務都是表現為一個積累,而一個服務提供者通常是集成或實現了服務定義類。服務提供類通常不會像代理類一樣為了正常提供服務而包含了請求者的許多信息。服務提供類一般傾向于高集成。
對這類服務提供類的唯一強制性要求就是必須有一個無參的構造函數。
provider 配置文件
META-INF/services目錄作為provider配置文件的存放路徑。provider配置文件中必須是全類名(包含package)。配置文件可以存在space tab 換行等字符,#作為注釋。
注意:provider配置文件必須是以UTF-8編碼。
總結:
service provider機制為程序的動態擴展提供了契機,在應用中你可以針對接口編程,通過RTTI技術可以比較完美的解決程序之間的耦合性。相比于spring DIP機制,這也是一個不錯的嘗試,至少它不需要耦合spring包。