package master

import (
    "net/http"
    "io/ioutil"
    "encoding/json"
    "time"
    "strings"
    "sync"
    "math/rand"
    "path/filepath"
    "fmt"
    "os"
    "github.com/030io/whalefs/utils/uuid"
)

type Size interface {
    Size() int64
}
//公共资源访问处理器
func (m *Master)publicEntry(w http.ResponseWriter, r *http.Request) {
    m.serverMutex.RLock()
    defer m.serverMutex.RUnlock()

    if r.Method == http.MethodGet || r.Method == http.MethodHead {
        m.getFile(w, r)
    } else {
        w.WriteHeader(http.StatusMethodNotAllowed)
    }
}
//卷文件访问
func (m *Master)masterEntry(w http.ResponseWriter, r *http.Request) {
    m.serverMutex.RLock()
    defer m.serverMutex.RUnlock()

    switch r.URL.Path {
    case "/__heartbeat":
        m.heartbeat(w, r)
    default:
        if r.URL.Path == "/favicon.ico" || len(r.URL.Path) <= 1 {
            http.NotFound(w, r)
            return
        }

        switch r.Method{
        case http.MethodGet, http.MethodHead:
            m.getFile(w, r)
        case http.MethodPost:
            m.uploadFile(w, r)
        case http.MethodDelete:
            m.deleteFile(w, r)
        default:
            w.WriteHeader(http.StatusMethodNotAllowed)
        }
    }
}
//心跳连接处理器
func (m *Master)heartbeat(w http.ResponseWriter, r *http.Request) {
    body, _ := ioutil.ReadAll(r.Body)
    newVms := new(VolumeManagerStatus)
    json.Unmarshal(body, newVms)
    newVms.LastHeartbeat = time.Now()

    remoteIP := r.RemoteAddr[:strings.LastIndex(r.RemoteAddr, ":")]
    if newVms.AdminHost == "" || newVms.AdminHost == "localhost" {
        newVms.AdminHost = remoteIP
    }
    if newVms.PublicHost == "" || newVms.PublicHost == "localhost" {
        newVms.PublicHost = remoteIP
    }
    if newVms.Machine == "" {
        newVms.Machine = remoteIP
    }

    m.updateVMS(newVms)

    if m.vmsNeedCreateVolume(newVms) {
        go m.createVolumeWithReplication(newVms)
    }
}

func (m *Master)getFile(w http.ResponseWriter, r *http.Request) {
    vid, fid, fileName, err := m.Metadata.Get(r.URL.Path)
    if err != nil {
        http.NotFound(w, r)
        return
    }

    m.statusMutex.RLock()
    vStatusList, ok := m.VStatusListMap[vid]
    m.statusMutex.RUnlock()
    if !ok {
        http.Error(w, "can't find volume", http.StatusNotFound)
        return
    }

    length := len(vStatusList)
    j := rand.Intn(length)
    for i := 0; i < length; i++ {
        vStatus := vStatusList[(i + j) % length]
        if vStatus.vmStatus.IsAlive() {
            http.Redirect(w, r, vStatus.getFileUrl(fid, fileName), http.StatusFound)
            return
        }
    }

    http.Error(w, "all volumes is dead", http.StatusInternalServerError)
}

