数据库设计三范式(数据库规范化)

第一范式(1NF)

第一范式是为了要排除 重复组 的出现,强调列的原子性并且不可再拆分, 如果列可能会出现重复的属性,比如一张表的字段为(姓名,性别,电话), 其中联系电话可能会有多个,那么应该拆分出新建实体,与原实体之间的关系为一对多, 另外为了排除重复组的出现,要求必须要有主键

第二范式(2NF)

首先包含1NF, 要求主键和候选键有完全依赖关系,而不能只依赖主键的一部分, 不符合 2NF 的设计容易产生冗余数据

比如有一张表为:

零件来源表

零件 ID (主键) 价格 供应商ID (主键) 供应商名称 供应商住址

以上字段都是单一值符合1NF范式, 但是同一个零件可能会有不同的供应商(相反,多个零件也可能属于同一个供应商),所以并不属于完全依赖,
另外供应商名称和供应商住址只和供应商ID有关(部分依赖),所以应该抽离出来,修改后如下:

供应商表

供应商ID (主键) 供应商名称 供应商住址

零件来源表

零件 ID (主键) 价格 供应商ID (主键)

第三范式(3NF)

要求所有非主键属性都只和候选键有相关性,也就是说非主键属性之间应该是独立无关的。

如下表,如果要符合第三范式, 应该将小计删除, 小计可以由单价乘以数量得出(但是删除后统计查询将稍微复杂)

订单ID 商品ID 单价 数量 小计 (Total)

总结

综上所述,三范式在具体需求场景下并不是一定要严格遵守, 但是必须要考虑在内, 特别是第一范式和第二范式, 错误的设计会导致后期的数据结构变动或更新数据困难, 还有大量冗余问题.

基于Redis实现普通队列和延时队列的两种方式与问题

普通队列

基于Redis实现的普通队列,可以用于各种异步任务的处理,如短信发送,代码如下

投递任务脚本,只需要通过Lpush将任务添加到列表

<?php

key = 'key1';i = 0;
redis = new \Redis();redis->connect('127.0.0.1', 6379);

while (true) {
    i += 1;
    echovalue = '投递任务:' . i;redis->Lpush(key,value);
    usleep(50000); //微妙
}

消费任务脚本,通过brPop阻塞获取任务

<?php

key = 'key1';redis = new \Redis();
redis->connect('127.0.0.1', 6379);

while (true) {arr = redis->brPop([key], 3);  //获取一条
    if (!empty(arr)) {
        echo "获得分配的任务:{arr[1]}\r\n";
    }
}

由于brPop提供阻塞获取的功能,所以在消费任务速度大于投递速度的情况下,可以认为是实时异步处理

延时队列

投递任务脚本

<?php

key = 'key';score = time();
i = 0;redis = new \Redis();
redis->connect('127.0.0.1', 6379);
while (true) {i += 1;
    echo value = '投递任务:' .i;
    redis->zAdd(key, score,value); //有序集合的成员是唯一的,但分数(score)却可以重复。
    usleep(50000); //微妙
}

消费任务脚本

<?php

key = 'key';redis = new \Redis();
redis->connect('127.0.0.1', 6379);

while (true) {arr = redis->zRange(key, 0, 0, true);  //获取一条

    if (empty(arr) orarr[key(arr)]>time()) {  //不存在或时间未到,等待并进入下一个循环
        usleep(500000); //微妙
        continue;
    }value = key(arr);
    if (redis->zRem(key,value)) {
        echo "获得分配的任务:{$value}\r\n";
    } else {
        echo "任务被其他进程抢占,进入下一个循环\r\n";
    }
}

 使用Redis和两种方式的优缺点

两种方式都是基于Redis所以优点基本也是Redis自身的优点,简单,稳定,快速,支持分布式,但是基于Redis的延时队列始终需要配置一个轮询时间,所以从某个角度来讲并不是实时消费,但是可以通过单独的消费主进程来投递任务到子进程的方式和减少轮询时间来优化,类似Nginx的任务处理流程.

关于消费端,如果使用php实现只适合处理普通的队列,用来异步发下短信等等, laravel框架其实已经有类似的集成,可以直接使用, 对于延时队列如果想高效处理还是需要使用go等对多线程或多进程支持友好的语言来处理

正常情况下在使用时还需要考虑开发和维护成本,比如异步短信,对于普通项目而言可能使用laravel或其他框架自身实现的Redis队列功能或插件反而更合适,不需要单独维护一套代码,但是对于并发任务较大的项目肯定是不适用的,此时消费端只能使用Go等语言

