當前位置:首頁 » 文件管理 » go上傳服務

go上傳服務

發布時間: 2022-06-15 04:58:46

1. golang怎麼通過數據流直接生成excel上傳至oss

import java.io.*; import jxl.*; … … … … try { //構建Workbook對象, 只讀Workbook對象 //直接從本地文件創建Workbook //從輸入流創建Workbook InputStream is = new FileInputStream(sourcefile); jxl.Workbook rwb = Workbook.getWorkbook(is); } catch (Exception e) { e.printStackTrace(); } 一旦創建了Workbook,我們就可以通過它來訪問Excel Sheet(術語:工作表)。參考下面的代碼片段: //獲取第一張Sheet表 Sheet rs = rwb.getSheet(0); 我們既可能通過Sheet的名稱來訪問它,也可以通過下標來訪問它。如果通過下標來訪問的話,要注意的一點是下標從0開始,就像數組一樣。 一旦得到了Sheet,我們就可以通過它來訪問Excel Cell(術語:單元格)。參考下面的代碼片段: //獲取第一行,第一列的值 Cell c00 = rs.getCell(0, 0); String strc00 = c00.getContents(); //獲取第一行,第二列的值 Cell c10 = rs.getCell(1, 0); String strc10 = c10.getContents(); //獲取第二行,第二列的值 Cell c11 = rs.getCell(1, 1); String strc11 = c11.getContents(); System.out.println("Cell(0, 0)" + " value : " + strc00 + "; type : " + c00.getType()); System.out.println("Cell(1, 0)" + " value : " + strc10 + "; type : " + c10.getType()); System.out.println("Cell(1, 1)" + " value : " + strc11 + "; type : " + c11.getType()); 如果僅僅是取得Cell的值,我們可以方便地通過getContents()方法,它可以將任何類型的Cell值都作為一個字元串返回。示例代碼中Cell(0, 0)是文本型,Cell(1, 0)是數字型,Cell(1,1)是日期型,通過getContents(),三種類型的返回值都是字元型。 如果有需要知道Cell內容的確切類型,API也提供了一系列的方法。參考下面的代碼片段: String strc00 = null; double strc10 = 0.00; Date strc11 = null; Cell c00 = rs.getCell(0, 0); Cell c10 = rs.getCell(1, 0); Cell c11 = rs.getCell(1, 1); if(c00.getType() == CellType.LABEL) { LabelCell labelc00 = (LabelCell)c00; strc00 = labelc00.getString(); } if(c10.getType() == CellType.NUMBER) { NmberCell numc10 = (NumberCell)c10; strc10 = numc10.getValue(); } if(c11.getType() == CellType.DATE) { DateCell datec11 = (DateCell)c11; strc11 = datec11.getDate(); } System.out.println("Cell(0, 0)" + " value : " + strc00 + "; type : " + c00.getType()); System.out.println("Cell(1, 0)" + " value : " + strc10 + "; type : " + c10.getType()); System.out.println("Cell(1, 1)" + " value : " + strc11 + "; type : " + c11.getType()); 在得到Cell對象後,通過getType()方法可以獲得該單元格的類型,然後與API提供的基本類型相匹配,強制轉換成相應的類型,最後調用相應的取值方法getXXX(),就可以得到確定類型的值。API提供了以下基本類型,與Excel的數據格式相對應,如下圖所示: 每種類型的具體意義,請參見Java Excel API Document。 當你完成對Excel電子表格數據的處理後,一定要使用close()方法來關閉先前創建的對象,以釋放讀取數據表的過程中所佔用的內存空間,在讀取大量數據時顯得尤為重要。參考如下代碼片段: //操作完成時,關閉對象,釋放佔用的內存空間 rwb.close(); Java Excel API提供了許多訪問Excel數據表的方法,在這里我只簡要地介紹幾個常用的方法,其它的方法請參考附錄中的Java Excel API Document。 • Workbook類提供的方法 1. int getNumberOfSheets() 獲得工作薄(Workbook)中工作表(Sheet)的個數,示例: jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile)); int sheets = rwb.getNumberOfSheets(); 2. Sheet[] getSheets() 返回工作薄(Workbook)中工作表(Sheet)對象數組,示例: jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile)); Sheet[] sheets = rwb.getSheets(); 3. String getVersion() 返回正在使用的API的版本號,好像是沒什麼太大的作用。 jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile)); String apiVersion = rwb.getVersion(); • Sheet介面提供的方法 1. String getName() 獲取Sheet的名稱,示例: jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile)); jxl.Sheet rs = rwb.getSheet(0); String sheetName = rs.getName(); 2. int getColumns() 獲取Sheet表中所包含的總列數,示例: jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile)); jxl.Sheet rs = rwb.getSheet(0); int rsColumns = rs.getColumns(); 3. Cell[] getColumn(int column) 獲取某一列的所有單元格,返回的是單元格對象數組,示例: jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile)); jxl.Sheet rs = rwb.getSheet(0); Cell[] cell = rs.getColumn(0); 4. int getRows() 獲取Sheet表中所包含的總行數,示例: jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile)); jxl.Sheet rs = rwb.getSheet(0); int rsRows = rs.getRows(); 5. Cell[] getRow(int row) 獲取某一行的所有單元格,返回的是單元格對象數組,示例子: jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile)); jxl.Sheet rs = rwb.getSheet(0); Cell[] cell = rs.getRow(0); 6. Cell getCell(int column, int row) 獲取指定單元格的對象引用,需要注意的是它的兩個參數,第一個是列數,第二個是行數,這與通常的行、列組合有些不同。 jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile)); jxl.Sheet rs = rwb.getSheet(0); Cell cell = rs.getCell(0, 0); 生成新的Excel工作薄 下面的代碼主要是向大家介紹如何生成簡單的Excel工作表,在這里單元格的內容是不帶任何修飾的(如:字體,顏色等等),所有的內容都作為字元串寫入。(完整代碼見ExcelWriting.java) 與讀取Excel工作表相似,首先要使用Workbook類的工廠方法創建一個可寫入的工作薄(Workbook)對象,這里要注意的是,只能通過API提供的工廠方法來創建Workbook,而不能使用WritableWorkbook的構造函數,因為類WritableWorkbook的構造函數為protected類型。示例代碼片段如下: import java.io.*; import jxl.*; import jxl.write.*; … … … … try { //構建Workbook對象, 只讀Workbook對象 //Method 1:創建可寫入的Excel工作薄 jxl.write.WritableWorkbook wwb = Workbook.createWorkbook(new File(targetfile)); //Method 2:將WritableWorkbook直接寫入到輸出流 /* OutputStream os = new FileOutputStream(targetfile); jxl.write.WritableWorkbook wwb = Workbook.createWorkbook(os); */ } catch (Exception e) { e.printStackTrace(); } API提供了兩種方式來處理可寫入的輸出流,一種是直接生成本地文件,如果文件名不帶全路徑的話,預設的文件會定位在當前目錄,如果文件名帶有全路徑的話,則生成的Excel文件則會定位在相應的目錄;另外一種是將Excel對象直接寫入到輸出流,例如:用戶通過瀏覽器來訪問Web伺服器,如果HTTP頭設置正確的話,瀏覽器自動調用客戶端的Excel應用程序,來顯示動態生成的Excel電子表格。 接下來就是要創建工作表,創建工作表的方法與創建工作薄的方法幾乎一樣,同樣是通過工廠模式方法獲得相應的對象,該方法需要兩個參數,一個是工作表的名稱,另一個是工作表在工作薄中的位置,參考下面的代碼片段: //創建Excel工作表 jxl.write.WritableSheet ws = wwb.createSheet("Test Sheet 1", 0); "這鍋也支好了,材料也准備齊全了,可以開始下鍋了!",現在要做的只是實例化API所提供的Excel基本數據類型,並將它們添加到工作表中就可以了,參考下面的代碼片段: //1.添加Label對象 jxl.write.Label labelC = new jxl.write.Label(0, 0, "This is a Label cell"); ws.addCell(labelC); //添加帶有字型Formatting的對象 jxl.write.WritableFont wf = new jxl.write.WritableFont(WritableFont.TIMES, 18, WritableFont.BOLD, true); jxl.write.WritableCellFormat wcfF = new jxl.write.WritableCellFormat(wf); jxl.write.Label labelCF = new jxl.write.Label(1, 0, "This is a Label Cell", wcfF); ws.addCell(labelCF); //添加帶有字體顏色Formatting的對象 jxl.write.WritableFont wfc = new jxl.write.WritableFont(WritableFont.ARIAL, 10, WritableFont.NO_BOLD, false, UnderlineStyle.NO_UNDERLINE, jxl.format.Colour.RED); jxl.write.WritableCellFormat wcfFC = new jxl.write.WritableCellFormat(wfc); jxl.write.Label labelCFC = new jxl.write.Label(1, 0, "This is a Label Cell", wcfFC); ws.addCell(labelCF); //2.添加Number對象 jxl.write.Number labelN = new jxl.write.Number(0, 1, 3.1415926); ws.addCell(labelN); //添加帶有formatting的Number對象 jxl.write.NumberFormat nf = new jxl.write.NumberFormat("#.##"); jxl.write.WritableCellFormat wcfN = new jxl.write.WritableCellFormat(nf); jxl.write.Number labelNF = new jxl.write.Number(1, 1, 3.1415926, wcfN); ws.addCell(labelNF); //3.添加Boolean對象 jxl.write.Boolean labelB = new jxl.write.Boolean(0, 2, false); ws.addCell(labelB); //4.添加DateTime對象 jxl.write.DateTime labelDT = new jxl.write.DateTime(0, 3, new java.util.Date()); ws.addCell(labelDT); //添加帶有formatting的DateFormat對象 jxl.write.DateFormat df = new jxl.write.DateFormat("dd MM yyyy hh:mm:ss"); jxl.write.WritableCellFormat wcfDF = new jxl.write.WritableCellFormat(df); jxl.write.DateTime labelDTF = new jxl.write.DateTime(1, 3, new java.util.Date(), wcfDF); ws.addCell(labelDTF); 這里有兩點大家要引起大家的注意。第一點,在構造單元格時,單元格在工作表中的位置就已經確定了。一旦創建後,單元格的位置是不能夠變更的,盡管單元格的內容是可以改變的。第二點,單元格的定位是按照下面這樣的規律(column, row),而且下標都是從0開始,例如,A1被存儲在(0, 0),B1被存儲在(1, 0)。 最後,不要忘記關閉打開的Excel工作薄對象,以釋放佔用的內存,參見下面的代碼片段: //寫入Exel工作表 wwb.write(); //關閉Excel工作薄對象 wwb.close(); 這可能與讀取Excel文件的操作有少少不同,在關閉Excel對象之前,你必須要先調用write()方法,因為先前的操作都是存儲在緩存中的,所以要通過該方法將操作的內容保存在文件中。如果你先關閉了Excel對象,那麼只能得到一張空的工作薄了。 拷貝、更新Excel工作薄 接下來簡要介紹一下如何更新一個已經存在的工作薄,主要是下面二步操作,第一步是構造只讀的Excel工作薄,第二步是利用已經創建的Excel工作薄創建新的可寫入的Excel工作薄,參考下面的代碼片段:(完整代碼見ExcelModifying.java) //創建只讀的Excel工作薄的對象 jxl.Workbook rw = jxl.Workbook.getWorkbook(new File(sourcefile)); //創建可寫入的Excel工作薄對象 jxl.write.WritableWorkbook wwb = Workbook.createWorkbook(new File(targetfile), rw); //讀取第一張工作表 jxl.write.WritableSheet ws = wwb.getSheet(0); //獲得第一個單元格對象 jxl.write.WritableCell wc = ws.getWritableCell(0, 0); //判斷單元格的類型, 做出相應的轉化 if(wc.getType() == CellType.LABEL) { Label l = (Label)wc; l.setString("The value has been modified."); } //寫入Excel對象 wwb.write(); //關閉可寫入的Excel對象 wwb.close(); //關閉只讀的Excel對象 rw.close(); 之所以使用這種方式構建Excel對象,完全是因為效率的原因,因為上面的示例才是API的主要應用。為了提高性能,在讀取工作表時,與數據相關的一些輸出信息,所有的格式信息,如:字體、顏色等等,是不被處理的,因為我們的目的是獲得行數據的值,既使沒有了修飾,也不會對行數據的值產生什麼影響。唯一的不利之處就是,在內存中會同時保存兩個同樣的工作表,這樣當工作表體積比較大時,會佔用相當大的內存,但現在好像內存的大小並不是什麼關鍵因素了。 一旦獲得了可寫入的工作表對象,我們就可以對單元格對象進行更新的操作了,在這里我們不必調用API提供的add()方法,因為單元格已經於工作表當中,所以我們只需要調用相應的setXXX()方法,就可以完成更新的操作了。 盡單元格原有的格式化修飾是不能去掉的,我們還是可以將新的單元格修飾加上去,以使單元格的內容以不同的形式表現。 新生成的工作表對象是可寫入的,我們除了更新原有的單元格外,還可以添加新的單元格到工作表中,這與示例2的操作是完全一樣的。 最後,不要忘記調用write()方法,將更新的內容寫入到文件中,然後關閉工作薄對象,這里有兩個工作薄對象要關閉,一個是只讀的,另外一個是可寫入的。 小結 本文只是對Java Excel API中常用的方法作了介紹,要想更詳盡地了解API,請大家參考API文檔,或源代碼。Java Excel API是一個開放源碼項目,請大家關注它的最新進展,有興趣的朋友也可以申請加入這個項目,或者是提出寶貴的意見。

2. 歡go提供哪些服務

「歡go快捷服務專區」是中國電信為廣大客戶方便快捷的辦理、查詢、繳費等業務而提供的集成化服務頁,以方便快捷、專業化、個性化為特色,包含:我的服務、費用查詢、充值繳費、業務辦理四個版塊。135號客服為你解答,以上信息僅供參考,廣西電信無憂卡,月租僅五元,流量階梯式計費,越用越便宜,詳情可登錄廣西電信網上營業廳查詢辦理,http://wx8102.gstai.com/UrlDispenseApp/index.php

3. 使用goagent出現Service Unavailable

①你創建了自己的appid沒有
②你正確上傳了服務端沒有
③你修改了proxy.ini中的appid沒有

【請先檢查以上三個問題】,如果你確定三個都沒問題,請你在出現這個提示的時候將GoAgent的窗口截圖。

4. 如何使用Go建開發高負載WebSocket伺服器

Mail.Ru有很多有狀態的系統。 用戶電子郵件存儲是其中之一。 跟蹤系統中的狀態變化和系統事件有幾種方法。 這主要是通過定期系統輪詢或關於其狀態變化的系統通知。

兩種方式都有利弊。 但是當涉及郵件時,用戶收到新郵件的速度越快越好。

郵件輪詢涉及每秒大約50,000個HTTP查詢,其中60%返回304狀態,這意味著郵箱沒有變化。

因此,為了減少伺服器上的負載並加快郵件傳遞給用戶,決定通過編寫發布-訂閱伺服器,一方面將接收有關狀態更改的通知,另一方面則會收到這種通知的訂閱。

先前

第一個方案顯示了以前的樣子。 瀏覽器定期輪詢API,並查詢有關Storage(郵箱服務)的更改。

第二個方案描述了新架構。 瀏覽器與通知API建立WebSocket連接,通知API是Bus伺服器的客戶端。收到新的電子郵件後,Storage會向Bus(1)發送一條通知,由Bus發送到訂閱者。 API確定連接以發送接收到的通知,並將其發送到用戶的瀏覽器(3)。

所以今天我們將討論API或WebSocket伺服器。 我們的伺服器將有大約300萬個在線連接。

實現方式

讓我們看看如何使用Go函數實現伺服器的某些部分,而無需任何優化。

在進行net/http ,我們來談談我們如何發送和接收數據。 站在WebSocket協議(例如JSON對象) 之上的數據在下文中將被稱為分組 。

我們開始實現包含通過WebSocket連接發送和接收這些數據包的Channel結構。

channel 結構

// Packet represents application level data.
type Packet struct {
...
}

// Channel wraps user connection.
type Channel struct {
conn net.Conn // WebSocket connection.
send chan Packet // Outgoing packets queue.
}

func NewChannel(conn net.Conn) *Channel {
c := &Channel{
conn: conn,
send: make(chan Packet, N),
}

go c.reader()
go c.writer()

return c
}

注意這里有reader和writer連個goroutines。 每個goroutine都需要自己的內存棧, 根據操作系統和Go版本可能具有2到8 KB的初始大小。

在300萬個在線連接的時候,我們將需要24 GB的內存 (堆棧為4 KB)用於維持所有連接。 這還沒有計算為Channel結構分配的內存,傳出的數據包ch.send和其他內部欄位消耗的內存。

I/O goroutines

我們來看看「reader」的實現:

func (c *Channel) reader() {
// We make a buffered read to rece read syscalls.
buf := bufio.NewReader(c.conn)

for {
pkt, _ := readPacket(buf)
c.handle(pkt)
}
}

這里我們使用bufio.Reader來減少read() syscalls的數量,並讀取與buf緩沖區大小一樣的數量。 在無限循環中,我們期待新數據的到來。 請記住: 預計新數據將會來臨。 我們稍後會回來。

我們將離開傳入數據包的解析和處理,因為對我們將要討論的優化不重要。 但是, buf現在值得我們注意:默認情況下,它是4 KB,這意味著我們需要另外12 GB內存。 「writer」有類似的情況:

func (c *Channel) writer() {
// We make buffered write to rece write syscalls.
buf := bufio.NewWriter(c.conn)

for pkt := range c.send {
_ := writePacket(buf, pkt)
buf.Flush()
}
}

我們遍歷c.send ,並將它們寫入緩沖區。細心讀者已經猜到的,我們的300萬個連接還將消耗12 GB的內存。

HTTP

我們已經有一個簡單的Channel實現,現在我們需要一個WebSocket連接才能使用。

注意:如果您不知道WebSocket如何工作。客戶端通過稱為升級的特殊HTTP機制切換到WebSocket協議。 在成功處理升級請求後,伺服器和客戶端使用TCP連接來交換二進制WebSocket幀。 這是連接中的框架結構的描述。

import (
"net/http"
"some/websocket"
)

http.HandleFunc("/v1/ws", func(w http.ResponseWriter, r *http.Request) {
conn, _ := websocket.Upgrade(r, w)
ch := NewChannel(conn)
//...
})

請注意, http.ResponseWriter為bufio.Reader和bufio.Writer (使用4 KB緩沖區)進行內存分配,用於*http.Request初始化和進一步的響應寫入。

無論使用什麼WebSocket庫,在成功響應升級請求後, 伺服器在responseWriter.Hijack()調用之後,連同TCP連接一起接收 I/O緩沖區。

提示:在某些情況下, go:linkname 可用於 通過調用 net/http.putBufio{Reader,Writer} 將緩沖區返回到 net/http 內 的 sync.Pool 。

因此,我們需要另外24 GB的內存來維持300萬個鏈接。

所以,我們的程序即使什麼都沒做,也需要72G內存。

優化

我們來回顧介紹部分中談到的內容,並記住用戶連接的行為。 切換到WebSocket之後,客戶端發送一個包含相關事件的數據包,換句話說就是訂閱事件。 然後(不考慮諸如ping/pong等技術信息),客戶端可能在整個連接壽命中不發送任何其他信息。

連接壽命可能是幾秒到幾天。

所以在最多的時候,我們的Channel.reader()和Channel.writer()正在等待接收或發送數據的處理。 每個都有4 KB的I/O緩沖區。

現在很明顯,某些事情可以做得更好,不是嗎?

Netpoll

你還記得bufio.Reader.Read()內部,Channel.reader()實現了在沒有新數據的時候conn.read()會被鎖。如果連接中有數據,Go運行時「喚醒」我們的goroutine並允許它讀取下一個數據包。 之後,goroutine再次鎖定,期待新的數據。 讓我們看看Go運行時如何理解goroutine必須被「喚醒」。 如果我們看看conn.Read()實現 ,我們將在其中看到net.netFD.Read()調用 :

// net/fd_unix.go

func (fd *netFD) Read(p []byte) (n int, err error) {
//...
for {
n, err = syscall.Read(fd.sysfd, p)
if err != nil {
n = 0
if err == syscall.EAGAIN {
if err = fd.pd.waitRead(); err == nil {
continue
}
}
}
//...
break
}
//...
}

Go在非阻塞模式下使用套接字。 EAGAIN表示,套接字中沒有數據,並且在從空套接字讀取時不會被鎖定,操作系統將控制權返還給我們。

我們從連接文件描述符中看到一個read()系統調用。 如果讀取返回EAGAIN錯誤 ,則運行時會使pollDesc.waitRead()調用 :

// net/fd_poll_runtime.go

func (pd *pollDesc) waitRead() error {
return pd.wait('r')
}

func (pd *pollDesc) wait(mode int) error {
res := runtime_pollWait(pd.runtimeCtx, mode)
//...
}

如果我們深入挖掘 ,我們將看到netpoll是使用Linux中的epoll和BSD中的kqueue來實現的。 為什麼不使用相同的方法來進行連接? 我們可以分配一個讀緩沖區,只有在真正有必要時才使用goroutine:當套接字中有真實可讀的數據時。

在github.com/golang/go上, 導出netpoll函數有問題 。

擺脫goroutines

假設我們有Go的netpoll實現 。 現在我們可以避免使用內部緩沖區啟動Channel.reader() goroutine,並在連接中訂閱可讀數據的事件:

ch := NewChannel(conn)

// Make conn to be observed by netpoll instance.
poller.Start(conn, netpoll.EventRead, func() {
// We spawn goroutine here to prevent poller wait loop
// to become locked ring receiving packet from ch.
go Receive(ch)
})

// Receive reads a packet from conn and handles it somehow.
func (ch *Channel) Receive() {
buf := bufio.NewReader(ch.conn)
pkt := readPacket(buf)
c.handle(pkt)
}

使用Channel.writer()更容易,因為只有當我們要發送數據包時,我們才能運行goroutine並分配緩沖區:

func (ch *Channel) Send(p Packet) {
if c.noWriterYet() {
go ch.writer()
}
ch.send <- p
}

請注意,當操作系統在 write() 系統調用時返回 EAGAIN 時,我們不處理這種情況 。 對於這種情況,我們傾向於Go運行時那樣處理。 如果需要,它可以以相同的方式來處理。

從ch.send (一個或幾個)讀出傳出的數據包後,writer將完成其操作並釋放goroutine棧和發送緩沖區。

完美! 通過擺脫兩個連續運行的goroutine中的堆棧和I/O緩沖區,我們節省了48 GB 。

資源控制

大量的連接不僅涉及高內存消耗。 在開發伺服器時,我們會經歷重復的競爭條件和死鎖,常常是所謂的自動DDoS,這種情況是當應用程序客戶端肆意嘗試連接到伺服器,從而破壞伺服器。

例如,如果由於某些原因我們突然無法處理ping/pong消息,但是空閑連接的處理程序會關閉這樣的連接(假設連接斷開,因此沒有提供數據),客戶端會不斷嘗試連接,而不是等待事件。

如果鎖定或超載的伺服器剛剛停止接受新連接,並且負載均衡器(例如,nginx)將請求都傳遞給下一個伺服器實例,那壓力將是巨大的。

此外,無論伺服器負載如何,如果所有客戶端突然想要以任何原因發送數據包(大概是由於錯誤原因),則先前節省的48 GB將再次使用,因為我們將實際恢復到初始狀態goroutine和並對每個連接分配緩沖區。

Goroutine池

我們可以使用goroutine池來限制同時處理的數據包數量。 這是一個go routine池的簡單實現:

package gopool

func New(size int) *Pool {
return &Pool{
work: make(chan func()),
sem: make(chan struct{}, size),
}
}

func (p *Pool) Schele(task func()) error {
select {
case p.work <- task:
case p.sem <- struct{}{}:
go p.worker(task)
}
}

func (p *Pool) worker(task func()) {
defer func() { <-p.sem }
for {
task()
task = <-p.work
}
}

現在我們的netpoll代碼如下:

pool := gopool.New(128)

poller.Start(conn, netpoll.EventRead, func() {
// We will block poller wait loop when
// all pool workers are busy.
pool.Schele(func() {
Receive(ch)
})
})

所以現在我們讀取數據包可以在池中使用了空閑的goroutine。

同樣,我們將更改Send() :

pool := gopool.New(128)

func (ch *Channel) Send(p Packet) {
if c.noWriterYet() {
pool.Schele(ch.writer)
}
ch.send <- p
}

而不是go ch.writer() ,我們想寫一個重用的goroutine。 因此,對於N goroutines池,我們可以保證在N請求同時處理並且到達N + 1我們不會分配N + 1緩沖區進行讀取。 goroutine池還允許我們限制新連接的Accept()和Upgrade() ,並避免大多數情況下被DDoS打垮。

零拷貝升級

讓我們從WebSocket協議中偏離一點。 如前所述,客戶端使用HTTP升級請求切換到WebSocket協議。 協議是樣子:

GET /ws HTTP/1.1
Host: mail.ru
Connection: Upgrade
Sec-Websocket-Key: A3xNe7sEB9HixkmBhVrYaA==
Sec-Websocket-Version: 13
Upgrade: websocket

HTTP/1.1 101 Switching Protocols
Connection: Upgrade
Sec-Websocket-Accept: ksu0wXWG+YmkVx+KQR2agP0cQn4=
Upgrade: websocket

也就是說,在我們的例子中,我們需要HTTP請求和header才能切換到WebSocket協議。 這個知識點和http.Request的內部實現表明我們可以做優化。我們會在處理HTTP請求時拋棄不必要的內存分配和復制,並放棄標準的net/http伺服器。

例如, http.Request 包含一個具有相同名稱的頭文件類型的欄位,它通過將數據從連接復制到值字元串而無條件填充所有請求頭。 想像一下這個欄位中可以保留多少額外的數據,例如大型Cookie頭。

但是要做什麼呢?

WebSocket實現

不幸的是,在我們的伺服器優化時存在的所有庫都允許我們對標準的net/http伺服器進行升級。 此外,所有庫都不能使用所有上述讀寫優化。 為使這些優化能夠正常工作,我們必須使用一個相當低級別的API來處理WebSocket。 要重用緩沖區,我們需要procotol函數看起來像這樣:

func ReadFrame(io.Reader) (Frame, error)
func WriteFrame(io.Writer, Frame) error

如果我們有一個這樣的API的庫,我們可以從連接中讀取數據包,如下所示(數據包寫入看起來差不多):

// getReadBuf, putReadBuf are intended to
// reuse *bufio.Reader (with sync.Pool for example).
func getReadBuf(io.Reader) *bufio.Reader
func putReadBuf(*bufio.Reader)

// readPacket must be called when data could be read from conn.
func readPacket(conn io.Reader) error {
buf := getReadBuf()
defer putReadBuf(buf)

buf.Reset(conn)
frame, _ := ReadFrame(buf)
parsePacket(frame.Payload)
//...
}

簡而言之,現在是製作我們自己庫的時候了。

github.com/gobwas/ws

為了避免將協議操作邏輯強加給用戶,我們編寫了WS庫。 所有讀寫方法都接受標準的io.Reader和io.Writer介面,可以使用或不使用緩沖或任何其他I/O包裝器。

除了來自標准net/http升級請求之外, ws支持零拷貝升級 ,升級請求的處理和切換到WebSocket,而無需內存分配或復制。 ws.Upgrade()接受io.ReadWriter ( net.Conn實現了這個介面)。 換句話說,我們可以使用標準的net.Listen()並將接收到的連接從ln.Accept()立即傳遞給ws.Upgrade() 。 該庫可以復制任何請求數據以供將來在應用程序中使用(例如, Cookie以驗證會話)。

以下是升級請求處理的基準 :標准net/http伺服器與net.Listen()加零拷貝升級:

BenchmarkUpgradeHTTP 5156 ns/op 8576 B/op 9 allocs/op
BenchmarkUpgradeTCP 973 ns/op 0 B/op 0 allocs/op

切換到ws和零拷貝升級節省了另外24 GB內存 - 這是由net/http處理程序請求處理時為I/O緩沖區分配的空間。

概要

讓我們結合代碼告訴你我們做的優化。

  • 讀取內部緩沖區的goroutine是非常昂貴的。 解決方案 :netpoll(epoll,kqueue); 重用緩沖區。

  • 寫入內部緩沖區的goroutine是非常昂貴的。 解決方案 :必要時啟動goroutine; 重用緩沖區。

  • DDOS,netpoll將無法工作。 解決方案 :重新使用數量限制的goroutines。

  • net/http不是處理升級到WebSocket的最快方法。 解決方案 :在連接上使用零拷貝升級。

  • 這就是伺服器代碼的樣子:

  • import (

  • "net"

  • "github.com/gobwas/ws"

  • )


  • ln, _ := net.Listen("tcp", ":8080")


  • for {

  • // Try to accept incoming connection inside free pool worker.

  • // If there no free workers for 1ms, do not accept anything and try later.

  • // This will help us to prevent many self-ddos or out of resource limit cases.

  • err := pool.ScheleTimeout(time.Millisecond, func() {

  • conn := ln.Accept()

  • _ = ws.Upgrade(conn)


  • // Wrap WebSocket connection with our Channel struct.

  • // This will help us to handle/send our app's packets.

  • ch := NewChannel(conn)


  • // Wait for incoming bytes from connection.

  • poller.Start(conn, netpoll.EventRead, func() {

  • // Do not cross the resource limits.

  • pool.Schele(func() {

  • // Read and handle incoming packet(s).

  • ch.Recevie()

  • })

  • })

  • })

  • if err != nil {

  • time.Sleep(time.Millisecond)

  • }

  • }

  • 結論

    過早優化是萬惡之源。 Donald Knuth

    當然,上述優化是有意義的,但並非所有情況都如此。 例如,如果可用資源(內存,CPU)和在線連接數之間的比例相當高(伺服器很閑),則優化可能沒有任何意義。 但是,您可以從哪裡需要改進以及改進內容中受益匪淺。

5. ubuntu安裝goagent上傳成功之後啟動服務提示Address already in use,我已經將listen address 改成9000

8087,一般不會有人用啊。你停掉,再試試看。
listen地址有多個,請注意修改原來為8087的那個。

6. goagent 上傳失敗 File "boot.py", line 39, in <mole> 怎麼解決。。。

設置方法有誤!!請按照如下方法設置:
軟體設置及瀏覽器的配置
1--1打開解壓出來的GoAgent文件夾,修改local\proxy.ini(用記事本打開)中找到[gae]下面的「appid=」,後面填入你的appid即可,比如這樣:appid = frog-in-301(多appid請用|隔開)。修改完畢保存。
1--2修改server\app.yaml下的your_appid為你的appid(每次只能上傳單個appid)。
1--3先啟動local\goagent.exe,雙擊server\upload.bat,然後會提示你輸入appid,這就是剛才你注冊時得到的appid。然後會要求你輸入GMail帳號密碼,按提示輸入即可。
注意:輸入密碼時不會顯示星號或者其他東西上傳成功後即可使用了 。
1--4配置瀏覽器
GoAgent使用於目前流行的很多種瀏覽器,像IE、360瀏覽器、Chrome、Firefox、遨遊……..設置方法都差不多,具體如下:
打開瀏覽器,點「工具」—>「Internet(IE)選項」 —>在接下來彈出的對話框中,點「連接」—>在彈出的對話框中,點「區域網設置」—>在接下來彈出的對話框中,一定要將「為LAN使用代理伺服器」前面的框勾上。代理地址:127.0.0.1,埠為8087,然後確定,重啟瀏覽器即可。想成功使用,要啟動local\goagent.exe。

7. 如何部署Golang程序到伺服器

編譯成二進制文件,直接執行啊

8. 如何升級goagent服務端升級

規則就是看項目主頁的更新歷史,月份後面的「是」表示需要更新服務端,「否」表示不需要更新,升級服務端就是重新上傳appid,方法主頁也寫的有,沒事多看看主頁的說明,這些都不是問題了

9. 用goagent客戶端將Google App Engine的ID上傳至谷歌伺服器時為什麼老是失敗

先確認你是否已經創建了這個appID,之後上傳的時候會先後讓你輸入appid,你的google賬號密碼。這和電腦或者網路環境應該沒關系,我在學校里也能傳上去。之後記得要改那個proxy.ini文件。不行的話,嘗試一下網速快一點的網路環境

10. go 關於伺服器和託管的問題

cpu是處理程序的速度
主板不怎麼好解釋,相當於搭載所有的硬體橋梁的東西,有足夠的寬度,才能保證數據的流暢
內存越大能保證處理的東西越多,不是快是多
網卡,連接交換機,保證網路連通
ip是分配給你的一個門牌號,在互聯網上能找到你們家「就是你的伺服器」
共享就是你的ip斷或者一個機櫃有多台伺服器,同時享用100m的帶寬,沒有限制
100m獨享,就是你的伺服器單獨用100m的帶寬。
肯定是10m獨享好,因為他網速穩定。

熱點內容
make編譯輸出 發布:2024-05-20 00:37:01 瀏覽:67
4200存儲伺服器 發布:2024-05-20 00:20:35 瀏覽:160
解壓小生活 發布:2024-05-20 00:15:03 瀏覽:143
粘土小游戲伺服器ip 發布:2024-05-20 00:14:00 瀏覽:196
魔獸世界如何快速增加伺服器 發布:2024-05-19 23:53:37 瀏覽:694
安卓手機如何轉入蘋果手機內 發布:2024-05-19 23:50:35 瀏覽:405
安卓哪個能安裝血染小鎮 發布:2024-05-19 23:45:57 瀏覽:901
tensorflowmac編譯 發布:2024-05-19 23:28:59 瀏覽:702
sqlmaxvarchar 發布:2024-05-19 23:24:02 瀏覽:703
linux配置網卡命令 發布:2024-05-19 23:22:57 瀏覽:505