分类 IT技术 下的文章

windows与centos-mysql主从同步

在主服务器新建一个用户,给从服务器同步使用。

GRANT ALL PRIVILEGES ON *.* TO 'syncAccount'@'172.16.254.170' IDENTIFIED BY 'syncPassword'; 

修改my.ini

server-id=1 
log-bin=mysql-bin 
binlog-do-db=lbqdb
binlog-ignore-db=mysql

重启主服务器 mysql服务

mysql>show master status;
+------------------+----------+--------------+------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+------------------+----------+--------------+------------------+
| mysql-bin.000001 |      446 | lbqdb        | mysql            |
+------------------+----------+--------------+------------------+
1 row in set

修改slave机中mysql配置文件my.cnf

同样在[mysqld]字段下添加如下内容

server-id=2  
master-host=xxx.xxx.xxx.xxx
master-user=syncAccount
master-password=syncPassword
master-port=3306 
master-connect-retry=60 
replicate-do-db=lbqdb

重启mysql报错:

service mysql start
Starting MySQL.. ERROR! The server quit without updating PID file (/var/lib/mysql/localhost.localdomain.pid).

经查 因为这些参数5.1.7以后就不支持了

master-host=
master-user=
master-password=
master-port=
master-connect-retry=

这些参数 mysql5.5已经废弃了

必须在slave上用change master to 来设置slave

重新编辑my.cnf

server_id = 2 
log_bin = mysql_bin
binlog_do_db = lbqdb
binlog_ignore_db = mysql

重启成功。

mysql –u root –p 

设置连接master:

mysql->change master to master_host='xxx.xxx.xxx.xxx',master_user='syncAccount',master_password='syncPassword', master_log_file='mysql-bin.000001',master_log_pos=446;

连接的账户 IP 密码为mastermysql的。master_log_file 和log_pos 为前期windows mysql的show master status 中看到。

出现OK之后

Start slave;启动slave

查看状态

Show slave status\G

*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 192.168.3.4
                  Master_User: syncAccount
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: mysql-bin.000001
          Read_Master_Log_Pos: 1682
               Relay_Log_File: localhost-relay-bin.000002
                Relay_Log_Pos: 1489
        Relay_Master_Log_File: mysql-bin.000001
             **Slave_IO_Running: Yes
            Slave_SQL_Running: Yes**
              Replicate_Do_DB:
          Replicate_Ignore_DB:
           Replicate_Do_Table:
       Replicate_Ignore_Table:
      Replicate_Wild_Do_Table:
  Replicate_Wild_Ignore_Table:
                   Last_Errno: 0
                   Last_Error:
                 Skip_Counter: 0
          Exec_Master_Log_Pos: 1682
              Relay_Log_Space: 1649
              Until_Condition: None
               Until_Log_File:
                Until_Log_Pos: 0
           Master_SSL_Allowed: No
           Master_SSL_CA_File:
           Master_SSL_CA_Path:
              Master_SSL_Cert:
            Master_SSL_Cipher:
               Master_SSL_Key:
        Seconds_Behind_Master: 0
Master_SSL_Verify_Server_Cert: No
                Last_IO_Errno: 0
                Last_IO_Error:
               Last_SQL_Errno: 0
               Last_SQL_Error:
  Replicate_Ignore_Server_Ids:
             Master_Server_Id: 1
1 row in set (0.00 sec)

重点关注这两个:

Slave_IO_Running: Yes
Slave_SQL_Running: Yes

当slave_IO_Running 和Slave_SQL_Running 都为yes 说明配置成功。

测试了一下,在主数据库新建表并插入数据,从数据库也新建了表,里面也有对应数据。证明数据库已经同步了。

MySQL主从不同步问题解决

问题 描述:主从复制在某个时刻,从服务器同步失败,并给出报错消息。

消息内容:基本意思是缺少某个库的某个表

原因:主从服务长时间不同步,主库已经生成很多新的表或者库,而从库却没有。

操作过程:
(1)登陆主服务器,查看主服务器的状态

mysql>show master status;

(2)登陆从服务器,执行同步操作。

