目錄
(一)什么是服務器并發(fā)處理能力
(二)有什么方法衡量服務器并發(fā)處理能力
1.吞吐率
2.壓力測試
(三)怎么提高服務器的并發(fā)處理能力
1,提高CPU并發(fā)計算能力
(1)多進程&多線程
(2)減少進程切換,使用線程,考慮進程綁定CPU
(3)減少使用不必要的鎖,考慮無鎖編程
(4)考慮進程優(yōu)先級
(5)關注系統(tǒng)負載
(6)關注CPU使用率,除了用戶空間和內核空間的CPU使用率以外,還要關注I/O wait
2,減少系統(tǒng)調用
3,考慮減少內存分配和釋放
(1)改善數(shù)據結構和算法復制度
(2)使用內存池
(3)考慮使用共享內存
4,考慮使用持久連接
5,改進I/O模型
(1)DMA技術
(2)異步I/O
(3)改進多路I/O就緒通知策略,epoll
(4)Sendfile
(5)內存映射
(6)直接I/O
6,改進服務器并發(fā)策略
(1)一個進程處理一個連接,非阻塞I/O,使用長連接
(2)一個進程處理多個連接,異步I/O,使用長連接
7,改進硬件環(huán)境
(一)什么是服務器并發(fā)處理能力?
一臺服務器在單位時間里能處理的請求越多,服務器的能力越高,也就是服務器并發(fā)處理能力越強
服務器的本質工作就是,爭取以最快的速度將內核緩沖區(qū)中的用戶請求數(shù)據一個不剩地都拿出來,然后盡快處理,再將響應數(shù)據放到一塊又能夠與發(fā)送數(shù)據的緩沖區(qū)中,接著處理下一撥請求。
(二)有什么方法衡量服務器并發(fā)處理能力?
一,吞吐率
量化指標:吞吐率,單位時間里服務器處理的最大請求數(shù),單位req/s
再深入一些,HTTP請求通常是對不同資源的請求,也就是請求不同的URL,有的是請求圖片,有的是獲取動態(tài)內容,有的是靜態(tài)頁面,顯然這些請求所花費的時間各不相同,而這些請求再不同時間的組成比例又是不確定的,所以實際情況下的吞吐率是非常復雜的。
正因為這些請求的性質不同,所以服務器并發(fā)能力強弱關鍵在于如何正對不同的請求性質來設計最優(yōu)并發(fā)策略。如一臺服務器處理諸多不同性質的請求,在一定程度上使得服務器的性能無法充分發(fā)揮。而并發(fā)策略的設計就是在服務器同時處理較多請求時,合理協(xié)調和充分利用CPU計算和I/O操作,使其在較大并發(fā)用戶數(shù)的情況下提供較高吞吐率。
另外,實際上多少用戶同時發(fā)來請求并不是服務器所能決定的,一旦實際并發(fā)用戶數(shù)過多,則勢必影響站點質量。所以得出最大并發(fā)用戶數(shù)的意義,在于了解服務器的承載能力,并且結合用戶規(guī)模考慮適當?shù)臄U展方案。
在考慮用戶模型時,用戶訪問web站點時通常使用瀏覽器,瀏覽器對于同一域名下URL的并發(fā)下載是多線程的,不過有最大限制的,所以前面說到的最大并發(fā)數(shù),具體到真實的用戶,可能不是一對一的關系。
而從服務器角度,實際并發(fā)用戶數(shù)的可以理解為服務器當前維護的代表不同用戶的文件描述符總數(shù),也就是并發(fā)連接數(shù)。服務器一般會限制同時服務的最多用戶數(shù),比如apache的MaxClents參數(shù)。
這里再深入一下,對于服務器來說,服務器希望支持高吞吐率,對于用戶來說,用戶只希望等待最少的時間,顯然,雙方不能滿足,所以雙方利益的平衡點,就是我們希望的最大并發(fā)用戶數(shù)。
二,壓力測試
有一個原理一定要先搞清楚,假如100個用戶同時向服務器分別進行10個請求,與1個用戶向服務器連續(xù)進行1000次請求,對服務器的壓力是一樣嗎?實際上是不一樣的,因對每一個用戶,連續(xù)發(fā)送請求實際上是指發(fā)送一個請求并接收到響應數(shù)據后再發(fā)送下一個請求。這樣對于1個用戶向服務器連續(xù)進行1000次請求,任何時刻服務器的網卡接收緩沖區(qū)中只有1個請求,而對于100個用戶同時向服務器分別進行10個請求,服務器的網卡接收緩沖區(qū)最多有100個等待處理的請求,顯然這時的服務器壓力更大。
壓力測試前提考慮的條件:
(1)并發(fā)用戶數(shù)
(2)總請求數(shù)
(3)請求資源描述
并發(fā)用戶數(shù)是指某一時刻同時向服務器發(fā)送請求的用戶總數(shù)。
壓力測試中關系的時間有細分以下2種,
(1) 用戶平均請求等待時間
(這里暫不把數(shù)據在網絡的傳輸時間,還有用戶PC本地的計算時間計算入內)
(2) 服務器平均請求處理時間
用戶平均請求等待時間主要用于衡量服務器在一定并發(fā)用戶數(shù)下,單個用戶的服務質量;而服務器平均請求處理時間就是吞吐率的倒數(shù),一般來說,用戶平均請求等待時間=服務器平均請求處理時間*并發(fā)用戶數(shù)
(三)怎么提高服務器的并發(fā)處理能力呢?
一,提高CPU并發(fā)計算能力
服務器之所以可以同時處理多個請求,在于操作系統(tǒng)通過多執(zhí)行流體系設計使得多個任務可以輪流使用系統(tǒng)資源,這些資源包括CPU,內存以及I/O.這里的I/O主要指磁盤I/O,和網絡I/O
(1)多進程&多線程
多執(zhí)行流的一般實現(xiàn)就是進程。多進程的好處不僅在于CPU時間的輪流使用,還在于對CPU計算和I/O操作進行很好的重疊利用,這里的I/O主要指磁盤I/O和網絡I/O.實際上,大多數(shù)進程的時間主要消耗在I/O操作上,現(xiàn)代計算機的DMA技術可以讓CPU不參與I/O操作的全過程,比如進程通過系統(tǒng)調用,使得CPU向網卡或者磁盤等I/O設備發(fā)出指令,然后進程被掛起,釋放出CPU資源,等待I/O設備完成工作后通過中斷來通知進程重新就緒。對于單任務而言,CPU大部分時間空閑,這時候多進程的作用尤為重要。
而且進程的優(yōu)越性還在其相互獨立帶來的穩(wěn)定性和健壯性方面。
進程的缺點:每個進程都有自己的獨立空間和生命周期。當子進程被父進程創(chuàng)建后,便將父進程地址空間的所有數(shù)據復制到自己的地址空間中,完全繼承父進程的上下文信息。進程的創(chuàng)建使用fork()系統(tǒng)調用,還是有一定的開銷的,這個開銷若太頻繁,其可能成為影響性能的主要因素。
那是否越多進程數(shù)越好呢?請看下面討論:
(2)減少進程切換
進程擁有獨立的內存空間,每個進程都只能共享CPU寄存器。一個進程被掛起的本質是將它在CPU寄存器中的數(shù)據拿出來暫存在內存態(tài)堆棧著那個,而一個進程恢復工作的本質就是把它的數(shù)據重新裝入CPU寄存器,這段裝入和移出的數(shù)據稱為“硬件上下文”,除此之外,進程上下文還包含進程允許所需的一切狀態(tài)信息。
當硬件上下文頻繁裝入和移出時,所消耗的時間是非?捎^的?捎肗mon工具監(jiān)視服務器每秒的上下文切換次數(shù)。
為了盡量減少上下文切換次數(shù),最簡單的做法就是減少進程數(shù),盡量使用線程并配合其它I/O模型來設計并發(fā)策略。
還可以考慮使用進程綁定CPU技術,增加CPU緩存的命中率。若進程不斷在各CPU上切換,這樣舊的CPU緩存就會失效。
(3)減少使用不必要的鎖
服務器處理大量并發(fā)請求時,多個請求處理任務時存在一些資源搶占競爭,這時一般采用“鎖”機制來控制資源的占用,當一個任務占用資源時,我們鎖住資源,這時其它任務都在等待鎖的釋放,這個現(xiàn)象稱為鎖競爭。
通過鎖競爭的本質,我們要意識到盡量減少并發(fā)請求對于共享資源的競爭。比如在允許情況下關閉服務器訪問日志,這可以大大減少在鎖等待時的延遲時間。要最大程度減少無辜的等待時間。
這里說下無鎖編程,就是由內核完成這個鎖機制,主要是使用原子操作替代鎖來實現(xiàn)對共享資源的訪問保護,使用原子操作時,在進行實際的寫操作時,使用了lock指令,這樣就可以阻止其他任務寫這塊內存,避免出現(xiàn)數(shù)據競爭現(xiàn)象。原子操作速度比鎖快,一般要快一倍以上。
例如fwrite(), fopen(),其是使用append方式寫文件,其原理就是使用了無鎖編程,無鎖編程的復雜度高,但是效率快,而且發(fā)生死鎖概率低。
(4)除了上述所說,要優(yōu)化服務器的并發(fā)處理能力,還要考慮進程優(yōu)先級(可由進程決定),進程調度器會動態(tài)調整運行隊列中進程的優(yōu)先級,通過top觀察進程的PR值
(5)還要關注系統(tǒng)負載,可在任何時刻查看/proc/l
OAdavg, top中的load average也可看出
(6)還要關注CPU使用率,除了用戶空間和內核空間的CPU使用率以外,還要關注I/O wait,它是指CPU空閑并且等待I/O操作完成的時間比例。(top中查看wa的值)
二,考慮系統(tǒng)調用
進程若運行在用戶態(tài),這時可使用CPU和內存來完成一些任務,而當進程需要對硬件外設進行操作的時候(如讀取磁盤文件,發(fā)送網絡數(shù)據等),就必須切換到內核態(tài),這時它擁有更多的權力來操縱整個計算機。
而系統(tǒng)調用涉及進程從用戶態(tài)到內核態(tài)的切換,導致一定的內存交換,這也是一定程度上的上下文切換,所以系統(tǒng)調用的開銷通常認為比較昂貴的。
所以要減少不必要的系統(tǒng)調用,也是服務器性能優(yōu)化的一個方面。例如在apache中,修改httpd.conf文件,可以減少對文件路徑中各級目錄下檢測是否存在.htacess文件這個open()系統(tǒng)調用; 還可以修改httpd.conf文件來減少多余的gettimeofday()系統(tǒng)調用。
但有時若使用一些簡單的系統(tǒng)調用能代替大量的邏輯運算,這樣反而使用系統(tǒng)調用更能優(yōu)化性能
三,考慮減少內存分配和釋放
服務器的工作過程中,需要大量的內存,使得內存的分配和釋放工作尤為重要。
可以通過改善數(shù)據結構和算法復制度來適當減少中間臨時變量的內存分配及數(shù)據復制時間,而服務器本身也使用了各自的策略來提高效率。
例如Apache,在運行開始時一次申請大片的內存作為內存池,若隨后需要時就在內存池中直接獲取,不需要再次分配,避免了頻繁的內存分配和釋放引起的內存整理時間。
再如Nginx使用多線程來處理請求,使得多個線程之間可以共享內存資源,從而令它的內存總體使用量大大減少,另外,nginx分階段的內存分配策略,按需分配,及時釋放,使得內存使用量保持在很小的數(shù)量范圍。
順便說下Linux進程的地址空間分段
1、棧(存放著局部變量和函數(shù)參數(shù)等數(shù)據),向下生長 (可讀可寫可執(zhí)行)
2、堆(給動態(tài)分配內存是使用),向上生長 (可讀可寫可執(zhí)行)
3、數(shù)據段(保存全局數(shù)據和靜態(tài)數(shù)據) (可讀可寫不可執(zhí)行)
4、代碼段(保存代碼)(可讀可執(zhí)行不可寫)
(1)代碼段(.text)。這里存放的是CPU要執(zhí)行的指令。代碼段是可共享的,相同的代碼在內存中只會有一個拷貝,同時這個段是只讀的,防止程序由于錯誤而修改自身的指令。
(2)初始化數(shù)據段(.data)。這里存放的是程序中需要明確賦初始值的變量,例如位于所有函數(shù)之外的全局變量:int val=100。需要強調的是,以上兩段都是位于程序的可執(zhí)行文件中,內核在調用exec函數(shù)啟動該程序時從源程序文件中讀入。
(3)未初始化數(shù)據段(.bss)。位于這一段中的數(shù)據,內核在執(zhí)行該程序前,將其初始化為0或者null。例如出現(xiàn)在任何函數(shù)之外的全局變量:int sum;
(4)堆(Heap)。這個段用于在程序中進行動態(tài)內存申請,例如經常用到的malloc,new系列函數(shù)就是從這個段中申請內存。
(5)棧(Stack)。函數(shù)中的局部變量以及在函數(shù)調用過程中產生的臨時變量都保存在此段中
還可以考慮使用共享內存
共享內存指在多處理器的計算機系統(tǒng)中,可以被不同中央處理器(CPU)訪問的大容量內存,也可以由不同進程共享,是非?斓倪M程通信方式。
但是使用共享內存也有不好的地方,就是對于多機器時數(shù)據不好統(tǒng)一
shell命令ipcs可用來顯示系統(tǒng)下共享內存的狀態(tài),函數(shù)shmget可以創(chuàng)建或打開一塊共享內存區(qū),函數(shù)shmat將一個存在的共享內存段連接到本進程空間,函數(shù)shmctl可以對共享內存段進行多種操作,函數(shù)shmdt函數(shù)分離該共享內存
四,考慮使用持久連接
持久連接也為長連接,它本身是TCP通信的一種普通方式,即在一次TCP連接中持續(xù)發(fā)送多分數(shù)據而不斷開連接,與它相反的方式稱為短連接,也就是建立連接后發(fā)送一份數(shù)據就斷開,然后再次建立連接發(fā)送下一份數(shù)據, 周而復始。是否采用持久連接,完全取決于應用特點。從性能角度看,建立TCP連接的操作本身是一項不小的開銷,在允許的情況下,連接次數(shù)越少,越有利于性能的提升;尤其對于密集型的圖片或網頁等小數(shù)據請求處理有明顯的加速所用。
HTTP長連接需要瀏覽器和web服務器的共同協(xié)作,目前瀏覽器普遍支持長連接,表現(xiàn)在其發(fā)出的HTTP請求數(shù)據頭中包含關于長連接的聲明,如下:Connection: Keep-Alive
主流的web服務器都支持長連接,比如apache中,可以用KeepAlive off關閉長連接。
對于長連接的有效使用,還有關鍵一點在于長連接超時時間的設置,即長連接在什么時候關閉嗎?Apache的默認設置為5s,若這個時間設置過長,則可能導致資源無效占有,維持大量空閑進程,影響服務器性能。
五,改進I/O 模型
I/O操作根據設備的不同分為很多類型,比如內存I/O, 網絡I/O, 磁盤I/O. 對于網絡I/O和磁盤I/O, 它們的速度要慢很多,盡管使用RAID磁盤陣列可通過并行磁盤磁盤來加快磁盤I/O速度,購買大連獨享網絡帶寬以及使用高帶寬網絡適配器可以提高網絡i/O的速度。但這些I/O操作需要內核系統(tǒng)調用來完成,這些需要CPU來調度,這使得CPU不得不浪費寶貴的時間來等待慢速I/O操作我們希望讓CPU足夠少的時間在i/O操作的調度上,如何讓高速的CPU和慢速的I/O設備更好地協(xié)調工作,是現(xiàn)代計算機一直探討的話題。各種I/O模型的本質區(qū)別在于CPU的參與方式。
(1)DMA技術I/O設備和內存之間的數(shù)據傳輸方式由DMA控制器完成。在DMA模式下,CPU只需向DMA下達命令,讓DMA控制器來處理數(shù)據的傳送,這樣可以大大節(jié)省系統(tǒng)資源。
(2)異步I/O
異步I/O指主動請求數(shù)據后便可以繼續(xù)處理其它任務,隨后等待I/O操作的通知,這樣進程在數(shù)據讀寫時不發(fā)生阻塞。而同步則在數(shù)據就緒后在讀寫時必須阻塞。
異步I/O是非阻塞的,當函數(shù)返回時,真正的I/O傳輸還沒開始,這讓CPU處理和I/O操作達到很好的重疊。
順便說說同步阻塞I/O的缺點,其雖然可以和多進程有效利用CPU資源,但代價是占用了大量的內存開銷。而同步非阻塞I/O需要進程執(zhí)行多次輪訓查看數(shù)據是否就緒,花費了大量的CPU時間。
(3)改進多路I/O就緒通知策略,epoll服務器同時處理大量的文件描述符是必不可少的,若采用同步非阻塞I/O模型,若同時接收TCP連接的數(shù)據,就必須輪流對每個socket調用接收數(shù)據的方法,不管這些socket有沒有可接收的數(shù)據,都要詢問一次,假如大部分socket并沒有數(shù)據可以接收,那么進程便會浪費很多CPU時間用于檢查這些socket.有沒有可以接收的數(shù)據,多路I/O就緒通知的出現(xiàn),提供了對大量文件描述符就緒檢查的高性能方案,它允許進程通過一種方法同時監(jiān)視所有文件描述符,并可以快速獲得所有就緒的文件描述符,然后只針對這些文件描述符進行數(shù)據訪問。
下面詳細介紹被公認為linux 2.6下性能最好的多路I/O就緒通知方法epoll.,幾乎具備select, poll, /dev/poll等模型的全部優(yōu)點
epoll可以同時支持水平觸發(fā)和邊緣觸發(fā),理論上邊緣觸發(fā)性能更高,但是代碼實現(xiàn)復雜,因為任何意外的丟失事件都會造成請求處理錯誤。
epoll主要有2大改進:
(1)epoll只告知就緒的文件描述符,而且當調用epoll_wait()獲得文件描述符時,返回并不是實際的描述符,而是一個代表就緒描述符數(shù)量的值,然后只需去epoll指定的一個數(shù)組中依次取得相應數(shù)量的文件描述符即可,這里使用了內存映射(mmap)技術,這樣徹底省掉了這些文件描述符在系統(tǒng)調用時復制的開銷。
(2)epoll采用基于事件的就緒通知方式。其事先通過epoll_ctrl()注冊每一個文件描述符,一旦某個文件描述符就緒時,內核會采用類似callback的回調機制,當進程調用epoll_wait()時得到通知
(4)Sendfile
大多數(shù)時候,我們都向服務器請求靜態(tài)文件,比如圖片,樣式表等,在處理這些請求時,磁盤文件的數(shù)據先經過內核緩沖區(qū),然后到用戶內存空間,不需經過任何處理,其又被送到網卡對應的內核緩沖區(qū),接著再被送入網卡進行發(fā)送。
Linux提供sendfile()系統(tǒng)調用,可以講磁盤文件的特定部分直接傳送到代表客戶端的socket描述符,加快了靜態(tài)文件的請求速度,同時減少CPU和內存的開銷。
適用場景: 對于請求較小的靜態(tài)文件,sendfile發(fā)揮的作用不那么明顯,因發(fā)送數(shù)據的環(huán)節(jié)在整個過程中所占時間的比例相比于大文件請求時小很多。
(5)內存映射
Linux內核提供一種訪問磁盤文件的特殊方式,它可以將內存中某塊地址空間和我們指定的磁盤文件相關聯(lián),從而對這塊內存的訪問轉換為對磁盤文件的訪問。這種技術稱為內存映射。
多數(shù)情況下,內存映射可以提高磁盤I/O的性能,無須使用read()或write()等系統(tǒng)調用來訪問文件,而是通過mmap()系統(tǒng)調用來建立內存和磁盤文件的關聯(lián),然后像訪問內存一樣自由訪問文件。
缺點:在處理較大文件時,內存映射會導致較大的內存開銷,得不償失。
(6)直接I/O
在linux 2.6中,內存映射和直接訪問文件沒有本質差異,因為數(shù)據需要經過2次復制,即在磁盤與內核緩沖區(qū)之間以及在內核緩沖區(qū)與用戶態(tài)內存空間。
引入內核緩沖區(qū)的目的在于提高磁盤文件的訪問性能,然而對于一些復雜的應用,比如數(shù)據庫服務器,它們?yōu)榱诉M一步提高性能,希望繞過內核緩沖區(qū),由自己在用戶態(tài)空間實現(xiàn)并管理I/O緩沖區(qū),比如數(shù)據庫可根據更加合理的策略來提高查詢緩存命中率。另一方面,繞過內核緩沖區(qū)也可以減少系統(tǒng)內存的開銷,因內核緩沖區(qū)本身就在使用系統(tǒng)內存。
Linux在open()系統(tǒng)調用中增加參數(shù)選項O_DIRECT,即可繞過內核緩沖區(qū)直接訪問文件,實現(xiàn)直接I/O。
在MySQL中,對于Innodb存儲引擎,自身進行數(shù)據和索引的緩存管理,可在my.cnf配置中分配raw分區(qū)跳過內核緩沖區(qū),實現(xiàn)直接I./O
六,改進服務器并發(fā)策略
服務器并發(fā)策略的目的,是讓I/O操作和CPU計算盡量重疊進行,一方面讓CPU在I/O等待時不要空閑,另一方面讓CPU在I/O調度上盡量花最少的時間。
(1)一個進程處理一個連接,非阻塞I/O,使用長連接
Apache使用這個模型,其進程的開銷限制了它的并發(fā)連接數(shù),但從穩(wěn)定性和兼容性的角度,則其相對安全,任何一個子進程的崩潰不會影響Apache本身,Apache父進程可以創(chuàng)建新的子進程;另一方面,Apache經過長期的考驗和廣發(fā)的使用,功能模塊非常豐富。所以對于一些并發(fā)數(shù)要求不高(如150以內),還對其它功能有依賴,那么可考慮Apache這個模型。
(2)一個進程處理多個連接,異步I/O,使用長連接
一個進程處理多個連接,潛在條件就是多路I/O就緒通知的應用。
服務器通常維護者大量的空閑連接,有些可能由于使用長連接而在等待超時,有些可能是網絡傳輸?shù)难訒r等等,這時epoll只會關注活躍連接,而不在死連接上浪費時間,但是select和poll會掃描所有文件描述符,這個是個非常昂貴的開銷。一個典型的應用就是圖片服務器,它們希望為用戶提供網頁中大量圖片的快速下載,采用長連接,但是這些大量連接在等待超時關閉前,處于空閑狀態(tài),這種情況下,epoll依然能很好工作。
POSIX的標準庫(aio.h)中定義了AIO的一系列接口,它幾乎屏蔽了一切網絡通信的細節(jié),對使用者而言非常簡單。AIO沒有提供非阻塞的open()方法,進程仍使用open()系統(tǒng)調用來打開文件,然后填充一些I/O請求的數(shù)據結構(struct aiocb),接下來調用aid_read()或aid_write()來發(fā)起異步I/O操作,一旦請求進入操作隊列后,函數(shù)便返回,進程可以在此調用aid_error()來檢查正在運行的I/O操作的狀態(tài)
aiocb中相關的域
AIO接口API
關于進程的數(shù)量,這個不是越多越好的。大量的進程可以維持更多的活躍連接數(shù),但每個連接的下載速度要遠遠小于前者(因上下文切換的CPU時間減少,有更多的時間用于發(fā)起sendfile()系統(tǒng)調用),則怎么決定worker的進程數(shù)取決于應用,例如是希望為更多的用戶同時提供慢速下載服務,還是希望為有限的用戶提供快速的下載服務。
對于動態(tài)內容,如PHP腳本,worker進程通常只是負責轉發(fā)請求給獨立的fastcgi進程,或者作為反向代理服務器將請求轉發(fā)給后端服務器,worker進程不太依賴太多的本地資源,可以適當提高并發(fā)連接數(shù),但太多的worker進程又會帶來更多的上下文切換開銷和內存開銷,從而整體上所有連接的相應時間變長。
而讀取磁盤文件可以考慮使用異步I/O,在某些場景比性能sendfile()更出色。
七,改進硬件環(huán)境
還有一點要提及的是硬件環(huán)境,服務器的硬件配置對站點代理的性能提升肯定是有的,但這里不作詳細討論。
核心關注:拓步ERP系統(tǒng)平臺是覆蓋了眾多的業(yè)務領域、行業(yè)應用,蘊涵了豐富的ERP管理思想,集成了ERP軟件業(yè)務管理理念,功能涉及供應鏈、成本、制造、CRM、HR等眾多業(yè)務領域的管理,全面涵蓋了企業(yè)關注ERP管理系統(tǒng)的核心領域,是眾多中小企業(yè)信息化建設首選的ERP管理軟件信賴品牌。
轉載請注明出處:拓步ERP資訊網http://www.ezxoed.cn/
本文標題:淺談如何提高服務器并發(fā)處理能力
本文網址:http://www.ezxoed.cn/html/consultation/10839621025.html