成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

輕量級高性能PHP框架ycroute

dailybird / 2716人閱讀

摘要:數(shù)據(jù)交互層可選如果你習(xí)慣了層結(jié)構(gòu),你可以加載層,作為與數(shù)據(jù)庫交互的層,而層作為業(yè)務(wù)層。

YCRoute

github: https://github.com/caohao-php...

目錄

框架介紹

運(yùn)行環(huán)境

代碼結(jié)構(gòu)

路由配置

過濾驗(yàn)簽

控制層

加載器

模型層

數(shù)據(jù)交互dao層(可選)

Redis緩存操作

數(shù)據(jù)庫操作

配置加載

公共類加載

公共函數(shù)

日志模塊

視圖層

RPC 介紹 - 像調(diào)用本地函數(shù)一樣調(diào)用遠(yuǎn)程函數(shù)

RPC Server

RPC Client

RPC 并行調(diào)用

附錄 - Core_Model 中的輔助極速開發(fā)函數(shù)

框架介紹

框架由3層架構(gòu)構(gòu)成,Controller、Model、View 以及1個(gè)可選的Dao層,支持PHP7,優(yōu)點(diǎn)如下:

1、框架層次分明,靈活可擴(kuò)展至4層架構(gòu)、使用簡潔(開箱即用)、功能強(qiáng)大。

2、基于 yaf 路由和 ycdatabase 框架,兩者都是C語言擴(kuò)展,保證了性能。

3、ycdatabase 是強(qiáng)大的數(shù)據(jù)庫 ORM 框架,功能強(qiáng)大,安全可靠,支持便捷的主從配置,支持穩(wěn)定、強(qiáng)大的數(shù)據(jù)庫連接池。具體參考 https://blog.csdn.net/caohao0...

4、支持Redis代理,簡便的主從配置,支持穩(wěn)定的redis連接池。具體參考:https://blog.csdn.net/caohao0...

5、強(qiáng)大的日志模塊、異常捕獲模塊,便捷高效的類庫、共用函數(shù)加載模塊

6、基于PHP7,代碼緩存opcache。

運(yùn)行環(huán)境

運(yùn)行環(huán)境: PHP 7

依賴擴(kuò)展: yaf 、 ycdatabase 擴(kuò)展

創(chuàng)建日志目錄:/data/app/logs ,目錄權(quán)限為 php 項(xiàng)目可寫。

yaf 介紹以及安裝: https://github.com/laruence/yaf

ycdatabase 介紹以及安裝: https://github.com/caohao-php...

代碼結(jié)構(gòu)
———————————————— 
|--- system                   //框架系統(tǒng)代碼
|--- conf                     //yaf配置路徑 
|--- application              //業(yè)務(wù)代碼 
         |----- config        //配置目錄
         |----- controller    //控制器目錄
                |------ User.php    //User控制器
         |----- core          //框架基類目錄
     |----- daos          //DAO層目錄(可選)
         |----- errors        //錯(cuò)誤頁目錄
         |----- helpers       //公共函數(shù)目錄
         |----- library       //公共類庫目錄
         |----- models        //模型層目錄
         |----- plugins       //yaf路由插件目錄,路由前后鉤子,(接口驗(yàn)簽在這里)
         |----- third         //第三方類庫
         |----- views         //視圖層
路由配置

路由配置位于: framework/conf/application.ini

示例: http://localhost/index.php?c=...

詳細(xì)參考文檔: http://php.net/manual/zh/book...

控制器由參數(shù)c決定,動(dòng)作有 m 決定。
參數(shù) 方式 描述
c GET 控制器,路由到 /application/controller/User.php 文件
m GET 入口方法, User.php 里面的 getUserInfoAction 方法

程序?qū)⒈宦酚傻?framework/application/controllers/User.php文件的 UserController::getUserInfoAction方法,其它路由細(xì)節(jié)參考Yaf框架

class UserController extends Core_Controller  
{  
    public function getUserInfoAction()  
    {  
    }  
}  
過濾驗(yàn)簽

framework/application/plugins/Filter.php , 在 _auth 中寫入驗(yàn)簽方法,所有接口都會(huì)在這里校驗(yàn), 所有GET、POST等參數(shù)放在 $this->params 里。

class FilterPlugin extends Yaf_Plugin_Abstract {
    var $params;

    //路由之前調(diào)用
    public function routerStartUp ( Yaf_Request_Abstract $request , Yaf_Response_Abstract $response) {
        $this->params = & $request->getParams();
       
           $this->_auth();
    }
    
    
    //驗(yàn)簽過程
    protected function _auth()
    {
        //在這里寫你的驗(yàn)簽邏輯
    }
    ...
}
控制層

所有控制器位于:framework/application/controllers 目錄,所有控制器繼承自Core_Controller方法,里面主要獲取GET/POST參數(shù),以及返回?cái)?shù)據(jù)的處理,Core_Controller繼承自 Yaf_Controller_Abstract, init方法會(huì)被自動(dòng)調(diào)用,更多細(xì)節(jié)參考 Yaf 框架控制器。

class UserController extends Core_Controller {
    public function init() {
        parent::init(); //必須

        $this->user_model = Loader::model("UserinfoModel"); //模型層

        $this->util_log = Logger::get_instance("user_log"); //日志
        Loader::helper("common_helper"); //公共函數(shù)

        $this->sample = Loader::library("Sample"); //加載類庫,加載的就是 framework/library/Sample.php 里的Sample類
    }

    //獲取用戶信息接口
    public function getUserInfoAction() {
        $userId = $this->params["userid"];
        $token = $this->params["token"];

        if (empty($userId)) {
            $this->response_error(10000017, "user_id is empty");
        }

        if (empty($token)) {
            $this->response_error(10000016, "token is empty");
        }

        $userInfo = $this->user_model->getUserinfoByUserid($userId);
        if (empty($userInfo)) {
            $this->response_error(10000023, "未找到該用戶");
        }

        if (empty($token) || $token != $userInfo["token"]) {
            $this->response_error(10000024, "token 校驗(yàn)失敗");
        }
        
        $this->response_success($userInfo);
    }
}

