对象存储 Mino 源码---Main 和 server 启动模式

1 篇文章 0 订阅
1 篇文章 0 订阅

对象存储 Mino 源码— 1.server 命令启动

Minio服务中间件

Minio是目前githug上star最多的Object Storage框架,这里Object Storage对象存储,minio可以用来搭建分布式存储服务,企业级开源对象存储。
大部分云厂商都有对象存储服务。

Minio优点

  • 对象存储的主动、多站点复制是任务关键型生产环境的关键要求。MinIO是目前唯一提供它的供应商。MinIO 提供存储桶级粒度,并支持同步和近同步复制,具体取决于架构选择和数据变化率。
  • 在对象存储领域,需要强大的加密才能在谈判桌上占有一席之地。MinIO 通过最高级别的加密以及广泛的优化提供更多功能,几乎消除了通常与存储加密操作相关的开销。
  • 自动化数据管理界面

Minio的介绍说明

  • Minio是Apache License v2.0下发布的对象存储服务器。它与Amazon S3云存储服务兼容。它最适合存储非结构化数据,如照片,视频,日志文件,备份和容器/ VM映像。对象的大小可以从几KB到最大5TB。

  • Minio服务器足够轻,可以与应用程序堆栈捆绑在一起,类似于NodeJS,Redis和MySQL。

Minio资料

官网地址:https://minio.org.cn/
github地址: https://github.com/minio/minio

Minio 环境搭建

docker 启动

docker pull minio/minio
docker images
mkdir -p /home/minio/config
mkdir -p /home/minio/data
# run docker
docker run -p 9000:9000 -p 9090:9090 \
     --net=host \
     --name minio \
     -d --restart=always \
     -e "MINIO_ACCESS_KEY=minioadmin" \
     -e "MINIO_SECRET_KEY=minioadmin" \
     -v /home/minio/data:/data \
     -v /home/minio/config:/root/.minio \
     minio/minio server \
     /data --console-address ":9090" -address ":9000"

docker run -p 9000:9000 -p 9090:9090 \
     --name minio \
     -d --restart=always \
     -e "MINIO_ACCESS_KEY=minioadmin" \
     -e "MINIO_SECRET_KEY=minioadmin" \
     -v /Users/linglingdai/opt/minio/data:/data \
     -v /Users/linglingdai/opt/minio/config:/root/.minio \
     minio/minio server \
     /data --console-address ":9090" -address ":9000"     

Minio 源码

Main 函数

采用自行封装的 cmd 功能,注入serverCmd和gatewayCmd,给每个命令挂载对应 Action,实际的调用函数

minio/cmd/main.go
 
func newApp(name string) *cli.App {
 // Collection of minio commands currently supported are.
 commands := []cli.Command{}
 //省略无关代码
 // registerCommand registers a cli command.
 registerCommand := func(command cli.Command) {
 commands = append(commands, command)
        commandsTree.Insert(command.Name)
    }
 //省略无关代码
 // Register all commands.
  registerCommand(serverCmd)
  // ->server_main.go::serverMain
 registerCommand(gatewayCmd)
  // ->server_main.go::gatewayMain
 // Set up app.
 cli.HelpFlag = cli.BoolFlag{
        Name:  "help, h",
        Usage: "show help",
    }
 app := cli.NewApp()
 app.Name = name
 //省略无关代码
 app.Commands = commands
 app.CustomAppHelpTemplate = minioHelpTemplate
 //省略无关代码
 return app
}
// Main main for minio server.
func Main(args []string) {
 // Set the minio app name.
 appName := filepath.Base(args[0])
 
 // Run the app - exit on error.
 if err := newApp(appName).Run(args); err != nil {
        os.Exit(1)
    }
}

模式

img

裸机(server)模式:minio存储系统的后端可以是磁盘,根据挂载的磁盘数量来决定是单机模式还是纠偏码模式。

网关(gateway)模式:minio存储系统后端也可以作为云网关,对接第三方的NAS系统、分布式文件系统或公有云存储资源,并为业务系统转换提供标准的对象访问接口。根据后面的命令参数来决定是使用什么代理模式进行,目前MinIO支持Google 云存储、HDFS、阿里巴巴OSS、亚马逊S3, 微软Azure Blob 存储等第三方存储资源。

从以上架构可以看出,从终端发起的S3 API都是通过网关这一层的 S3 API Router提供的,通过S3 API Router统一了后端的API,也就是提供了统一的S3 兼容API。