func (m *Master)uploadFile(w http.ResponseWriter, r *http.Request) {
    file, header, err := r.FormFile("file")
    if err != nil {
        http.Error(w, "r.FromFile: " + err.Error(), http.StatusInternalServerError)
        return
    }
    defer file.Close()

    var dst string
    if r.URL.Path[len(r.URL.Path) - 1] == '/' {
        dst = r.URL.Path + filepath.Base(header.Filename)
    } else {
        dst = r.URL.Path
    }
    fileName := filepath.Base(dst)

    if m.Metadata.Has(dst) {
        http.Error(w, "file is existed, you should delete it at first.", http.StatusNotAcceptable)
        return
    }

    var fileSize int64
    switch file.(type){
    case *os.File:
        s, _ := file.(*os.File).Stat()
        fileSize = s.Size()
    case Size:
        fileSize = file.(Size).Size()
    }

    vStatusList, err := m.getWritableVolumes(uint64(fileSize))
    if err != nil {
        http.Error(w, "m.getWritableVolumes: " + err.Error(), http.StatusInternalServerError)
        return
    }

    data, _ := ioutil.ReadAll(file)
    fid := uuid.GenerateUUID()

    wg := sync.WaitGroup{}
    var errVS *VolumeStatus
    for _, vStatus := range vStatusList {
        wg.Add(1)
        go func(vs *VolumeStatus) {
            e := vs.uploadFile(fid, fileName, data)
            if e != nil {
                err = e
                errVS = vs
            }
            wg.Done()
        }(vStatus)
    }
    wg.Wait()

    if err != nil {
        for _, vStatus := range vStatusList {
            go vStatus.delete(fid, fileName)
        }
        http.Error(w,
            fmt.Sprintf("host: %s port: %d error: %s", errVS.vmStatus.AdminHost, errVS.vmStatus.AdminPort, err),
            http.StatusInternalServerError)
        return
    }

    m.Metadata.Set(dst, vStatusList[0].Id, fid, fileName)
    if err != nil {
        http.Error(w, "m.Metadata.Set: " + err.Error(), http.StatusInternalServerError)
        return
    }
    w.WriteHeader(http.StatusCreated)
}

func (m *Master)deleteFile(w http.ResponseWriter, r *http.Request) {
    vid, fid, fileName, err := m.Metadata.Get(r.URL.Path)
    if err != nil {
        http.NotFound(w, r)
        return
    }

    m.statusMutex.RLock()
    vStatusList, ok := m.VStatusListMap[vid]
    m.statusMutex.RUnlock()
    if !ok {
        http.Error(w, "can't find volume", http.StatusNotFound)
        return
    } else if !m.volumesIsValid(vStatusList) || !volumesIsWritable(vStatusList, 0) {
        http.Error(w, "can't delete file, because it's(volumes) readonly.", http.StatusNotAcceptable)
    }

    wg := sync.WaitGroup{}
    var deleteErr []error
    for _, vStatus := range vStatusList {
        wg.Add(1)
        go func(vStatus *VolumeStatus) {
            e := vStatus.delete(fid, fileName)
            if e != nil {
                deleteErr = append(
                    deleteErr,
                    fmt.Errorf("%s:%d %s", vStatus.vmStatus.AdminHost, vStatus.vmStatus.AdminPort, e.Error()),
                )
            }
            wg.Done()
        }(vStatus)
    }
    wg.Wait()

    err = m.Metadata.Delete(r.URL.Path)
    if err != nil {
        deleteErr = append(deleteErr, fmt.Errorf("m.Metadata.Delete(%s) %s", r.URL.Path, err.Error()))
    }

    if len(deleteErr) == 0 {
        w.WriteHeader(http.StatusAccepted)
    } else {
        errStr := ""
        for _, err := range deleteErr {
            errStr += err.Error() + "\n"
        }
        http.Error(w, errStr, http.StatusInternalServerError)
        return
    }
}