通過 $this->response_error(10000017, "user_id is empty"); 返回錯(cuò)誤結(jié)果

{
    "errno":10000017,
    "errmsg":"user_id is empty"
}

通過 $this->response_success($result); 返回JSON格式成功結(jié)果,格式如下:

{
    "errno":0,
    "union":"",
    "amount":0,
    "session_key":"ZqwsC+Spy4C31ThvqkhOPg==",
    "open_id":"oXtwn4_mrS4zIxtSeV0yVT2sAuRo",
    "nickname":"涼之渡",
    "last_login_time":"2018-09-04 18:53:06",
    "regist_time":"2018-06-29 22:03:38",
    "user_id":6842811,
    "token":"c9bea5dee1f49488e2b4b4645ff3717e",
    "updatetime":"2018-09-04 18:53:06",
    "avatar_url":"https://wx.qlogo.cn/mmopen/vi_32/xfxHib91BictV8T4ibRQAibD10DfoNpzpB1LBqZvRrz0icPkN0gdibZg62EPJL3KE1Y5wkPDRAhibibymnQCFgBM2nuiavA/132",
    "city":"Guangzhou",
    "province":"Guangdong",
    "country":"China",
    "appid":"wx385863ba15f573b6",
    "gender":1,
    "form_id":""
}
加載器

通過 Loader 加載器可以加載模型層,公共類庫,公共函數(shù),數(shù)據(jù)庫,緩存等對象, Logger 為日志類。

模型層

framework/application/models/Userinfo.php ,模型層,你可以繼承自Core_Model, 也可以不用,Core_Model 中封裝了許多常用SQL操作。最后一章會(huì)介紹各個(gè)函數(shù)用法。

通過 $this->user_model = Loader::model("UserinfoModel") 加載模型層,模型層與數(shù)據(jù)庫打交道。

class UserinfoModel extends Core_Model {
    public function __construct() {
        $this->db = Loader::database("default");
        $this->util_log = Logger::get_instance("userinfo_log");
    }

    function register_user($appid, $userid, $open_id, $session_key) {
        $data = array();
        $data["appid"] = $appid;
        $data["user_id"] = $userid;
        $data["open_id"] = $open_id;
        $data["session_key"] = $session_key;
        $data["last_login_time"] = $data["regist_time"] = date("Y-m-d H:i:s", time());
        $data["token"] = md5(TOKEN_GENERATE_KEY . time() . $userid . $session_key);
        $ret = $this->db->insert("user_info", $data);
        if ($ret != -1) {
            return $data["token"];
        } else {
            $this->util_log->LogError("error to register_user, DATA=[".json_encode($data)."]");
            return false;
        }
    }
    
    ...
}
數(shù)據(jù)交互Dao層(可選)

如果你習(xí)慣了4層結(jié)構(gòu),你可以加載Dao層,作為與數(shù)據(jù)庫交互的層,而model層作為業(yè)務(wù)層。這個(gè)時(shí)候 Model 最好不要繼承 Core_Model,而由 Dao 層來繼承。

framework/application/daos/UserinfoDao.php ,數(shù)據(jù)庫交互層,你可以繼承自Core_Model, 也可以不用,Core_Model 中封裝了許多常用SQL操作。最后一章會(huì)介紹各個(gè)函數(shù)用法。

通過 $this->user_dao = Loader::dao("UserinfoDao") 加載dao層,我們建議一個(gè)數(shù)據(jù)庫對應(yīng)一個(gè)Dao層。

redis 緩存操作

加載 redis 緩存: Loader::redis("default_master"); 參數(shù)為framework/application/config/redis.php 配置鍵值,如下:

$redis_conf["default_master"]["host"] = "127.0.0.1";
$redis_conf["default_master"]["port"] = 6379;
$redis_conf["default_slave"]["host"] = "/tmp/redis_pool.sock";  //unix socket redis連接池,需要配置 openresty-pool/conf/nginx.conf,并開啟代理,具體參考 https://blog.csdn.net/caohao0591/article/details/85679702

$redis_conf["userinfo"]["host"] = "127.0.0.1";
$redis_conf["userinfo"]["port"] = 6379;

return $redis_conf;

使用例子:

$redis = Loader::redis("default_master"); //主寫
$redis->set("pre_redis_user_${userid}", serialize($result));
$redis->expire("pre_redis_user_${userid}", 3600);

$redis = Loader::redis("default_slave"); //從讀
$data = $redis->get("pre_redis_user_${userid}");

連接池配置 openresty-pool/conf/nginx.conf :

worker_processes  1;        #nginx worker 數(shù)量

error_log logs/error.log;   #指定錯(cuò)誤日志文件路徑

events {
    worker_connections 1024;
}

stream {
    lua_code_cache on;

    lua_check_client_abort on;

    server {
        listen unix:/tmp/redis_pool.sock;
        content_by_lua_block {
            local redis_pool = require "redis_pool"
            pool = redis_pool:new({ip = "127.0.0.1", port = 6380, auth = "password"})
            pool:run()
        }
    }
    
    server {

        listen unix:/var/run/mysql_sock/mysql_user_pool.sock;
        
        content_by_lua_block {
            local mysql_pool = require "mysql_pool"
            
            local config = {host = "127.0.0.1", 
                    user = "root", 
                    password = "test123123",
                    database = "userinfo", 
                    timeout = 2000, 
                    max_idle_timeout = 10000, 
                    pool_size = 200}
                           
            pool = mysql_pool:new(config)
            
            pool:run()
        }
    }
}
數(shù)據(jù)庫操作

數(shù)據(jù)庫加載: Loader::database("default"); 參數(shù)為 framework/application/config/database.php 里配置鍵值,如下:

$db["default"]["unix_socket"] = "/var/run/mysql_sock/mysql_user_pool.sock";  //unix socket 數(shù)據(jù)庫連接池,具體使用參考 https://blog.csdn.net/caohao0591/article/details/85255704
$db["default"]["pconnect"] = FALSE;
$db["default"]["db_debug"] = TRUE;
$db["default"]["char_set"] = "utf8";
$db["default"]["dbcollat"] = "utf8_general_ci";
$db["default"]["autoinit"] = FALSE;

$db["payinfo_master"]["host"]     = "127.0.0.1";   //地址
$db["payinfo_master"]["username"] = "root";        //用戶名
$db["payinfo_master"]["password"] = "test123123";  //密碼
$db["payinfo_master"]["dbname"]   = "payinfo";     //數(shù)據(jù)庫名
$db["payinfo_master"]["pconnect"] = FALSE;         //是否連接池
$db["payinfo_master"]["db_debug"] = TRUE;          //debug標(biāo)志,線上關(guān)閉,打開后,異常SQL會(huì)顯示到頁面,不安全,僅在測試時(shí)打開,(注意,上線一定得將 db_debug 置為 FALSE,否則一定概率可能暴露數(shù)據(jù)庫配置)
$db["payinfo_master"]["char_set"] = "utf8";
$db["payinfo_master"]["dbcollat"] = "utf8_general_ci";
$db["payinfo_master"]["autoinit"] = FALSE;         //自動(dòng)初始化,Loader的時(shí)候就連接,建議關(guān)閉
$db["payinfo_master"]["port"] = 3306;

$db["payinfo_slave"]["host"]     = "192.168.0.7";
$db["payinfo_slave"]["username"] = "root";
$db["payinfo_slave"]["password"] = "test123123";
$db["payinfo_slave"]["dbname"]   = "payinfo";
$db["payinfo_slave"]["pconnect"] = FALSE;
$db["payinfo_slave"]["db_debug"] = TRUE;
$db["payinfo_slave"]["char_set"] = "utf8";
$db["payinfo_slave"]["dbcollat"] = "utf8_general_ci";
$db["payinfo_slave"]["autoinit"] = FALSE;
$db["payinfo_slave"]["port"] = 3306;
原生SQL:
$data = $this->db->query("select * from user_info where country="China" limit 3");
查詢多條記錄:
$data = $this->db->get("user_info", ["regist_time[<]" => "2018-06-30 15:48:39", 
                                    "gender" => 1,
                                    "country" => "China",
                    "city[!]" => null,
                                    "ORDER" => [
                                        "user_id",
                                        "regist_time" => "DESC",
                                        "amount" => "ASC"
                                        ],
                                    "LIMIT" => 10], "user_id,nickname,city");
echo json_encode($data);exit;
[
    {
        "nickname":"芒果",
        "user_id":6818810,
        "city":"Yichun"
    },
    {
        "nickname":"Smile、格調(diào)",
        "user_id":6860814,
        "city":"Guangzhou"
    },
    {
        "nickname":"Yang",
        "user_id":6870818,
        "city":"Hengyang"
    },
    {
        "nickname":"涼之渡",
        "user_id":7481824,
        "city":"Guangzhou"
    }
]
查詢單列
$data = $this->db->get("user_info", ["regist_time[<]" => "2018-06-30 15:48:39", 
                                    "gender" => 1,
                                    "country" => "China",
                    "city[!]" => null,
                                    "ORDER" => [
                                        "user_id",
                                        "regist_time" => "DESC",
                                        "amount" => "ASC"
                                        ],
                                    "LIMIT" => 10], "nickname");
echo json_encode($data);exit;
[
    "芒果",
    "Smile、格調(diào)",
    "Yang",
    "涼之渡"
]
查詢單條記錄
$data = $this->db->get_one("user_info", ["user_id" => 6818810]);
{
    "union":null,
    "amount":0,
    "session_key":"Et1yjxbEfRqVmCVsYf5qzA==",
    "open_id":"oXtwn4wkPO4FhHmkan097DpFobvA",
    "nickname":"芒果",
    "last_login_time":"2018-10-04 16:01:27",
    "regist_time":"2018-06-29 21:24:45",
    "user_id":6818810,
    "token":"5a350bc05bbbd9556f719a0b8cf2a5ed",
    "updatetime":"2018-10-04 16:01:27",
    "avatar_url":"https://wx.qlogo.cn/mmopen/vi_32/DYAIOgq83epqg7FwyBUGd5xMXxLQXgW2TDEBhnNjPVla8GmKiccP0pFiaLK1BGpAJDMiaoyGHR9Nib2icIX9Na4Or0g/132",
    "city":"Yichun",
    "province":"Jiangxi",
    "country":"China",
    "appid":"wx385863ba15f573b6",
    "gender":1,
    "form_id":"" 
}
插入數(shù)據(jù)
function register_user($appid, $userid, $open_id, $session_key) {
    $data = array();
        $data["appid"] = $appid;
        $data["user_id"] = $userid;
        $data["open_id"] = $open_id;
        $data["session_key"] = $session_key;
        $data["last_login_time"] = $data["regist_time"] = date("Y-m-d H:i:s", time());
        $data["token"] = md5(TOKEN_GENERATE_KEY . time() . $userid . $session_key);
        $ret = $this->db->insert("user_info", $data);
        if ($ret != -1) {
            return $data["token"];
        } else {
            $this->util_log->LogError("error to register_user, DATA=[".json_encode($data)."]");
            return false;
        }
}
更新數(shù)據(jù)
function update_user($userid, $update_data) {
        $redis = Loader::redis("userinfo");
        $redis->del("pre_redis_user_info_" . $userid);

        $ret = $this->db->update("user_info", ["user_id" => $userid], $update_data);
        if ($ret != -1) {
            return true;
        } else {
            $this->util_log->LogError("error to update_user, DATA=[".json_encode($update_data)."]");
            return false;
        }
}
刪除操作
$ret = $this->db->delete("user_info", ["user_id" => 7339820]);
更多操作參考

通過 $this->db->get_ycdb(); 可以獲取ycdb句柄進(jìn)行更多數(shù)據(jù)庫操作, ycdb 的使用教程如下:
英文: https://github.com/caohao-php...

中文: https://blog.csdn.net/caohao0...

配置加載

通過 Loader::config("xxxxx"); 加載 /application/config/xxxxx.php 的配置。例如:

$config = Loader::config("config");
var_dump($config);
公共類加載

所有的公共類庫位于superci/application/library目錄,但是注意的是, 如果你的類位于library子目錄下面,你的類必須用下劃線"_"分隔;