Arduino使用入门笔记

配置编辑器IDE

需要配置三个选项

  1. 工具=>开发板 =>Arduino Nano
  2. 工具=>处理器=>ATmega328
  3. 工具=>端口=> 目标端口

然后在设备管理器配置端口波特率为11520,注意VIN是电源输入脚,接正极电源,有些板子支持的电源范围较宽

目前手里面有的板子主要为

UNO R3

XTWduino nano V3.0 ATMEGA328P (兼容板)

Arduino pro mini改进版 ATMEGA328P 5V/16M

1. 14个数字输入/输出端口RX,TX,D2~D13
2. 8个模拟输入端口A0~A7
3. 1对TTL电平串口收发端口RX/TX
4.6个PWM端口,D3, D5, D6, D9, D10, D11
5.采用Atmel Atmega328P-AU单片机
6. 支持串口下载
7.支持外接3.3V~12V直流电源供电
8.支持9V电池供电
9. 时钟频率16MHz
10.尺寸:33.3*18.0(mm)

参考链接https://blog.csdn.net/c80486/article/details/52506975

 

 

 

记一次交换机环路故障现象,大量EAPOL协议数据包

公司陆续有同事反应无法上网,检查发现大部分电脑在接收大量数据包, 网卡带宽占用30%~90%以上,  但是并没有反应到具体的应用程序,通过抓取本地数据包发现大量EAPOL协议类型的数据传输:

网上搜索一番,发现发生环路故障时会产生此现象,通过拔除网线的方式一一排查,发现是个别工位拓展的小型交换机插了两根入口线导致(很容易出现此问题),拔除后问题解决.

发生此问题还有另外一个现象就是核心交换机的CPU占用率会很高,记得达到了86%以上(经过观察正常情况下占用才10%,路由型号H3C ER3100)

Android机顶盒破解思路与获取ROOT流程

一,开启adb调试模式

两种开启方法

1.一般设备都有预留隐藏操作开启调试模式的方法,比如按某个组合键,具体通过互联网搜索可获得

