kubernetes源碼解析---- apiserver路由構建解析(1)


kubernetes源碼解析---- apiserver路由構建解析(1)

apiserver作為k8s集群的唯一入口,內部主要實現了兩個功能,一個是請求的路由和處理,簡單說就是監聽一個端口,把接收到的請求正確地轉到相應的處理邏輯上,另一個功能就是認證及權限控制。本文主要對apiserver的路由構建過程進行解析。
apiserver使用go-restful來構建REST-style Web服務,所以我們先來了解一下這個包的相關內容,以便更好地理解apiserver的源碼。

(kubernetes代碼版本:1.3.6 Commit id:ed3a29bd6aeb)

go-restful

Package restful, a lean package for creating REST-style WebServices without magic.

go-restful是用於構建REST-style Web服務的一個golang包,它的來歷參見go-restful-api-design,大體意思就是一個javaer在golang中沒找到順手的REST-based服務構建包,所以就按照他在Java里常用的JAX-RS的設計,在golang中造了一個輪子。

go-restful里的幾個組件和特性####

  1. Route
    路由包含兩種,一種是標准JSR311接口規范的實現RouterJSR311,一種是快速路由CurlyRouter。CurlyRouter支持正則表達式和動態參數,相比RouterJSR311更加輕量級,apiserver中使用的就是這種路由。
    一條Route的設定包含:請求方法(Http Method),請求路徑(URL Path),處理方法以及可選的接受內容類型(Content-Type),響應內容類型(Accept)等。

  2. WebService
    WebService邏輯上是Route的集合,功能上主要是為一組Route統一設置包括root path,請求響應的數據類型等一些通用的屬性。需要注意的是,WebService必須加入到Container中才能生效。

	func InstallVersionHandler(mux Mux, container *restful.Container) {
		// Set up a service to return the git code version.
		versionWS := new(restful.WebService)
	
		versionWS.Path("/version")
		versionWS.Doc("git code version from which this is built")
		versionWS.Route(
			versionWS.GET("/").To(handleVersion).
				Doc("get the code version").
				Operation("getCodeVersion").
				Produces(restful.MIME_JSON).
				Consumes(restful.MIME_JSON).
				Writes(version.Info{}))
		container.Add(versionWS)
	}
  1. Container
    Container邏輯上是WebService的集合,功能上可以實現多終端的效果。例如,下面代碼中創建了兩個Container,分別在不同的port上提供服務。
	func main() {
		ws := new(restful.WebService)
		ws.Route(ws.GET("/hello").To(hello))
	 	// ws被添加到默認的container restful.DefaultContainer中
		restful.Add(ws)
		go func() {
	      // restful.DefaultContainer 監聽在端口8080上
			http.ListenAndServe(":8080", nil)
		}()
	
		container2 := restful.NewContainer()
		ws2 := new(restful.WebService)
		ws2.Route(ws2.GET("/hello").To(hello2))
	 	// ws2被添加到container container2中
		container2.Add(ws2)
	 	// container2中監聽在端口8081上
		server := &http.Server{Addr: ":8081", Handler: container2}
		log.Fatal(server.ListenAndServe())
	}

	func hello(req *restful.Request, resp *restful.Response) {
		io.WriteString(resp, "default world")
	}
	
	func hello2(req *restful.Request, resp *restful.Response) {
		io.WriteString(resp, "second world")
	}
  1. Filter
    Filter用於動態的攔截請求和響應,類似於放置在相應組件前的鈎子,在相應組件功能運行前捕獲請求或者響應,主要用於記錄log,驗證,重定向等功能。go-restful中有三種類型的Filter:

    • Container Filter
      運行在Container中所有的WebService執行之前。
    // install a (global) filter for the default container (processed before any webservice)
    restful.Filter(globalLogging)
    
    • WebService Filter
      運行在WebService中所有的Route執行之前。
    // install a webservice filter (processed before any route)
    ws.Filter(webserviceLogging).Filter(measureTime)
    
    • Route Filter
      運行在調用Route綁定的方法之前。
    // install 2 chained route filters (processed before calling findUser)
    ws.Route(ws.GET("/{user-id}").Filter(routeLogging).Filter(NewCountFilter().routeCounter).To(findUser))
    