$this->sample = Loader::library("Sample");

加載的就是 framework/application/library/Sample.php 中的 Sample類。

$this->ip_location = Loader::library("Ip_Location");

加載的是 framework/application/library/Ip/Location.php 中的Ip_Location類

公共函數(shù)

所有的公共類庫位于superci/application/helpers目錄,通過 Loader::helper("common_helper"); 方法包含進(jìn)來。

日志

日志使用方法如下:

$this->util_log = Logger::get_instance("userinfo");
$this->util_log->LogInfo("register success");
$this->util_log->LogError("not find userinfo");

日志級別:

const DEBUG  = "DEBUG";   /* 級別為 1 ,  調(diào)試日志,   當(dāng) DEBUG = 1 的時(shí)候才會(huì)打印調(diào)試 */
const INFO   = "INFO";    /* 級別為 2 ,  應(yīng)用信息記錄,  與業(yè)務(wù)相關(guān), 這里可以添加統(tǒng)計(jì)信息 */
const NOTICE = "NOTICE";  /* 級別為 3 ,  提示日志,  用戶不當(dāng)操作,或者惡意刷頻等行為,比INFO級別高,但是不需要報(bào)告*/
const WARN  = "WARN";    /* 級別為 4 ,  警告,   應(yīng)該在這個(gè)時(shí)候進(jìn)行一些修復(fù)性的工作,系統(tǒng)可以繼續(xù)運(yùn)行下去 */
const ERROR   = "ERROR";   /* 級別為 5 ,  錯(cuò)誤,     可以進(jìn)行一些修復(fù)性的工作,但無法確定系統(tǒng)會(huì)正常的工作下去,系統(tǒng)在以后的某個(gè)階段, 很可能因?yàn)楫?dāng)前的這個(gè)問題,導(dǎo)致一個(gè)無法修復(fù)的錯(cuò)誤(例如宕機(jī)),但也可能一直工作到停止有不出現(xiàn)嚴(yán)重問題 */
const FATAL  = "FATAL";   /* 級別為 6 ,  嚴(yán)重錯(cuò)誤,  這種錯(cuò)誤已經(jīng)無法修復(fù),并且如果系統(tǒng)繼續(xù)運(yùn)行下去的話,可以肯定必然會(huì)越來越亂, 這時(shí)候采取的最好的措施不是試圖將系統(tǒng)狀態(tài)恢復(fù)到正常,而是盡可能的保留有效數(shù)據(jù)并停止運(yùn)行 */

FATAL和ERROR級別日志文件以 .wf 結(jié)尾, DEBUG級別日志文件以.debug結(jié)尾,日志目錄存放于 /data/app/localhost 下面,localhost為你的項(xiàng)目域名,比如:

[root@gzapi: /data/app/logs/localhost]# ls
userinfo.20190211.log  userinfo.20190211.log.wf

日志格式: [日志級別] [時(shí)間] [錯(cuò)誤代碼] [文件|行數(shù)] [ip] [uri] [referer] [cookie] [統(tǒng)計(jì)信息] "內(nèi)容"

[INFO] [2019-02-11 18:57:01] - - [218.30.116.8] - - - [] "register success"
[ERROR] [2019-02-11 18:57:01] [0] [index.php|23 => | => User.php|35 => Userinfo.php|93] [218.30.116.8] [/index.php?c=user&m=getUserInfo&userid=6842811&token=c9bea5dee1f49488e2b4b4645ff3717e] [] [] - "not find userinfo"
VIEW層

視圖層參考yaf視圖渲染那部分, 我沒有寫案例。

RPC 介紹 - 像調(diào)用本地函數(shù)一樣調(diào)用遠(yuǎn)程函數(shù) 傳統(tǒng)web應(yīng)用弊端

傳統(tǒng)的Web應(yīng)用, 一個(gè)應(yīng)用隨著業(yè)務(wù)快速增長, 開發(fā)人員的流轉(zhuǎn), 就會(huì)慢慢的進(jìn)入一個(gè)惡性循環(huán), 代碼量上只有加法沒有了減法. 因?yàn)殡S著系統(tǒng)變復(fù)雜, 牽一發(fā)就會(huì)動(dòng)全局, 而新來的維護(hù)者, 對原有的體系并沒有那么多時(shí)間給他讓他全面掌握. 即使有這么多時(shí)間, 要想掌握以前那么多的維護(hù)者的思維的結(jié)合, 也不是一件容易的事情…

那么, 長次以往, 這個(gè)系統(tǒng)將會(huì)越來越不可維護(hù)…. 到一個(gè)大型應(yīng)用進(jìn)入這個(gè)惡性循環(huán), 那么等待他的只有重構(gòu)了.

那么, 能不能對這個(gè)系統(tǒng)做解耦呢? 我們已經(jīng)做了很多解耦了, 數(shù)據(jù), 中間件, 業(yè)務(wù), 邏輯, 等等, 各種分層. 但到Web應(yīng)用這塊, 還能怎么分呢, MVC我們已經(jīng)做過了….

解決利器---微服務(wù)

目前比較流行的解決方案是微服務(wù),它可以讓我們的系統(tǒng)盡可能快地響應(yīng)變化,微服務(wù)是指開發(fā)一個(gè)單個(gè)小型的但有業(yè)務(wù)功能的服務(wù),每個(gè)服務(wù)都有自己的處理和輕量通訊機(jī)制,可以部署在單個(gè)或多個(gè)服務(wù)器上。微服務(wù)也指一種種松耦合的、有一定的有界上下文的面向服務(wù)架構(gòu)。也就是說,如果每個(gè)服務(wù)都要同時(shí)修改,那么它們就不是微服務(wù),因?yàn)樗鼈兙o耦合在一起;如果你需要掌握一個(gè)服務(wù)太多的上下文場景使用條件,那么它就是一個(gè)有上下文邊界的服務(wù),這個(gè)定義來自DDD領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)。

相對于單體架構(gòu)和SOA,它的主要特點(diǎn)是組件化、松耦合、自治、去中心化,體現(xiàn)在以下幾個(gè)方面:

一組小的服務(wù)

服務(wù)粒度要小,而每個(gè)服務(wù)是針對一個(gè)單一職責(zé)的業(yè)務(wù)能力的封裝,專注做好一件事情。

獨(dú)立部署運(yùn)行和擴(kuò)展

每個(gè)服務(wù)能夠獨(dú)立被部署并運(yùn)行在一個(gè)進(jìn)程內(nèi)。這種運(yùn)行和部署方式能夠賦予系統(tǒng)靈活的代碼組織方式和發(fā)布節(jié)奏,使得快速交付和應(yīng)對變化成為可能。

獨(dú)立開發(fā)和演化

技術(shù)選型靈活,不受遺留系統(tǒng)技術(shù)約束。合適的業(yè)務(wù)問題選擇合適的技術(shù)可以獨(dú)立演化。服務(wù)與服務(wù)之間采取與語言無關(guān)的API進(jìn)行集成。相對單體架構(gòu),微服務(wù)架構(gòu)是更面向業(yè)務(wù)創(chuàng)新的一種架構(gòu)模式。

獨(dú)立團(tuán)隊(duì)和自治

團(tuán)隊(duì)對服務(wù)的整個(gè)生命周期負(fù)責(zé),工作在獨(dú)立的上下文中,自己決策自己治理,而不需要統(tǒng)一的指揮中心。團(tuán)隊(duì)和團(tuán)隊(duì)之間通過松散的社區(qū)部落進(jìn)行銜接。

我們可以看到整個(gè)微服務(wù)的思想就如我們現(xiàn)在面對信息爆炸、知識爆炸是一樣的:通過解耦我們所做的事情,分而治之以減少不必要的損耗,使得整個(gè)復(fù)雜的系統(tǒng)和組織能夠快速的應(yīng)對變化。

微服務(wù)的基石---RPC服務(wù)框架

微服務(wù)包含的東西非常多,這里我們只討論RPC服務(wù)框架,ycroute框架基于Yar擴(kuò)展為我們提供了RPC跨網(wǎng)絡(luò)的服務(wù)調(diào)用基礎(chǔ),Yar是一個(gè)非常輕量級的RPC框架, 使用非常簡單, 對于Server端和Soap使用方法很像,而對于客戶端,你可以像調(diào)用本地對象的函數(shù)一樣,調(diào)用遠(yuǎn)程的函數(shù)。

RPC Server 安裝環(huán)境 (客戶端服務(wù)端都需要安裝)

擴(kuò)展: yar.so

擴(kuò)展: msgpack.so 可選,一個(gè)高效的二進(jìn)制打包協(xié)議,用于客戶端和服務(wù)端之間包傳輸,還可以選php、json, 如果要使用Msgpack做為打包協(xié)議, 就需要安裝這個(gè)擴(kuò)展。

服務(wù)加載

我們在 framework/application/controllers/Rpcserver.php 中將 Model 層作為服務(wù),提供給遠(yuǎn)程的其它程序調(diào)用,RPC Client 便可以像調(diào)用本地函數(shù)一樣,調(diào)用遠(yuǎn)程的服務(wù),如下我們將 UserinfoModel 和 TradeModel 兩個(gè)模型層提供給遠(yuǎn)程程序調(diào)用。

class RpcserverController extends Core_Controller {
    public function init() {
        parent::init(); //必須
    }

    //用戶信息服務(wù)
    public function userinfoModelAction() {
        $user_model = Loader::model("UserinfoModel"); //模型層
        $yar_server = new Yar_server($user_model);
        $yar_server->handle();
        exit;
    }
    
    //支付服務(wù)
    public function tradeModelAction() {
        $trade_model = Loader::model("TradeModel"); //模型層
        $yar_server = new Yar_server($trade_model);
        $yar_server->handle();
        exit;
    }
}

上面一共提供了2個(gè)服務(wù),UserinfoModel 和 TradeModel 分別通過http://localhost/index.php?c=... 和 http://localhost/index.php?c=... 來訪問,我們來看看 UserinfoModel 一共有哪些服務(wù):

從上圖可以看到,UserinfoModel 類的所有 public 方法都會(huì)被當(dāng)做服務(wù)提供,包括他繼承的父類 public 方法。

服務(wù)校驗(yàn)

為了安全,我們最好對客戶端發(fā)起的RPC服務(wù)請求做校驗(yàn)。在 framework/application/plugins/Filter.php 中做校驗(yàn):

class FilterPlugin extends Yaf_Plugin_Abstract {
    var $params;

    //路由之前調(diào)用
    public function routerStartUp ( Yaf_Request_Abstract $request , Yaf_Response_Abstract $response) {
        $this->params = & $request->getParams();

        $this->_auth();
        
        if(!empty($this->params["rpc"])) {
            $this->_rpc_auth(); //rpc 調(diào)用校驗(yàn)
        }
    }
    
    //rpc調(diào)用校驗(yàn)
    protected function _rpc_auth()
    {
           $signature = $this->get_rpc_signature($this->params);
           if($signature != $this->params["signature"]) {
               $this->response_error(1, "check failed");
           }
    }
    
    //rpc簽名計(jì)算,不要改函數(shù)名,在RPC客戶端中 system/YarClientProxy.php 我們也會(huì)用到這個(gè)函數(shù),做簽名。
    public function get_rpc_signature($params) 
    {
        $secret = "MJCISDYFYHHNKBCOVIUHFUIHCQWE";
        unset($params["signature"]);
        ksort($params);
    reset($params);
    unset($auth_params["callback"]);
    unset($auth_params["_"]);
    $str = $secret;
    foreach ($params as $value) {
        $str = $str . trim($value);
    }
            
    return md5($str);
    }
    
    ...
    
}

切記不要修改簽名生成函數(shù) get_rpc_signature 的名字和參數(shù),因?yàn)樵?RPC Client 我們也會(huì)利用這個(gè)函數(shù)做簽名,如果需要修改,請?jiān)?system/YarClientProxy.php 中做相應(yīng)修改,以保證客戶端和服務(wù)器之間的調(diào)用正常。

RPC Client