mysql>stop slave;mysql > change master to ...(此处省略);mysql > start slave;

(3)从服务器上查看状态

mysql > show slave status\G

看报错信息少什么表或库,少什么就直接从主服务器通过scp复制,然后重复过程(1)~(3)直到不报错为止

以上说明:同步前,主从数据库的数据库结构要相同。

PS:主键自增id重复的错误

......
Slave_SQL_Running: No
......
Last_SQL_Error: Error 'Duplicate entry '60503' for key 'PRIMARY'' on query....

在主服务器做备份,导入到从服务器的时候,由于没有停服务,也没有锁表,在生成binlog文件的时刻,与导出数据的时刻存在误差,导致从服务器上数据多出一些,同步时,某些自增id就重复了,根据给出的提示,我们可以将多出的数据删除,将AUTO_INCREMENT设为binlog中的值。都设置完成后,再次

start slave;

成功!

也就是说,start slave报错的话,不用担心脏数据,可以解决错误后,继续start slave.

ReactJS学习-使用webpack构建工程,使用materialUI构建前端,与hprose后端通讯

实现目标:

Material-UI 是一套用React写成的,符合Google Material Design 的UI组件库。

http://www.material-ui.com/

前端通过Material-UI构造界面,然后通过hprose-html5调用后端hprose服务取数据

hprose服务参考 上一篇hprose实践,

环境配置:

必须先安装nodejs与npm

新建工程目录

mkdir react-workspace
cd react-workspace
npm init

安装依赖

npm install --save react react-dom react-tap-event-plugin material-ui
npm install --save-dev babel-core babel-loader 
npm install --save-dev babel-preset-es2015 babel-preset-react babel-preset-stage-1
npm install --save-dev webpack

第一行是生产用的 React 与 Material-UI 部分。
第二行是Babel转换器的核心部分。
第三行是Babel转换器的三个额外配置:ES2015(ES6),React,Stage1(ES7)。
第四行是Webpack的部分。

第二、三、四行的内容只在工程构建之前有用(用于开发:-dev)。

安装完毕之后呢,可以先检查一下 package.json
应该会看见如下内容

"dependencies": {
    "material-ui": "^0.15.0",
    "react": "^15.1.0",
    "react-dom": "^15.1.0",
    "react-tap-event-plugin": "^1.0.0"
  },
  "devDependencies": {
    "babel-core": "^6.9.1",
    "babel-loader": "^6.2.4",
    "babel-preset-es2015": "^6.9.0",
    "babel-preset-react": "^6.5.0",
    "babel-preset-stage-1": "^6.5.0",
    "webpack": "^1.13.1"
  },

配置 Babel

在 package.json 中添加一个域"babel",与之前的"dependencies" 同级。

"babel": {
    "presets": [
      "es2015",
      "react",
      "stage-1"
    ],
    "plugins": []
  }

配置 Webpack

在项目目录新建一个webpack.config.js ,并写入:

var path = require('path');

module.exports = {
    entry: './entry.js',
    output: {
        path: path.join(__dirname, '/dist'),
        filename: 'bundle.js'
    },
    resolve: {
        extensions: ['', '.js', '.jsx']
    },
    module: {
        loaders: [
            { test: /\.jsx?$/, loaders: ['babel'] }
        ]
    }
}

这里对Webpack的打包行为做了配置,主要分为几个部分:

entry:指定打包的入口文件,每有一个键值对,就是一个入口文件
output:配置打包结果,path定义了输出的文件夹,filename则定义了打包结果文件的名称
resolve:定义了解析模块路径时的配置,常用的就是extensions,可以用来指定模块的后缀,这样在引入模块时就不需要写后缀了,会自动补全
module:定义了对模块的处理逻辑,这里可以用loaders定义了一系列的加载器,以及一些正则。当需要加载的文件匹配test的正则时,就会调用后面的loader对文件进行处理,这正是webpack强大的原因。比如这里定义了凡是.js结尾的文件都是用babel-loader做处理,而.jsx结尾的文件会先经过jsx-loader处理,然后经过babel-loader处理。当然这些loader也需要通过npm install安装
plugins: 这里定义了需要使用的插件,比如commonsPlugin在打包多个入口文件时会提取出公用的部分,生成common.js