handler.go的更多相关文章

  1. android Handler介绍

    Handler使用介绍: Handler根据接收的消息,处理UI更新.Thread线程发出消息,通知Handler更新UI. Handler mHandler = new Handler() {  p ...

  2. Handler

    1.1 继承AbstractController优点:能定制请求方式 package cn.happyl.controller; import javax.servlet.http.HttpServl ...

  3. Android消息处理机制(Handler、Looper、MessageQueue与Message)

    Android是消息驱动的,实现消息驱动有几个要素: 消息的表示:Message 消息队列:MessageQueue 消息循环,用于循环取出消息进行处理:Looper 消息处理,消息循环从消息队列中取 ...

  4. Android笔记——Handler Runnable与Thread的区别

    在java中可有两种方式实现多线程,一种是继承Thread类,一种是实现Runnable接口:Thread类是在java.lang包中定义的.一个类只要继承了Thread类同时覆写了本类中的run() ...

  5. Android消息传递之Handler消息机制

    前言: 无论是现在所做的项目还是以前的项目中,都会遇见线程之间通信.组件之间通信,目前统一采用EventBus来做处理,在总结学习EventBus之前,觉得还是需要学习总结一下最初的实现方式,也算是不 ...

  6. Handler系列之内存泄漏

    本篇简单的讲一下平常使用Handler时造成内存泄漏的问题. 什么是内存泄漏?大白话讲就是分配出去的内存,回收不回来.严重会导致内存不足OOM.下面来看一下造成内存泄漏的代码: public clas ...

  7. Handler系列之创建子线程Handler

    上一篇我介绍了Handler机制的工作原理,默认情况下,ActivityThread类为我们创建的了主线程的Looper和消息队列,所以当你创建Handler之后发送消息的时候,消息的轮训和handl ...

  8. Handler系列之原理分析

    上一节我们讲解了Handler的基本使用方法,也是平时大家用到的最多的使用方式.那么本节让我们来学习一下Handler的工作原理吧!!! 我们知道Android中我们只能在ui线程(主线程)更新ui信 ...

  9. Handler系列之使用

    作为一个Android开发者,我们肯定熟悉并使用过Handler机制.最常用的使用场景是"在子线程更新ui",实际上我们知道上面的说话是错误的.因为Android中只有主线程才能更 ...

  10. 阶段一:用Handler和Message实现计时效果及其中一些疑问

    “阶段一”是指我第一次系统地学习Android开发.这主要是对我的学习过程作个记录. 本来是打算继续做天气预报的优化的,但因为某些原因,我要先把之前做的小应用优化一下.所以今天就插播一下用Handle ...

随机推荐

  1. private static final 修饰符

    java修饰符分类修饰符字段修饰符方法修饰符根据功能同主要分下几种 1.权限访问修饰符 public,protected,default,private,四种级别修饰符都用来修饰类.方法和字段 包外 ...

  2. Oracle100w数据大表割接

    [现网问题] 最近在给咪咕做视频后台管理,移动那边希望页面上,码流字段可以支持1位小数,如8.0.自己查看数据库,发现码流字段是Number整型,也就是要换类型,打算直接换成varchar2.因为自己 ...

  3. python---购物车---更新

    购物车程序更新: 更新商家入口,实现以下功能: 1. 商家能够修改商品价格: 2. 商家能够下线商品: 3. 商家能够增加商品: 4. 商品信息存在文件中 # -*- coding:utf-8 -*- ...

  4. AngularJS数据绑定中数据监控的机制说明

    from : http://docs.angularjs.org/guide/scope When the browser calls into JavaScript the code execute ...

  5. Getting Real内容浓缩

    今天看完,想整理一下,可能会更好,也给别人提供一个快速学习的途径第一章 什么是 Getting Real?表达形式省略.精炼.精益.敏捷.用户体验.迭代改进.产品简化.第二章 建构从简做得比竟争对手少 ...

  6. DDGScreenShot--iOS 图片处理--多图片拼接 (swift)

    写在前面 最近总结了关于图片处理相关的内容,之前在二三四五工作的时候,也做过关于这方面的分享,图片的处理内容很多,会分很多模块来讲解. 今天简单讲多图片的拼接. 上代码 func composeIma ...

  7. java之web开发过滤器

    我们通常上网的时候都会遇到一个问题,看到一个视频之类的,想要点开观看,点击之后,网页 提醒你:您尚未登录,是否要登录?然后巴拉巴拉跑去输账号密码. 那么这就是一个过滤器的功能,当你要访问一个资源的时候 ...

  8. Coursera-AndrewNg(吴恩达)机器学习笔记——第三周编程作业

    一. 逻辑回归 1.背景:使用逻辑回归预测学生是否会被大学录取. 2.首先对数据进行可视化,代码如下: pos = find(y==); %找到通过学生的序号向量 neg = find(y==); % ...

  9. 原生javascript写自己的运动库(匀速运动篇)

    网上有很多JavaScript的运动库,这里和大家分享一下用原生JavaScript一步一步写一个运动函数的过程,如读者有更好的建议欢迎联系作者帮助优化完善代码.这个运动函数完成后,就可以用这个运动函 ...

  10. 大厂们的 redis 集群方案

    redis 集群方案主要有两类,一是使用类 codis 的架构,按组划分,实例之间互相独立: 另一套是基于官方的 redis cluster 的方案:下面分别聊聊这两种方案: 类 codis 架构 这 ...