Coder Social home page Coder Social logo

arcs's People

Contributors

clms2 avatar

Watchers

 avatar

arcs's Issues

apache配置允许跨域

httpd.conf需打开mod_headers
允许跨域且允许带Authorization的,myweb替换成具体域名

<IfModule mod_headers.c>	
    SetEnvIf Origin ^(https?://(?:.+\.)?myweb\.com)   CORS_ALLOW_ORIGIN=$1
    SetEnvIf Origin null CORS_ALLOW_ORIGIN=*
    Header append Access-Control-Allow-Origin  %{CORS_ALLOW_ORIGIN}e   env=CORS_ALLOW_ORIGIN
    Header merge  Vary "Origin"
	
    Header set Access-Control-Allow-Methods: "GET,POST,PUT,DELETE,OPTIONS"
    Header set Access-Control-Allow-Headers: "Accept,Authorization,Cache-Control,Content-Type,DNT,If-Modified-Since,Keep-Alive,Origin,User-Agent,X-Mx-ReqToken,X-Requested-With,loginToken,X-Xsrf-Token"
    Header set Access-Control-Allow-Credentials: true
    Header set Access-Control-Max-Age: 86400
</IfModule>

php

php7:

  • $a = $c ?? $b; 等同于 $a = isset($c) ? $c : $b;
  • $a = $c ?: $b; 等同于 $a = $c ? $c : $b;
  • linux nginx php-fpm环境设置上传文件大小,需要额外在nginx里配置timeout参数,具体如下:
php.ini:
        max_execution_time = 300
        post_max_size = 50M
nginx:
client_max_body_size 50M
fastcgi_connect_timeout 300;
fastcgi_read_timeout 300;
fastcgi_send_timeout 300;


php-fpm:
request_terminate_timeout 300

php-fpm:
fpm可以配置多个池,一个挂了不影响另外个站
pm.start_servers= min_spare_servers + (max_spare_servers - min_spare_servers) / 2
高并发需修改 /etc/sysctl.conf
net.core.somaxconn=10485 # 默认为128
net.core.netdev_max_backlog=10485 # 默认为1000
net.ipv4.tcp_max_syn_backlog=10485 # 默认为1024
echo 1000 >/proc/sys/net/core/somaxconn

linux删除软链不能带/

linux删除软链不能带/,否则会把文件夹下内容全删了。
正确的删除命令:rm -rf ./test_chk_ln

sublime3个人配置

按键绑定:

[
    { "keys": ["ctrl+down"], "command": "find_under_expand" },
    { "keys": ["ctrl+d"], "command": "run_macro_file", "args": {"file": "res://Packages/Default/Delete Line.sublime-macro"} },
    { "keys": ["ctrl+shift+k"], "command": "find_under_expand" },
    { "keys": ["ctrl+alt+0"], "command": "alignment" },
    { "keys": ["ctrl+alt+9"], "command": "js_format" },
    { "keys": ["f1"], "command": "goto_definition" },

    { "keys": ["f3"], "command": "expand_fqcn", "args": {"leading_separator": true} },
    { "keys": ["shift+f3"], "command": "expand_fqcn" },
    { "keys": ["f2"], "command": "find_use" },
    { "keys": ["shift+f1"], "command": "goto_definition_scope" },

    // { "keys": ["ctrl+q"]},
    { "keys": ["ctrl+alt+p"], "command": "show_overlay", "args": {"overlay": "command_palette"} },
    { "keys": ["alt+p"], "command": "prompt_select_workspace" },

    { "keys": ["ctrl+shift+r"], "command": "goto_symbol_in_project" },

    { "keys": ["ctrl+b"], "command": "build" }
]

用户设置:

{
    "theme": "Cobalt2.sublime-theme",
    "color_scheme": "Packages/Theme - Cobalt2/cobalt2.tmTheme",
    "enable_tab_scrolling": false,
    "font_size": 14,
    "ignored_packages":
    [
        "Vintage"
    ],
    "remember_full_screen": true,
    "remember_open_files": false,
    "scroll_past_end": false,
    "show_definitions": false,
    "tab_size": 4,
    "translate_tabs_to_spaces": true,
    "trim_trailing_white_space_on_save": true,
    "update_check": false,
    "word_wrap": true,
    "mini_diff": false,
}

package control, add channels: http://cst.stu.126.net/u/json/cms/channel_v3.json
插件包: alignment, all autocomplete, chineseLocalizations, docblocker, emmet, gotoLastEdit, laravel blade highlighter, php companion, sublimeLinter, sublimeLinter-php, theme-cobalt2
https://github.com/clms2/arcs/raw/master/res/other/sublime%20packages.7z

svn

  • 权限配置:
[code/project1:/]
@dev_group = rw
[resource:/]
@dev_group = rw
  • 新建一个资源仓库
    切换到svn仓库目录
    svnadmin create test

  • 日志看不到问题
    anon-access = none
    清楚客户端缓存数据

  • 启动svn服务
    svnserve -d -r svn仓库目录

  • 停止svn服务
    killall svnserve

  • 将本地项目导入到仓库
    svn import [源路径] [目标版本库路径] -m [日志信息]

  • 删除.svn
    find . -type d -name '.svn' | xargs rm -rf
    find . -type d -name '.svn' -exec rm -rf {} \;

  • 重新定位
    a. svn info查看url
    b. svn switch --relocate 原地址 新地址

  • svn 检出中文文件失败
    export LC_CTYPE="zh_CN.UTF-8"
    不行的话用export LANG="zh_CN.UTF-8"

  • svn代码库从一个仓库迁到另一个仓库
    a. 切换到svn目录
    b. 导出项目 包括提交日志
    svnadmin dump pnew > dd
    pnew:要导出的仓库名
    c. 导入
    svnadmin load code --parent-dir zmq_admin < dd
    code:新仓库名
    zmq_admin:新仓库下的文件夹名

  • svn将已有文件纳入到版本库
    svn co svn://localhost/proa . --username xx --password aa --force

  • svn: Can't convert string from 'UTF-8' to native encoding
    export LC_CTYPE="zh_CN.UTF-8"

U:表示从服务器收到文件更新了
G:表示本地文件以及服务器文件都已更新,而且成功的合并了
其他的如下:
A:表示有文件或者目录添加到工作目录
R:表示文件或者目录被替换了.
C:表示文件的本地修改和服务器修改发生冲突

vue采坑

  • 如果有轮播图并且有多组轮播图需要点击后切换显示指定组,如使用了swiper插件,那么不能在方法里面直接调用new Swiper,否则首次加载可能会加载不出来。可使用侦听器方式,侦听页面渲染后再加载轮播插件:
vm.$watch('packageImgs', function(){  
    vm.$nextTick(function() {  
        //轮播
        if (typeof swiper != 'undefined') {
            swiper.destroy();
        }
        swiper = new Swiper('.swiper-container', {
            pagination: '.swiper-pagination',
            nextButton: '.swiper-button-next',
            prevButton: '.swiper-button-prev',
            paginationClickable: true,
            spaceBetween: 0,
            centeredSlides: true,
            autoplay: 3000,
            autoplayDisableOnInteraction: false,
            loop: true,
            paginationType: 'fraction',
        });
    });  
}) 
  • 如果使用的是vue-resource, 那么当使用this.$http.post时除了第二个参数指定传递的参数外,可能还需要加个第三个参数, {emulateJSON:true},后端才能正确的获得传参。。

极验使用

html:

<form id="form1" action="<?php echo siteurl('mobile/user/dologin');?>" onsubmit="return false">
        <ul>
            <li class="mobile clearfix">
                <input type="tel" id="user_name" name="user_name" maxlength="11" onkeyup="this.value = this.value.replace(/[^\d]/g, '')" class="fl" value="" placeholder="请输入手机号码" maxlength="11"   />
                <i></i>
            </li>

            <div id="embed-captcha"></div>
            <p id="wait" class="show">正在加载验证......</p>

            <li class="code clearfix">
                <input type="tel" id="verify" name="verify" class="fl" placeholder="请输入短信验证码" />
                <a href="javascript:;" id="getGeet" class="fr" data-text="获得验证码">获得验证码</a>
                <input type="hidden" id="embed-submit" />
            </li>
        </ul>
        <input type="hidden" name="login_type" value="1">
    </form>

js:

var canGetMobileVerify = true;

    var handlerEmbed = function (captchaObj) {
        $("#getGeet").unbind('click').click(function() {
            var user_name = $("#user_name").val();
            if(!isMobile(user_name)){
                show_warn("请输入正确的手机号");
                return;
            }

            captchaObj.verify();
        });
        captchaObj.bindForm('#form1');

        // 将验证码加到id为captcha的元素里,同时会有三个input的值:geetest_challenge, geetest_validate, geetest_seccode
        captchaObj.appendTo("#embed-captcha");
        captchaObj.onReady(function () {
            $("#wait")[0].className = "hide";
        }).onSuccess(function(){
            getMobileCaptcha();
        }).onError(function(){
            
        });

    };

    $.ajax({
        // 获取id,challenge,success(是否启用failback)
        url: "<?php echo siteurl('mobile/GeeTest/init') ?>?",
        type: "get",
        cache: false,
        dataType: "json",
        success: function (data) {
            // 使用initGeetest接口
            // 参数1:配置参数
            // 参数2:回调,回调的第一个参数验证码对象,之后可以使用它做appendTo之类的事件
            initGeetest({
                gt: data.gt,
                challenge: data.challenge,
                new_captcha: data.new_captcha,
                product: "bind", // 产品形式,包括:float,embed,popup。注意只对PC版验证码有效
                offline: !data.success // 表示用户后台检测极验服务器是否宕机,一般不需要关注
                // 更多配置参数请参见:http://www.geetest.com/install/sections/idx-client-sdk.html#config
            }, handlerEmbed);
        }
    });

    function getMobileCaptcha() {
        clear_warn();

        var user_name = $("#user_name").val(),
            _this = $(this),
            geetest_challenge = $("#form1 input[name=geetest_challenge]").val(),
            geetest_validate = $("#form1 input[name=geetest_validate]").val(),
            geetest_seccode = $("#form1 input[name=geetest_seccode]").val();

        if(!isMobile(user_name)){
            show_warn("请输入正确的手机号");
            return;
        }

        if (!geetest_challenge || !geetest_validate || !geetest_seccode) {
            show_warn("请先完成验证");
            return;
        }

        if (!canGetMobileVerify) {
            return;
        }

        canGetMobileVerify = false;
        $.ajax({
            url: '<?php echo siteurl('mobile/user/get_verify') ?>',
            type: 'post',
            dataType: 'json',
            data: {user_name: user_name, geetest_challenge: geetest_challenge, geetest_validate: geetest_validate, geetest_seccode: geetest_seccode},
            error:function(){alert('error')},
            success: function(result){
                if(result.status == 1){
                    //发送成功
                    $("#box p").text("短信已发送");
                    $("#mask").addClass("active");
                    $("#box").addClass("active");
                    setTimeout(function() { 
                        $("#mask").removeClass("active");
                        $("#box").removeClass("active");
                    }, 1500);     
                    
                    flag = false;

                    $("#getGeet").countDown({
                        leftSeconds: 60,
                        format: 's',
                        endCallback: function(txt_obj){
                            txt_obj.text(txt_obj.data('text'));
                            canGetMobileVerify = true;
                        }
                    });
                }else {
                    show_warn(result.message);
                }
            }
        });
    }

php:

class GeeTest extends Mobile_Controller{

    function init(){
        if(!$this->session->userdata('openid')){
            exit('err');
        }
        
        require_once APPPATH . "libraries/geetest/lib/class.geetestlib.php";
        require_once APPPATH . "libraries/geetest/config/config.php";
        $GtSdk = new GeetestLib(CAPTCHA_ID, PRIVATE_KEY);

        $data = array(
            "user_id"     => $this->session->userdata('openid'),
            "client_type" => "h5",
            "ip_address"  => $this->input->ip_address(),
        );

        $status = $GtSdk->pre_process($data, 1);
        $_SESSION['gtserver'] = $status;

        echo $GtSdk->get_response_str();
    }

}


    /**
     * 获取短信验证码 ajax
     */
    public function get_verify() {
        $user_name = $this->input->post('user_name');
        $geetest_challenge = $this->input->post('geetest_challenge');
        $geetest_validate = $this->input->post('geetest_validate');
        $geetest_seccode = $this->input->post('geetest_seccode');

        if (!$geetest_challenge || !$geetest_validate || !$geetest_seccode) {
            ajax_return(null, '请完成验证', 0);
            exit();
        }

        require_once APPPATH . "libraries/geetest/lib/class.geetestlib.php";
        require_once APPPATH . "libraries/geetest/config/config.php";
        $GtSdk = new GeetestLib(CAPTCHA_ID, PRIVATE_KEY);

        $data = array(
            "user_id"     => $this->session->userdata('openid'),
            "client_type" => "h5",
            "ip_address"  => $this->input->ip_address(),
        );

        if (isset($_SESSION['gtserver']) && $_SESSION['gtserver'] == 1) {   //服务器正常
            $result = $GtSdk->success_validate($geetest_challenge, $geetest_validate, $geetest_seccode, $data);
            if (!$result) {
                ajax_return(null, '请完成验证', 0);
                exit();
            }
        }else{  //服务器宕机,走failback模式
            if (!$GtSdk->fail_validate($geetest_challenge, $geetest_validate, $geetest_seccode)) {
                ajax_return(null, '请完成验证', 0);
                exit();
            }
        }
}

apache laravel .htaccess

<IfModule mod_rewrite.c>
    <IfModule mod_negotiation.c>
        Options -MultiViews
    </IfModule>

    RewriteEngine On

    # Redirect Trailing Slashes If Not A Folder...
      #RewriteCond %{REQUEST_FILENAME} !-d
	  RewriteCond $1 !^(index\.php|assets|css|js|img|cache|favicon.ico|uploads|ueditor)
      RewriteRule ^(.*)/$ /$1 [L,R=301]

    # Handle Front Controller...
      #RewriteCond %{REQUEST_FILENAME} !-d
      RewriteCond %{REQUEST_FILENAME} !-f
	  RewriteCond $1 !^(index\.php|assets|css|js|img|cache|favicon.ico|uploads|ueditor)
      RewriteRule ^ index.php [L]

    # Handle Authorization Header
      RewriteCond %{HTTP:Authorization} .
      RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
</IfModule>

linux常用命令

  1. 跟踪所有php-fpm进程,ps -ef|grep php-fpm|awk '{print " -p " $2" -e trace=file -tt -T -v -f -s 10000 -o /a/strace_"$2".log"}'|xargs strace ; 跟踪指定进程:strace -o output.txt -T -tt -e trace=all -p 33923
  2. 重启php-fpm:service php-fpm restart/etc/init.d/php-fpm {start|stop|force-quit|restart|reload|status}
  3. 删除指定目录下的文件和文件夹:rm -rf xxx/*
  4. 删除.svn目录:find . -type d -name ".svn"|xargs rm -rf
  5. 在windows下上传的shell linux报错bad interpreter: set ff=unix即可。
  6. 服务器之间内网同步文件: scp -r web/ 10.22.11.1:/vdb1/, -r会把包括文件夹在内一起复制过去
  7. 导出error log指定时间段的日志:sed -n '/2018\/10\/02/,/2018\/10\/02/p' /vdb1/nginx_log/error_log > /root/081002.txt 也可以找包含某个具体时间段的数据:sed -n -r '/2021\/12\/29 00:0/p' ./xx.cn.log >22.txt
  8. 平滑重启php-fpm:ps -aux | grep php-fpm , kill -SIGUSR2 process_id
  9. 查看目录的文件夹大小情况: du -sh *
  10. 同步某个文件夹下的文件到其他所有文件夹:
#!/bin/bash

for dir in $(ls ./ -F)
do
	if [[ -d $dir ]] && [[ $dir != "syncDir/" ]];then
		cp -r syncDir/* $dir
	fi
done 
rm syncDir/* -rf
  1. ulimit -n永久设置:
    vi /etc/pam.d/login,添加 session required pam_limits.so
    vi /etc/security/limits.conf添加:
* soft nproc 1048576
* hard nproc 1048576
* soft nofile 1048576
* hard nofile 1048576
vi /etc/rc.local, 添加  ulimit -n 1048576

nginx 需要额外修改:vi /etc/systemd/system/nginx.service 再service下添加 LimitNOFILE=100000

  1. crontab在线测试工具
  2. find ./ -maxdepth 2 -name .env |xargs grep "xxx.com"
  3. ps aux|head -n1;ps aux|grep -v PID|sort -nr -k6|head -n100内存占用量前100
  4. 删除指定名称的所有进程:ps -ef | grep firefox | grep -v grep | cut -c 9-15 | xargs kill -s 9
  5. 定时任务防止重复运行:*/3 * * * * /usr/bin/flock -w 0 /data/cron-minute-lock.lock /data/cron-minute.sh
  6. 匹配vhost所有站:grep -r "server_name" ./ |cut -d " " -f 2,3,4,5,6,7,8 > site.txt
  7. 压缩文件夹:tar -czvf xxx.tar.gz xxx/
  8. nginx https配置
  9. 删除7天前的文件:find ./ -ctime +7 -name \*.xlsx | xargs rm -f
  10. sed正则替换(server_name后追加支持端口访问):sed -i -r "/.*server_name.*/s#(.*);#\1 127.0.0.1:${current_port};#" $dir

更多

mysql不常用sql

  • 授权 %:所有远程主机
    grant all privileges on database_name.* to 'username'@'%' identified by 'password' with grant option;
  • mysql5.7设置密码
    set password for 'root'@'127.0.0.1' = password('123');
  • 删除重复记录只保留id最大的一条:
DELETE
FROM goods
WHERE id IN(SELECT *
            FROM (SELECT
                    id
                  FROM goods
                  WHERE good_name IN(SELECT
                                        good_name
                                      FROM goods g
                                      
                                      GROUP BY good_name
                                      HAVING COUNT(1) > 1)
                      AND id NOT IN(SELECT
                                      MAX(id) AS id
                                    FROM goods
                                    GROUP BY good_name
                                    HAVING COUNT(1) > 1)) a);
  • 查询文章的最新2条评论:
SELECT c.*
FROM `comment` c
LEFT JOIN `comment` c2 ON c2.arc_id=c.arc_id AND c.id<=c2.id
WHERE c.arc_id IN(1,2,3)
GROUP BY c.arc_id,c.id
HAVING COUNT(c2.id)<=2
  • 将\u编码的转成unicode函数
DELIMITER //

CREATE FUNCTION STRINGDECODE(str TEXT CHARSET utf8)
RETURNS text CHARSET utf8 DETERMINISTIC
BEGIN
declare pos int;
declare escape char(6) charset utf8;
declare unescape char(3) charset utf8;
set pos = locate('\\u', str);
while pos > 0 do
    set escape = substring(str, pos, 6);
    set unescape = char(conv(substring(escape,3),16,10) using ucs2);
    set str = replace(str, escape, unescape);
    set pos = locate('\\u', str, pos+1);
end while;
return str;
END//

DELIMITER ;
  • 不支持json转换的mysql版本的json字符串截取
SUBSTRING(spec_json, LOCATE(':"', spec_json) + 2, LOCATE('",', spec_json) -  30 )
  • mysqld不存在可以
which mysqld
cp /usr/local/mysql/bin/mysqld /usr/local/bin/mysqld
  • mysql修改了datadir后初始化数据库:mysqld --initialize-insecure --datadir=/www/mysqlData --user=mysql

js

  • js排列组合:
/**
 * 所有选项的排列组合
 * @param  {array} data 二维数组 [['a', 'b'], ['c']]
 * @return {array}      二维数组 [['a', 'c'], ['b', 'c']]
 */
function getAllGroups(data) {
    return Object.values(data).reduce((result,property)=>{
        return property.reduce((acc,value)=>{
            return acc.concat(result.map(ele => [].concat(ele,value)))
        },[]);
    });
}
  • 算力验证码:
$randstr = md5(uniqid(mt_rand(1, 1000),true));//,'<br>';
$rand = mt_rand(1, 10000);
$rand = 10000;

echo $privateText =  $randstr . md5($randstr . $rand),'<br>';
echo $privateKey= md5($rand . $randstr);

js:
let result = 'fe9a2271a9899070bbcf32159b0d393fdb72939e594d1fa15387be9b61730e54';
let range = 10000;
let randomString = result.substring(0, 32)
let valueString = result.substring(32)
let answerString
for (let i = 1; i <= range; i++) {
	let s = md5(randomString + i)

	if (s == valueString) {
		answerString = md5(i + randomString)
		break
	}
}
  • jq each遍历可以在循环体里使用this.xx获取到值,但这可能会在和插件结合并且插件使用了call/apply调用你的带遍历的函数时引发bug,解决方案是直接使用each的第二个参数读取值而不是使用this

1对多关系的表,获取1表的20条数据,同时获取多表最新几条数据

获取用户的订单时只获取最新的2条:

$dbprefix = config('database.connections.mysql.prefix');
$orderCount = 2;
$users = User::with(['order' => function($q) use($dbprefix, $orderCount){    
    $q->select(['o.order_id','o.user_id','o.order_sn'])
    ->leftJoin('order as o', function($join){
        $join->on('o.user_id', '=', 'order.user_id')->on('order.order_id', '>=', 'o.order_id');
    })
    ->groupBy('o.user_id')
    ->groupBy('o.order_id')
    ->having(DB::raw("count({$dbprefix}o.order_id)"), '<=', $orderCount);
}])->limit(20)->get();

gitlab custom hooks每次提交同步到web目录

假设A服务器是gitlab仓库同时也是测试环境,当客户端提交到develop分支时,需要同时把代码更新到A服务器对应的web目录;当提交到master分支时,需要同步更新到B生产服务器的web目录。

  1. 首先B服务器要支持A服务器的ssh公钥登录
  2. A服务器的web目录需要为git客户端,将已有的目录作为git客户端, 并且所有者是git: chown git:git webdir -R
  3. 在A服务器对应的gitlab仓库根目录下新建custom_hooks, 并chown git:git custom_hooks -R
  4. 在custom_hooks下新建post-receive

apache常用命令

  • ab -n 50 -c 50 -C ci_session=0027c47b3aa38745731ffb67733ea85282112113 http://p-test.com/new_mobile_branch/mobile/user/lottery?use_points=1

notedpad++导出宏

C:\Documents and Settings\%username%\Application Data\Notepad++\shortcuts.xml文件里保存了所有的宏,我使用的宏:

<NotepadPlus>
    <InternalCommands />
    <Macros>
        <Macro name="&#x6E05;&#x7A7A;&#x4FDD;&#x5B58;" Ctrl="yes" Alt="no" Shift="no" Key="8">
            <Action type="0" message="2013" wParam="0" lParam="0" sParam="" />
            <Action type="0" message="2326" wParam="0" lParam="0" sParam="" />
            <Action type="2" message="0" wParam="41006" lParam="0" sParam="" />
        </Macro>
        <Macro name="&#x5220;&#x9664;&#x884C;" Ctrl="yes" Alt="no" Shift="no" Key="68">
            <Action type="0" message="2453" wParam="0" lParam="0" sParam="" />
            <Action type="0" message="2452" wParam="0" lParam="0" sParam="" />
            <Action type="0" message="2180" wParam="0" lParam="0" sParam="" />
            <Action type="0" message="2180" wParam="0" lParam="0" sParam="" />
        </Macro>
        <Macro name="&#x53EF;&#x9009;&#x9879;&#x8F6C;php&#x6620;&#x5C04;" Ctrl="no" Alt="no" Shift="no" Key="0">
            <Action type="3" message="1700" wParam="0" lParam="0" sParam="" />
            <Action type="3" message="1601" wParam="0" lParam="0" sParam=" " />
            <Action type="3" message="1625" wParam="0" lParam="1" sParam="" />
            <Action type="3" message="1602" wParam="0" lParam="0" sParam="\r\n" />
            <Action type="3" message="1702" wParam="0" lParam="768" sParam="" />
            <Action type="3" message="1701" wParam="0" lParam="1609" sParam="" />
            <Action type="3" message="1700" wParam="0" lParam="0" sParam="" />
            <Action type="3" message="1601" wParam="0" lParam="0" sParam="(.*):(.*)" />
            <Action type="3" message="1625" wParam="0" lParam="2" sParam="" />
            <Action type="3" message="1602" wParam="0" lParam="0" sParam="&apos;$1&apos; =&gt; &apos;$2&apos;," />
            <Action type="3" message="1702" wParam="0" lParam="768" sParam="" />
            <Action type="3" message="1701" wParam="0" lParam="1609" sParam="" />
        </Macro>
        <Macro name="&#x5BFC;&#x51FA;&#x5B57;&#x6BB5;&#x8F6C;&#x6CE8;&#x91CA;" Ctrl="no" Alt="no" Shift="no" Key="0">
            <Action type="3" message="1700" wParam="0" lParam="0" sParam="" />
            <Action type="3" message="1601" wParam="0" lParam="0" sParam=", " />
            <Action type="3" message="1625" wParam="0" lParam="1" sParam="" />
            <Action type="3" message="1602" wParam="0" lParam="0" sParam="\n" />
            <Action type="3" message="1702" wParam="0" lParam="768" sParam="" />
            <Action type="3" message="1701" wParam="0" lParam="1609" sParam="" />
            <Action type="3" message="1700" wParam="0" lParam="0" sParam="" />
            <Action type="3" message="1601" wParam="0" lParam="0" sParam=".*" />
            <Action type="3" message="1625" wParam="0" lParam="2" sParam="" />
            <Action type="3" message="1602" wParam="0" lParam="0" sParam="// $0" />
            <Action type="3" message="1702" wParam="0" lParam="768" sParam="" />
            <Action type="3" message="1701" wParam="0" lParam="1609" sParam="" />
            <Action type="3" message="1700" wParam="0" lParam="0" sParam="" />
            <Action type="3" message="1601" wParam="0" lParam="0" sParam="\n" />
            <Action type="3" message="1625" wParam="0" lParam="1" sParam="" />
            <Action type="3" message="1602" wParam="0" lParam="0" sParam="\n$row-&gt;xxx,\n" />
            <Action type="3" message="1702" wParam="0" lParam="768" sParam="" />
            <Action type="3" message="1701" wParam="0" lParam="1609" sParam="" />
            <Action type="3" message="1700" wParam="0" lParam="0" sParam="" />
            <Action type="3" message="1601" wParam="0" lParam="0" sParam=".*$" />
            <Action type="3" message="1625" wParam="0" lParam="2" sParam="" />
            <Action type="3" message="1702" wParam="0" lParam="1792" sParam="" />
            <Action type="3" message="1701" wParam="0" lParam="1" sParam="" />
        </Macro>
        <Macro name="&#x8F6C;&#x9017;&#x53F7;&#x4E00;&#x884C;" Ctrl="no" Alt="no" Shift="no" Key="0">
            <Action type="3" message="1700" wParam="0" lParam="0" sParam="" />
            <Action type="3" message="1601" wParam="0" lParam="0" sParam="\r\n" />
            <Action type="3" message="1625" wParam="0" lParam="1" sParam="" />
            <Action type="3" message="1602" wParam="0" lParam="0" sParam="," />
            <Action type="3" message="1702" wParam="0" lParam="1792" sParam="" />
            <Action type="3" message="1701" wParam="0" lParam="1609" sParam="" />
        </Macro>
    </Macros>
    <UserDefinedCommands>
        <Command name="Launch in Firefox" Ctrl="yes" Alt="yes" Shift="yes" Key="88">firefox &quot;$(FULL_CURRENT_PATH)&quot;</Command>
        <Command name="Launch in IE" Ctrl="yes" Alt="yes" Shift="yes" Key="73">iexplore &quot;$(FULL_CURRENT_PATH)&quot;</Command>
        <Command name="Launch in Chrome" Ctrl="yes" Alt="yes" Shift="yes" Key="82">chrome &quot;$(FULL_CURRENT_PATH)&quot;</Command>
        <Command name="Launch in Safari" Ctrl="yes" Alt="yes" Shift="yes" Key="70">safari &quot;$(FULL_CURRENT_PATH)&quot;</Command>
        <Command name="Get php help" Ctrl="no" Alt="yes" Shift="no" Key="112">http://www.php.net/%20$(CURRENT_WORD)</Command>
        <Command name="Google Search" Ctrl="no" Alt="yes" Shift="no" Key="113">http://www.google.com/search?q=$(CURRENT_WORD)</Command>
        <Command name="Wikipedia Search" Ctrl="no" Alt="yes" Shift="no" Key="114">http://en.wikipedia.org/wiki/Special:Search?search=$(CURRENT_WORD)</Command>
        <Command name="Open file" Ctrl="no" Alt="yes" Shift="no" Key="116">$(NPP_DIRECTORY)\notepad++.exe $(CURRENT_WORD)</Command>
        <Command name="Open in another instance" Ctrl="no" Alt="yes" Shift="no" Key="117">$(NPP_DIRECTORY)\notepad++.exe $(CURRENT_WORD) -nosession -multiInst</Command>
        <Command name="Send via Outlook" Ctrl="yes" Alt="yes" Shift="yes" Key="79">outlook /a &quot;$(FULL_CURRENT_PATH)&quot;</Command>
    </UserDefinedCommands>
    <PluginCommands />
    <ScintillaKeys>
        <ScintKey ScintID="2469" menuCmdID="42010" Ctrl="no" Alt="no" Shift="no" Key="0" />
    </ScintillaKeys>
</NotepadPlus>

laravel other

  • 使用DB::unprepared()可以执行lock tables之类的命令
  • 如果是跨库关联,比如pgsql模型1对1mysql模型,那么需要两个模型都指定protected $connection,否则引号会判断错
  • 可使用$request->offsetSet('key', 'value')在中间件里修改/新增参数, offsetSet设置的参数可直接通过$request->all()获取到,如果通过$request->aa = 'value'设置的话不会在->all()里获取到
  • 可通过$model->newCollection([$model1, $model2])把原本是很多模型的数组变成可以使用$models->load()预加载关系的形式
  • 在列表页包含筛选条件的时候,如果用模型进行筛选,然后还要group by某个字段,那么普通的$model->groupBy('field')->count()获取到的总数是第一个分组的数量是错的,解决方案是在获取总数时使用$model->count(DB::raw('distinct field'))获取,这样原本带筛选条件的模型, clone一下就可以用来统计总数了
  • linux队列简单监听执行:nohup php artisan queue:work --queue=email --timeout=180 --tries=3 --delay=1 > /dev/null 2>&1 &
  • api如果用的cookie认证,且用了encryptCookies中间件,那么在api加载的中间件顺序时候需要把encryptCookies放第一个
  • 使用集合的merge函数时,如果是普通的集合(Illuminate\Support\Collection)那么是正常覆盖相同键的;如果集合是模型(从数据库查询出来的Eloquent对象),那么merge时会使用主键,而不是指定的key,所以keyBy无效, 源码在\vendor\laravel\framework\src\Illuminate\Database\Eloquent\Collection.phpmerge函数,可以看到使用了模型的getKey()函数获取主键。

mysql explain/desc说明

  • id:代表优先级 id值越大,越先执行,id值相同,从上往下执行。
  • select_type:表示select类型 取值如下:
    • simple 简单表 即不使用表连接或者子查询
    • primary 包含union或者子查询的主查询 即外层的查询
    • union UNION中的第二个或者后面的查询语句
    • subquery 一般子查询中的子查询被标记为subquery,也就是位于select列表中的查询
    • derived 派生表 该临时表是从子查询派生出来的
    • ..
  • type:表示MySQL在表中查找数据的方式,或者叫访问类型,以下对于type取值的说明 从上往下性能由最差到最好
    • all:全表扫描,MySQL遍历全表来找到匹配的行
    • index:索引全扫描,MySQL遍历整个索引来查询匹配的行
    • range:索引范围扫描,常见于<、<=、>、>=、between等操作符
    • ref:使用非唯一索引或唯一索引的前缀扫描,返回匹配的单行数据
    • eq_ref:类似ref,区别就在于使用的索引是唯一索引,简单来说,就是多表连接中使用primary key或者unique index作为关联条件。
    • const/system:单表中最多有一个匹配行,查询起来非常迅速,常见于根据primary key或者唯一索引 unique index进行的单表查询
    • null:mysql不用访问表或者索引,直接就能够得到查询的结果,例如select 1+2 as result。
  • possible_keys:表示查询时可能使用的索引
  • key:表示实际使用的索引
  • key_len:使用到索引字段的长度
  • rows:扫描行数
  • Extra:执行情况的说明和描述,包含不适合在其他列中显示但是对执行计划非常重要的额外信息,常用取值如下:
    • Using index:直接访问索引就取到了数据,高性能的表现。
    • Using where:直接在主键索引上过滤数据,必带where子句,而且用不上索引
    • Using index condition:先条件过滤索引,再查数据,
    • Using filesort:使用了外部文件排序 只要见到这个 就要优化掉
    • Using temporary:创建了临时表来处理查询 只要见到这个 也要尽量优化掉

git常用命令

  1. 导出修改的文件:
        a. git archive --format=zip HEAD `git diff --name-only 8bbf69c253801228ff504ab080ce7cf44a924971` > 1.zip
        b. git diff-tree -r --no-commit-id --name-only c9b0b3d64ac12a5a66922f7f43a4d5ea6b308edd | xargs tar -rf 1.tar

  2. 忽略已经加入到版本控制里的文件
        a. 先加到.gitignore
        b. 执行git rm --cached xxx/xxx.xx(--cached会保留本地文件,不加则会删除本地文件)
        c. git commit -m 'ignore'
        d. git push

  3. 将某个分支中的某个提交合并到master中(参考)
        a. git checkout master
        b. git cherry-pick version-md5 要回滚刚才的pick:git cherry-pick --abort,有冲突的话可以解决后--continue

如果要pick多个版本使用:git cherry-pick 26d5bb1^..af336ef, 包含26d5bb1版本。

  1. 记住密码: git config --global credential.helper store

5.变更远程origin:
- git remote -v
- git remote rm origin
- git remote add origin xxx

  1. git stash暂存不提交,用于切换到另一分支继续开发, 然后切回来后git stash apply即可

  2. git config --unset user.password: 移除指定的config项

  3. git config http.postBuffer 524288000;git config http.sslVerify "false":解决下载慢和下载证书报错问题,不行的话再去git ip address看下ip然后修改hosts

  4. git fetch --all; git reset --hard origin/master: 拉取最新文件,忽略本地修改

  5. 统计指定时间段代码提交行数: git log --since=2019-03-25 --until=2019-05-26 --author="$(git config --get user.name)" --pretty=tformat: --numstat | gawk '{ add += $1 ; subs += $2 ; loc += $1 - $2 } END { printf "added lines: %s removed lines : %s total lines: %s\n",a dd,subs,loc }' -

  6. 切换分支:git checkout branchName

  7. 查看全局配置:git config --global --list , 查看项目配置: git config --local --list, 查看系统配置:git config --system --list

阳明心学学习总结

    本次学习经历了从刚开始抱着一颗好奇的心想看看这到底是卖的什么药,慢慢的感受到了阳明心学里几句不离本的致良知之心,到后来开始从想和说到做的转变。期间总共分享了35则心得感悟,其中有依葫芦画瓢的,有花心思去写的,也有敷衍了事的。不管如何,最终还是有两大收获的。

1. 学会了自律

    还记得写18年展望的时候,我写了每周跑步两次。刚开始确实挺有干劲的,但后来不知不觉间就变得三天打鱼、两天晒网了,偶尔也有达标的几周,但实际上跑的距离也不是很远。自从经历了阳明心学之后,跑步计划最终得以落实,现在基本上可以坚持每天跑5公里了。这不仅仅得归功于每次写分享到群里时要带上“践行承诺的行为”这种形式上的监督,主要还是通过这慢慢得培养了自己的自律性,从而让我感受到了跑步的益处,便开始能够慢慢坚持起来。其实跑步还是挺不错的,全身运动,跑几圈出一身汗(买质量好的全棉速干衣服不会粘身体很舒服),然后回家喝一瓶5100,再洗个热水澡、吃个饭、休息会、睡个安稳觉,第二天醒来会觉得世界如此美好,坚持下来便很少会有上班经常打哈欠、眼皮打架这样的亚健康状态了。

2. 知晓了需要在心上下功夫

    俗话说“江山易改,本性难移”、“人不为己天诛地灭”,在接触到这些词句时被我断章取义的当成名句记了下来,再加上独生子女家庭普遍的娇生惯养方式,利己之心便慢慢的开始生根发芽,一旦遇到些什么不顺心的事情时便会恼羞成怒,心情败坏。自从经历了阳明心学之后,慢慢得开始反思自己,我这么做是否正确?这么做是否是我内心的本意还是碍于面子等原因的蒙蔽内心的行为?当我这么做时他人的感受会如何?虽然“本性难移”,但并不是不能移,只要坚持反思坚持从良知之心出发,相信这段偏移的本性曲线最终会回到正轨。

    曾经冒出过个想法,如果所有人都学了阳明心学,那么社会便会是一个以礼待人、和睦融洽的和谐社会。但这想法就等同于我学了这个,那么你也最好要学这个,你不学,哦,那你就不一定是个好人,我也不一定需要以好人姿态待你。其实这其中夹杂了私欲,让别人去干嘛干嘛,把自己的想法和要求强加于人了。如果换种方式,即使他人不能面面俱到亦或是只关注了他自己的利益,我也不因这个而影响到了自己的心态从而做出些与本心不符的行为,而是继续以利他宽容之心待他,那么我相信这件事便可以做好,渐渐得周边的人也会感受到良知利他之心,说不定正能量还会得以继续传递下去。

    最后,摒弃浮躁的内心,放缓脚步,多反思一言一行,你并不比他人走得慢。

ssh公钥登录

参考
假设从A服务器登录B服务器
在A服务器:

  1. ssh-keygen
  2. ssh-copy-id user@host
  3. ssh user@host之后下次即可自动登录

B服务器:

  1. vi /etc/ssh/sshd_config
  2. /RSAAuthentication 确保已打开:RSAAuthentication yesPubkeyAuthentication yesAuthorizedKeysFile .ssh/authorized_keys
  3. 如果没打开第2步的话打开之后再执行service sshd restart

最后在A服务器执行ssh登录即可。

laravel 5 windows下支持memcache

php_memcached.dll扩展在windows下暂时找不到安装方式,原因

所以laravel下使用memcache扩展并修改相关的系统文件。原文地址, 防止以后访问不了,这边也记录下。
打包下载
手工修改:

  • vendor/laravel/framework/src/Illuminate/Session/SessionManager.php,并增加如下代码:
/**
     * Create an instance of the Memcached session driver.
     *
     * @return IlluminateSessionStore
     */
    protected function createMemcacheDriver()
    {
        return $this->createCacheBased('memcache');
    }
  • vendor/laravel/framework/src/Illuminate/Cache/CacheManager.php 增加以下代码:
/**
     * Create an instance of the Memcache cache driver.
     *
     * @return IlluminateCacheMemcachedStore
     */
    protected function createMemcacheDriver(array $config)
    {
        $prefix = $this->getPrefix($config);

        $memcache = $this->app['memcache.connector']->connect($config['servers']);

        return $this->repository(new MemcacheStore($memcache, $prefix));
    }
  • /vendor/laravel/framework/src/Illuminate/Cache/CacheServiceProvider.php 修改代码:
/**
     * Register the service provider.
     *
     * @return void
     */
    public function register()
    {
        $this->app->singleton('cache', function ($app) {
            return new CacheManager($app);
        });

        $this->app->singleton('cache.store', function ($app) {
            return $app['cache']->driver();
        });

        $this->app->singleton('memcached.connector', function () {
            return new MemcachedConnector;
        });

        $this->app->singleton('memcache.connector', function () {
            return new MemcacheConnector;
        });

        $this->registerCommands();
    }

/**
     * Get the services provided by the provider.
     *
     * @return array
     */
    public function provides()
    {
        return [
            'cache', 'cache.store', 'memcached.connector', 'command.cache.clear', 'command.cache.table', 'memcache.connector',
        ];
    }
  • 新增文件 /vendor/laravel/framework/src/Illuminate/Cache/MemcacheConnector.php:
<?php
namespace Illuminate\Cache;

use Memcache;
use RuntimeException;

class MemcacheConnector {

    /**
     * Create a new Memcached connection.
     *
     * @param array  $servers
     * @return Memcache
     */
    public function connect(array $servers)
    {
        $memcache = $this->getMemcache();

        // For each server in the array, we'll just extract the configuration and add
        // the server to the Memcache connection. Once we have added all of these
        // servers we'll verify the connection is successful and return it back.
        foreach ($servers as $server)
        {
            $memcache->addServer($server['host'], $server['port'], $server['weight']);
        }

        if ($memcache->getVersion() === false)
        {
            throw new RuntimeException("Could not establish Memcache connection.");
        }

        return $memcache;
    }

    /**
     * Get a new Memcache instance.
     *
     * @return Memcached
     */
    protected function getMemcache()
    {
        return new Memcache;
    }
}
  • 新增文件 /vendor/laravel/framework/src/Illuminate/Cache/MemcacheStore.php:
<?php

namespace Illuminate\Cache;

use Memcache;
use Illuminate\Contracts\Cache\Store;

class MemcacheStore extends TaggableStore implements Store
{
    /**
     * The Memcached instance.
     *
     * @var \Memcached
     */
    protected $memcached;

    /**
     * A string that should be prepended to keys.
     *
     * @var string
     */
    protected $prefix;

    public function __construct(Memcache $memcache, $prefix = '')
    {
        $this->memcache = $memcache;
        $this->prefix = strlen($prefix) > 0 ? $prefix.':' : '';
    }

    /**
     * Retrieve an item from the cache by key.
     *
     * @param  string|array  $key
     * @return mixed
     */
    public function get($key)
    {
        return $this->memcache->get($this->prefix.$key);
    }


    /**
     * Retrieve multiple items from the cache by key.
     *
     * Items not found in the cache will have a null value.
     *
     * @param  array  $keys
     * @return array
     */
    public function many(array $keys)
    {
        $prefixedKeys = array_map(function ($key) {
            return $this->prefix.$key;
        }, $keys);

        $values = $this->memcache->getMulti($prefixedKeys, null, Memcache::GET_PRESERVE_ORDER);

        if ($this->memcache->getResultCode() != 0) {
            return array_fill_keys($keys, null);
        }

        return array_combine($keys, $values);
    }

    /**
     * Store an item in the cache for a given number of minutes.
     *
     * @param  string  $key
     * @param  mixed   $value
     * @param  int     $minutes
     * @return void
     */
    public function put($key, $value, $minutes)
    {
        $compress = is_bool($value) || is_int($value) || is_float($value) ? false : MEMCACHE_COMPRESSED;
        $this->memcache->set($this->prefix.$key, $value, $compress, $minutes * 60);
    }

    /**
     * Store multiple items in the cache for a given number of minutes.
     *
     * @param  array  $values
     * @param  int  $minutes
     * @return void
     */
    public function putMany(array $values, $minutes)
    {
        $prefixedValues = [];

        foreach ($values as $key => $value) {
            $prefixedValues[$this->prefix.$key] = $value;
        }

        $this->memcache->setMulti($prefixedValues, $minutes * 60);
    }
     /**
     * Store an item in the cache if the key doesn't exist.
     *
     * @param  string  $key
     * @param  mixed   $value
     * @param  int     $minutes
     * @return bool
     */
    public function add($key, $value, $minutes)
    {
        return $this->memcache->add($this->prefix.$key, $value, $minutes * 60);
    }

    /**
     * Increment the value of an item in the cache.
     *
     * @param  string  $key
     * @param  mixed   $value
     * @return int|bool
     */
    public function increment($key, $value = 1)
    {
        return $this->memcache->increment($this->prefix.$key, $value);
    }

    /**
     * Decrement the value of an item in the cache.
     *
     * @param  string  $key
     * @param  mixed   $value
     * @return int|bool
     */
    public function decrement($key, $value = 1)
    {
        return $this->memcache->decrement($this->prefix.$key, $value);
    }

    /**
     * Store an item in the cache indefinitely.
     *
     * @param  string  $key
     * @param  mixed   $value
     * @return void
     */
    public function forever($key, $value)
    {
        $this->put($key, $value, 0);
    }

    /**
     * Remove an item from the cache.
     *
     * @param  string  $key
     * @return bool
     */
    public function forget($key)
    {
        return $this->memcache->delete($this->prefix.$key);
    }

    /**
     * Remove all items from the cache.
     *
     * @return void
     */
    public function flush()
    {
        $this->memcache->flush();
    }

    /**
     * Get the underlying Memcached connection.
     *
     * @return \Memcached
     */
    public function getMemcached()
    {
        return $this->memcache;
    }

    /**
     * Get the cache key prefix.
     *
     * @return string
     */
    public function getPrefix()
    {
        return $this->prefix;
    }

    /**
     * Set the cache key prefix.
     *
     * @param  string  $prefix
     * @return void
     */
    public function setPrefix($prefix)
    {
        $this->prefix = ! empty($prefix) ? $prefix.':' : '';
    }
}
  • 修改config/cache.php 新增代码:
'memcache' => [
            'driver'  => 'memcache',
            'servers' => [
                [
                    'host' => env('MEMCACHED_HOST', '127.0.0.1'),
                    'port' => env('MEMCACHED_PORT', 11211),
                    'weight' => 100,
                ],
            ],
        ],
  • 最后,再需要用到memcache的地方把driver修改即可,eg:'driver' => 'memcache',

swap

  • 检查swap文件是否存在,返回空则不存在
    swapon -s
  • 查看文件系统,检查空间是否足够创建swap
    df -hal
  • 创建一个swap目录
    mkdir /swap
  • 创建并允许swap文件 16G
    dd if=/dev/zero of=/swap/swapfile bs=1024 count=16384000
  • 格式化swap文件
    mkswap -f /swap/swapfile
  • 激活swap
    swapon /swap/swapfile
  • 设置开机启动(vim 打开 /etc/fstab)
    /swap/swapfile swap swap defaults 0 0
  • 赋予Swap文件适当的权限
    chown root:root /tmp/swapfile
    chmod 600 /tmp/swapfile
  • 查看swappiness权重(swappiness=0 的时候表示最大限度使用物理内存,然后才是 Swap 空间,swappiness=100 的时候表示积极的使用 Swap 分区,并且把内存上的数据及时的搬运到 Swap 空间里面。)
    cat /proc/sys/vm/swappiness
  • 临时修改swappiness
    sysctl vm.swappiness=10
  • 要永久设置修改swappiness
    vim /etc/sysctl.conf
    在这个文档的最后加上这样一行
    # Search for the vm.swappiness setting. Uncomment and change it as necessary.
    vm.swappiness=10

linux误删文件恢复

  1. 需先确保磁盘不再有过多的读写,新的写入会把之前删除的节点覆盖掉,文件就没法恢复了
  2. 使用extundelete

简要说明:

  1. 首先执行 df 被删的目录 , 找到被删文件所在的磁盘,如:/dev/vdb1
  2. debugfs => open /dev/vdb1 => ls -d => 显示的内容中找到删除的文件夹,后面会包含一个<>包裹的信息,即是inode节点
  3. 然后可以--restore-inode指定节点或者extundelete /dev/vdb1 --restore-all

关于微服务化的api project demo

关于此项目

  • 该项目为微服务**的部分体现,采用了按照模块拆分成多个项目的形式(如订单模块、用户模块、商品模块等),目的是解决目前传统集中式架构的一些问题,eg:
    • 集中式形式如目前的3.0后台,模块间依赖严重、耦合度高导致的后期维护和复用以及定位解决bug上带来的困难,容易造成牵一发而动全身的局面。
    • 一旦后台服务器卡的话,会影响到调用方,如微信端,而且会影响全部接口。
    • 当业务需要扩展的时候现在都是直接复制一整套项目,这样会复制更多的bug以及后期当需要多个平台同步需求或者同步部分需求时的难以操作。
    • ...
  • 关于微服务化,或者说api化的一些优点:
    • 首先业务的复杂必然会造成代码的复杂,代码复杂性不可避免,但是api化之后可以一定程度上解耦。
    • 也有利于后期假设业务发展迅速,php性能没法支撑的情况下,可以通过使用其他语言重构某个模块,仅重构该模块即可,而无需重构调用方,因为走得都是api接口。
    • 调用方只需关注接口的输入和输出,而无需关注你的内部实现。
    • 便于扩展,当某个模块压力较大响应较慢时可以借鉴分布式架构,单独一台或者多台服务器。
    • 或许后期可以把它搞成容器化,那么可以解决单机上集中式架构的一个问题:当某个模块需要占用大量cpu或者内存时,会影响其他的用户体验,比如:有人下载个全年数据那么对整个系统的影响是很大的;而把它容器化之后,可以针对每个模块单独设置cpu或者内存上限,可减轻单台机器上某个模块的压力对整个系统的影响程度。
    • ...
  • 当然也有一些缺点:
    • 全部走http请求性能较差,但请求方式是有优化空间的
    • 跨项目的事务问题,不过有个初步方案(初期相信每个api都是可靠的,也就是需要跨项目调用不同api的业务只在当前项目实现事务控制,同时建议api请求在当前项目的事务结束之后再去调用;初期只做好数据记录,也就是调用失败api请求的入参及响应;后期会逐步完善并实现重试、回滚、补偿、告警等自动化功能)
    • ...

安装

  • clone各个具体模块项目后,需要每个项目走一遍以下流程:

  • copy .env.example .env

  • composer install

  • composer dump-autoload -o

  • 本地环境按照指定的api地址规则配置各个api项目的vhost。eg,userApi(直接按照项目名命名存放的文件夹,否则会造成api请求失败或者命令不可用):

    • 配置local-userapi.xx.com指向到对应的api项目的public目录
    • 修改C:\Windows\System32\drivers\etc\hosts,添加127.0.0.1 local-userapi.xx.com

    其他:

    • 到测试环境接口域名会变成:test-userapi.xx.com(可能不会配置域名解析, 所以公网请求不到,除非是对外接口运维会配个)
    • 到生产环境会变成:userapi.xx.com

命令

  • php artisan syncFile filePath: 将文件同步到其他api项目,说明:

    • filePath: 相对根目录路径,如:app\Support\helps.php

    可选参数:

    • --y: 不会询问会直接覆盖现有文件
    • --n: 不会询问也不会覆盖现有文件
    • --b: 会以_bak后缀备份下现有文件然后覆盖
  • php artisan deleteFile filePath: 删除所有api项目的指定文件,说明:

    • filePath: 相对根目录路径,如:app\Support\helps.php
    • 实际使用unlink函数删除如果文件存在的话
  • php artisan make:module moduleName childProject,childProject: 根据指定的业务模块名和关联的子项目名创建service和model,说明:

    • moduleName: 业务模块名,如: car
    • childProject: 关联的子项目名,多个以英文逗号隔开,如:zmq,5100

    可选参数:

    • --pk: primaryKey 表的主键 不传则默认为:moduleName_id
    • --t: tableName 表名 不传默认为:moduleName
    • --y: 不会询问会直接覆盖现有文件
    • --n: 不会询问也不会覆盖现有文件
    • --only-models: 只创建基本模型和子模型
    • --only-service: 只创建base service和子service

接口约定

幂等性是系统的接口对外一种承诺(而不是实现), 承诺只要调用接口成功, 外部多次调用对系统的影响是一致的. 声明为幂等的接口会认为外部调用失败是常态, 并且失败之后必然会有重试.

  • 部分接口需要保证幂等性(如订单支付通知接口),可通过业务代码本身实现,也可以缓存请求和响应,检测到重复请求后直接返回上一次的响应。
  • 如果其他子系统会涉及到数据变更(只是select类型就不用管),那么本地事务提交成功后才可调用其他子系统,目前系统没有分库,可能几个api项目涉及到同一个数据库。
  • 假如一个接口比如支付成功回调接口,调用方只需调用Order项目的一个接口,然后由Order项目去调用其他api项目,那么需注意:
    • 如果一个业务接口需要按顺序调用A、B、C三项目的接口,当B失败时,后续关联的接口也不需要调用,关联接口:C接口的参数是B接口的返回值
    • 调用接口按业务关联性从高到低调用。
  • 接口调整时需保证向后兼容,也就是兼容以前的调用方式,而不是接口一改所有调用方都改。
  • 如果接口是类似getById形式的,那么id参数就只能传一个;如果id可以传一个又可以传多个,那么接口建议命名成getByIds复数形式。

接口请求

  • 请求参数:
    • 可以给接口加个fields参数,只返回所需的字段,要返回多个字段以英文逗号隔开

接口响应

  • 响应格式:内部接口统一使用json格式响应。

  • 响应参数:包含响应码(code)(必须)和数据(data)(可选, 包含具体业务场景的自定义参数),当在service中使用$this->innerApiGet/Post调用其他项目接口时返回的数据如果包含data键,则会直接返回对应的值(可少写行代码~)。

    code约定:

    • code >= 1: 业务处理成功 [需解决框架的自带code]
    • code == 0: 代码异常(语法错误、对象不存在等非业务性的异常)
    • code < 0: 业务处理失败

    eg:

    • 成功响应: {"code": 1}
    • 成功响应: {"code": 1, "data": []}
    • 失败响应: {"code": -1, "errmsg": "param error"}
    • 失败响应: {"code": 0, "errmsg": "trying to get property of non-object"}

路由

  • 路由定义在routes/api.php
  • 所有路由须有统一的前缀,前缀代表当前的项目,eg:$app->group(['prefix' => 'user', 'middleware' => ['auth.inner', 'project', 'log.inner']], function () use ($app) { });代表userApi项目
  • 在路由文件中可再按照具体的小模块划分,比如goodsApi项目包含套餐、组合等,那么可建立GroupControllerPackageController,然后路由文件中再细分前缀,如:goods/package/xx找的是PackageControllergoods/group/xx找的是GroupController.
  • 可尽量往restful风格靠:
    • select类型的业务使用get请求
    • update类型的业务使用post请求

控制器

  • BaseController为所有控制器的父类,可存放不涉及到业务的公共内容,如Request对象,已存放在其中,所以子业务控制器无需再依赖注入Request, 使用$this->request即可获取Request实例。
  • 业务控制器如果需要在初始化的时候载入一些东西需要先调用parent::__construct();
  • 业务控制器和BaseController存放在同级
  • 业务控制器只实例化业务公共Service,不关心项目差异(如果是对外接口可忽略该条)
  • 建议调用业务层函数时统一使用数组形式,且get类型只select数据的请求不再需要每一个都try catch一下(除了事务外:事务要求再异常时能够回滚)(示例代码),异常统一在app\Exceptions\Handler.php里处理
  • 事务:控制器中可以使用$this->beginTransaction/commit/rollBack控制
  • 建议控制器中调用业务层的方法名和控制器名一致,这样有问题时可直接找业务层

模型

  • BaseModel
    • 说明:所有子Model的父类
    • 路径:App\Models\
    • 内容:可存放不涉及到业务的公共内容,如laravel框架不自带的updateBatch批量更新功能
    • 其他:会根据不同的项目将具体的子模型的$connection$table动态附加到业务基础模型上。
  • 一个业务至少包含2个模型(如果是对外接口可忽略该条),一个为业务基础模型,一个为继承业务基础模型的业务具体子模型
    • 业务基础模型约定:
      • 路径:和BaseModel同级
      • 命名:具体业务 + BaseModel, 如:SmsLogBaseModel
      • 其他:业务基础模型无需指定$connection$table, 会在运行时动态确定。
    • 业务具体子模型约定:
      • 路径:按照具体业务区分在Models下建立子文件夹并以Child作为后缀,如:App\Models\SmsLogChild\
      • 命名:具体业务 + 项目名 + Model, 如: SmsLogZmqModel, 这里的具体业务需和上面的业务基础模型里的具体业务保持一致。
      • 其他:需覆盖掉父类的__construct, 不能调用父类的否则会报错;需指定$connection(也可不指定,则使用默认的数据库连接)和$table;

业务层

  • CommonService:
    • 说明:存放当前项目的公共业务函数。
  • BaseService
    • 说明:所有子Service类的父类,所有api项目内容一致。
    • 路径:App\Service\
    • 内容:可存放涉及到业务且是公共业务的内容(比如记录一些业务上的系统日志,子service中使用$this->systemLog($msg)),也可放置一些公共的内容如Request对象,已存放在其中,所以子业务Service无需再依赖注入Request, 使用$this->request即可获取Request实例。
  • 一个业务至少包含2个Service(如果是对外接口可忽略该条), 一个为业务公共Service,一个为继承业务公共Service的业务具体子Service
    • 业务公共Service约定:
      • 说明:各个项目的公共化的代码
      • 路径:和BaseService同级
      • 命名:具体业务 + BaseService, 如:SmsLogBaseService
      • 使用:只实例化业务基础模型,不关心项目差异
      • 其他:函数需为protected, 这样才会使代码按照先走子Service再走公共(先调用个性的代码再调用通用的代码)的流程。
    • 业务具体子Service
      • 说明:各个项目的差异化代码
      • 路径:按照具体业务区分在Service下建立子文件夹并以Child作为后缀,如:App\Service\SmsLogChild\
      • 命名:具体业务 + 项目名 + Service, 如:SmsLogZmqService, 这里的具体业务需和上面的业务公共Service里的具体业务保持一致。
      • 其他:如果需要在初始化的时候载入一些东西需要先调用parent::__construct();
  • 业务层不返回json数据,交由控制器处理
  • 业务层所有函数不推荐使用return ['code' => -10]这种形式标识有问题(如参数不全之类的), 而是直接抛出异常信息和异常码,由调用方接收处理
  • 业务层函数除了getById,getByxx之类的有明确参数形式的函数(或者能够肯定该函数固定这么多参数以后不会再追加(一般函数参数推荐<=3))外,其他函数推荐使用数组/集合/对象形式传一个参,可避免函数参数过长。。
  • 事务:业务service中可以使用$this->beginTransaction/commit/rollBack控制

其他

  • 接口设计时最好考虑相对较全的应用场景。eg1:页面1需要返回数据库的A字段,然后写个接口返回A字段,可能后期页面2需要返回B字段,那么需改接口。eg2: 如果页面1需要三维数组形式的数据格式(如按照某个字段groupBy下的形式),页面2需要二维数组形式,那么接口可设计成只返回二维数组,由页面1的调用方简单处理下数据即可。
  • 当不能确定某个地方的代码永远都不会出异常或者说可能会有其他未考虑到的情形时可以手工记录下日志,比如:省市区名称转换成region_code接口可能存在一个名字匹配到多条但这时也是匹配成功的,不能抛异常影响接口使用,这时就可以使用$this->systemLog('msg')记录下多条的情况,方便后期完善接口。
  • 支持浏览器调试,在浏览器访问时带上参数&project=zmq即可,所以建议给post请求也配置一个get路由(可判断下env,如:if (env('APP_DEBUG'))才会有get路由)
  • git提交时需要过一下所有提交的变更地方,确认是你需要提交的内容,如果只是加了个换行空格什么的没具体代码的,除非是确实需要换行下什么的,否则就不用提交该处修改。

附录

  1. php编码风格
  2. 数据库风格

mysql partition

需注意:原表需要有且仅有一个作为区分分区字段的键(分区字段需包含在唯一/主键索引里):
  • alter table order_subscribe add primark key(id,created_at)
  • 在现有表上进行分区:
ALTER TABLE order_subscribe PARTITION BY RANGE COLUMNS(created_at)
(   
PARTITION p1 VALUES LESS THAN ('2018-02-01 00:00:00'),
PARTITION p2 VALUES LESS THAN ('2018-03-01 00:00:00'),
PARTITION p3 VALUES LESS THAN ('2018-04-01 00:00:00'),
PARTITION p4 VALUES LESS THAN ('2018-05-01 00:00:00'),
PARTITION p5 VALUES LESS THAN ('2018-06-01 00:00:00'),
PARTITION p6 VALUES LESS THAN ('2018-07-01 00:00:00'),
PARTITION p7 VALUES LESS THAN ('2018-08-01 00:00:00'),
PARTITION p8 VALUES LESS THAN ('2018-09-01 00:00:00'),
PARTITION p9 VALUES LESS THAN ('2018-10-01 00:00:00'),
PARTITION p10 VALUES LESS THAN MAXVALUE
);
  • 在现有分区上进行再次分区:
ALTER TABLE  order_subscribe REORGANIZE PARTITION p0 INTO (
	PARTITION p1 VALUES LESS THAN ('2018-05-01 00:00:00'),
	PARTITION p2 VALUES LESS THAN ('2018-06-01 00:00:00')
);
  • 有大量数据删除时优化分区:ALTER TABLE order_subscribe OPTIMIZE PARTITION
  • 又想还原回原来的单表形式:ALTER TABLE order_subscribe REMOVE PARTITIONING

使用模型和模型关系的好处

https://segmentfault.com/q/1010000014056473/a-1020000014069076
补充点,模型关系底层的sql语句是单独去查的,比如$order->user->user_name,会执行两条sql,一条查order数据,一条根据order数据返回的user_id去查user表数据。意味着如果分库了只需在模型里加个对应的connection指定下即可,而用join形式就比较麻烦,需要改对应的join代码, 而且也不支持跨库join,用关系的话可以跨库获取数据。

线上bug/生产事故汇总

linux文件误删

场景:运维同事在删除某个软链时由于多加了个/导致删除了物理文件。
解决linux误删文件恢复

php少写了一行代码导致服务器cpu爆表

场景:有个开发同事自己写了个curl函数,由于少写了一句curl_close($ch)导致php-fpm的cpu占用率一直100%。
解决:网络请求需要设置适当的超时时间和及时关闭资源。

重定向get参数截断

场景:有个跨公众号支付的需求(A公众号的用户在无感知的情况下支付给B公众号的商户平台),我这边是B公众号,然后在接收到参数后进行重定向时少加了个urlencode,同时请求参数中又包含了#号,导致#之后参数被当作hash而截断了。
解决:get参数需要encode下。

lumen框架时间不对

场景:有个需求,到了指定的时间展示指定的商品。我这边实现方式通过添加商品的开始和结束时间去控制,然后后端用的lumen框架作api按照当前时间筛选是否有可展示的商品并返回(类似:start_time <= current time and end_time >= current time)。然后很奇怪的是,本来要在明天凌晨时候展示的商品提前展示了。看了半天的代码,没看出任何端倪,也把对应的sql语句放到gui中执行,没有任何问题,但是通过lumen接口就是会多返回不该展示的商品。
解决:反复查看后,确定了代码没有任何问题,猜测可能是环境的问题,于是翻了下本地mysql的完整query log,发现了一行sql:set time_zone="+00:00",瞬间就反应过来是时区问题,lumen框架默认的时区是格林尼治标准时间,我们东8区需要+8,改了配置文件后一切ok了。。

git配置泄露

场景:web目录挂了git配置,方便钩子同步代码(上传到git自动同步到测试环境),导致可以通过域名/.git/config读取配置信息
解决:nginx中加入相关禁止访问.git的配置:

location ~ /\.git {
    deny all;
}

sql分页数据丢失

场景: 第一页和第二页的数据都没有某条记录
解决: 因为order by的字段有重复数据,导致mysql分页时候排序异常,再加上id之类的唯一键排序即可。

master分支合了测试的分支到线上

场景:提交了测试代码到线上
解决:如果测试分支的提交次数很多的话,那么可以找个干净的分支git push origin master --force
避免:使用master分支创建新的bug修复分支,不能用测试分支。

php基础问题导致数据不正确

场景: empty('0.00')是false的
解决: 使用>0判断

sonarQube代码质量管理工具使用

windows

  1. 首先需安装java,记录下安装的目录,如:C:\Program Files\Java\jdk1.8.0_171,下面用到。
  2. 配置java环境变量。
    a. 系统变量 => 新建JAVA_HOME,输入第1步的路径。
    b. 修改系统变量PATH, 再后面新增:%JAVA_HOME%\bin;%JAVA_HOME%\jre\bin;
    c. 系统变量 => 新建CLASSPATH:.;%JAVA_HOME%\lib;%JAVA_HOME%\lib\tools.jar
  3. 解压下载的sonarQube可汉化下(jar包放到\extensions\plugins下即可),cmd进入到bin目录,找到你的操作系统对应的目录进入,执行StartSonar.bat
  4. 访问http://localhost:9000,点击login,使用默认的admin/admin登陆,然后跟随tutorial创建个项目
  5. tutorial最后一步会提示Download and unzip the Scanner for Windows,点击download,安装scanner
  6. tutorial最后一步还会提供条命令行语句(eg:sonar-scanner.bat -Dsonar.projectKey=virtualCard -Dsonar.sources=. -Dsonar.host.url=http://localhost:9000 -Dsonar.login=f793da46643811590895f58860aa10428056e8d4),点击copy,然后cmd进入到项目根目录执行即可。(建议在项目目录下新建个sonar-project.properties, 下一次执行的时候直接进到对应目录执行sonar-scanner即可。)
  7. 等scanner分析完然后返回首页便能查看到相应的报告。

可能的问题

  1. sonar-scanner之后显示 Failed to upload report - 500: An error has occurred.
    查看sonarQube的logs文件夹下的 web.log,其中有一条com.mysql.jdbc.PacketTooBigException: Packet for query is too large (4308770 > 4194304). You can change this value on the server by setting the max_allowed_packet' variable。然后设置了mysql的max_allowed_packet并重启mysql但是依旧报这个错,结果重启下sonarQube即可。。可能java缓存了mysql的配置

附录

  1. 如果需要使用mysql,版本需在5.6及以上且小于8.0,mysql升级
  2. 语言包需翻墙,提供个1.22版本
  3. 合作开发保证代码质量

本地apache配置https访问

  1. httpd.conf: 打开mod_ssl.sohttpd-mpm.confhttpd-ssl.conf、mod_socache_shmcb.so`的注释
  2. httpd-ssl.conf: 打开SSLCertificateFile、SSLCertificateKeyFile、SSLCACertificateFile、SSLVerifyClient、SSLVerifyDepth
  3. 生成证书:dos进入到apache的bin目录,
    a. set OPENSSL_CONF=../conf/openssl.cnf
    b. openssl genrsa -out server.key 2048
    c. openssl req -new -key server.key -out server.csr -sha256 -config ../conf/openssl.cnf
    d. openssl genrsa -out client.key 2048
    e. openssl req -new -key client.key -out client.csr -sha256 -config ../conf/openssl.cnf
    f. openssl req -new -x509 -keyout ca.key -out ca.crt -sha256 -config ../conf/openssl.cnf
  4. bin目录新建demoCA文件夹,新建demoCA/newcertsdemoCA/index.txtdemoCA/serial并输入01保存
  5. openssl ca -in server.csr -out server.crt -cert ca.crt -keyfile ca.key -config ../conf/openssl.cnf
  6. openssl ca -in client.csr -out client.crt -cert ca.crt -keyfile ca.key -config ../conf/openssl.cnf
  7. openssl pkcs12 -export -in ca.crt -inkey ca.key -out ca.pfx,找到ie浏览器的证书选项,导入该文件
  8. openssl rsa -in server.key -out server.key,把该文件和server.crt复制到../conf目录
  9. demoCA/index.txt.attr,修改unique_subject为no

报错

  1. httpd: Could not reliably determine the server's fully qualified domain name: 修改httpd.conf,打开ServerName的注释,ServerName localhost:80

yum安装mysql5.7

yum安装mysql5.7

配置镜像:
https://developer.aliyun.com/mirror/centos?spm=a2c6h.13651102.0.0.3e221b11CWEHlO
centos7:
wget -O /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo
yum makecache

或者可执行这个
curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo
yum -y install vim wget

安装:
wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo
wget -i -c http://dev.mysql.com/get/mysql57-community-release-el7-10.noarch.rpm
yum -y install mysql57-community-release-el7-10.noarch.rpm
yum -y install mysql-community-server --nogpgcheck

linux php编译完的环境新增扩展

cd ./ext/zlib
mv config0.m4 config.m4
/usr/local/php5/bin/phpize
./configure --prefix=/usr/local/php/lib/php/extensions --with-php-config=/usr/local/php/bin/php-config
make
make install

css

  • 取消浏览器的默认双击选中样式:-webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; -o-user-select: none;user-select: none;
  • p标签超出宽度自动...: .title{overflow : hidden;text-overflow: ellipsis;display: inline-block;white-space:nowrap} 额外设置宽度不超过父级:
$(".title").css('width', function () {
    return $(this).parent().width()
});

laravel最佳实践

laravel代码开发最佳实践
补充:

  • 多用模型少用DB:table
  • 多用关系少用join
  • 集合函数可以多熟悉下,封装了常用的操作
  • laravel可以在模型里定义访问器如getStatusAttribute可以方便的将数据库的1、2、3之类的存储型数据转换成展示型数据展示,但也有个弊端就是需要用到原始值时还需要getOriginal下才行。参考了同事的方案,可以使用getStatusZhAttribute方式定义访问器,然后展示的时候用->status_zh即可
  • 事件可以方便解耦,可以把一些需要记录日志的新增/修改/删除操作定义在Observers
  • 可以使用scopeQuery提升可读性与可复用性。eg:
// 已支付的订单
$orders = Order::where('order_status', 2)->get();

变更为

Order.php:
function scopePaid($q)
{
     return $q->where('order_status', 2);
}

$orders = Order::paid()->get();

数据库记录慢查询

[mysqld]
log_output=table
slow_query_log=ON
long_query_time=2

日志会记录在mysql.slow_log

数据库规范

  • 字段值使用1标识正常/有效之类的,使用0标识失败/无效之类的(和php一致, 0为false, 1为true), eg: status 1:正常 0:禁用
  • 如果字段值不是属于状态类型的,如buy_type:1,可享受水机福利,2,不可享受水机福利 0,全部, 建议命名成语义化点的(hasGift, noGift, all),而不是123这种难以记忆或容易搞混的数字型的, 可避免再在代码里写上很多注释; 如果非要用数字型的话可以在代码中使用常量/配置参数(config(const.xx))替代123.
  • 命名采用驼峰式?
  • 主键命名:采用表简称+id,如:userId,
    • 如果表名比较长可以忽略部分前缀
    • 和该表有关联关系的外键字段名也使用userId
  • 同一个功能的字段在任意表里面都是相同的命名。不能这个表叫package_goods_id, 那个表叫goods_package_id
  • 建完表时记得:
    • 在需要加索引的字段加上索引
    • 加上表备注
    • 更改表为合适的引擎(除了后台添加会insert记录之外,其他的都是读取操作可设置为MyIsam;会在业务中insert记录的设置为Innodb)
  • 字段注释:
    • 常用的单词一看就知道的可不加注释
    • 如果字段是外键,则需要注明对应哪个表,eg:对应goods_package表主键
  • 如果有多个字段表示同一含义,那么只保留一个字段
  • 不要建“未来字段”:在目前项目需求中明确用不到的字段,后期有需要了再加,不要为了以后可能的需求建些无关字段,可能以后永远用不到,其他不了解需求的同事既看不懂又不敢删

python踩坑

  • 在使用sqlalchemy时,链接数据库如果用了pymysql那么会遇到字符集报错问题:Warning: (1366, "Incorrect string value:,改为使用mysql+mysqlconnector连接,需要安装包: pip install mysql-connector-python, 无需import

other

notepad++

ssr

  • 服务器端搭建:
    • wget --no-check-certificate -O shadowsocks-all.sh https://raw.githubusercontent.com/teddysun/shadowsocks_install/master/shadowsocks-all.sh
    • chmod +x shadowsocks-all.sh
    • ./shadowsocks-all.sh 2>&1 | tee shadowsocks-all.log

破解压缩包等的密码

1.hashcat :https://hashcat.net
2.john the ripper :https://www.openwall.com
注:官网是英文的,可以通过谷歌浏览器翻译成中文
只需用到2个命令:
rar2john.exe xxxx.rar   –获取hash值
hashcat.exe -m 13000 -w 4 -a 3 $rar5$16$b88c1d7d2c96dc9d1b1a5ccdc5c25d50$15$8f0b287c982535c868bbff486ee9acd2$8$43907bfa03430471 -o password.txt  — 开始

demo controller

// 不带事务:

    /**
     * 获取购物车数量
     * @param  integer $request.user_id
     * @return json 
     */
    public function getCount()
    {
        $ret['code'] = 1;

        $this->validate($this->request, [
            'user_id' => 'required|integer|min:1',
        ]);

        $ret['data'] = $this->carBaseService->getCount($this->request->input());

        return $ret;
    }

// 带事务:
    /**
     * 设置购物车内物品已选
     * @param  integer $request.user_id *
     * @param  string $request.car_ids *按照英文逗号分隔的购物车表主键
     * @return json [code => 1]
     */
    public function chooseGoods()
    {
        $ret['code'] = 1;
        try {
            $this->validate($this->request, [
                'user_id' => 'required|integer|min:1',
                'car_ids' => 'required',
            ]);

            $this->beginTransaction();
            $this->carBaseService->chooseGoods($this->request->input());
            $this->commit();

        } catch (\Exception $e) {
            $this->rollBack();
            throw new \Exception($e->getMessage(), $e->getCode());
        }

        return $ret;

    }
    

nginx https配置

server {
	listen 80;
	server_name www.xx.com xx.com;
	rewrite ^(.*) https://$server_name$1 permanent;		
	
}

server {
	listen 443 ssl;
	server_name www.xx.com xx.com;
	access_log    /data1/wwwlogs/access/xx.com.log;
        error_log    /data1/wwwlogs/error/xx.com.log;

	ssl_certificate      /usr/local/nginx/conf/cert/xx.com/1_www.xx.com_bundle.crt;
	ssl_certificate_key  /usr/local/nginx/conf/cert/xx.com/2_www.xx.com.key;
	ssl_session_timeout 10m;
	ssl_session_tickets on;
	ssl_prefer_server_ciphers on;
	ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
	ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
	
	location ~ /\.
	{
		deny all;
	}

	location / {
		proxy_set_header request_uri $request_uri;
		proxy_set_header   X-Real-Host             $host;
		proxy_set_header   X-Real-IP        $remote_addr;
		proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
		proxy_pass http://$ip7:501; 
	}
}

常用配置

  • 慢查询记录到数据库:
log_output=table
slow_query_log=ON
long_query_time=2
  • 所有查询记录到文件:
general_log=ON
general_log_file=D:/phpenv/logs/query.log
  • 按照阿里云的参数配置的my.cnf,服务器16g内存

  • 如果linux已经设置好了ulimit,但是mysql不生效(查看/proc/msyqlpid/limits),问题的原因是systemctl启动方式覆盖了系统参数
    cat /usr/lib/systemd/system/mysqld.service | grep -A2 open_files_limit
    不建议修改mysqld.service,会影响mysql升级,使用以下方式:
    mkdir /usr/lib/systemd/system/mysqld.service.d

cat >> /usr/lib/systemd/system/mysqld.service.d/override.conf <<EOF
[Service]
LimitNOFILE=40960
EOF
  • 修改了mysql的data目录然后mysqld --initialize,需要再执行chown -R /datadir才能开启mysql
  • mysql主从配置

composer常用命令

  1. 指定版本安装: composer create-project laravel/lumen folderName 5.3.*, 最高版本--prefer-dist
  2. 可以使用composer dump-autoload -o优化加载,提升一定的加载性能~
  3. 不确定当前项目是否已包含某插件时可通过composer show查看所有安装的插件包。
  4. composer阿里云国内镜像:https://developer.aliyun.com/composer

将已有的目录作为git客户端

  1. 切换到该目录, git init
  2. git remote add origin [email protected],如果本机同时也是gitlab仓库,那可以直接git remote add origin /path/to/.git, 如果是http的可以改成http://user:[email protected]记住密码
  3. 存储密码: git config --global credential.helper store;
  4. 要关联的分支: git pull origin master/develop

php编码风格

前言

需求是暂时的,只有变化才是永恒的,面向变化编程,而不是面向需求编程。

不要过分追求技巧,降低程序的可读性。

简洁的代码可以让bug无处藏身。要写出明显没有bug的代码,而不是没有明显bug的代码。

先把眼前的问题解决掉,解决好,再考虑将来的扩展问题。

from iOS 团队编程规范

编码规范

  • 缩进统一使用4个空格缩进
  • 函数和变量命名统一使用驼峰式
  • 涉及到数据库字段的变量尽量和数据库命名保持一致,所以数据库字段也采用驼峰式?
  • 同一含义的变量尽量在整个项目中保持同一命名
  • 控制器只负责传递输入和输出以及调用基本的业务函数,尽量不在控制器中处理业务逻辑
  • 可以使用单引号就没必要使用双引号,单引号不会解析里面是否有php变量之类的,所以会有一丁丁丁丁点的性能提升
  • dry -- dont repeat yourself: 减少代码冗余性,重复的代码建议封装起来。刚开始可能只是几行简单的业务代码,这里复制下,那里复制下,看似快速方便,但后期一旦该块业务有改动还是得整合起来(可能会漏)或者所有地方都改一遍。。

Laravel

  • 如果有这样的表:feedback, feedback_result, feedback_result_gift, 那么在Feedback模型里建立关系result对应feedback_result,在FeedbackResult里建立关系gift对应feedback_result_gift

函数

  • 函数获取传递参数尽量都在函数顶端定义,以便其他不熟悉该函数的可以对所有参数一目了然,(当然可以通过注释完成,但很多时候注释往往是跟实际代码有出入的~)eg:
public function saveUser($param)
{
        $user_name = $param->user_name;
        $gender    = $param->gender;
    
        // ...
        // ...
        // ...
        
        // 不要再在非函数顶端去获取参数
        $this->sendSms($param->mobile, ...);
}
  • 函数的注释使用phpdoc格式,建议用 * 标识参数是必传
  • 参数多的话建议按等号或者箭头对齐,sublime有个alignment插件, eg:
$cate_id     = $param['cate_id'];
$goods_label = $param['goods_label'];
$province    = $param['province'];
$city        = $param['city'];
$district    = $param['district'];
$arr = [
        'cate_id'      => $cate_id,
        'package_type' => $package_type,
        'fields'       => ['goods_label'],
];
  • 除了入口函数(需要调用多个子函数或者api才能完成业务)外,其他的函数需要遵循单一职责原则,一个暴露给外部调用的函数只做一件事

附录

  1. laravel最佳实践
  2. 代码整洁

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.