使用樣例####

下面代碼是官方提供的例子。

package main

import (
	"github.com/emicklei/go-restful"
	"log"
	"net/http"
)

type User struct {
	Id, Name string
}

type UserResource struct {
	// normally one would use DAO (data access object)
	users map[string]User
}

func (u UserResource) Register(container *restful.Container) {
  	// 創建新的WebService
	ws := new(restful.WebService)
  
  	// 設定WebService對應的路徑("/users")和支持的MIME類型(restful.MIME_XML/ restful.MIME_JSON)
	ws.
		Path("/users").
		Consumes(restful.MIME_XML, restful.MIME_JSON).
		Produces(restful.MIME_JSON, restful.MIME_XML) // you can specify this per route as well

  	// 添加路由: GET /{user-id} --> u.findUser
	ws.Route(ws.GET("/{user-id}").To(u.findUser))
  
  	// 添加路由: POST / --> u.updateUser
	ws.Route(ws.POST("").To(u.updateUser))
  
  	// 添加路由: PUT /{user-id} --> u.createUser
	ws.Route(ws.PUT("/{user-id}").To(u.createUser))
  
  	// 添加路由: DELETE /{user-id} --> u.removeUser
	ws.Route(ws.DELETE("/{user-id}").To(u.removeUser))

  	// 將初始化好的WebService添加到Container中
	container.Add(ws)
}

// GET http://localhost:8080/users/1
//
func (u UserResource) findUser(request *restful.Request, response *restful.Response) {
	id := request.PathParameter("user-id")
	usr := u.users[id]
	if len(usr.Id) == 0 {
		response.AddHeader("Content-Type", "text/plain")
		response.WriteErrorString(http.StatusNotFound, "User could not be found.")
	} else {
		response.WriteEntity(usr)
	}
}

// POST http://localhost:8080/users
// <User><Id>1</Id><Name>Melissa Raspberry</Name></User>
//
func (u *UserResource) updateUser(request *restful.Request, response *restful.Response) {
	usr := new(User)
	err := request.ReadEntity(&usr)
	if err == nil {
		u.users[usr.Id] = *usr
		response.WriteEntity(usr)
	} else {
		response.AddHeader("Content-Type", "text/plain")
		response.WriteErrorString(http.StatusInternalServerError, err.Error())
	}
}

// PUT http://localhost:8080/users/1
// <User><Id>1</Id><Name>Melissa</Name></User>
//
func (u *UserResource) createUser(request *restful.Request, response *restful.Response) {
	usr := User{Id: request.PathParameter("user-id")}
	err := request.ReadEntity(&usr)
	if err == nil {
		u.users[usr.Id] = usr
		response.WriteHeader(http.StatusCreated)
		response.WriteEntity(usr)
	} else {
		response.AddHeader("Content-Type", "text/plain")
		response.WriteErrorString(http.StatusInternalServerError, err.Error())
	}
}

// DELETE http://localhost:8080/users/1
//
func (u *UserResource) removeUser(request *restful.Request, response *restful.Response) {
	id := request.PathParameter("user-id")
	delete(u.users, id)
}

func main() {
  	// 創建一個空的Container
	wsContainer := restful.NewContainer()
  
    // 設定路由為CurlyRouter
	wsContainer.Router(restful.CurlyRouter{})
  
  	// 創建自定義的Resource Handle(此處為UserResource)
	u := UserResource{map[string]User{}}
  
  	// 創建WebService,並將WebService加入到Container中
	u.Register(wsContainer)

	log.Printf("start listening on localhost:8080")
	server := &http.Server{Addr: ":8080", Handler: wsContainer}
  	
  	// 啟動服務
	log.Fatal(server.ListenAndServe())
}

上面的示例構建Restful服務,分為幾個步驟,apiserver中也是類似的:

  1. 創建Container。
  2. 創建自定義的Resource Handle,實現Resource相關的處理方法。
  3. 創建對應於Resource的WebService,在WebService中添加相應Route,並將WebService加入到Container中。
  4. 啟動監聽服務。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM