handler.go
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的更多相关文章
- android Handler介绍
Handler使用介绍: Handler根据接收的消息,处理UI更新.Thread线程发出消息,通知Handler更新UI. Handler mHandler = new Handler() { p ...
- Handler
1.1 继承AbstractController优点:能定制请求方式 package cn.happyl.controller; import javax.servlet.http.HttpServl ...
- Android消息处理机制(Handler、Looper、MessageQueue与Message)
Android是消息驱动的,实现消息驱动有几个要素: 消息的表示:Message 消息队列:MessageQueue 消息循环,用于循环取出消息进行处理:Looper 消息处理,消息循环从消息队列中取 ...
- Android笔记——Handler Runnable与Thread的区别
在java中可有两种方式实现多线程,一种是继承Thread类,一种是实现Runnable接口:Thread类是在java.lang包中定义的.一个类只要继承了Thread类同时覆写了本类中的run() ...
- Android消息传递之Handler消息机制
前言: 无论是现在所做的项目还是以前的项目中,都会遇见线程之间通信.组件之间通信,目前统一采用EventBus来做处理,在总结学习EventBus之前,觉得还是需要学习总结一下最初的实现方式,也算是不 ...
- Handler系列之内存泄漏
本篇简单的讲一下平常使用Handler时造成内存泄漏的问题. 什么是内存泄漏?大白话讲就是分配出去的内存,回收不回来.严重会导致内存不足OOM.下面来看一下造成内存泄漏的代码: public clas ...
- Handler系列之创建子线程Handler
上一篇我介绍了Handler机制的工作原理,默认情况下,ActivityThread类为我们创建的了主线程的Looper和消息队列,所以当你创建Handler之后发送消息的时候,消息的轮训和handl ...
- Handler系列之原理分析
上一节我们讲解了Handler的基本使用方法,也是平时大家用到的最多的使用方式.那么本节让我们来学习一下Handler的工作原理吧!!! 我们知道Android中我们只能在ui线程(主线程)更新ui信 ...
- Handler系列之使用
作为一个Android开发者,我们肯定熟悉并使用过Handler机制.最常用的使用场景是"在子线程更新ui",实际上我们知道上面的说话是错误的.因为Android中只有主线程才能更 ...
- 阶段一:用Handler和Message实现计时效果及其中一些疑问
“阶段一”是指我第一次系统地学习Android开发.这主要是对我的学习过程作个记录. 本来是打算继续做天气预报的优化的,但因为某些原因,我要先把之前做的小应用优化一下.所以今天就插播一下用Handle ...
随机推荐
- vicoapp使用备忘
vico是一个模式编辑器,意味着没用过vi之类编辑器的童鞋用起来肯定觉得很不习惯. 模式切换 i:切至编辑模式,在光标前插入 a:切至编辑模式,在在光标后插入 I:类似于i,不过在行首插入 esc键: ...
- ASP.NET MVC不可或缺的部分——DI(IOC)容器及控制器重构的剖析(DI的实现原理)
IoC框架最本质的东西:反射或者EMIT来实例化对象.然后我们可以加上缓存,或者一些策略来控制对象的生命周期,比如是否是单例对象还是每次都生成一个新的对象. DI实现其实很简单,首先设计类来实现接口, ...
- WebStorm常用快捷键总结
在使用WebStorm的过程中,常用快捷键整理: 1. 必备快捷键 Ctrl+/:注释当前行 Ctrl+Shift+/:当前位置插入注释 Ctrl+Alt+/:块注释,并Focus到首行,写注释说明 ...
- 我的sql数据库存储过程分页- -
以前用到数据库存储过程分页的时候都是用 not in 但是最近工作的时候,随着数据库记录的不断增大,发现not in的效率 真的不行 虽然都设置了索引,但是当记录达到10w的时候就发现不行了,都是需要 ...
- 安装Emacs并设置racket环境
最近在阅读sicp这本书,书中的代码是使用scheme实现的.之前阅读的时候是使用Dr.Racket来完成写练习的,可我觉得与其这样,不如一步到位,使用emacs+lisp解释器来的比较快. 安装em ...
- 【JDK1.8】JUC——AbstractQueuedSynchronizer
一.前言 在上一篇中,我们对LockSupport进行了阅读,因为它是实现我们今天要分析的AbstractQueuedSynchronizer(简称AQS)的基础,重新用一下最开始的图: 可以看到,在 ...
- Django基本视图
Django基本视图 下面这三个类也许不能提供项目所需的所有的功能,这些应用于基于类的视图或Mixins情形下. 大多数Django的内建视图继承于其他基于类的视图或者各种mixins中,因为继承链是 ...
- Cesium 云服务
前言 所有行业内都知道云是未来或者现在的趋势,但是真正的完完全全提供地理信息云服务的恐怕只有 Google 一家,然而今天我居然发现 Cesium 提供了云服务,你没有看错,就是曾经的开源 3D 渲染 ...
- 一个resin启动bug的解决
这个bug的问题后来被确认为Resin所在目录层有中文目录名.--------------------------------------------------------------------- ...
- Android 众多的布局属性详解
http://www.open-open.com/lib/view/open1328686184311.html Android功能强大,界面华丽,但是众多的布局属性就害苦了开发者,下面这篇文章结合了 ...