aboutsummaryrefslogtreecommitdiff
path: root/weed/command/server.go
diff options
context:
space:
mode:
Diffstat (limited to 'weed/command/server.go')
-rw-r--r--weed/command/server.go291
1 files changed, 291 insertions, 0 deletions
diff --git a/weed/command/server.go b/weed/command/server.go
new file mode 100644
index 000000000..6ed1e5228
--- /dev/null
+++ b/weed/command/server.go
@@ -0,0 +1,291 @@
+package command
+
+import (
+ "net/http"
+ "os"
+ "runtime"
+ "runtime/pprof"
+ "strconv"
+ "strings"
+ "sync"
+ "time"
+
+ "github.com/chrislusf/seaweedfs/weed/glog"
+ "github.com/chrislusf/seaweedfs/weed/server"
+ "github.com/chrislusf/seaweedfs/weed/storage"
+ "github.com/chrislusf/seaweedfs/weed/util"
+ "github.com/gorilla/mux"
+)
+
+type ServerOptions struct {
+ cpuprofile *string
+}
+
+var (
+ serverOptions ServerOptions
+ filerOptions FilerOptions
+)
+
+func init() {
+ cmdServer.Run = runServer // break init cycle
+}
+
+var cmdServer = &Command{
+ UsageLine: "server -port=8080 -dir=/tmp -volume.max=5 -ip=server_name",
+ Short: "start a server, including volume server, and automatically elect a master server",
+ Long: `start both a volume server to provide storage spaces
+ and a master server to provide volume=>location mapping service and sequence number of file ids
+
+ This is provided as a convenient way to start both volume server and master server.
+ The servers are exactly the same as starting them separately.
+
+ So other volume servers can use this embedded master server also.
+
+ Optionally, one filer server can be started. Logically, filer servers should not be in a cluster.
+ They run with meta data on disk, not shared. So each filer server is different.
+
+ `,
+}
+
+var (
+ serverIp = cmdServer.Flag.String("ip", "localhost", "ip or server name")
+ serverBindIp = cmdServer.Flag.String("ip.bind", "0.0.0.0", "ip address to bind to")
+ serverMaxCpu = cmdServer.Flag.Int("maxCpu", 0, "maximum number of CPUs. 0 means all available CPUs")
+ serverTimeout = cmdServer.Flag.Int("idleTimeout", 10, "connection idle seconds")
+ serverDataCenter = cmdServer.Flag.String("dataCenter", "", "current volume server's data center name")
+ serverRack = cmdServer.Flag.String("rack", "", "current volume server's rack name")
+ serverWhiteListOption = cmdServer.Flag.String("whiteList", "", "comma separated Ip addresses having write permission. No limit if empty.")
+ serverPeers = cmdServer.Flag.String("master.peers", "", "other master nodes in comma separated ip:masterPort list")
+ serverSecureKey = cmdServer.Flag.String("secure.secret", "", "secret to encrypt Json Web Token(JWT)")
+ serverGarbageThreshold = cmdServer.Flag.String("garbageThreshold", "0.3", "threshold to vacuum and reclaim spaces")
+ masterPort = cmdServer.Flag.Int("master.port", 9333, "master server http listen port")
+ masterMetaFolder = cmdServer.Flag.String("master.dir", "", "data directory to store meta data, default to same as -dir specified")
+ masterVolumeSizeLimitMB = cmdServer.Flag.Uint("master.volumeSizeLimitMB", 30*1000, "Master stops directing writes to oversized volumes.")
+ masterConfFile = cmdServer.Flag.String("master.conf", "/etc/weedfs/weedfs.conf", "xml configuration file")
+ masterDefaultReplicaPlacement = cmdServer.Flag.String("master.defaultReplicaPlacement", "000", "Default replication type if not specified.")
+ volumePort = cmdServer.Flag.Int("volume.port", 8080, "volume server http listen port")
+ volumePublicPort = cmdServer.Flag.Int("volume.port.public", 0, "volume server public port")
+ volumeDataFolders = cmdServer.Flag.String("dir", os.TempDir(), "directories to store data files. dir[,dir]...")
+ volumeMaxDataVolumeCounts = cmdServer.Flag.String("volume.max", "7", "maximum numbers of volumes, count[,count]...")
+ volumePulse = cmdServer.Flag.Int("pulseSeconds", 5, "number of seconds between heartbeats")
+ volumeIndexType = cmdServer.Flag.String("volume.index", "memory", "Choose [memory|leveldb|boltdb] mode for memory~performance balance.")
+ volumeFixJpgOrientation = cmdServer.Flag.Bool("volume.images.fix.orientation", true, "Adjust jpg orientation when uploading.")
+ volumeReadRedirect = cmdServer.Flag.Bool("volume.read.redirect", true, "Redirect moved or non-local volumes.")
+ volumeServerPublicUrl = cmdServer.Flag.String("volume.publicUrl", "", "publicly accessible address")
+ isStartingFiler = cmdServer.Flag.Bool("filer", false, "whether to start filer")
+
+ serverWhiteList []string
+)
+
+func init() {
+ serverOptions.cpuprofile = cmdServer.Flag.String("cpuprofile", "", "cpu profile output file")
+ filerOptions.master = cmdServer.Flag.String("filer.master", "", "default to current master server")
+ filerOptions.collection = cmdServer.Flag.String("filer.collection", "", "all data will be stored in this collection")
+ filerOptions.port = cmdServer.Flag.Int("filer.port", 8888, "filer server http listen port")
+ filerOptions.dir = cmdServer.Flag.String("filer.dir", "", "directory to store meta data, default to a 'filer' sub directory of what -mdir is specified")
+ filerOptions.defaultReplicaPlacement = cmdServer.Flag.String("filer.defaultReplicaPlacement", "", "Default replication type if not specified during runtime.")
+ filerOptions.redirectOnRead = cmdServer.Flag.Bool("filer.redirectOnRead", false, "whether proxy or redirect to volume server during file GET request")
+ filerOptions.disableDirListing = cmdServer.Flag.Bool("filer.disableDirListing", false, "turn off directory listing")
+ filerOptions.cassandra_server = cmdServer.Flag.String("filer.cassandra.server", "", "host[:port] of the cassandra server")
+ filerOptions.cassandra_keyspace = cmdServer.Flag.String("filer.cassandra.keyspace", "seaweed", "keyspace of the cassandra server")
+ filerOptions.redis_server = cmdServer.Flag.String("filer.redis.server", "", "host:port of the redis server, e.g., 127.0.0.1:6379")
+ filerOptions.redis_password = cmdServer.Flag.String("filer.redis.password", "", "redis password in clear text")
+ filerOptions.redis_database = cmdServer.Flag.Int("filer.redis.database", 0, "the database on the redis server")
+}
+
+func runServer(cmd *Command, args []string) bool {
+ filerOptions.secretKey = serverSecureKey
+ if *serverOptions.cpuprofile != "" {
+ f, err := os.Create(*serverOptions.cpuprofile)
+ if err != nil {
+ glog.Fatal(err)
+ }
+ pprof.StartCPUProfile(f)
+ defer pprof.StopCPUProfile()
+ }
+
+ if *filerOptions.redirectOnRead {
+ *isStartingFiler = true
+ }
+
+ *filerOptions.master = *serverIp + ":" + strconv.Itoa(*masterPort)
+
+ if *filerOptions.defaultReplicaPlacement == "" {
+ *filerOptions.defaultReplicaPlacement = *masterDefaultReplicaPlacement
+ }
+
+ if *volumePublicPort == 0 {
+ *volumePublicPort = *volumePort
+ }
+
+ if *serverMaxCpu < 1 {
+ *serverMaxCpu = runtime.NumCPU()
+ }
+ runtime.GOMAXPROCS(*serverMaxCpu)
+
+ folders := strings.Split(*volumeDataFolders, ",")
+ maxCountStrings := strings.Split(*volumeMaxDataVolumeCounts, ",")
+ var maxCounts []int
+ for _, maxString := range maxCountStrings {
+ if max, e := strconv.Atoi(maxString); e == nil {
+ maxCounts = append(maxCounts, max)
+ } else {
+ glog.Fatalf("The max specified in -max not a valid number %s", maxString)
+ }
+ }
+ if len(folders) != len(maxCounts) {
+ glog.Fatalf("%d directories by -dir, but only %d max is set by -max", len(folders), len(maxCounts))
+ }
+ for _, folder := range folders {
+ if err := util.TestFolderWritable(folder); err != nil {
+ glog.Fatalf("Check Data Folder(-dir) Writable %s : %s", folder, err)
+ }
+ }
+
+ if *masterMetaFolder == "" {
+ *masterMetaFolder = folders[0]
+ }
+ if *isStartingFiler {
+ if *filerOptions.dir == "" {
+ *filerOptions.dir = *masterMetaFolder + "/filer"
+ os.MkdirAll(*filerOptions.dir, 0700)
+ }
+ if err := util.TestFolderWritable(*filerOptions.dir); err != nil {
+ glog.Fatalf("Check Mapping Meta Folder (-filer.dir=\"%s\") Writable: %s", *filerOptions.dir, err)
+ }
+ }
+ if err := util.TestFolderWritable(*masterMetaFolder); err != nil {
+ glog.Fatalf("Check Meta Folder (-mdir=\"%s\") Writable: %s", *masterMetaFolder, err)
+ }
+
+ if *serverWhiteListOption != "" {
+ serverWhiteList = strings.Split(*serverWhiteListOption, ",")
+ }
+
+ if *isStartingFiler {
+ go func() {
+ r := http.NewServeMux()
+ _, nfs_err := weed_server.NewFilerServer(r, *serverBindIp, *filerOptions.port, *filerOptions.master, *filerOptions.dir, *filerOptions.collection,
+ *filerOptions.defaultReplicaPlacement,
+ *filerOptions.redirectOnRead, *filerOptions.disableDirListing,
+ *filerOptions.secretKey,
+ *filerOptions.cassandra_server, *filerOptions.cassandra_keyspace,
+ *filerOptions.redis_server, *filerOptions.redis_password, *filerOptions.redis_database,
+ )
+ if nfs_err != nil {
+ glog.Fatalf("Filer startup error: %v", nfs_err)
+ }
+ glog.V(0).Infoln("Start Seaweed Filer", util.VERSION, "at port", strconv.Itoa(*filerOptions.port))
+ filerListener, e := util.NewListener(
+ ":"+strconv.Itoa(*filerOptions.port),
+ time.Duration(10)*time.Second,
+ )
+ if e != nil {
+ glog.Fatalf("Filer listener error: %v", e)
+ }
+ if e := http.Serve(filerListener, r); e != nil {
+ glog.Fatalf("Filer Fail to serve: %v", e)
+ }
+ }()
+ }
+
+ var raftWaitForMaster sync.WaitGroup
+ var volumeWait sync.WaitGroup
+
+ raftWaitForMaster.Add(1)
+ volumeWait.Add(1)
+
+ go func() {
+ r := mux.NewRouter()
+ ms := weed_server.NewMasterServer(r, *masterPort, *masterMetaFolder,
+ *masterVolumeSizeLimitMB, *volumePulse, *masterConfFile, *masterDefaultReplicaPlacement, *serverGarbageThreshold,
+ serverWhiteList, *serverSecureKey,
+ )
+
+ glog.V(0).Infoln("Start Seaweed Master", util.VERSION, "at", *serverIp+":"+strconv.Itoa(*masterPort))
+ masterListener, e := util.NewListener(*serverBindIp+":"+strconv.Itoa(*masterPort), time.Duration(*serverTimeout)*time.Second)
+ if e != nil {
+ glog.Fatalf("Master startup error: %v", e)
+ }
+
+ go func() {
+ raftWaitForMaster.Wait()
+ time.Sleep(100 * time.Millisecond)
+ myAddress := *serverIp + ":" + strconv.Itoa(*masterPort)
+ var peers []string
+ if *serverPeers != "" {
+ peers = strings.Split(*serverPeers, ",")
+ }
+ raftServer := weed_server.NewRaftServer(r, peers, myAddress, *masterMetaFolder, ms.Topo, *volumePulse)
+ ms.SetRaftServer(raftServer)
+ volumeWait.Done()
+ }()
+
+ raftWaitForMaster.Done()
+ if e := http.Serve(masterListener, r); e != nil {
+ glog.Fatalf("Master Fail to serve:%s", e.Error())
+ }
+ }()
+
+ volumeWait.Wait()
+ time.Sleep(100 * time.Millisecond)
+ if *volumePublicPort == 0 {
+ *volumePublicPort = *volumePort
+ }
+ if *volumeServerPublicUrl == "" {
+ *volumeServerPublicUrl = *serverIp + ":" + strconv.Itoa(*volumePublicPort)
+ }
+ isSeperatedPublicPort := *volumePublicPort != *volumePort
+ volumeMux := http.NewServeMux()
+ publicVolumeMux := volumeMux
+ if isSeperatedPublicPort {
+ publicVolumeMux = http.NewServeMux()
+ }
+ volumeNeedleMapKind := storage.NeedleMapInMemory
+ switch *volumeIndexType {
+ case "leveldb":
+ volumeNeedleMapKind = storage.NeedleMapLevelDb
+ case "boltdb":
+ volumeNeedleMapKind = storage.NeedleMapBoltDb
+ }
+ volumeServer := weed_server.NewVolumeServer(volumeMux, publicVolumeMux,
+ *serverIp, *volumePort, *volumeServerPublicUrl,
+ folders, maxCounts,
+ volumeNeedleMapKind,
+ *serverIp+":"+strconv.Itoa(*masterPort), *volumePulse, *serverDataCenter, *serverRack,
+ serverWhiteList, *volumeFixJpgOrientation, *volumeReadRedirect,
+ )
+
+ glog.V(0).Infoln("Start Seaweed volume server", util.VERSION, "at", *serverIp+":"+strconv.Itoa(*volumePort))
+ volumeListener, eListen := util.NewListener(
+ *serverBindIp+":"+strconv.Itoa(*volumePort),
+ time.Duration(*serverTimeout)*time.Second,
+ )
+ if eListen != nil {
+ glog.Fatalf("Volume server listener error: %v", eListen)
+ }
+ if isSeperatedPublicPort {
+ publicListeningAddress := *serverIp + ":" + strconv.Itoa(*volumePublicPort)
+ glog.V(0).Infoln("Start Seaweed volume server", util.VERSION, "public at", publicListeningAddress)
+ publicListener, e := util.NewListener(publicListeningAddress, time.Duration(*serverTimeout)*time.Second)
+ if e != nil {
+ glog.Fatalf("Volume server listener error:%v", e)
+ }
+ go func() {
+ if e := http.Serve(publicListener, publicVolumeMux); e != nil {
+ glog.Fatalf("Volume server fail to serve public: %v", e)
+ }
+ }()
+ }
+
+ OnInterrupt(func() {
+ volumeServer.Shutdown()
+ pprof.StopCPUProfile()
+ })
+
+ if e := http.Serve(volumeListener, volumeMux); e != nil {
+ glog.Fatalf("Volume server fail to serve:%v", e)
+ }
+
+ return true
+}