關於 Subversion 協議動態代理服務器

##前言 在2015年下半年的時候,筆者的工做主要轉向 GIT@OSC 分佈式後端服務器的實現,GIT@OSC 分佈式對於不一樣的接入有有不一樣的解決方案,HTTP 訪問使用 nginx 模塊實現動態代理,對於 ssh ,則是使用的端口轉發。對於 svn, 早先是不使用分佈式解決方案,也就是使用舊式的 NFS 方案,對於分佈式來講,這很是的不完全,NFS 依然是系統的一個瓶頸,而且 svn 服務器的奔潰影響的是全部的用戶,某日開會,老大參與後,就問我,能不能實現 svn 協議的代理,我想了想,因而就答應下來,後來就有了 svnsrv 項目,2016 年春節前,svnsrv 做爲 GIT@OSC svn 分佈式解決方案率先上線,通過屢次改進,svnsrv 逐漸穩定,目前已經穩定運行至今,春節期間遇到的訪問故障通過分析均不是 svnsrv 的問題,而是後端 sserver 服務器的問題。linux

OSChina 創立在開源分享的精神之上,咱們也將回饋社區。將 svnsrv 剝離核心路由庫開源出來,項目地址: https://git.oschina.net/oschina/svnsrvnginx

##svnsrv 概覽 svnsrv 是基於C++11 開發的 svn 協議動態代理服務器軟件,本來運行在 linux 平臺,網絡框架使用了 Boost.Asio 庫,全異步模式,在上線初期,咱們曾經使用同步和異步混合的方式使用 Boost.Asio ,那段時間 svnsrv 常常運行若干個小時就沒法接受新的鏈接,後來改爲全異步模式就沒有這個問題了。核心使用 Boost.Asio 庫給移植到 windows 平臺帶來了便利,春節事後,筆者就將 svnsrv 移植到 Windows 平臺了,支持 Visual Studio 2013, Visual Studio 2015, 使用 nuget 工具下載 boost 依賴庫,一樣也支持 MSYS2 的 Mingw64 編譯,不支持 cygwin, 對於 BSD 類系統,daemon 功能是沒有實現的。其餘功能未測試。開發者能夠 fork 改進後發起 Pull Request,實現 svnsrv 對於其餘平臺的支持和對 svnsrv bug 的修復,功能的改進。git

##svn 協議代理實現 筆者曾經寫過一篇博客:Subversion 和 GIT 開發者演進 在文中曾經介紹了 svn 協議的主要內容,而且提出裏 svn 代理服務器的實現思路。svn 客戶端鏈接到服務器時,最早會握手(handshake)交換握手信息,客戶端的數據攜帶了訪問的 URL, 咱們解析 數據取得 URL 後,而後解析 URL 得到用戶名,經過查詢路由模塊,即可以得到後端的服務器地址和端口,與後端創建鏈接後,svnsrv 交換兩方數據,即實現了 svn 的代理。windows

svnsrv 使用線程池併發,源碼在 SubversionServer, 併發線程數目能夠在配置文件中配置。後端

順序的操做有以下函數:服務器

echo_downstream_handshake 
--> read_downstream_handshake 
-->async_connect_upstream 
-->read_upstream_handshake 
-->echo_upstream_handshake

代理的核心函數是這幾個:網絡

downstream_read
downstream_write
upstream_read
upstream_write

代理功能實現 SubversionSession.cc 源碼總共 200多行代碼,使用 C++11 智能指針,避免了手動釋放內存。架構

##服務器程序的套路 與 helloworld 顯著不一樣的是,服務器程序一般須要在後臺運行,從終端啓動後須要脫離終端的掌控,在 posix 系統,能夠實現 daemon (守護進程)機制的程序。
一個知識咱們得知道,不管是 Linux 仍是 Windows , 終端或者命令行啓動進程本質上與 C 函數 system 是一致的,system 函數不討論內部機制如何,啓動進程後都將等待進程執行完畢,在 Windows 平臺上,subsystem 不是 console 的進程除外( GUI 程序就是如此)。併發

守護進程的第一步就是讓終端認爲 進程已經結束,在 posix 平臺,使用 fork 後結束父進程,道理就是如此,而後,爲了不子進程成爲殭屍進程,讓子進程被 init 進程收養就變得很重要,後面的就是修改 stdout stderr stdin 標準輸入輸出,重定向到特定設備文件,好比 /dev/null。框架

posix 的 fork 是一種寫時複製的進程建立機制,fork 出來的程序會從 緊接着 fork() 調用後面的代碼執行,除了 fork() 函數返回值和一些特殊的變量不同,其餘都是一致的。

在 Windows 上,可使用 Windows 服務,爲了不須要修改更多的代碼實現兼容,我在 daemonize 中,先取得 進程的 STARTUPINFO 信息,判斷 wShowWindow 是否等於 SW_HIDE 來決定是否結束到進程自身,建立新的進程,在 svnsrv 代碼中,調用 daemonize 前沒有操做目錄,能夠直接使用 GetCommandLine 或得自身的 commandline ,將此值傳遞到 CreateProcess, 調用 CreateProcess 前設置 STARTINFO wShowWindow 的值爲 SW_HIDE 便可。啓動子進程成功後 使用 ExitProcess(0) 結束自身,這樣便實現了 Windows 版的 daemon。

關於命令行的獲取, linux 使用 read /proc/pid/cmdline , Windows 中有兩種,一個是 MSVC 使用 COM WMI 類 Win32_Process 得到 cmdline, 另外一種 Mingw 使用 ZwQueryInformationProcess 讀取 特定進程 PEB ,獲得 cmdline ,這個有個限制,目標進程和自身的架構得一致,也就是 32位智能讀取 32位,64位智能讀取 64位。

因爲沒有實現 BSD 的命令行讀取,因此,沒有實現 BSD 的 daemon 功能,命令行主要用於重啓。

##結尾 svnsrv 也期待其餘用戶參與進來改進。

相關文章
相關標籤/搜索