S3 API Router的具体实现又是通过ObjectLayer这一层实现的,ObjectLayer是个接口,它定义了MinIO对象存储服务针对对象操作的所有API。ObjectLayer接口不止每个具体的网关会实现(比如GCS),MinIO本身作为存储服务器也会实现,这样对于对象的操作通过ObjectLayer接口就统一了(面向接口编程),具体的实现可以定义来实现不同的功能,比如MinIO 单点存储、分布式存储(纠察模式)、各个具体的网关存储,都是接口ObjectLayer的具体实现。

当每个具体的网关( 比如GCS)实现了ObjectLayer接口后,它对于具体后端存储的操作就是通过各个第三方存储SDK实现了。以GCS网关为例,终端通过S3 APi获取存储桶列表,那么最终的实现会通过GCS SDK访问GCS服务获取存储桶列表,然后包装成S3标准的结构返回给终端。

server初始化重点步骤:

MinIO server启动有两种模式,一个是单点模式,一种是纠察码模式,其中单点模式就是只传了一个endpoint给minio,使用的是文件系统的操作方式,更详细的可以研究FSObjects的源代码实现。

minio server PATH

1. 加入DNS的Cache的停止的hook
2. 注册系统关闭的信号量
3. 设置分配多少字节进行内存采样的阀植,关闭 mutex prof,关闭统计阻塞的event统计
4. 初始化全局console日志,并作为target加入
5. 处理命令行参数
6. 处理环境变量
7. 设置分布式节点名称
8. 处理所有的帮助信息
9. 初始化以下子系统
   1.  healState 
   2.  notification 
   3.  BucketMetadata 
   4.  BucketMonitor 
   5.  ConfigSys 
   6.  IAM 
   7.  Policy
   8.  Lifecycle
   9.  BucketSSEConfig
   10. BucketObjectLock
   11. BucketQuota
   12. BucketVersioning
   13. BucketTarget
10. https 启用后的证书检查
11. 升级检查
12. 根据操作系统进程的最大内存,fd,线程数设置
13. 配置路由
    1.  分布式模式下,注册以下路由
        1.  StorageREST
        2.  PeerREST
        3.  BootstrapREST
        4.  LockREST
    2. STS 相关路由
    3. ADMIN 相关路由
    4. HealthCheck 相关路由
    5. Metrics 相关路由
    6. Web 相关路由
    7. API 相关路由
14. 注册以下hook
	// 处理未初始化object 层的重定向
	setRedirectHandler,
	// 设置 请求头 x-amz-request-id 字段.
	addCustomHeaders,
	// 添加头部安全字段例如 Content-Security-Policy.
	addSecurityHeaders,
	// 转发path style 请求到真正的主机上
	setBucketForwardingHandler,
	// 验证请求
	setRequestValidityHandler,
	// 统计
	setHTTPStatsHandler,
	// 限制请求大小
	setRequestSizeLimitHandler,
	// 限制请求头大小
	setRequestHeaderSizeLimitHandler,
	// 添加 'crossdomain.xml' 策略来处理 legacy flash clients.
	setCrossDomainPolicy,
	// 重定向一些预定义的浏览器请求到静态路由上
	setBrowserRedirectHandler,
	// 如果请求是restricted buckets 则验证
	setReservedBucketHandler,
	// 为所有浏览器请求添加cache
	setBrowserCacheControlHandler,
	// 验证所有请求流量,以便有有效期的标头
	setTimeValidityHandler,
	// 验证所有的url,以便使客户端收到不受支持的url的报错
	setIgnoreResourcesHandler,
	// 验证授权
	setAuthHandler,
	// 一些针对ssl特殊的处理
	setSSETLSHandler,
	// 筛选http头,这些标记作为meta信息保留,仅供内部使用
	filterReservedMetadata,
15. 注册最外层hook
    1.  criticalErrorHandler(处理panic)
    2.  corsHandler(处理CORS)
16. 如果是纠偏码模式
    1.  验证配置
17. 初始化 Object 层
18. 设置 deploment 的id
19. 如果是纠偏码模式
    1.  初始化自动 Heal
    2.  初始化后台 Replication
    3.  初始化后台 Transition
20. 初始化 DataCrawler
21. 初始化server
22. 如果启用缓存,初始化缓存层
23. 打印启动信息
24. 验证认证信息是否是默认认证信息,如果是,提示修改
# 博客内容: https://fengmeng.xyz/p/go-minio/
# code 注解基于新的 version: RELEASE.2023-09-04T19-57-37Z-2-g812f5a02d,内容有出入

serverm_main 主函数分析