当然Webpack还有很多其他的配置,具体可以参照它的配置文档

配置 npm 脚本
现在我们还缺少一个构建脚本,编辑package.json 中的 "scripts" 域:

"scripts": {
    "build": "webpack",
    "build-dev": "webpack -w -d"
  }

接下来我们就可以在项目目录下使用简单的构建脚本了:

$ npm run build

如果是开发中,可以使用监听式的Webpack,差量打包,提升效率。

$ npm run build-dev

按照配置,打包生成的文件为 dist/bundle.js 。

至此,基本的环境已经配置完毕,我们来尝试一下调用Material-UI库。

代码编写

创建Web入口
在项目目录下创建一个index.html,写入:

<!doctype html>
<html>
    <head>
        <meta charset="utf-8" />
    </head>
    <body>
        <script src="dist/bundle.js"></script>
    </body>
</html>

编写Webpack入口
编辑项目目录下的 entry.js,写入:

import React from 'react';
import ReactDOM from 'react-dom';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import AppBar from 'material-ui/AppBar';

const App = () => (
  <MuiThemeProvider muiTheme={getMuiTheme()}>
    <AppBar title="Hello, Material-UI!" />
  </MuiThemeProvider>
);

let app = document.createElement('div');
ReactDOM.render(<App />, app);
document.body.appendChild(app);

运行构建脚本:

npm run build

输出
6.png

现在可以看到目录下有了一个dist/bundle.js

打开index.html

7.png
成功

以上内容严重参考:http://blog.csdn.net/zccz14/article/details/51421324

浏览器自动刷新

如果需要一直输入 npm run build 确实是一件非常无聊的事情,幸运的是,我们可以把让他安静的运行,让我们设置 webpack-dev-server。

npm install --save webpack-dev-server

修改package.json文件的scripts:

"scripts": {
        "build": "webpack",
        "dev": "webpack-dev-server --devtool eval --progress --colors --hot --content-base build"
      }

当你在命令行里运行 npm run dev 的时候他会执行 dev 属性里的值。这是这些指令的意思:

webpack-dev-server - 在 localhost:8080 建立一个 Web 服务器
--devtool eval - 为你的代码创建源地址。当有任何报错的时候可以让你更加精确地定位到文件和行号
--progress - 显示合并代码进度
--colors - Yay,命令行中显示颜色!
--content-base build - 指向设置的输出目录
总的来说,当你运行 npm run dev 的时候,会启动一个 Web 服务器,然后监听文件修改,然后自动重新合并你的代码。真的非常简洁!

访问 http://localhost:8080 你会看到效果。

访问发现 “Cannot GET /” 错误

因为我们的index.html不是在build目录下

调整项目结构为

  • /app
    • main.js
    • component.js
  • /build
    • bundle.js (自动创建)
    • index.html
  • package.json
  • webpack.config.js

修改index.html

<!doctype html>
    <html>
        <head>
            <meta charset="utf-8" />
        </head>
        <body>
            <script src="bundle.js"></script>
        </body>
    </html>

将entry.js移动到app/main.js

修改webpack.config.js,将入口文件设置为app/main.js,output设为build目录下的bundle.js,
并新增入口点,使得浏览器在文件修改之后会自动刷新。

var path = require('path');

module.exports = {
    entry: [
      'webpack/hot/dev-server',
      'webpack-dev-server/client?http://localhost:8080',
      path.resolve(__dirname, 'app/main.js')
    ],
    output: {
        path: path.join(__dirname, '/build'),
        filename: 'bundle.js'
    },
    resolve: {
        extensions: ['', '.js', '.jsx']
    },
    module: {
        loaders: [
            { test: /\.jsx?$/, loaders: ['babel'] }
        ]
    }
}

运行命令 npm run dev, 然后访问http://localhost:8080/
看到之前同样页面,然后修改一下main.js

<AppBar title="Hello, Material-UI!" />

