frp 是一個可用於內網穿透的高性能的反向代理應用,支持 tcp, udp 協議,為 http 和 https 應用協議提供了額外的能力,且嘗試性支持了點對點穿透。(來自frp官方文檔: https://github.com/fatedier/frp/blob/master/README_zh.md)如果你不知道什麼是frp,你可以查看一下官方文檔了解更多信息。
本文主要是配合changelog從代碼層面來解讀這次的更新。
frp作者於今天下午發布了 0.23.0 版本,就在這時我還在寫0.22.0 的更新解讀,還沒寫完,於是現在文章 的標題變成了0.22.0 和 0.23.0 的更新解讀了。
另,0.23.0 有一個不算bug的「bug」, 嚴格來說算一個typo, 由於我第一時間編譯並測試了,因此fix了這個在dashboard會看不到任何proxy的問題。(git commit: https://github.com/fatedier/frp/commit/0bd8f9cd9bc8a63d849bf23e014acded8808a76b)
我提交了一個PR, 目前已經被合併到主線master分支了。
另外,無燈最新的frps和frpc 已經應用了此patch.
下載地址:
http://files.80x86.io/router/rom/opt/frp/
0.23.0 更新
改變:frps 中的 auth_timeout 參數被移除
增加功能:配置文件模版渲染 (配置文件支持使用系統環境變量進行模版渲染,模版格式採用 Go 的標準格式)
https://github.com/fatedier/frp/blob/655c52f9cea69a60911844e5c291c2f0eb9b2fcd/README_zh.md#%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6%E6%A8%A1%E7%89%88%E6%B8%B2%E6%9F%93
例如:
示例配置如下:# frpc.ini[common]server_addr = {{ .Envs.FRP_SERVER_ADDR }}server_port = 7000[ssh]type = tcplocal_ip = 127.0.0.1local_port = 22remote_port = {{ .Envs.FRP_SSH_REMOTE_PORT }}啟動 frpc 程序:
export FRP_SERVER_ADDR="x.x.x.x"export FRP_SSH_REMOTE_PORT="6000"./frpc -c ./frpc.ini個人看法,這種配置方式,可能更加方便golang程式設計師開發和調試吧。對於普通用戶來說,好像沒有太多用處。
frp 0.22.0 更新
https://github.com/fatedier/frp/releases/tag/v0.22.0
bug 修復咱就不說了,沒有太多影響,不是緊要的bug.
關注一下新增加的功能
Support health check for each proxy in frpc.配置文件方面,增加了結構 HealthCheckConf (https://github.com/fatedier/frp/blob/698219b6218897241aef7e148e2e611f9b8c6ccf/models/config/proxy.go#L381):
type HealthCheckConf struct {HealthCheckType string `json:"health_check_type"`HealthCheckTimeout int `json:"health_check_timeout"`HealthCheckMaxFailed int `json:"health_check_max_failed"`HealthCheckIntervalS int `json:"health_check_interval_s"`HealthCheckUrl string `json:"health_check_url"`HealthCheckAddr string `json:"-"`}根據 https://github.com/fatedier/frp/commit/c33b5152e722a50d65cd6925ba498f3016c7c19c#diff-84745d65d0dd850a2c083879213a24fb
對於tcp類型的檢測:
增加以下配置即可
# enable health check for the backend service, it support 'tcp' and 'http' now# frpc will connect local service's port to detect it's healthy statushealth_check_type = tcphealth_check_max_failed = 3health_check_interval_s = 10http類型的檢測,增加以下配置即可:
health_check_type = http# frpc will send a GET http request '/status' to local http service# http service is alive when it return 2xx http response codehealth_check_url = /statushealth_check_max_failed = 3health_check_interval_s = 10當frpc檢測到對應的health check項失敗次數達到 health_check_max_failed 次數時,
就會將此proxy從服務端(frps)移除. 在代碼中表現為:發送一個 CloseProxyMsg 到服務端.
相關代碼:
client/health/health.go
func (monitor *HealthCheckMonitor) checkWorker() {if monitor.l != nil {monitor.l.Warn("do one health check failed: %v", err)}monitor.failedTimes++if monitor.statusOK && int(monitor.failedTimes) >= monitor.maxFailedTimes && monitor.statusFailedFn != nil {if monitor.l != nil {monitor.l.Warn("health check status change to failed")}monitor.statusOK = falsemonitor.statusFailedFn()}}當檢測失敗並且實際失敗次數 monitor.failedTimes 大於等於 最大失敗次數 monitor.maxFailedTimes 時,
並且 statusFailedFn 不是 nil, 則執行 monitor.statusFailedFn()
statusFailedFn 是在func NewHealthCheckMonitor(checkType string, intervalS int, timeoutS int, maxFailedTimes int, addr string, url string,
statusNormalFn func(), statusFailedFn func()) *HealthCheckMonitor 函數中設定的。client/proxy/proxy_wrapper.go 裡的NewProxyWrapper() 會調用 health.NewHealthCheckMonitor() ,設置 statusFailedFn 為 pw.statusFailedCallback,我們看下它的代碼:
func (pw *ProxyWrapper) statusFailedCallback() {atomic.StoreUint32(&pw.health, 1)errors.PanicToError(func() {select {case pw.healthNotifyCh <- struct{}{}:default:}})pw.Info("health check failed")}然後再看proxy是如何關閉的:
client/proxy/proxy_wrapper.go 裡的 func (pw *ProxyWrapper) checkWorker()
func (pw *ProxyWrapper) checkWorker() {if pw.monitor != nil {time.Sleep(500 * time.Millisecond)}for {now := time.Now()if atomic.LoadUint32(&pw.health) == 0 {//...} else {pw.mu.Lock()if pw.Status == ProxyStatusRunning || pw.Status == ProxyStatusWaitStart {pw.handler(event.EvCloseProxy, &event.CloseProxyPayload{CloseProxyMsg: &msg.CloseProxy{ProxyName: pw.Name,},})pw.Trace("change status from [%s] to [%s]", pw.Status, ProxyStatusCheckFailed)pw.Status = ProxyStatusCheckFailed}pw.mu.Unlock()}select {case <-pw.closeCh:returncase <-time.After(statusCheckInterval):case <-pw.healthNotifyCh:}}}這裡,關閉proxy的主要代碼是:
pw.handler(event.EvCloseProxy, &event.CloseProxyPayload{CloseProxyMsg: &msg.CloseProxy{ProxyName: pw.Name,},})pw.Trace("change status from [%s] to [%s]", pw.Status, ProxyStatusCheckFailed)pw.Status = ProxyStatusCheckFailedpw.handler又是個啥呢? 這個欄位在NewProxyWrapper() 中設置,具體調用在proxy_manager.go中有:
pxy := NewProxyWrapper(cfg, pm.HandleEvent, pm.logPrefix)pm.proxies[name] = pxy然後,我們再看 pm.HandleEvent :
func (pm *ProxyManager) HandleEvent(evType event.EventType, payload interface{}) error {var m msg.Messageswitch e := payload.(type) {case *event.StartProxyPayload:m = e.NewProxyMsgcase *event.CloseProxyPayload:m = e.CloseProxyMsgdefault:return event.ErrPayloadType}err := errors.PanicToError(func() {pm.sendCh <- m})return err}對於關閉的情況,傳入的第2個參數是: event.CloseProxyPayload , 因此會發送 CloseProxyMsg 到 pm.sendCh
pm.sendCh 又是個啥呢?
client/control.go
ctl.pm = proxy.NewProxyManager(ctl.sendCh, runId)從代碼可看出,實際調用的時候是傳入 ctl.sendCh
消息是在client/control.go的 func (ctl *Control) writer() 方法中發送給frps的:
msg.WriteMsg(encWriter, m)
m, ok := <-ctl.sendChmsg.WriteMsg(encWriter, m)當然,msg.WriteMsg 又進行了一層包裝. 這裡就不進一步分析了.
add module comments for vgo
這個修改主要是為了兼容新版本的vgo (GO111MODULE).
https://github.com/fatedier/frp/commit/42ee536dae2ace3d423de6c5262819b71e82312c
主要是對main包加了注釋,類似這樣:
package mainpackage main為了獲得更好的閱讀體驗,查看完整的代碼,獲得最新的更新之後的文章內容,建議點擊 "原文連結" 查看原文。
--EOF