// serverMain handler called for 'minio server' command.
func serverMain(ctx *cli.Context) {
  //注册系统关闭信号量
	signal.Notify(globalOSSignalCh, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT)

	go handleSignals()
	// 设置性能分析
	setDefaultProfilerRates()

	// Handle all server environment vars.
  // 处理环境变量设置 各类参数,serverURL,globalFSOSync,rootDiskSize,domains,domainIPs
	serverHandleEnvVars()

	// Handle all server command args.
  // 处理命令行参数
  // 跟踪serverHandleCmdArgs的执行->ctx 中携带的参数,并设置ttl定期刷新 DNS 缓存
	bootstrapTrace("serverHandleCmdArgs", func() {
    // 并生成Endpoint结构
		serverHandleCmdArgs(ctx)
	})

	// Initialize globalConsoleSys system
	bootstrapTrace("newConsoleLogger", func() {
		globalConsoleSys = NewConsoleLogger(GlobalContext)
		logger.AddSystemTarget(GlobalContext, globalConsoleSys)

		// Set node name, only set for distributed setup.
    // 设置分布式节点名称, globalLocalNodeName在serverHandleCmdArgs(ctx) 函数中处理了
		globalConsoleSys.SetNodeName(globalLocalNodeName)
	})

	// Perform any self-tests
  //自检查
	bootstrapTrace("selftests", func() {
    // 执行自检以确保 bitrot算法计算正确的校验和
		bitrotSelfTest()
    // 执行自检以确保纠察算法计算预期的纠察码
		erasureSelfTest()
    // 执行自检以确保压缩算法完成往返
		compressSelfTest()
	})

	// Initialize KMS configuration
	bootstrapTrace("handleKMSConfig", handleKMSConfig)

	// Initialize all help
  // 处理所有的帮助信息
	bootstrapTrace("initHelp", initHelp)

	// Initialize all sub-systems
	bootstrapTrace("initAllSubsystems", func() {
    // 初始化子系统
		initAllSubsystems(GlobalContext)
	})

// Is distributed setup, error out if no certificates are found for HTTPS endpoints.
// 如果是分布式设置,如果没有找到 HTTPS 端点的证书,则产生错误。
if globalIsDistErasure {
    // 如果全局设置为分布式 Erasure 模式

    // 如果全局端点要求使用 HTTPS 但未启用 TLS 加密
    if globalEndpoints.HTTPS() && !globalIsTLS {
        // 产生一个致命错误,指示未找到证书并且无法启动服务器。
        logger.Fatal(config.ErrNoCertsAndHTTPSEndpoints(nil), "Unable to start the server")
    }

    // 如果全局端点要求使用 HTTP 但启用了 TLS 加密
    if !globalEndpoints.HTTPS() && globalIsTLS {
        // 产生一个致命错误,指示找到了证书但无法启动服务器。
        logger.Fatal(config.ErrCertsAndHTTPEndpoints(nil), "Unable to start the server")
    }
}


// Check for updates in non-blocking manner.
// 在非阻塞方式下检查更新。

go func() {
    // 如果全局 CLI 上下文不是安静模式且未禁用就地更新
    if !globalCLIContext.Quiet && !globalInplaceUpdateDisabled {
        // 检查来自 dl.min.io 的新更新。
        bootstrapTrace("checkUpdate", func() {
            // 使用 getMinioMode() 获取 Minio 的运行模式,并检查是否有新的更新。
            checkUpdate(getMinioMode())
        })
    }
}()


	// Set system resources to maximum.
  // 设置最大系统资源 根据操作系统进程的最大内存,fd,线程数设置
	bootstrapTrace("setMaxResources", func() {
		_ = setMaxResources()
	})

	// Verify kernel release and version.
  // 检查 kenl 版本
	if oldLinux() {
		logger.Info(color.RedBold("WARNING: Detected Linux kernel version older than 4.0.0 release, there are some known potential performance problems with this kernel version. MinIO recommends a minimum of 4.x.x linux kernel version for best performance"))
	}

maxProcs := runtime.GOMAXPROCS(0)
// 获取当前系统的 GOMAXPROCS 数量,并将其赋值给 maxProcs 变量。
cpuProcs := runtime.NumCPU()
// 获取当前系统的 CPU 核心数量,并将其赋值给 cpuProcs 变量。

if maxProcs < cpuProcs {
    // 如果当前的 GOMAXPROCS 数量小于 CPU 核心数量,发出警告信息。
    logger.Info(color.RedBoldf("WARNING: Detected GOMAXPROCS(%d) < NumCPU(%d), please make sure to provide all PROCS to MinIO for optimal performance", maxProcs, cpuProcs))
}
// 此段代码用于检查当前的 GOMAXPROCS 数量是否小于 CPU 核心数量,如果是,会发出警告信息。
// 这个警告信息提醒用户可以通过适当增加 GOMAXPROCS 数量来提高 MinIO 服务器的性能。

var getCert certs.GetCertificateFunc
// 声明一个名为 getCert 的变量,其类型为 certs.GetCertificateFunc。

if globalTLSCerts != nil {
    // 如果全局变量 globalTLSCerts 不为空(即已经配置了 TLS 证书),
    // 则将 globalTLSCerts.GetCertificate 赋值给 getCert 变量。
    getCert = globalTLSCerts.GetCertificate
}


// Configure server.
bootstrapTrace("configureServer", func() {
    // 配置服务器处理程序,传入之前处理好的 globalEndpoints
    handler, err := configureServerHandler(globalEndpoints)
    if err != nil {
        logger.Fatal(config.ErrUnexpectedError(err), "Unable to configure one of server's RPC services")
    }

    // 创建 HTTP 服务器。
    httpServer := xhttp.NewServer(getServerListenAddrs()).
 				 // 设置错误处理器,跨域处理器
        UseHandler(setCriticalErrorHandler(corsHandler(handler))).
        UseTLSConfig(newTLSConfig(getCert)).
        UseShutdownTimeout(ctx.Duration("shutdown-timeout")).
        UseIdleTimeout(ctx.Duration("idle-timeout")).
        UseReadHeaderTimeout(ctx.Duration("read-header-timeout")).
        UseBaseContext(GlobalContext).
        UseCustomLogger(log.New(io.Discard, "", 0)). // 关闭 Go stdlib 的随机日志记录
  			// globalTCPOptions设置 tcp
        UseTCPOptions(globalTCPOptions)

    // 设置 HTTP 服务器的跟踪选项。
    httpServer.TCPOptions.Trace = bootstrapTraceMsg

    // 启动 HTTP 服务器的主 goroutine。
    go func() {
      // 返回一个function: serveFn
      // http server 重点是配置 控制器 handler => handler.ServeHTTP(w, r)
        serveFn, err := httpServer.Init(GlobalContext, func(listenAddr string, err error) {
            logger.LogIf(GlobalContext, fmt.Errorf("Unable to listen on `%s`: %v", listenAddr, err))
        })
        if err != nil {
            globalHTTPServerErrorCh <- err
            return
        }
      // serveFn 在这里真正执行
        globalHTTPServerErrorCh <- serveFn()
    }()

    // 设置全局 HTTP 服务器。
    setHTTPServer(httpServer)
})

// 根据特定条件执行相应的系统配置验证和服务冻结操作,以确保服务器在启动时具备必要的配置和准备状态。
if globalIsDistErasure {
    bootstrapTrace("verifying system configuration", func() {
        // 在分布式Erasure模式下,验证系统配置和设置。
        if err := verifyServerSystemConfig(GlobalContext, globalEndpoints); err != nil {
            logger.Fatal(err, "Unable to start the server")
        }
    })
}

if !globalDisableFreezeOnBoot {
    // 冻结服务,直到桶通知子系统初始化完成。
    bootstrapTrace("freezeServices", freezeServices)
}

  // 创建新的对象层
	var newObject ObjectLayer
	bootstrapTrace("newObjectLayer", func() {
		var err error
    // 其中单点模式就是只传了一个Endpoint给minio,使用的是文件系统的操作方式,即minio自带的单点模式下的文件对象操作结构体FSObjects
		newObject, err = newObjectLayer(GlobalContext, globalEndpoints)
		if err != nil {
			logFatalErrs(err, Endpoint{}, true)
		}
	})
  // 设置全局部署ID和MinIO版本信息
	xhttp.SetDeploymentID(globalDeploymentID)
	xhttp.SetMinIOVersion(Version)
	// 处理全局节点信息
	for _, n := range globalNodes {
		nodeName := n.Host
		if n.IsLocal {
			nodeName = globalLocalNodeName
		}
    // 在globalNodeNamesHex map 中对 非自身的节点的构建一个空struct{}{}
    // 用来存储对其他节点操作的信息
		nodeNameSum := sha256.Sum256([]byte(nodeName + globalDeploymentID))
		globalNodeNamesHex[hex.EncodeToString(nodeNameSum[:])] = struct{}{}
	}

	bootstrapTrace("newSharedLock", func() {
    // 创建全局共享锁: leader.lock 用来选举 leader
		globalLeaderLock = newSharedLock(GlobalContext, newObject, "leader.lock")
	})

	// Enable background operations on
	//
	// - Disk auto healing
	// - MRF (most recently failed) healing
	// - Background expiration routine for lifecycle policies
	bootstrapTrace("initAutoHeal", func() {
    // 初始化 Disk自动修复(Disk auto healing)
		initAutoHeal(GlobalContext, newObject)
	})
  // 初始化 most recently failed 的 healing策略
	bootstrapTrace("initHealMRF", func() {
		initHealMRF(GlobalContext, newObject)
	})
  // 初始化 后台生命周期策略过期例行程序
	bootstrapTrace("initBackgroundExpiry", func() {
		initBackgroundExpiry(GlobalContext, newObject)
	})

	var err error
	bootstrapTrace("initServerConfig", func() {
		if err = initServerConfig(GlobalContext, newObject); err != nil {
		... 错误处理
		}
		// 严格的AWS S3兼容性 警告
		if !globalCLIContext.StrictS3Compat {
			logger.Info(color.RedBold("WARNING: Strict AWS S3 compatible incoming PUT, POST content payload validation is turned off, caution is advised do not use in production"))
		}
	})
	// 默认凭证(Default Credentials)警告
	if globalActiveCred.Equal(auth.DefaultCredentials) {
		msg := fmt.Sprintf("WARNING: Detected default credentials '%s', we recommend that you change these values with 'MINIO_ROOT_USER' and 'MINIO_ROOT_PASSWORD' environment variables",
			globalActiveCred)
		logger.Info(color.RedBold(msg))
	}

	// Initialize users credentials and policies in background right after config has initialized.
	go func() {
    // 配置初始化后立即初始化用户凭证和策略 -- IAM
		bootstrapTrace("globalIAMSys.Init", func() {
			globalIAMSys.Init(GlobalContext, newObject, globalEtcdClient, globalRefreshIAMInterval)
		})

		// Initialize Console UI
    // 初始化控制台 server
	...
				srv, err := initConsoleServer()
				setConsoleSrv(srv)
				newConsoleServerFn().Serve()
		...

		// if we see FTP args, start FTP if possible
    go startFTPServer(ctx)
		
		// If we see SFTP args, start SFTP if possible
    go startSFTPServer(ctx)

		// Initialize data scanner.
    initDataScanner(GlobalContext, newObject)

		// Initialize background replication
    initBackgroundReplication(GlobalContext, newObject)
		globalTransitionState.Init(newObject)
    
		// Initialize batch job pool.
    globalBatchJobPool = newBatchJobPool(GlobalContext, newObject, 100)

		// Initialize the license update job
    initLicenseUpdateJob(GlobalContext, newObject)
    
			// Initialize transition tier configuration manager
      // 初始化 层级配置管理器
      globalTierConfigMgr.Init(GlobalContext, newObject)
 
		// initialize the new disk cache objects.
    // 磁盘的 cache 对象层
		if globalCacheConfig.Enabled {
			logger.Info(color.Yellow("WARNING: Drive caching is deprecated for single/multi drive MinIO setups."))
			var cacheAPI CacheObjectLayer
			cacheAPI, err = newServerCacheObjects(GlobalContext, globalCacheConfig)
			logger.FatalIf(err, "Unable to initialize drive caching")

			setCacheObjectLayer(cacheAPI)
		}

		// Initialize bucket notification system.
    // 初始化 bucket 的事件系统
		bootstrapTrace("initBucketTargets", func() {
			logger.LogIf(GlobalContext, globalEventNotifier.InitBucketTargets(GlobalContext, newObject))
		})

		var buckets []BucketInfo
		// List buckets to initialize bucket metadata sub-sys.
    // 初始化 bucket的元数据的子系统
			buckets, err = newObject.ListBuckets(GlobalContext, BucketOptions{})
      // Initialize bucket metadata sub-system.
      globalBucketMetadataSys.Init(GlobalContext, buckets, newObject)
 
		// initialize replication resync state. 复制重新同步状态
			globalReplicationPool.initResync(GlobalContext, buckets, newObject)

		// Initialize site replication manager after bucket metadata
    // 备份管理器
			globalSiteReplicationSys.Init(GlobalContext, newObject)
 
		// Initialize quota manager.配额管理器
    globalBucketQuotaSys.Init(newObject)
    
		// Populate existing buckets to the etcd backend 流行的 etcd 客户端
			// Background this operation. 初始化Federator后端
     // 将现有存储桶的信息写入 etcd 后端,以支持联合命名空间的操作
      go initFederatorBackend(buckets, newObject)

		// Prints the formatted startup message, if err is not nil then it prints additional information as well.用于打印格式化的启动消息
		printStartupMessage(getAPIEndpoints(), err)

		// Print a warning at the end of the startup banner so it is more noticeable
    // 打印警告信息,表示标准奇偶校验设置为 0 可能会导致数据丢失
		if newObject.BackendInfo().StandardSCParity == 0 {
			logger.Error("Warning: The standard parity is set to 0. This can lead to data loss.")
		}
	}()
	// 获取 region
	region := globalSite.Region
	if region == "" {
		region = "us-east-1"
	}
	bootstrapTrace("globalMinioClient", func() {
    // 客户端用于与其他 MinIO 节点通信。它使用服务器配置中的访问密钥、安全设置和区域信息进行初始化
		globalMinioClient, err = minio.New(globalLocalNodeName, &minio.Options{
			Creds:     credentials.NewStaticV4(globalActiveCred.AccessKey, globalActiveCred.SecretKey, ""),
			Secure:    globalIsTLS,
			Transport: globalProxyTransport,
			Region:    region,
		})
		logger.FatalIf(err, "Unable to initialize MinIO client")
	})

	// Add User-Agent to differentiate the requests.
  // 置应用程序信息,以区分请求的用户代理
	globalMinioClient.SetAppInfo("minio-perf-test", ReleaseTag)

	if serverDebugLog {
    // 打印调试模式已启用的信息以及当前环境变量的设置,但不会显示敏感的凭据信息
		logger.Info("== DEBUG Mode enabled ==")
		logger.Info("Currently set environment settings:")
		ks := []string{
			config.EnvAccessKey,
			config.EnvSecretKey,
			config.EnvRootUser,
			config.EnvRootPassword,
		}
		for _, v := range os.Environ() {
			// Do not print sensitive creds in debug.
			if slices.Contains(ks, strings.Split(v, "=")[0]) {
				continue
			}
			logger.Info(v)
		}
		logger.Info("======")
	}
	// 发送通知,表示 MinIO 服务器已准备好接受连接。
	daemon.SdNotify(false, daemon.SdNotifyReady)
	// 使用 <-globalOSSignalCh 阻塞主 goroutine,直到收到操作系统的信号,以便服务器能够正确地维护和退出
	<-globalOSSignalCh
}