2.通过拆机,接入TTL线,  然后输入su 切换到root模式下(此时显示#), 再输入start adbd启动调试模式, 连接TTL线的时候只需要连接GND,RX,TX三根即可

二, 安装悟空助手

当开启调试模式后,就可以通过悟空助手通过ip连接到智能盒子,进行安装软件的操作(此时还没有获取ROOT权限), 虽然没有获取ROOT权限,但是已经突破基本限制,可以随意安装软件,只是不能禁用自启动和删除预装软件

三,获取ROOT

在完成第一步开启调试模式的前提下, 可以使用一根两头都是USB插头的线连接电脑和盒子,再使用刷机精灵等软件获取ROOT,如果一个不行可以多试几个

如果没有线还能通过在盒子上安装软件来ROOT,测试通过的ROOT软件有如下几种, 可以通过应用商城或拷贝到U盘安装

  1. 360超级ROOT
  2. 百度ROOT
  3. KingRoot

以上流程在电信盒子创维E900-S上测试通过

 

TTL使用说明

网上很多教程提到putty,secureCRT等连接工具其实都可以使用Xshell代替, Xshel使用时需要先选择SERIAL协议, 然后在左侧选择SERIAL做后续操作, 其次在计算机属性中确定COM口后右键属性=>端口设置=>设置波特率为115200,Xshell中需要设置相同的波特率

NGINX开启HTTP2.0记录

注意

如果是最新的系统,比如Centos7,应该已经是受支持的环境,假设软件都是用yum安装,完成后只需要直接配置nginx http2即可

其他环境流程(Centos6)

需要编译最新的openssl (OpenSSL 1.0.1e-fips 不支持) 1.1.0 或者以上

如果使用yun安装的nginx 需要重新编译安装 ,虽然nginx的版本可能支持http2,但是配置无效,主要是由于nginx调用了老的openssl

nginx编译的时候注意指定openssl地址, 指向源码目录即可: –with-openssl=/work/source/openssl-1.1.0g

NGINX配置HTTP2.0

和普通站点大致相同,只是要求是https,然后在listen配置栏增加http2即可

listen       443 ssl http2;
server_name   xx.xxx.com
ssl	on;
ssl_certificate   /etc/nginx/conf.d/cert/214415858850320.pem;
ssl_certificate_key  /etc/nginx/conf.d/cert/214415858850320.key;
ssl_session_timeout 5m;
#ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
#ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
#ssl_prefer_server_ciphers on;

 

ESP8266 & NodeMCU入门

NodeMcu环境搭建

  1. 写入固件,相当于安装系统,工具地址,同时需要一个固件,编译自己的固件,刷入固件时GPIO0引脚需要拉低,相反正常运行时需要拉高或浮动,正常的开发板只需要重启后按住FLASH键即可,更新时勾选Erase flash清除所有数据(如果卡死,只需要将GPIO0接GND,重新刷入固件即可,可以通过将所有程序放入一个10秒的定时器内执行防止程序卡死,或者不在init.lua文件执行)
    tmr.alarm(0, 10*1000, 0, function() 
    -- 代码
    end )
    
  2. 安装java环境
  3. 下载编译工具ESPlorer,用来编写程序,正常情况下DTR和RTS按钮为灰色
  4. api文档地址

ESPlorer使用说明

打开esplorer软件右侧上方选择端口,电机open按钮, 按下板子上RST按钮重启,窗口会显示具体版本信息,表示链接成功

左侧为操作区,右侧主要为信息返回区,显示串口返回的相关信息

 

  1. 点亮自带的发光二极管 http://blog.csdn.net/leytton/article/details/51650082
  2. 驱动pwm http://www.jianshu.com/p/8863316267a0
  3. gpio引脚 http://blog.csdn.net/leytton/article/details/51646624
gpio.mode(0,gpio.OUTPUT)  
gpio.write(0, gpio.LOW)

由于NodeMCU在启动时会执行”init.lua” 文件,一旦这个文件中存在漏洞,NodeMCU可能会不断地重启。幸运地是,Lua是异步执行的,在重启的同时,执行”file.remove(‘init.lua’)”命令即可删除”init.lua” 文件,下次启动时就不会执行这个文件了。修复漏洞后,再将”init.lua” 文件上传执行就行了。

引脚说明

有些参数选择引脚时需要使用pin参数,这个时候需要对照下对看pin对应的GPIO口是多少,比如设置PWM时pin 2参数对应GPIO4
极简版6个引脚的ESP8266也是根据GPIO编号对照下图寻找pin脚,如GPIO0=PIN3

编程时,一般指定的引脚为pin的编码,而不是GPIO编码

使用注意

如果使用了TTL相关IO口输出会导致TTL传输数据显示异常, 需要留意,如IO7~IO10还有IO4,如果不需要显示数据则可忽略

打造属于自己的AI智能家居控制中心(通用改装方案)

前言

一直想将家里的一些设备打造成懒人专用的智能控制方式,所以工作之余经常留意智能家居和AI的发展,发现最近国内掀起一股智能音箱的热潮,主要还是起源于亚马逊发布ECHO,据报道ECHO的平均使用率还是挺高的,但是好像大部分都还是听歌和设置闹钟….. 我更加关注的还是智能音箱的远场语音识别技术,通过语音交互控制各种设备, 既然有这么多用户,应该做的很不错了,ECHO在国内水土不服,所以开始寻找国内比较好的产品ing

选择智能音箱

发现目前国内主流的产品主要有:

  1. 叮咚音响
  2. 小米音箱
  3. 天猫精灵
  4. 喜马拉雅“小雅”音箱

对比各类评测后选择了叮咚音箱,主要是当时加了小米和叮咚的开发群, 发现只有叮咚的开放平台是已经可以申请,并且有一些开发者,另外一个原因是叮咚音箱是和讯飞合作推出的,有讯飞背书应该不会太烂吧…..花了399购入叮咚TOP:

开工

首先定一个开发方案,画出通信控制流程图

根据流程制定工作清单:

  1. 选择合适的控制模块,继电器,供电电源,控制系统一般都是低压供电
  2. ESP8266刷入NodeMCU编写控制程序
  3. 服务器环境搭建,安装PHP Swoole服务,开启HTTPS和WebSocket通信
  4. 制作简单的控制中心页面(HTTP Page),用于调试和手机控制
  5. 接入智能音箱完成ABNF语法编写
  6. 实现离线终端配置(这个是后来加的,防止WIFI密码变更需要再修改程序)
  7. 实现简单的安全访问控制
  8. 测试安装

一.选择硬件控制模块

首先想到的是由ESP8266的GPIO口直接控制继电器,然后控制客厅灯的开关,延迟开关,亮度调节等,那么需要的零件如下,一个电源模块(3.3V)和一个继电器模块,加起来不到20元的价格

但是这两个模块体积都比较大(相对开关盒子而言),集成度不高,先做为备选,再继续寻找集成度更高的方案,然后想到了很久之前花49买的小米的智能插座,话说买了后就没用过了,直接拆开分析后发现可以改造,自带5V供电和继电器, 就选这个了!如果空间稍微大点可以选择前面的方案

拆开后的小米智能插座

旁边的小板就是他的WiFi无线控制板,我们要做的就是替换掉他,通过分析发现电路板上有几个测试点标识有 RELAY EN, USB EN,WiFi控制板上还有个3.3V测试点,RELAY EN就是继电器使能, 只需要给他高电平或者低电平就能控制继电器的开合了,然后ESP8266正好可以从3.3V取电.

二.ESP8266刷入NodeMCU固件,编写客户端程序

刷入固件可以参考这个链接.写的比较详细

NodeMCU固件比AT指令更加简单,并且有很多现成的功能模块可以使用,尽量用最少的代码干更多的事,NodeMCU基于lua语言,语法有点类似Node.js.

通过查找文档发现NodeMCU对MQTT协议和WebSocket还有基本的Socket都实现了简单的支持,但是Swoole对WebSocket支持更加全面,所以先用Socket尝试了下简单的控制,发现还OK, 考虑到需要一个控制页面,最终选择了WebSocket,因为Js可以直接通信,最终完成的lua代码如下:

wifi.setmode(wifi.STATIONAP)
wifi.ap.config({ssid="MyPersonalSSID", auth=wifi.OPEN})
enduser_setup.manual(true)
print("ap ip:"..wifi.ap.getip())
print("ap mac:"..wifi.ap.getmac())
print("sta mac:"..wifi.sta.getmac())

device_id=0
pin=3  -- 尽量避开TTL口
gpio.mode(pin,gpio.OUTPUT);

function explode(sep, str)
    t={}
    for w in string.gmatch(str,"([^'"..sep.."']+)") do     -- 分割字符串
        table.insert(t,w)
    end
    return t
end

enduser_setup.start(
  function()
    print("sta ip:" .. wifi.sta.getip())
    wifi.setmode(wifi.STATION)

    ws = websocket.createClient()
    ws:on("connection", function(ws)
        ws:send("device_id:"..device_id)  --send device id
        print('got ws connection')
        tmr.alarm(1, 5000, 1, function()
             ws:send(device_id.."-life...")  -- 发送心跳包
        end)
    end)
    ws:on("receive", function(_, msg, opcode)  --接收消息
      if string.len(msg)>5 then
        data=explode(':',msg);
        gpio.write(pin,tonumber(data[2]));
      end
      print('got message:', msg) -- opcode is 1 for text message, 2 for binary
    end)
    ws:on("close", function(_, status)   --关闭连接
      print('connection closed', status)
      --ws = nil -- required to lua gc the websocket client
      tmr.alarm(1, 5000, 0, function()
        ws:connect('wss://xxx.cn')
      end)
    end)
    ws:connect('wss://xxx.cn')

  end,
  function(err, str)
    print("enduser_setup: Err #" .. err .. ": " .. str)
  end
)

实现了断网自动重连,测试发现稳定性还不错,不用担心断网后需要去重启等问题

刚开始不知道有cjson模块,所以用分割字符串的方式来传输数据体,因为是简单的数据交互,所以就懒得改了,还节省空间…

三.安装PHP Swoole,搭建HTTPS和WebSocket服务

因为HTTPS加密需要使用到openssl,所以只能编译安装Swoole,编译流程如下

#先安装openssl
yum install openssl openssl-devel
#下载源码
wget http://pecl.php.net/get/swoole-1.9.21.tgz
tar zxvf swoole-1.9.21.tgz
#需要开启openssl以支持https
./configure --enable-openssl
make && make install

Swoole使用过程中遇到一些问题,首先是测试发现HTTPS页面浏览器可以正常访问,但是叮咚的服务器无法正常请求,提示

WARN swSSL_accept: SSL_do_handshake() failed.

刚开始以为是openssl版本的问题,编译最新版本后问题依旧,通过查看源码没有发现什么特别的地方,猜测是加密方式不对导致,通过一番搜索找到了修改加密方式的配置,问题解决!

'ssl_ciphers' => 'ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP',

另外一个问题就是HTTP服务和WebSocket服务底层都是继承过来的,虽然同时启动的时候需要分别设置端口,但是启用ssl加密后,都只能通过443一个端口连接,另外一个端口是无效的,控制端核心代码如下:

//websocket请求  当服务器收到来自客户端的数据帧时会回调此函数。
this->server->on('message', function (swoole_websocket_serverserver, frame) {
    echo "device_data: {frame->data}\n";
    data = explode(':',frame->data);
    if (data[0] == 'device_id') {this->device_list[data[1]] = ['fd' =>frame->fd, 'status' => 0];
        echo "receive from {frame->fd}:{frame->data},opcode:{frame->opcode},fin:{frame->finish}\n";
        //发送数据到前端
        if (this->web_fd) {server->push(this->web_fd, json_encode(this->device_list, true));
        }
    }
    if (frame->data == 'WebPage') {this->web_fd = frame->fd;
    }

});

//在收到一个完整的Http请求后,会回调此函数。回调函数共有2个参数:this->server->on('request', function (request,response) {
    response->header('Content-Type', 'text/html;charset=utf-8');uri = request->server['request_uri'];
    echo "请求地址: {uri}\n";
    url_exp = explode('/',uri);
    if (url_exp[1]) {fun = url_exp[1] . 'Action';
        if (method_exists(this, fun)) {this->fun(request, response);
        } else {response->status(404);
            response->end("<h1>404</h1>");
        }
    } else {
        //访问的首页response->end("<h1>Hello Swoole. #" . rand(1000, 9999) . " Home Page</h1>");
    }
});

四.添加HTTP控制页面

HTTP请求实现了一个最简单的路由功能

$url_exp = explode('/', $uri);
   if ($url_exp[1]) {
       $fun = $url_exp[1] . 'Action';
       if (method_exists($this, $fun)) {
           $this->$fun($request, $response);
       } else {
           $response->status(404);
           $response->end("<h1>404</h1>");
       }
   }

然后添加HTTP控制页面,控制页面通过WebSocket实现了设备在线和离线时动态显示,核心代码:

<div id="app" class="container" style="padding: 30px 0px;">
    <?php // var_dump(this->device_list);?>
    <div v-if="!list.length">
        <h2>没有设备连接!</h2>
    </div>
    <button type="button" class="btn btn-success" style="margin: 5px;" v-for="(value,key) in list"
            v-on:click="change(value)">
        设备号:{{value}},status:<span v-html="key.status"></span> <span
                v-bind:class="parseInt(key.status)?'glyphicon glyphicon-flash':''"></span></button>
</div>
<div id="output"></div>
</body>
<script>

    vm = new Vue({
        el: '#app',
        data: {'list':<?php echo json_encode(this->device_list, true)?>},
        methods: {
            change: function (value) {
                vm.list[value].status = vm.list[value].status == 1 ? 0 : 1
                $.get('/change', {'device': value, 'status': vm.list[0].status}, function (res) {
                    //alert(res)
                })
            }
        }
    })