yar 除了支持 http 之外,還支持tcp, unix domain socket傳輸協(xié)議,不過ycroute中只用了 http ,當(dāng)然 http 也可以開啟 keepalive 以獲得更高的傳輸性能,只不過相比 socket, http 協(xié)議還是多了不少的協(xié)議頭部的開銷。

安裝環(huán)境

擴(kuò)展: yar.so

擴(kuò)展: msgpack.so 可選,一個(gè)高效的二進(jìn)制打包協(xié)議,用于客戶端和服務(wù)端之間包傳輸,還可以選php、json, 如果要使用Msgpack做為打包協(xié)議, 就需要安裝這個(gè)擴(kuò)展。

調(diào)用邏輯

例子:

class UserController extends Core_Controller {

    ...
    
    //獲取用戶信息(從遠(yuǎn)程)
    public function getUserInfoByRemoteAction() {
        $userId = $this->params["userid"];
        
        if (empty($userId)) {
            $this->response_error(10000017, "user_id is empty");
        }
        
        $model = Loader::remote_model("UserinfoModel");
        $userInfo = $model->getUserinfoByUserid($userId);
        $this->response_success($userInfo);
    }
    
    ...
}

通過 $model = Loader::remote_model("UserinfoModel"); 可以獲取遠(yuǎn)程 UserinfoModel,參數(shù)是framework/application/config/rpc.php配置里的鍵值:

$remote_config["UserinfoModel"]["url"] = "http://localhost/index.php?c=rpcserver&m=userinfoModel&rpc=true";  //服務(wù)地址
$remote_config["UserinfoModel"]["packager"] = FALSE;         //RPC包類型,F(xiàn)ALSE則選擇默認(rèn),可以為 "json", "msgpack", "php", msgpack 需要安裝擴(kuò)展
$remote_config["UserinfoModel"]["persitent"] = FALSE;        //是否長鏈接,需要服務(wù)端支持keepalive
$remote_config["UserinfoModel"]["connect_timeout"] = 1000;   //連接超時(shí)(毫秒),默認(rèn) 1秒 
$remote_config["UserinfoModel"]["timeout"] = 5000;           //調(diào)用超時(shí)(毫秒), 默認(rèn) 5 秒
$remote_config["UserinfoModel"]["debug"] = TRUE;             //DEBUG模式,調(diào)用異常是否會(huì)打印到屏幕,線上關(guān)閉

$remote_config["TradeModel"]["url"] = "http://localhost/index.php?c=rpcserver&m=tradeModel&rpc=true";
$remote_config["TradeModel"]["packager"] = FALSE;
$remote_config["TradeModel"]["persitent"] = FALSE;
$remote_config["TradeModel"]["connect_timeout"] = 1000; 
$remote_config["TradeModel"]["timeout"] = 5000;       
$remote_config["TradeModel"]["debug"] = TRUE;            

這樣,我們就可以把 model 當(dāng)成本地對象一樣調(diào)用遠(yuǎn)程 UserinfoModel 的成員方法。

url簽名

調(diào)用遠(yuǎn)程服務(wù)的時(shí)候,system/YarClientProxy.php 會(huì)從配置中獲取服務(wù)的 url, 然后調(diào)用 FilterPlugin::get_rpc_signature 方法對 URL 做簽名,并將簽名參數(shù)拼接到 url 結(jié)尾,發(fā)起調(diào)用。

class YarClientProxy {
    
    ...
    
    public static function get_signatured_url($url) {
        $get = array();
        $t = parse_url($url, PHP_URL_QUERY);
        parse_str($t, $get);
        $get["timestamp"] = time();
        $get["auth"] = rand(11111111, 9999999999);
        $signature = FilterPlugin::get_rpc_signature($get);
        return $url . "×tamp=" . $get["timestamp"] . "&auth=" . $get["auth"] . "&signature=" . $signature;
    }
    
    ...
}
調(diào)用異常日志

日志位于 /data/app/logs/localhost 下,localhost 為項(xiàng)目域名。

[root@gzapi: /data/app/logs/localhost]# ls
yar_client_proxy.20190214.log.wf

[ERROR] [2019-02-14 18:57:13] [0] [index.php|23 => | => User.php|61 => YarClientProxy.php|46] [218.30.116.3] [/index.php?c=user&m=getUserInfoByRemote&userid=6818810&token=c9bea5dee1f49488e2b4b4645ff3717e1] [] [] - "yar_client_call_error URL=[http://tr.gaoqu.site/index.ph...] , Remote_model=[UserinfoModel] Func=[getUserinfoByUserid] Exception=[server responsed non-200 code "500"]"

RPC 并行調(diào)用

yar框架支持并行調(diào)用,可以同時(shí)調(diào)用多個(gè)服務(wù),這樣可以充分利用CPU性能,避免IO等待,提升系統(tǒng)性能,按照yar的流程,你首先得一個(gè)個(gè)注冊服務(wù),然后發(fā)送注冊的調(diào)用,然后reset 重置調(diào)用。在ycroute 中,一個(gè)函數(shù)就可以了。

用 Loader::concurrent_call($call_params); 來并行調(diào)用RPC服務(wù), 其中 call_params是調(diào)用參數(shù)數(shù)組。

如下數(shù)組包含4個(gè)元素,每個(gè)調(diào)用都包含 model, method 兩個(gè)必輸參數(shù),以及 parameters, callback , error_callback 三個(gè)可選參數(shù)。

model : 服務(wù)名,是framework/application/config/rpc.php配置里的鍵值。

method : 調(diào)用函數(shù)

parameters : 函數(shù)的參數(shù),是一個(gè)數(shù)組,數(shù)組的個(gè)數(shù)為參數(shù)的個(gè)數(shù)

callback : 回調(diào)函數(shù),調(diào)用成功之后回調(diào),針對的是各自的回調(diào)。

error_callback : 調(diào)用失敗之后會(huì)回調(diào)這個(gè)函數(shù),其中調(diào)用超時(shí)不會(huì)回調(diào)該方法, 針對的也是各自的回調(diào)。

