源码地址:https://github.com/polarismesh/polaris-sidecar
polaris-sidecar 作为 polaris 的本地边车代理,提供两个可选功能模式:
- 本地 DNS:使用 DNS 解析的方式访问北极星上的服务
- 服务网格:通过劫持流量的方式实现服务发现和治理,开发侵入性低
解释器接口
代码语言:javascript复制type NamingResolver interface {
// Name will return the name to resolver
Name() string
// Initialize will init the resolver on startup
Initialize(c *ConfigEntry) error
// Start the plugin runnable
Start(context.Context)
// Destroy will destroy the resolver on shutdown
Destroy()
// ServeDNS is like dns.Handler except ServeDNS may return an response or nil
ServeDNS(context.Context, dns.Question) *dns.Msg
}
dns和mesh两种模式都分别实现了这几个接口
初始化
代码语言:javascript复制func newAgent(configFile string, bootConfig *BootConfig) (*Agent, error) {
var err error
polarisAgent := &Agent{}
polarisAgent.config, err = parseYamlConfig(configFile, bootConfig)
if nil != err {
log.Errorf("[agent] fail to parse sidecar config, err: %v", err)
return nil, err
}
nameservers, searchNames := parseResolvConf(polarisAgent.config.bindLocalhost())
log.Infof("[agent] finished to parse /etc/resolv.conf, nameservers %s, search %s", nameservers, searchNames)
if len(polarisAgent.config.Recurse.NameServers) == 0 {
polarisAgent.config.Recurse.NameServers = nameservers
}
log.Infof("[agent] finished to parse sidecar config, current active config is %s", *polarisAgent.config)
// 初始化日志打印
err = log.Configure(polarisAgent.config.Logger)
log.Infof("[agent] success to init log config")
if err != nil {
return nil, err
}
for _, resolverCfg := range polarisAgent.config.Resolvers {
if !resolverCfg.Enable {
log.Infof("[agent] resolver %s is not enabled", resolverCfg.Name)
continue
}
name := resolverCfg.Name
handler := resolver.NameResolver(name)
if nil == handler {
log.Errorf("[agent] resolver %s is not found", resolverCfg.Name)
return nil, fmt.Errorf("fail to lookup resolver %s, consider it's not registered", name)
}
err = handler.Initialize(resolverCfg)
if nil != err {
for _, initHandler := range polarisAgent.resolvers {
initHandler.Destroy()
}
log.Errorf("[agent] fail to init resolver %s, err: %v", resolverCfg.Name, err)
return nil, err
}
log.Infof("[agent] finished to init resolver %s", resolverCfg.Name)
polarisAgent.resolvers = append(polarisAgent.resolvers, handler)
}
recurseAddresses := make([]string, 0, len(polarisAgent.config.Recurse.NameServers))
for _, nameserver := range polarisAgent.config.Recurse.NameServers {
recurseAddresses = append(recurseAddresses, fmt.Sprintf("%s:53", nameserver))
}
polarisAgent.udpServer = &dns.Server{
Addr: polarisAgent.config.Bind ":" strconv.Itoa(polarisAgent.config.Port), Net: "udp"}
polarisAgent.udpServer.Handler = &dnsHandler{
protocol: "udp",
resolvers: polarisAgent.resolvers,
searchNames: searchNames,
recursorTimeout: time.Duration(polarisAgent.config.Recurse.TimeoutSec) * time.Second,
recursors: recurseAddresses,
recurseEnable: polarisAgent.config.Recurse.Enable,
}
polarisAgent.tcpServer = &dns.Server{
Addr: polarisAgent.config.Bind ":" strconv.Itoa(polarisAgent.config.Port), Net: "tcp"}
polarisAgent.tcpServer.Handler = &dnsHandler{
protocol: "tcp",
resolvers: polarisAgent.resolvers,
searchNames: searchNames,
recursorTimeout: time.Duration(polarisAgent.config.Recurse.TimeoutSec) * time.Second,
recursors: recurseAddresses,
recurseEnable: polarisAgent.config.Recurse.Enable,
}
return polarisAgent, nil
}
上面代码主要逻辑如下:
- 解析配置文件初始化给polarisAgent的config对象
- 解析/etc/resolve.conf配置文件获取本地dns的nameservers等信息,用来初始化polarisAgent对象相关属性
- 根据config的解释器配置,初始化解释器handler,执行handler的初始化方法
- 初始化了两个dns服务器,一个是tcp协议的,一个是udp协议的
启动流程
代码语言:javascript复制func (p *Agent) Start(ctx context.Context) error {
for _, handler := range p.resolvers {
handler.Start(ctx)
log.Infof("[agent] success to start resolver %s", handler.Name())
}
errChan := make(chan error)
go func() {
errChan <- p.tcpServer.ListenAndServe()
}()
go func() {
errChan <- p.udpServer.ListenAndServe()
}()
var recvErrCounts int
defer func() {
for _, handler := range p.resolvers {
handler.Destroy()
}
}()
for {
select {
case err := <-errChan:
if nil != err {
return err
}
recvErrCounts
if recvErrCounts == 2 {
return nil
}
case <-ctx.Done():
return nil
}
}
}
主要逻辑如下:
- 执行每个解释器的start方法:dns和mesh两种
- 启动dns服务器
下面来看看两种情况的启动流程
本地DNS
略
服务网格
代码语言:javascript复制func (r *resolverMesh) Start(ctx context.Context) {
interval := time.Duration(r.config.ReloadIntervalSec) * time.Second
go func() {
ticker := time.NewTicker(interval)
defer ticker.Stop()
var currentServices map[string]struct{}
for {
select {
case <-ticker.C:
services, err := r.registry.GetCurrentNsService()
if err != nil {
log.Errorf("[mesh] error to get services, err: %v", err)
continue
}
if ifServiceListChanged(currentServices, services) {
log.Infof("[Mesh] services lookup are %v", services)
r.localDNSServer.UpdateLookupTable(services, r.config.DNSAnswerIp)
currentServices = services
}
case <-ctx.Done():
return
}
}
}()
}
主要流程如下:
- 启动一个协程
- 轮询操作:获取当前名字空间service,更新本地dns缓存表