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里的幾個組件和特性####
-
Route
路由包含兩種,一種是標准JSR311接口規范的實現RouterJSR311,一種是快速路由CurlyRouter。CurlyRouter支持正則表達式和動態參數,相比RouterJSR311更加輕量級,apiserver中使用的就是這種路由。
一條Route的設定包含:請求方法(Http Method),請求路徑(URL Path),處理方法以及可選的接受內容類型(Content-Type),響應內容類型(Accept)等。 -
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)
}
- 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")
}
-
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)) - Container Filter
使用樣例####
下面代碼是官方提供的例子。
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中也是類似的:
- 創建Container。
- 創建自定義的Resource Handle,實現Resource相關的處理方法。
- 創建對應於Resource的WebService,在WebService中添加相應Route,並將WebService加入到Container中。
- 啟動監聽服務。