使用了Vue.js,有设备在线或离线时通过WebSocket发送数据并替换掉list的值即可,很简单.

完整的页面如下,可以通过手机随时访问,有设备掉线时按钮消失,上线立刻显示.

五.接入智能音箱完成ABNF语法编写

购买音箱的同时申请了开放平台账号,由于叮咚是和讯飞合作, 所以语法这块用的ABNF语法,使用起来有点像正则表达式…..主要用到的几个语法如下

$_ti_ch_ 任意匹配
|  或者匹配
[] 可选匹配
() 分组匹配,起到明确界限作用
<> 重复  +(N次以上) -(N次以下)

完成后的语法类似下面的:

#ABNF 1.0 UTF-8;
business my_house;
#include "pre_word.lst"
root main;
#ABNF HEAD-END;
//业务唤醒前缀词
pre_word = (u_LST_pre_word);
//称谓
call_name_must = 小(杰|姐|结|节);
//设备名称  可以使用通配符device_name{device_name} = _ti_ch_<1-4>;switch{switch}=(开|关);
switch_on=([打]switch [启])|(switch [闭] [掉]);
//让小杰帮我打开电视机call_content = [pre_word] (让|请)call_name_must [帮我];
content= [call_content ] switch_ondevice_name;
main{biz:my_house} =_ti_ch_<8->content_ti_ch_<8->;