修改为

<AppBar title="Hello, World!" />

保存一下,再回到浏览器,会发现自动刷新了,内容也变成了Hello, World!

通过hprose-html5取数据

下面通过后端服务取数据,来替代Hello,World!

现已有hprose for php 构建的hprose服务,在远程服务器http://xx.xx.xx.xx:8080/
方法为getUserByID

修改index.html,引入hprose-html5.js,使用的是bootcss提供的cdn

<script type="text/javascript" src="http://cdn.bootcss.com/hprose-html5/2.0.8/hprose-html5.js"></script>

修改main.js

import React from 'react';
import ReactDOM from 'react-dom';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import AppBar from 'material-ui/AppBar';

const muiTheme = getMuiTheme();

class App extends React.Component{

  constructor(props){
    super(props);
    this.state = {
      data:"none",
    };
    this.componentDidMount = this.componentDidMount.bind(this);
  }

  componentDidMount() {
      this.setState({data: "block"});
      let self = this;
      let client = hprose.Client.create("http://xx.xx.xx.xx:8080/", ["getUserByID"]);
      client.getUserByID(2, function(result) {
            console.log(result.player_name);
            self.setState({data: result.player_name});
        }, function(name, err) {
            console.log(err);
        });
  }
  render() {
    return (
    <MuiThemeProvider muiTheme={muiTheme}>
    <AppBar title={this.state.data} />
    </MuiThemeProvider>
    );
  }
}

let app = document.createElement('div');
ReactDOM.render(<App />, app);
document.body.appendChild(app);

查看结果

5.png

从后端服务取到了数据“keyunqqq”

PS:React在ES6的实现中去掉了getInitialState这个hook函数,规定state在constructor中实现,如下:

Class App extends React.Component {

constructor(props) {
super(props);
this.state = {};
}
...
}

Babel的Blog上还有一种实现方法,即直接使用赋值语句:

Class App extends React.Component {

constructor(props) {
super(props);
}

state = {}
...
}

ES6中this需要手动绑定:

this.componentDidMount = this.componentDidMount.bind(this);

nginx/php-fpm及网站目录的权限设置

核心总结:nginx、php-fpm 进程所使用的用户,不能是网站文件所有者。 凡是违背这个原则,则不符合最小权限原则。

最佳实践:
nginx 以nobody运行
修改nginx.conf
user nobody
重启nginx服务器

php-fpm 也以nobody运行
编辑文件php-fpm.conf
user nobody
group nobody
重启php-fpm

网站目录设置为可读即可,某些需要写入权限的目录设置为777

nginx静态资源 403 forbidden
有可能是selinux的原因

临时关闭selinux
setenforce 0

永久关闭selinux
vi /etc/selinux/config
SELINUX=disabled

重启服务器

PS:
访问出现异常,chrome控制台报错ERR_INCOMPLETE_CHUNKED_ENCODING

查看日志 /var/log/nginx/error.log

open() "/var/cache/nginx/fastcgi_temp/4/09/0000000094" failed (13: Permission denied) while reading upstream,

原因是:
nginx会使用fastcgi_buffer_size指定的大小的缓冲区用于缓存fastcgi流的内容。当大小超出此大小时会继续用fastcgi_buffers指定的数量和大小申请缓冲区。如果依然超出此大小,会将多出的内容写入临时文件。上面日志中反应的就是nginx无权写入临时文件。

于是将/var/cache/nginx/用户也设为nobody

chown nobody:nobody -R /var/cache/nginx/

解决问题。

基于swoole的Hprose for PHP 开发后端API服务 实践


注:Hprose for PHP 已更新到2.0 本文代码已失效 请参阅最新官方文档
https://github.com/hprose/hprose-php/wiki


Hprose(High Performance Remote Object Service Engine)
是一款先进的轻量级、跨语言、跨平台、无侵入式、高性能动态远程对象调用引擎库。它不仅简单易用,而且功能强大。
你无需专门学习,只需看上几眼,就能用它轻松构建分布式应用系统。
http://hprose.com/