class UserController extends Core_Controller {
    //獲取用戶信息(并行遠(yuǎn)程調(diào)用)
    public function multipleGetUsersInfoByRemoteAction() {
        $userId = $this->params["userid"];
        
        $call_params = array();
        $call_params[] = ["model" => "UserinfoModel", 
                          "method" => "getUserinfoByUserid", 
                          "parameters" => array($userId), 
                          "callback" => array($this, "callback1")];
                          
        $call_params[] = ["model" => "UserinfoModel", 
                          "method" => "getUserInUserids", 
                          "parameters" => array(array(6860814, 6870818)), 
                          "callback" => array($this, "callback2"),
                          "error_callback" => array($this, "error_callback")];
                          
        $call_params[] = ["model" => "UserinfoModel", 
                          "method" => "getUserByName", 
                          "parameters" => array("CH.smallhow")];
              
        //不存在的方法
        $call_params[] = ["model" => "UserinfoModel", 
                          "method" => "unknownMethod", 
                          "parameters" => array(),
                          "error_callback" => array($this, "error_callback")];
                          
        Loader::concurrent_call($call_params);
        echo json_encode($this->retval);
        exit;
    }
    
    //回調(diào)函數(shù)1
    public function callback1($retval, $callinfo) {
        $this->retval["callback1"]["retval"] = $retval;
        $this->retval["callback1"]["callinfo"] = $callinfo;
    }
    
    //回調(diào)函數(shù)2
    public function callback2($retval, $callinfo) {
        $this->retval["callback2"]["retval"] = $retval;
        $this->retval["callback2"]["callinfo"] = $callinfo;
    }
    
    //錯(cuò)誤回調(diào)
    public function error_callback($type, $error, $callinfo) {
        $tmp["type"] = $type;
        $tmp["error"] = $error;
        $tmp["callinfo"] = $callinfo;
        $this->retval["error_callback"][] = $tmp;
    }
}

我特意將第4個(gè)調(diào)用的method設(shè)置一個(gè)不存在的函數(shù),大家可以看下上面的并行調(diào)用的結(jié)果:

{
    "error_callback":[
        {
            "type":4,
            "error":"call to undefined api ::unknownMethod()",
            "callinfo":{
                "sequence":4,
                "uri":"http://tr.gaoqu.site/index.php?c=rpcserver&m=userinfoModel&rpc=true×tamp=1550142590&auth=5930400101&signature=fc0ed911c624d9176523544421a0248d",
                "method":"unknownMethod"
            }
        }
    ],
    "callback1":{
        "retval":{
            "user_id":"6818810",
            "appid":"wx385863ba15f573b6",
            "open_id":"oXtwn4wkPO4FhHmkan097DpFobvA",
            "union":null,
            "session_key":"Et1yjxbEfRqVmCVsYf5qzA==",
            "nickname":"芒果",
            "city":"Yichun",
            "province":"Jiangxi",
            "country":"China",
            "avatar_url":"https://wx.qlogo.cn/mmopen/vi_32/DYAIOgq83epqg7FwyBUGd5xMXxLQXgW2TDEBhnNjPVla8GmKiccP0pFiaLK1BGpAJDMiaoyGHR9Nib2icIX9Na4Or0g/132",
            "gender":"1",
            "form_id":"",
            "token":"5a350bc05bbbd9556f719a0b8cf2a5ed",
            "amount":"0",
            "last_login_time":"2018-10-04 16:01:27",
            "regist_time":"2018-06-29 21:24:45",
            "updatetime":"2018-10-04 16:01:27"
        },
        "callinfo":{
            "sequence":1,
            "uri":"http://tr.gaoqu.site/index.php?c=rpcserver&m=userinfoModel&rpc=true×tamp=1550142590&auth=8384256613&signature=c0f9c944ae070d2eb38c8e9638723a2e",
            "method":"getUserinfoByUserid"
        }
    },
    "callback2":{
        "retval":{
            "6860814":{
                "user_id":"6860814",
                "nickname":"Smile、格調(diào)",
                "avatar_url":"https://wx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTKNE5mFLk33q690Xl1N6mrehQr0ggasgk8Y4cuaUJt4CNHORwq8rVjwET7H06F3aDjU5UiczjpD4nw/132",
                "city":"Guangzhou"
            },
            "6870818":{
                "user_id":"6870818",
                "nickname":"Yang",
                "avatar_url":"https://wx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTLTKBoU1tdRicImnUHyr43FdMulSHRhAlsQwuYgAyOlrwQaLGRoFEHbgfVuyEV1K1VU2NMmm0slS4w/132",
                "city":"Hengyang"
            }
        },
        "callinfo":{
            "sequence":2,
            "uri":"http://tr.gaoqu.site/index.php?c=rpcserver&m=userinfoModel&rpc=true×tamp=1550142590&auth=7249482640&signature=26c419450bb4747ac166fbaa4a242b77",
            "method":"getUserInUserids"
        }
    }
}
附錄 - Core_Model 中的輔助極速開發(fā)函數(shù)(不關(guān)心可以跳過)

$this->redis_conf_path = "default_master"; //用到快速緩存時(shí),需要在 __construct 構(gòu)造函數(shù)中加上 redis 緩存配置

/**
 * 插入表記錄
 * @param string table 表名
 * @param array data 表數(shù)據(jù)
 * @param string redis_key redis 緩存鍵值, 可空, 非空時(shí)清理鍵值緩存
 */
public function insert_table($table, $data, $redis_key = "");
/**
 * 更新表記錄
 * @param string table 表名
 * @param array where 查詢條件
 * @param array data 更新數(shù)據(jù)
 * @param string redis_key redis 緩存鍵值, 可空, 非空時(shí)清理鍵值緩存
 */
public function update_table($table, $where, $data, $redis_key = "");
/**
 * 替換表記錄
 * @param string table 表名
 * @param array data 替換數(shù)據(jù)
 * @param string redis_key redis 緩存鍵值, 可空, 非空時(shí)清理鍵值緩存
 */
public function replace_table($table, $data, $redis_key = "");
/**
 * 刪除表記錄
 * @param string table 表名
 * @param array where 查詢條件
 * @param string redis_key redis緩存鍵值, 可空, 非空時(shí)清理鍵值緩存
 */