性能分析设置
// 性能分析设置
func setDefaultProfilerRates() {
  // 分配 128kb 内存后采样
	runtime.MemProfileRate = 128 << 10 // 512KB -> 128K - Must be constant throughout application lifetime.
  // 关闭互斥锁分析
	runtime.SetMutexProfileFraction(0) // Disable until needed
  // 关闭阻塞分析
	runtime.SetBlockProfileRate(0)     // Disable until needed
}

监控 chan 执行关闭操作
// 在 server 命令里启动新的协程监控 chan
func serverMain(ctx *cli.Context) {
	signal.Notify(globalOSSignalCh, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT)

	go handleSignals()
}

func handleSignals() {
	// Custom exit function
  // 构建 exit 函数
	exit := func(success bool) {
		// If global profiler is set stop before we exit.
    // 关闭  global profiler
		globalProfilerMu.Lock()
		defer globalProfilerMu.Unlock()
		for _, p := range globalProfiler {
			p.Stop()
		}

		if success {
			os.Exit(0)
		}

		os.Exit(1)
	}

	stopProcess := func() bool {
		// send signal to various go-routines that they need to quit.
    // 向相关的需要 quit 的子 context 传播 cancel: 轮询子 ctx 的 chan,发送值到 chan
		cancelGlobalContext()
		// 锁住globalObjLayerMutex 关闭 [ http server, ObjectLayer, ConsoleServer]
		if httpServer := newHTTPServerFn(); httpServer != nil {
			if err := httpServer.Shutdown(); err != nil && !errors.Is(err, http.ErrServerClosed) {
				logger.LogIf(context.Background(), err)
			}
		}
		// 关闭 ObjectLayer
		if objAPI := newObjectLayerFn(); objAPI != nil {
			logger.LogIf(context.Background(), objAPI.Shutdown(context.Background()))
		}
		// 关闭 控制管理的 server
		if srv := newConsoleServerFn(); srv != nil {
			logger.LogIf(context.Background(), srv.Shutdown())
		}

		if globalEventNotifier != nil {
			globalEventNotifier.RemoveAllBucketTargets()
		}

		return true
	}

	for {
		select {
      // 对globalHTTPServerErrorCh ,globalOSSignalCh直接关
		case err := <-globalHTTPServerErrorCh:
			logger.LogIf(context.Background(), err)
			exit(stopProcess())
		case osSignal := <-globalOSSignalCh:
			logger.Info("Exiting on signal: %s", strings.ToUpper(osSignal.String()))
			daemon.SdNotify(false, daemon.SdNotifyStopping)
			exit(stopProcess())
		case signal := <-globalServiceSignalCh:
			switch signal {
			case serviceRestart:
				logger.Info("Restarting on service signal")
				daemon.SdNotify(false, daemon.SdNotifyReloading)
				stop := stopProcess()
				rerr := restartProcess()
				if rerr == nil {
					daemon.SdNotify(false, daemon.SdNotifyReady)
				}
				logger.LogIf(context.Background(), rerr)
				exit(stop && rerr == nil)
			case serviceStop:
				logger.Info("Stopping on service signal")
				daemon.SdNotify(false, daemon.SdNotifyStopping)
				exit(stopProcess())
			}
		}
	}
}