Hprose for PHP
https://github.com/hprose/hprose-php

swoole PHP的异步、并行、高性能网络通信引擎
http://www.swoole.com/

简单示例,目标:开发一个后台服务,通过id查询数据库的用户表数据,
当服务器端发布的方法被调用前,通过Hprose提供的onBeforeInvoke函数来验证IP

软件安装:
mariadb 10.1.13
php 5.6.21
swoole 1.8.4

https://github.com/hprose/hprose-php/tree/master/src
hprose文件下载到工作目录下

服务端:
目录结构
QQ图片20160524142310.png

数据库配置文件:config.php

<?php
/**
 *  数据库连接配置
 */
define('DB_NAME', 'xxx');
define('DB_USER', 'xxx');
define('DB_PWD', 'xxx');
define('DB_HOST', '127.0.0.1');
define('DB_PORT','3306');
define('DB_TYPE','mysql');
define('DB_CHARSET','utf8');
define('PAGINATE_LIMIT', '5');
define('PREFIX', '');

User类文件:User.php

<?php
class User {
    
    public function getUserByID($user_id) {
        
        try {

            $dbh = new PDO('mysql:host='.DB_HOST.';dbname='.DB_NAME, DB_USER, DB_PWD);

            $rs = array();
            
            foreach($dbh->query("SELECT * from XXXX WHERE `id` = {$user_id}") as $row) {
                $rs[] = $row;
            }
            
            return $rs;

        } catch (PDOException $e) {
            return "Error!: " . $e->getMessage() . "<br/>";
        }
    }
}

服务器文件:server.php

<?php
/**
 *  配置文件    
 */
require_once("config.php");

/**
 *  引入Hprose
 */
require_once("Hprose.php");

/**
 *  autoload加载,将文件都放在classes目录下
 */
spl_autoload_register(function ($class) {
    include 'classes/' . $class . '.php';
});

/**
 *  使用 HproseSwooleServer 来创建一个独立的 hprose 服务
 *  绑定8080端口
 */
$server = new HproseSwooleServer('http://0.0.0.0:8080/');

/**
 *  addInstanceMethods用来发布指定对象上的指定类层次上声明的所有public实例方法
 *  它有三个参数,其中后两个是可选参数。
 *  如果您在使用addInstanceMethods方法时,不指定类层次(或者指定为NULL),则发布这个对象所在类上声明的所有public实例方法
 *  设置别名为my
 */
$server->addInstanceMethods(new User(), NULL, 'my');

/**
 *  onBeforeInvoke 会在接口被调用前执行
 *  因此可以用来验证IP
  */
$server->onBeforeInvoke = function ($name, $args, $byref, $context) {
    if($context->request->server['remote_addr'] == '127.0.0.1') {

    } else {
        //不是正确的IP则抛出异常,终止服务
        throw new Exception("unknow client IP");    
    }
};

//启动服务
$server->start();

客户端:

<?php
require_once("Hprose.php");
$client = new HproseSwooleClient('http://localhost:8080');
//调用getUserByID方法,需要加上设置的别名my_
print_r($client->my_getUserByID(4003));

直接打印出了数据,则走通了整个流程。

Hprose for HTML5客户端调用跨域问题

用Hprose for HTML5的客户端调用,发现不能跨域,
查源码,发现Hprose提供了解决办法,server.php启动服务前加上以下语句,
允许跨域,并将调用的域名加入AccessControlAllowOrigin

$server->setCrossDomainEnabled();
$server->addAccessControlAllowOrigin('http://xxxxxxxxxx');

可解决跨域问题。

在Ubuntu 14.04中使SublimeText 3支持中文输入法(搜狗)

在Ubuntu 14.04中安装了SublimeText 3 以及搜狗输入法linux

发现SublimeText输入不了中文

解决办法如下:

安装C/C++的编译环境和gtk libgtk2.0-dev 命令如下

sudo apt-get install build-essential 
sudo apt-get install libgtk2.0-dev 
sudo apt-get install gtk+-2.0

创建sublime_imfix.c文件,可以放在当前用户目录~下,内容如下:

/*
sublime-imfix.c
Use LD_PRELOAD to interpose some function to fix sublime input method support for linux.
By Cjacker Huang <jianzhong.huang at i-soft.com.cn>

gcc -shared -o libsublime-imfix.so sublime_imfix.c  `pkg-config --libs --cflags gtk+-2.0` -fPIC
LD_PRELOAD=./libsublime-imfix.so sublime_text
*/
#include <gtk/gtk.h>
#include <gdk/gdkx.h>
typedef GdkSegment GdkRegionBox;

struct _GdkRegion
{
  long size;
  long numRects;
  GdkRegionBox *rects;
  GdkRegionBox extents;
};

GtkIMContext *local_context;

void
gdk_region_get_clipbox (const GdkRegion *region,
            GdkRectangle    *rectangle)
{
  g_return_if_fail (region != NULL);
  g_return_if_fail (rectangle != NULL);

  rectangle->x = region->extents.x1;
  rectangle->y = region->extents.y1;
  rectangle->width = region->extents.x2 - region->extents.x1;
  rectangle->height = region->extents.y2 - region->extents.y1;
  GdkRectangle rect;
  rect.x = rectangle->x;
  rect.y = rectangle->y;
  rect.width = 0;
  rect.height = rectangle->height; 
  //The caret width is 2; 
  //Maybe sometimes we will make a mistake, but for most of the time, it should be the caret.
  if(rectangle->width == 2 && GTK_IS_IM_CONTEXT(local_context)) {
        gtk_im_context_set_cursor_location(local_context, rectangle);
  }
}

//this is needed, for example, if you input something in file dialog and return back the edit area
//context will lost, so here we set it again.

static GdkFilterReturn event_filter (GdkXEvent *xevent, GdkEvent *event, gpointer im_context)
{
    XEvent *xev = (XEvent *)xevent;
    if(xev->type == KeyRelease && GTK_IS_IM_CONTEXT(im_context)) {
       GdkWindow * win = g_object_get_data(G_OBJECT(im_context),"window");
       if(GDK_IS_WINDOW(win))
         gtk_im_context_set_client_window(im_context, win);
    }
    return GDK_FILTER_CONTINUE;
}

void gtk_im_context_set_client_window (GtkIMContext *context,
          GdkWindow    *window)
{
  GtkIMContextClass *klass;
  g_return_if_fail (GTK_IS_IM_CONTEXT (context));
  klass = GTK_IM_CONTEXT_GET_CLASS (context);
  if (klass->set_client_window)
    klass->set_client_window (context, window);

  if(!GDK_IS_WINDOW (window))
    return;
  g_object_set_data(G_OBJECT(context),"window",window);
  int width = gdk_window_get_width(window);
  int height = gdk_window_get_height(window);
  if(width != 0 && height !=0) {
    gtk_im_context_focus_in(context);
    local_context = context;
  }
  gdk_window_add_filter (window, event_filter, context); 
}

然后编译成共享库

gcc -shared -o libsublime-imfix.so sublime_imfix.c  `pkg-config --libs --cflags gtk+-2.0` -fPIC

在目录下,得到libsublime-imfix.so

命令行启动sublime,检测一下能否输入中文:

LD_PRELOAD=./libsublime-imfix.so subl

假如成功,就可以输入中文了。

接下来我们还要通过修改sublime_text.desktop达到点击图标启动,也可以输入中文。

将libsublime-imfix.so拷贝到系统库的默认路径中:

sudo cp libsublime-imfix.so /usr/lib/

修改sublime_text.desktop

cd /usr/share/applications
 
sudo gedit sublime_text.desktop

打开sublime_text.desktop后,修改如下

  将Exec=/opt/sublime_text/sublime_text %F  修改为  Exec=bash -c 'LD_PRELOAD=/usr/lib/libsublime-imfix.so /opt/sublime_text/sublime_text' %F

  将Exec=/opt/sublime_text/sublime_text -n  修改为  Exec=bash -c 'LD_PRELOAD=/usr/lib/libsublime-imfix.so /opt/sublime_text/sublime_text' -n