### session_start()初始化工作
- 讀取名為PHPSESSID的cookie值,這個值內容保存這session對應存儲的文件名,假設為abc123(由系統生成)
- 若PHPSESSID存在,創建$_SESSION變量,讀取session文件(如SESS_abc123),讀取到的內容填充到$_SESSION變量中;若PHPSESSID不存在,創建$_SESSION和session文件(SESS_abc123),將abc123作為名喂PHPSESSID的cookie值返回給游覽器
- 由此可見,如果需要解決跨域問題,第一需要共享session文件,第二客戶端不能丟失sessionID
### session_id($id string)
返回當前會話ID,如果指定參數$id,則需要在session_start()之前調用,返回設置為$id的會話
### 垃圾回收機制
session回收發生在session_start(),但不是每次都會執行回收,由session.gc_probability 和 session.gc_divisor兩個選項決定,計算如下:
系統會根據session.gc_probability/session.gc_divisor 公式計算概率,例如選項 session.gc_probability = 1,選項 session.gc_divisor = 100,這樣概率就變成了 1/100,也就是 session_start()函數被調用 100 次才會啟動一次 “ 垃圾回收程序 ” 。所以對會話頁面訪問越頻繁,啟動的概率就越來越小。一般的建議為 調用1000-5000次才會啟動一次: 1/(1000~5000)。
### 登錄問題
一般登錄時候不會設置cookie過期時間,當游覽器退出時,cookie失效,用戶需要再次登錄;若設置了記住密碼,實際上是設置了cookie的過期時間,當游覽器退出時,只要cookie沒過期,就不需要再次登錄(前提是在退出游覽器時,用戶不能點擊退出登錄)
### GO實現session
```go
package session
import (
"encoding/json"
"io/ioutil"
"log"
"math/rand"
"net/http"
"os"
"sync"
"time"
)
const (
SESS_PREFIX string = "sess_"
COOKIE_NAME string = "PHPSESSID"
GC_PROBABILITY int = 1
GC_DIVISOR int = 100
SAVE_PATH string = "sess_files/"
MAX_LIFE_TIME int64 = 86400
)
type Session struct {
Req http.Request
Writer http.ResponseWriter // 請求
Sfile string // session文件名
Sdata map[string]string // session值
Lock sync.RWMutex
IsStart bool
}
func (s *Session) start() {
defer s.Lock.Unlock()
s.Lock.Lock()
if s.IsStart {
return
}
cookie, err := s.Req.Cookie(COOKIE_NAME)
if err != nil {
log.Fatal(err)
}
// 設置為啟動
s.IsStart = true
// 首次訪問,生成名為PHPSESSID的cookie到客戶端
if !cookie {
s.Sfile = SESS_PREFIX + GetRandomString(10)
cookie = http.Cookie{Name: COOKIE_NAME, Value: s.SFile, Path: "/", MaxAge: "86400"}
http.SetCookie(http.Writer, &cookie)
return
} else {
s.Sfile = SESS_PREFIX + cookie.Value
}
// 讀取session值,并設置值
file, err := os.Open(s.Sfile)
if err != nil {
log.Fatal(err)
}
buf := make([]byte, 0)
_, err := file.Read(buf)
if err != nil {
log.Fatal(err)
}
s.Sdata = make(map[string]string)
json.Unmarshal(buf, s.Sdata)
// 回收機制
rand.Seed(time.Now().Unix())
base := rand.Intn(GC_DIVISOR)
if GC_PROBABILITY <= base {
overtimeFiles, err := ioutil.ReadDir(SAVE_PATH)
if err != nil {
log.Fatal(err)
}
for _, fi := range overtimeFiles {
modTime := fi.ModTime().Unix()
if modTime-time.Now().Unix() > MAX_LIFE_TIME {
os.Remove(SAVE_PATH + string(os.PathSeparator) + fi.Name())
}
}
}
}
func (s *Session) Set(key string, value string) {
if !s.IsStart {
log.Fatal("session is not start")
}
if s.Sdata == nil {
s.Sdata = make(map[string]string)
}
s.Sdata[key] = value
}
func (s *Session) Get(key string) string {
if !s.IsStart {
log.Fatal("session is not start")
}
if v, ok := s.Sdata[key]; ok {
return string(v)
} else {
return ""
}
}
func (s *Session) Save() {
if !s.IsStart {
log.Fatal("session is not start")
}
str, err := json.Marshal(s.Sdata)
if err != nil {
log.Fatal(err)
}
file, err := os.Open(s.Sfile)
if err != nil {
log.Fatal(err)
}
n, err := file.Write([]byte(str))
if err != nil || n != len(str) {
log.Fatal("save session failed")
}
}
// 隨機字符串
func GetRandomString(l int) string {
str := "0123456789abcdefghijklmnopqrstuvwxyz"
bytes := []byte(str)
result := []byte{}
r := rand.New(rand.NewSource(time.Now().UnixNano()))
for i := 0; i < l; i++ {
result = append(result, bytes[r.Intn(len(bytes))])
}
return string(result)
}
```
- php
- 編譯安裝
- 基本概念
- 垃圾回收機制
- 生命周期
- zval底層實現
- c擴展開發
- gdb調試工具
- 自定義擴展簡單demo
- 鉤子函數
- 讀取php.ini配置
- 數組
- 函數
- 類
- yaf擴展底層源碼
- swoole擴展底層源碼
- memoryGlobal內存池
- swoole協程使用記錄
- 單點登錄sso原理
- compser使用
- session實現機制
- c & linux
- gcc
- 指針
- 結構體,聯合和位字段
- 宏定義井號說明
- printf家族函數和可變參數
- 共享函數
- 靜態庫和動態庫
- makefile自動化構建
- 信號一
- 信號二
- inotify監控文件事件
- socket編程
- 簡介
- UNIX DOMAIN
- Internet DOMAIN
- TCP/IP
- 文件IO多路復用
- 內存管理
- 進程組,會話和控制終端
- daemon守護進程
- 多進程
- 多線程
- 常用進制轉換
- go
- 入門知識
- 字節和整數裝換
- python
- redis
- 應用場景
- 消息隊列
- 熱點數據
- 掃碼登錄
- 訂閱發布
- 次數限制
- 搶購超賣
- 持久化機制
- mysql
- 工作流程
- MyISAM和InnoDB區別
- 用戶和權限管理
- 執行計劃
- sql優化
- 事務和鎖
- 慢查詢日志
- case...when...then...end用法
- sql
- 參考
- linux
- 內核參數優化
- 防火墻設置
- docker
- docker入門知識
- 算法
- 多維數組合
- DFA算法
- 紅包金額分配