命令行参数解析
func serverHandleCmdArgs(ctx *cli.Context) {
    // 处理通用命令参数
    handleCommonCmdArgs(ctx)

    // 检查本地服务器地址是否有效
    logger.FatalIf(CheckLocalServerAddr(globalMinioAddr), "无法验证传递的参数")

    var err error
    var setupType SetupType

    // 检查和加载TLS证书
    globalPublicCerts, globalTLSCerts, globalIsTLS, err = getTLSConfig()
    logger.FatalIf(err, "无法加载TLS配置")

    // 检查和加载根证书颁发机构
    globalRootCAs, err = certs.GetRootCAs(globalCertsCADir.Get())
    logger.FatalIf(err, "无法读取根证书颁发机构 (%v)", err)

    // 将全局公共证书添加到全局根证书颁发机构中
    for _, publicCrt := range globalPublicCerts {
        globalRootCAs.AddCert(publicCrt)
    }

    // 注册根证书颁发机构供远程环境使用
    env.RegisterGlobalCAs(globalRootCAs)

    // 创建服务器端点和设置类型
    globalEndpoints, setupType, err = createServerEndpoints(globalMinioAddr, serverCmdArgs(ctx)...)
    logger.FatalIf(err, "无效的命令行参数")
    globalNodes = globalEndpoints.GetNodes()

    // 获取本地节点的对等节点的值, 并计算其哈希值
    globalLocalNodeName = GetLocalPeer(globalEndpoints, globalMinioHost, globalMinioPort)
    nodeNameSum := sha256.Sum256([]byte(globalLocalNodeName))
    globalLocalNodeNameHex = hex.EncodeToString(nodeNameSum[:])

    // 初始化并检查服务运行的网络接口
    setGlobalInternodeInterface(ctx.String("interface"))

    // 允许传输为HTTP/1.1以用于代理
    globalProxyTransport = NewCustomHTTPProxyTransport()()
    globalProxyEndpoints = GetProxyEndpoints(globalEndpoints)
    globalInternodeTransport = NewInternodeHTTPTransport()()
    globalRemoteTargetTransport = NewRemoteTargetHTTPTransport(false)()

    // 创建请求转发器
    globalForwarder = handlers.NewForwarder(&handlers.Forwarder{
        PassHost:     true,
        RoundTripper: NewHTTPTransportWithTimeout(1 * time.Hour),
        Logger: func(err error) {
            if err != nil && !errors.Is(err, context.Canceled) {
                logger.LogIf(GlobalContext, err)
            }
        },
    })

    // 设置TCP选项
    globalTCPOptions = xhttp.TCPOptions{
        UserTimeout: int(ctx.Duration("conn-user-timeout").Milliseconds()),
        Interface:   ctx.String("interface"),
    }

    // 检查端口的可用性
    logger.FatalIf(xhttp.CheckPortAvailability(globalMinioHost, globalMinioPort, globalTCPOptions), "无法启动服务器")

    // 设置全局Erasure标志 
    globalIsErasure = (setupType == ErasureSetupType)
    globalIsDistErasure = (setupType == DistErasureSetupType)
    if globalIsDistErasure {
        globalIsErasure = true
    }
    globalIsErasureSD = (setupType == ErasureSDSetupType)

    // 设置连接读取和写入的截止时间
    globalConnReadDeadline = ctx.Duration("conn-read-deadline")
    globalConnWriteDeadline = ctx.Duration("conn-write-deadline")
}	
加入DNS的Cache的刷新和停止hook