public function delete_table($table, $where, $redis_key = "");
/**
 * 獲取表數(shù)據(jù)
 * @param string table 表名
 * @param array where 查詢條件
 * @param string redis_key redis 緩存鍵值, 可空, 非空時(shí)清理鍵值緩存
 * @param int redis_expire redis 緩存到期時(shí)長(秒)
 * @param boolean set_empty_flag 是否標(biāo)注空值,如果標(biāo)注空值,在表記錄更新之后,一定記得清理空值標(biāo)記緩存
 */
public function get_table_data($table, $where = array(), $redis_key = "", $redis_expire = 600, $set_empty_flag = true);
/**
 * 根據(jù)key獲取表記錄
 * @param string table 表名
 * @param string key 鍵名
 * @param string value 鍵值
 * @param string redis_key redis 緩存鍵值, 可空, 非空時(shí)清理鍵值緩存
 * @param int redis_expire redis 緩存到期時(shí)長(秒)
 * @param boolean set_empty_flag 是否標(biāo)注空值,如果標(biāo)注空值,在表記錄更新之后,一定記得清理空值標(biāo)記緩存
 */
public function get_table_data_by_key($table, $key, $value, $redis_key = "", $redis_expire = 300, $set_empty_flag = true);

/**
 * 獲取一條表數(shù)據(jù)
 * @param string table 表名
 * @param array where 查詢條件
 * @param string redis_key redis 緩存鍵值, 可空, 非空時(shí)清理鍵值緩存
 * @param int redis_expire redis 緩存到期時(shí)長(秒)
 * @param boolean set_empty_flag 是否標(biāo)注空值,如果標(biāo)注空值,在表記錄更新之后,一定記得清理空值標(biāo)記緩存
 */
public function get_one_table_data($table, $where, $redis_key = "", $redis_expire = 600, $set_empty_flag = true);

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/30976.html

相關(guān)文章

  • 新一代量級PHP擴(kuò)展框架 Asf

    摘要:一是什么全稱用語言編寫的輕量級擴(kuò)展框架專注于開發(fā)。這里只是給出了一種測試方法通過多次不同并發(fā)數(shù)測試結(jié)果得知與原生性能消耗是。業(yè)務(wù)開發(fā)速度結(jié)論采用框架開發(fā)業(yè)務(wù)代碼量能節(jié)約。 showImg(https://segmentfault.com/img/bVbamHp?w=320&h=320); 一、Asf 是什么? 全稱 API Services Framework, 用C語言編寫的輕量級P...

    iOS122 評論0 收藏0
  • 你不可不知道的20個(gè)優(yōu)秀PHP框架

    摘要:每一個(gè)開發(fā)者都知道,擁有一個(gè)強(qiáng)大的框架可以讓開發(fā)工作變得更加快捷安全和有效。官方網(wǎng)站是一款老牌的框架,現(xiàn)在穩(wěn)定版本已經(jīng)是了。官方網(wǎng)站是由最大的社區(qū)之一的管理開發(fā)的,也是一個(gè)開源的框架。 對于Web開發(fā)者來說,PHP是一款非常強(qiáng)大而又受歡迎的編程語言。世界上很多頂級的網(wǎng)站都是基于PHP開發(fā)的。 每一個(gè)開發(fā)者都知道,擁有一個(gè)強(qiáng)大的框架可以讓開發(fā)工作變得更加快捷、安全和有效。在開發(fā)項(xiàng)目之前選...

    zombieda 評論0 收藏0
  • swoolefy-基于swoole擴(kuò)展實(shí)現(xiàn)的性能的常駐內(nèi)存型API和Web應(yīng)用服務(wù)框架

    摘要:是一個(gè)基于擴(kuò)展實(shí)現(xiàn)的輕量級高性能的常駐內(nèi)存型的和應(yīng)用服務(wù)框架高度封裝了,,服務(wù)器,以及基于實(shí)現(xiàn)可擴(kuò)展的服務(wù),同時(shí)支持包方式安裝部署項(xiàng)目?;趯?shí)用,抽象事件處理類,實(shí)現(xiàn)與底層的回調(diào)的解耦,支持同步異步調(diào)用,內(nèi)置等常用組件等。 swoolefy swoolefy是一個(gè)基于swoole擴(kuò)展實(shí)現(xiàn)的輕量級高性能的常駐內(nèi)存型的API和Web應(yīng)用服務(wù)框架,高度封裝了http,websocket,ud...

    lewinlee 評論0 收藏0
  • PHP性能I/O框架:Libevent(一)

    摘要:是一個(gè)用語言編寫的輕量級的開源高性能框架,支持多種多路復(fù)用技術(shù)和等支持,定時(shí)器和信號等事件注冊事件優(yōu)先級。定時(shí)器提供了系列函數(shù),實(shí)現(xiàn)一次性定時(shí)器,精度微秒。 Libevent 是一個(gè)用C語言編寫的、輕量級的開源高性能I/O框架,支持多種 I/O 多路復(fù)用技術(shù): epoll、 poll、 dev/poll、 select 和 kqueue 等;支持 I/O,定時(shí)器和信號等事件;注冊事件優(yōu)...

    klivitamJ 評論0 收藏0
  • 【譯】PHP:40+開發(fā)工具推薦

    摘要:今天,就為開發(fā)者介紹個(gè)方便的工具。對開發(fā)者來說,是一個(gè)非常有用的工具,它提供了超過個(gè)有用的函數(shù)。該工具檢查輸入源代碼和報(bào)告任何違反給定的標(biāo)準(zhǔn)??蚣苁且粋€(gè)開發(fā)的工具。它側(cè)重于安全性和性能,絕對是最安全的開發(fā)框架之一。 PHP是為Web開發(fā)設(shè)計(jì)的服務(wù)器腳本語言,但也是一種通用的編程語言。超過2.4億個(gè)索引域使用PHP,包括很多重要的網(wǎng)站,例如Facebook、Digg和WordPress。...

    dreambei 評論0 收藏0

發(fā)表評論

0條評論

最新活動(dòng)
閱讀需要支付1元查看
<