可以识别的语句能够匹配到很多,比如

让小杰帮我打开客厅灯
让小杰打开客厅灯
让小杰开灯
请小杰开灯
...

六,实现离线终端配置

想到wifi密码变更需要拆开重新改程序等简直不能忍,于是寻找离线配置方案,发现已经有一个现成的模块”enduser_setupstop”,可以实现没有网络时开启AP模式,直用手机连上去配置,页面也可以自定义,效果如下,完美!

七,访问控制

考虑安全问题,在路由前面加了一层简单的HTTP Basic认证, 因为是走HTTPS,所以稍微强一丁点,只要地址不暴露应该还算安全吧,哈哈,代码如下

//http基本认证
    public function authorization(request,response)
    {
        if (!isset(request->header['authorization'])) {response->header('WWW-Authenticate', 'Basic realm="My Realm"');
            response->status(401);response->end();
        } else {
            if (str_replace('Basic ', '', request->header['authorization']) !== base64_encode("{this->user}:{this->pwd}")) {response->end("<h1>401 unauthorized</h1>");
            }
        }
    }

 

八,测试安装

从小米的小板接3.3V和GND供电

ESP8266编写程序测试

组装完毕

先连接到台灯上测试

下一步接到客厅灯 ing, 发现一些传统布线方式导致的问题,和解决方案,然后也了解了下未来智能家居会使用到的几种通信方式,比如星形,环形,现场总线等,改完后再陆续更新上来

最后

  • 总价格,一个遗忘的小米插座+ESP8266不管用哪种方案,基本都是50元内+智能音箱399
  • 总用时,几个没有吃鸡的晚上,
  • 其他照片 :

临时做的超好用的供电电源,电压可调

临时工作台

其他测试视频:

end

转载请联系原作者vip@wwjie.cn,保留所有权利

jenkins持续部署

安装Jenkins

安装执行如下命令,参考

sudo wget -O /etc/yum.repos.d/jenkins.repo https://pkg.jenkins.io/redhat-stable/jenkins.repo
sudo rpm --import https://pkg.jenkins.io/redhat-stable/jenkins.io.key
yum install jenkins

启动

service jenkins restart

 配置

权限问题,设置root启动 如果使用ssh部署,可以不需要root权限

修改/etc/sysconfig/jenkins文件中,

# user id to be invoked as (otherwise will run as root; not wise!)
JENKINS_USER="root"
JENKINS_GROUP="root"

#内存占用问题 /etc/sysconfig/jenkins 添加配置
JENKINS_JAVA_OPTIONS="-Djava.awt.headless=true -Xms256m -Xmx512m -XX:MaxNewSize=256m"

重启Jenkins

service jenkins restart

如果需要远程执行ssh命令,安装 SSH plugin 然后进入系统全局设置 => remote ssh

权限设置

如果远程触发出现403 在安全设置 取消 防止跨站点请求伪造

允许匿名触发构建需要设置权限(Configure Global Security) 授权策略=>安全矩阵 管理员给 Overall 权限 匿名用户给Overall read权限和 job read权限,  此处注意先添加管理员并给所有权限,不然会导致无法登录, 设置完成后匿名用户可以触发,但是也能查看到触发结果


或者使用登录用户权限触发

curl -s -u userxxx:password -X POST http://10.0.32.10:8080/job/wft-test/build?token=123456
http://blog.sctux.com/?p=549

 

#部署代码:

CODE_PATH=/work/backup/code/www.test.com
WWW_PATH=/work/www/www.test.com
\cp -rf {CODE_PATH}/((BUILD_NUMBER-1)){CODE_PATH}/{BUILD_NUMBER}
\cp -rf{WORKSPACE}/* {CODE_PATH}/{BUILD_NUMBER}/
chown www:www {CODE_PATH}/{BUILD_NUMBER} -R
ln -sfn {CODE_PATH}/{BUILD_NUMBER} {WWW_PATH}


#回滚
http://www.itdadao.com/articles/c15a556417p0.html

url="{VersionName}"
last_id=0;
url={url//\// }
for element inurl
do
last_id=element
done
CODE_PATH=/work/backup/code/www.test.com/{last_id}
WWW_PATH=/work/www/www.test.com
if [ ! -d "{CODE_PATH}" ]; then
exit 1
fi
ln -sfn{CODE_PATH} ${WWW_PATH}

#处理只保留指定个数的版本 删除 job-N 的目录

 

build 邮件通知
http://blog.csdn.net/songjiaping/article/details/51496977
nginx 缓存路径问题处理
把 fastcgi_param SCRIPT_FILENAME document_rootfastcgi_script_name;
改成 fastcgi_param SCRIPT_FILENAME realpath_rootfastcgi_script_name;

mysql主从复制[转载]

怎么安装mysql数据库,这里不说了,只说它的主从复制,步骤如下:

1、主从服务器分别作以下操作:
1.1、版本一致
1.2、初始化表,并在后台启动mysql
1.3、修改root的密码

2、修改主服务器master:
#vi /etc/my.cnf
[mysqld]
log-bin=mysql-bin //[必须]启用二进制日志
server-id=222 //[必须]服务器唯一ID,默认是1,一般取IP最后一段

3、修改从服务器slave:
#vi /etc/my.cnf
[mysqld]
log-bin=mysql-bin //[不是必须]启用二进制日志
server-id=226 //[必须]服务器唯一ID,默认是1,一般取IP最后一段

4、重启两台服务器的mysql
/etc/init.d/mysql restart

5、在主服务器上建立帐户并授权slave:
#/usr/local/mysql/bin/mysql -uroot -pmttang
mysql>GRANT REPLICATION SLAVE ON *.* to 'mysync'@'%' identified by 'q123456'; //一般不用root帐号,'mysync'@'%'表示所有客户端都可能连,只要帐号,密码正确,此处可用具体客户端IP代替,如192.168.145.226,加强安全。

6、登录主服务器的mysql,查询master的状态
mysql>show master status;
+------------------+----------+--------------+------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+------------------+----------+--------------+------------------+
| mysql-bin.000004 | 308 | | |
+------------------+----------+--------------+------------------+
1 row in set (0.00 sec)
注:执行完此步骤后不要再操作主服务器MYSQL,防止主服务器状态值变化

7、配置从服务器Slave:
mysql>change master to master_host='192.168.145.222',master_user='mysync',master_password='q123456',
master_log_file='mysql-bin.000004',master_log_pos=308; //注意不要断开,308数字前后无单引号。

Mysql>start slave; //启动从服务器复制功能

8、检查从服务器复制功能状态:

mysql> show slave status\G

*************************** 1. row ***************************

Slave_IO_State: Waiting for master to send event
Master_Host: 192.168.2.222 //主服务器地址
Master_User: mysync //授权帐户名,尽量避免使用root
Master_Port: 3306 //数据库端口,部分版本没有此行
Connect_Retry: 60
Master_Log_File: mysql-bin.000004
Read_Master_Log_Pos: 600 //#同步读取二进制日志的位置,大于等于Exec_Master_Log_Pos
Relay_Log_File: ddte-relay-bin.000003
Relay_Log_Pos: 251
Relay_Master_Log_File: mysql-bin.000004
Slave_IO_Running: Yes //此状态必须YES
Slave_SQL_Running: Yes //此状态必须YES
......

注:Slave_IO及Slave_SQL进程必须正常运行,即YES状态,否则都是错误的状态(如:其中一个NO均属错误)。

以上操作过程,主从服务器配置完成。

9、主从服务器测试:

主服务器Mysql,建立数据库,并在这个库中建表插入一条数据:

mysql> create database hi_db;
Query OK, 1 row affected (0.00 sec)

mysql> use hi_db;
Database changed

mysql> create table hi_tb(id int(3),name char(10));
Query OK, 0 rows affected (0.00 sec)

mysql> insert into hi_tb values(001,'bobu');
Query OK, 1 row affected (0.00 sec)

mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| hi_db |
| mysql |
| test |
+--------------------+
4 rows in set (0.00 sec)

从服务器Mysql查询:

mysql> show databases;

+--------------------+
| Database |
+--------------------+
| information_schema |
| hi_db | //I'M here,大家看到了吧
| mysql |
| test |

+--------------------+
4 rows in set (0.00 sec)

mysql> use hi_db
Database changed
mysql> select * from hi_tb; //查看主服务器上新增的具体数据
+------+------+
| id | name |
+------+------+
| 1 | bobu |
+------+------+
1 row in set (0.00 sec)
10、完成:
编写一shell脚本,用nagios监控slave的两个yes(Slave_IO及Slave_SQL进程),如发现只有一个或零个yes,就表明主从有问题了,发短信警报吧。

原文地址 http://369369.blog.51cto.com/319630/790921