func handleCommonCmdArgs(){
  ...
// Check if we have configured a custom DNS cache TTL.
// 
	dnsTTL := ctx.Duration("dns-cache-ttl")
	if dnsTTL <= 0 {
		dnsTTL = 10 * time.Minute
	}

	// Call to refresh will refresh names in cache.
	go func() {
		// Baremetal setups set DNS refresh window up to dnsTTL duration.
    // 定时触发器
		t := time.NewTicker(dnsTTL)
		defer t.Stop()
		for {
			select {
			case <-t.C:
				globalDNSCache.Refresh()
			// 一旦接受 done 停止 for-select,则停止 dns 的刷新
			case <-GlobalContext.Done():
				return
			}
    }
	}()
}
server.Init
// Init - init HTTP server
func (srv *Server) Init(listenCtx context.Context, listenErrCallback func(listenAddr string, err error)) (serve func() error, err error) {
	... config
	handler := srv.Handler // if srv.Handler holds non-synced state -> possible data race

	// Create new HTTP listener.
  // 设置tcp socket 服务端启动的 addrs 
	var listener *httpListener
	listener, listenErrs := newHTTPListener(
		listenCtx,
		srv.Addrs,
		srv.TCPOptions,
	)
	... error处理 -> listenErrCallback(srv.Addrs[i], listenErrs[i])...
	

	// Wrap given handler to do additional
	// * return 503 (service unavailable) if the server in shutdown.
	wrappedHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// If server is in shutdown.
    // 包装 handler 增加inShutdown的专门处理
		if atomic.LoadUint32(&srv.inShutdown) != 0 {
			// To indicate disable keep-alives
			w.Header().Set("Connection", "close")
			w.WriteHeader(http.StatusServiceUnavailable)
			w.Write([]byte(http.ErrServerClosed.Error()))
			return
		}

		atomic.AddInt32(&srv.requestCount, 1)
		defer atomic.AddInt32(&srv.requestCount, -1)

		// Handle request using passed handler.
		handler.ServeHTTP(w, r)
	})
  // 再设置 srv
	srv.listenerMutex.Lock()
	srv.Handler = wrappedHandler
	srv.listener = listener
	srv.listenerMutex.Unlock()

	var l net.Listener = listener
	if tlsConfig != nil {
    // 创建自定义的网络监听器,该监听器用于接受来自内部监听器的连接请求。
    // 返回的监听器对象实现了 net.Listener 接口,可以用于接受和处理客户端连接请求
		l = tls.NewListener(listener, tlsConfig)
	}
	// 返回的函数
	serve = func() error {
    // 走到 http 包的func (srv *Server) Serve(l net.Listener) 
    // 真正启动一个 http server 服务
		return srv.Server.Serve(l)
	}

	return
}
获取共享锁newSharedLock
type sharedLock struct {
	lockContext chan LockContext
}
// LockContext带一个常规的 ctx 和设置CancelFunc
type LockContext struct {
	ctx    context.Context
	cancel context.CancelFunc
}

