缓存是计算机最重要的概念
本文分析下如何获取一个RestClient
重要接口
RoundTripper
RoundTripper
是一个很简单的接口,用于执行一个简单的http请求。
// RoundTripper is an interface representing the ability to execute a
// single HTTP transaction, obtaining the Response for a given Request.
//
// A RoundTripper must be safe for concurrent use by multiple
// goroutines.
type RoundTripper interface {
// RoundTrip executes a single HTTP transaction, returning
// a Response for the provided Request.
//
// RoundTrip should not attempt to interpret the response. In
// particular, RoundTrip must return err == nil if it obtained
// a response, regardless of the response's HTTP status code.
// A non-nil err should be reserved for failure to obtain a
// response. Similarly, RoundTrip should not attempt to
// handle higher-level protocol details such as redirects,
// authentication, or cookies.
// The Request's URL and Header fields must be initialized.
RoundTrip(*Request) (*Response, error)
}
RestClient
RestClient是client-go操作kubernetes API请求的工具类:
其实现了Inteface接口(具体实现先不看,看懂接口,后续根据接口的功能去分析实现):
// Interface captures the set of operations for generically interacting with Kubernetes REST apis.
type Interface interface {
GetRateLimiter() flowcontrol.RateLimiter
Verb(verb string) *Request
Post() *Request
Put() *Request
Patch(pt types.PatchType) *Request
Get() *Request
Delete() *Request
APIVersion() schema.GroupVersion
}
如何实现构造RestClient
以 UnversionedRESTClientFor
函数为例子,看看如何构造一个RestClient
// UnversionedRESTClientFor is the same as RESTClientFor, except that it allows
// the config.Version to be empty.
func UnversionedRESTClientFor(config *Config) (*RESTClient, error) {
// 获取Transport
transport, err := TransportFor(config)
var httpClient *http.Client
if transport != http.DefaultTransport {
// Transport嵌入到http.Client
httpClient = &http.Client{Transport: transport}
if config.Timeout > 0 {
httpClient.Timeout = config.Timeout
}
}
}
Transport是RoundTripper的一个实现,可以实现连接复用。也就是不用重复建立TCP连接。
// TransportFor returns an http.RoundTripper that will provide the authentication
// or transport level security defined by the provided Config. Will return the
// default http.DefaultTransport if no special case behavior is needed.
func TransportFor(config *Config) (http.RoundTripper, error) {
// Config对象转化为transport.Config
// Config对象是apiServer总配置的子字段,transport.Config 属于Client go
cfg, err := config.TransportConfig() // 1
if err != nil {
return nil, err
}
return transport.New(cfg) // 2
}
- 1
config.TransportConfig()
转化的实现基本就是相同语义的字段的依次拷贝
// TransportConfig converts a client config to an appropriate transport config.
func (c *Config) TransportConfig() (*transport.Config, error) {
conf := &transport.Config{
UserAgent: c.UserAgent,
Transport: c.Transport,
// ...
TLS: transport.TLSConfig{
Insecure: c.Insecure,
ServerName: c.ServerName,
CAFile: c.CAFile,
CAData: c.CAData,
CertFile: c.CertFile,
CertData: c.CertData,
KeyFile: c.KeyFile,
KeyData: c.KeyData,
NextProtos: c.NextProtos,
},
Username: c.Username,
Password: c.Password,
// ...
}
// ...
return conf, nil
}
- 2
// New returns an http.RoundTripper that will provide the authentication
// or transport level security defined by the provided Config.
func New(config *Config) (http.RoundTripper, error) {
// Set transport level security
if config.Transport != nil && (config.HasCA() || config.HasCertAuth() || config.HasCertCallback() || config.TLS.Insecure) {
return nil, fmt.Errorf("using a custom transport with TLS certificate options or the insecure flag is not allowed")
}
var (
rt http.RoundTripper
err error
)
if config.Transport != nil {
rt = config.Transport // config.Transport是个http.RoundTripper类型的字段
} else {
rt, err = tlsCache.get(config) // 2-1 根据配置获取Transport类型http.RoundTripper
if err != nil {
return nil, err
}
}
return HTTPWrappersForConfig(config, rt)
}
tlsTransportCache
- 2-1
这里引入一个新的对象tlsTransportCache
, 这个对象是一个根据配置缓存Transport的缓存。
tlsTransportCache
对象定义比较简单:
// TlsTransportCache caches TLS http.RoundTrippers different configurations. The
// same RoundTripper will be returned for configs with identical TLS options If
// the config has no custom TLS options, http.DefaultTransport is returned.
type tlsTransportCache struct {
mu sync.Mutex // 读写锁,用于并发读写
transports map[tlsCacheKey]*http.Transport // 使用map进行存储
}
根据配置获取Transport类型http.RoundTripper
func (c *tlsTransportCache) get(config *Config) (http.RoundTripper, error) {
key, canCache, err := tlsConfigKey(config) // 获取map key
if err != nil {
return nil, err
}
// 如果Config是可缓存的,则查找成功则返回
if canCache {
// Ensure we only create a single transport for the given TLS options
c.mu.Lock()
defer c.mu.Unlock()
// See if we already have a custom transport for this config
if t, ok := c.transports[key]; ok {
return t, nil
}
}
// Get the TLS options for this client config
tlsConfig, err := TLSConfigFor(config) // 2-1-1 获取TLS连接配置对象 tls.Config (go SDK)
if err != nil {
return nil, err
}
// The options didn't require a custom TLS config
if tlsConfig == nil && config.Dial == nil && config.Proxy == nil {
// 如果没有要求tls安全连接,直接返回默认的http.Transport
return http.DefaultTransport, nil
}
// ...
proxy := http.ProxyFromEnvironment
if config.Proxy != nil {
proxy = config.Proxy
}
// 构造一个新的http.Transport, 缓存起来
transport := utilnet.SetTransportDefaults(&http.Transport{
Proxy: proxy,
TLSHandshakeTimeout: 10 * time.Second,
TLSClientConfig: tlsConfig,
MaxIdleConnsPerHost: idleConnsPerHost,
DialContext: dial,
DisableCompression: config.DisableCompression,
})
if canCache {
// Cache a single transport for these options
c.transports[key] = transport
}
return transport, nil
}
tls.Config
tls.Config is a Config structure is used to configure a TLS client or server.
- 2-1-1 获取TLS连接配置对象 tls.Config
// TLSConfigFor returns a tls.Config that will provide the transport level security defined
// by the provided Config. Will return nil if no transport level security is requested.
func TLSConfigFor(c *Config) (*tls.Config, error) {
// 检查部分,检查tls配置需要的字段是否都存在
if !(c.HasCA() || c.HasCertAuth() || c.HasCertCallback() || c.TLS.Insecure || len(c.TLS.ServerName) > 0 || len(c.TLS.NextProtos) > 0) {
return nil, nil
}
if c.HasCA() && c.TLS.Insecure {
return nil, fmt.Errorf("specifying a root certificates file with the insecure flag is not allowed")
}
// 加载TLS证书文件
if err := loadTLSFiles(c); err != nil {
return nil, err
}
tlsConfig := &tls.Config{
// Can't use SSLv3 because of POODLE and BEAST
// Can't use TLSv1.0 because of POODLE and BEAST using CBC cipher
// Can't use TLSv1.1 because of RC4 cipher usage
MinVersion: tls.VersionTLS12, // TLS v1.2
InsecureSkipVerify: c.TLS.Insecure, // 是否支持自定义证书
ServerName: c.TLS.ServerName, // SNI Server name
NextProtos: c.TLS.NextProtos, // 偏好协议,从大到小,比如 [http1.1,http2]
}
if c.HasCA() {
tlsConfig.RootCAs = rootCertPool(c.TLS.CAData) // 客户端认证Server证书的CA文件
}
var staticCert *tls.Certificate
// Treat cert as static if either key or cert was data, not a file
if c.HasCertAuth() && !c.TLS.ReloadTLSFiles {
// If key/cert were provided, verify them before setting up
// tlsConfig.GetClientCertificate.
cert, err := tls.X509KeyPair(c.TLS.CertData, c.TLS.KeyData)
if err != nil {
return nil, err
}
staticCert = &cert
}
var dynamicCertLoader func() (*tls.Certificate, error)
if c.TLS.ReloadTLSFiles {
dynamicCertLoader = cachingCertificateLoader(c.TLS.CertFile, c.TLS.KeyFile)
}
if c.HasCertAuth() || c.HasCertCallback() {
// GetClientCertificate 是服务端认证客户端SSL证书时,调用的函数
tlsConfig.GetClientCertificate = func(*tls.CertificateRequestInfo) (*tls.Certificate, error) {
// Note: static key/cert data always take precedence over cert
// callback.
if staticCert != nil {
return staticCert, nil
}
// key/cert files lead to ReloadTLSFiles being set - takes precedence over cert callback
if dynamicCertLoader != nil {
return dynamicCertLoader()
}
// 是否配置有获取证书的回调函数
if c.HasCertCallback() {
// 调用获取证书的回调函数
cert, err := c.TLS.GetCert()
if err != nil {
return nil, err
}
// GetCert may return empty value, meaning no cert.
if cert != nil {
return cert, nil
}
}
// Both c.TLS.CertData/KeyData were unset and GetCert didn't return
// anything. Return an empty tls.Certificate, no client cert will
// be sent to the server.
return &tls.Certificate{}, nil
}
}
return tlsConfig, nil
}
- 加载TLS证书文件
// loadTLSFiles copies the data from the CertFile, KeyFile, and CAFile fields into the CertData,
// KeyData, and CAFile fields, or returns an error. If no error is returned, all three fields are
// either populated or were empty to start.
func loadTLSFiles(c *Config) error {
var err error
// 服务端CA证书
c.TLS.CAData, err = dataFromSliceOrFile(c.TLS.CAData, c.TLS.CAFile)
if err != nil {
return err
}
// Check that we are purely loading from files
if len(c.TLS.CertFile) > 0 && len(c.TLS.CertData) == 0 && len(c.TLS.KeyFile) > 0 && len(c.TLS.KeyData) == 0 {
c.TLS.ReloadTLSFiles = true
}
// CertData & CertFile 是服务端证书, 但是一个是字节格式的,一个是文件路径格式的。
// 字节格式优先
c.TLS.CertData, err = dataFromSliceOrFile(c.TLS.CertData, c.TLS.CertFile)
if err != nil {
return err
}
// KeyData & KeyFile 是客户端私钥,格式同理
c.TLS.KeyData, err = dataFromSliceOrFile(c.TLS.KeyData, c.TLS.KeyFile)
if err != nil {
return err
}
return nil
}
// 这个函数就是根据路径读取证书内容的helper函数
// dataFromSliceOrFile returns data from the slice (if non-empty), or from the file,
// or an error if an error occurred reading the file
func dataFromSliceOrFile(data []byte, file string) ([]byte, error) {
if len(data) > 0 {
return data, nil
}
if len(file) > 0 {
fileData, err := ioutil.ReadFile(file)
if err != nil {
return []byte{}, err
}
return fileData, nil
}
return nil, nil
}