// 将获取到的锁上下文 lkctx 发送到 lockContext 通道中,
// 以便其他协程可以获取到共享锁的上下文,从而进行需要锁的操作。
func (ld sharedLock) backgroundRoutine(ctx context.Context, objAPI ObjectLayer, lockName string) {
	for {
    // 获取到  objAPI.NewNSLock 对象层的空间锁
		locker := objAPI.NewNSLock(minioMetaBucket, lockName)
    // 获得其互斥锁,得不到会阻塞,并返回获取锁的上下文 lkctx,以及可能出现的错误 err
		lkctx, err := locker.GetLock(ctx, sharedLockTimeout)
    // 错误
		if err != nil {
      // 失败了会进入下一轮的 for 循环获取锁
			continue
		}
	// 保持锁在使用的状态
	keepLock:
		for {
			select {
			case <-ctx.Done():
        // 如果传入的上下文 ctx 被取消,退出循环,结束协程
				return
			case <-lkctx.Context().Done():
				// 如果锁的上下文被取消,这可能是因为锁在分布式环境中失去了一致性
				// 在这种情况下,跳出内部循环,尝试重新获取锁
				break keepLock
			case ld.lockContext <- lkctx:
        // 将获取到的锁上下文传递给需要锁的代码块,发送后又回到第二次 for-select 等待锁的释放并退出该协程
				// Send the lock context to anyone asking for it
			}
		}
	}
}

// 工厂函数,用于创建共享锁的实例
func newSharedLock(ctx context.Context, objAPI ObjectLayer, lockName string) *sharedLock {
    // 创建 sharedLock 结构的实例
    l := &sharedLock{
        lockContext: make(chan LockContext),
    }
    
    // 启动后台协程,用于处理锁的后台任务
    go l.backgroundRoutine(ctx, objAPI, lockName)

    // 返回创建的 sharedLock 实例
    return l
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值