Ubuntu Server 安装 Samba 并配置匿名可读写访问

1. 安装 Samba

# apt-get install samba

2. 编辑配置文件

# vim /etc/samba/smb.conf

在最后面插入以下几行

[liubuntu-share] # 这个是你要共享的名字,可以在链接到这台机器的上看到,名字可以随便取
path=/data # 你要共享的目录,设置你自己的目录就好了
public=yes # 访问权限设置为 public
writable=yes # 可写
security=share

3. 重启

# service samba restart

 

Ubuntu Server 静态IP简洁配置

1、配置静态IP地址:

vim /etc/network/interfaces

原内容有如下4行:

auto lo
iface lo inet loopback

auto eth0
iface eth0 inet dhcp

以上表示默认使用DHCP分配IP,修改为如下:

auto lo
iface lo inet loopback

auto eth0
#iface eth0 inet dhcp
iface eth0 inet static
address 192.168.1.110
netmask 255.255.255.0
gateway 192.168.1.1

保存退出。
注意:只需要设置address(IP地址)、netmask(子网掩码)、gateway(网关)这三项就OK,network和broadcast这两项参数是可以不写的。

2、手动设置DNS服务器:

vim /etc/resolvconf/resolv.conf.d/base

添加如下内容(这点所有Linux发行版都通用):

nameserver 8.8.8.8
nameserver 8.8.4.4

3、重启网卡使其生效:

ifdown eth0 # 如果你的网卡名不是这个,请更改为对应的
ifup eth0
# service networking restart 这个命令是不会成功的

 

大部分来自:http://www.ha97.com/4895.html,本文有修改。

MySQL 配置最基础的主从

Basic Steps in Replication

This chapter will introduce several sophisticated techniques for maximizing the effi‐ ciency and value of replication, but as a first step, we will set up the simple replication shown in Figure 3-1—a single instance of replication from a master to a slave. This does not require any knowledge of the internal architecture or execution details of the rep‐ lication process (we’ll explore these before we take on more complicated scenarios).

Setting up basic replication can be summarized in three easy steps:

  1. Configure one server to be a master.
  2. Configure one server to be a slave.
  3. Connect the slave to the master.

Unless you plan replication from the start and include the right configuration options in the my.cnf files, you will have to restart each server to carry out steps 1 and 2.

To follow the procedures in this section, it is easiest if you have a shell account on the machine with privileges to change the my.cnf file as well as an account on the server with ALL privileges granted.1

You should be very restrictive in granting privileges in a production environment. For precise guidelines, consult “Privileges for the User Configuring Replication” on page 27.

Configuring the Master

To configure a server so that it can act as master, ensure the server has an active binary log and a unique server ID. We will examine the binary log in greater detail later, but for now it is sufficient to say that it keeps a record of all the changes the master has made so that they can be repeated on the slave. The server ID is used to distinguish two servers from each other. To set up the binary log and server ID, you have to take the server down and add the log-bin, log-bin-index, and server-id options to the my.cnf configuration file as shown in Example 3-1. The added options are in boldface.

Example 3-1. Options added to my.cnf to configure a master

[mysqld]
user = mysql
pid-file = /var/run/mysqld/mysqld.pid
socket = /var/run/mysqld/mysqld.sock 
port = 3306
basedir = /usr
datadir = /var/lib/mysql
tmpdir = /tmp
log-bin = master-bin
log-bin-index = master-bin.index
server-id = 1

The log-bin option gives the base name for all the files created by the binary log (as you will see later, the binary log consists of several files). If you create a filename with an extension to log-bin, the extension will be ignored and only the file’s base name will be used (i.e., the name without the extension).

1. On Windows, the command-line prompt (CMD) or PowerShell can be used in place of the Unix “shell.” Basic Steps in Replication | 25

The log-bin-index option gives the name of the binary log index file, which keeps a list of all binlog files.

Strictly speaking, it is not necessary to give a name in the log-bin option. The default value is hostname-bin. The value for hostname is taken from the option for pid-file, which by default is the name of the host (as given by the gethostname(2) system call). If an administrator later changes the machine’s hostname, the binlog files will change names as well, but they will be tracked correctly in the index file. However, it is a good idea to create a name that is unique for the MySQL server and not tied to the machine the server is running on because it can be confusing to work with a series of binlog files that suddenly change name midstream.

If no value is provided for log-bin-index, the default value will be the same base name as for the binlog files (hostname-bin if you don’t give a default for log-bin). This means that if you do not provide a value for log-bin-index, the index file will change its name when you change the name of the host. So if you change the name of the host and start the server, it will not find the index file and therefore assume that it does not exist, and this will give you an empty binary log.

Each server is identified by a unique server ID, so if a slave connects to the master and has the same server-id as the master, an error will be generated indicating that the master and the slave have the same server ID.

Once you have added the options to the configuration file, start the server again and finish its configuration by adding a replication user.

After you make the change to the master’s configuration file, restart the master for the changes to take effect.

The slave initiates a normal client connection to the master and requests the master to send all changes to it. For the slave to connect, a user with special replication privileges is required on the master. Example 3-2 shows a standard mysql client session on the master server, with commands that add a new user account and give it the proper privilege.

Example 3-2. Creating a replication user on the master

GRANT REPLICATION SLAVE ON *.* TO repl_user IDENTIFIED BY 'xyzzy';

There is nothing special about the REPLICATION SLAVE privilege except that the user can retrieve the binary log from the master. It is perfectly viable to have a normal user account and grant that user the REPLICATION SLAVE privilege. It is, however, a good idea to keep the replication slave user separate from the other users. If you do that, you can remove the user if you need to disallow certain slaves from connecting later.

Configuring the Slave

After configuring the master, you must configure the slave. As with the master server, you need to assign each slave a unique server ID. You may also want to consider adding the names of the relay log and the relay log index files to the my.cnf file (we will discuss the relay log in more detail in “Replication Architecture Basics” on page 228) using the options relay-log and relay-log-index. The recommended configuration options are given in Example 3-3, with the added options highlighted.

Example 3-3. Options added to my.cnf to configure a slave

[mysqld]
user = mysql
pid-file = /var/run/mysqld/mysqld.pid socket = /var/run/mysqld/mysqld.sock port = 3306
basedir = /usr
datadir = /var/lib/mysql
tmpdir = /tmp
server-id = 2
relay-log-index = slave-relay-bin.index relay-log = slave-relay-bin

Like the log-bin and log-bin-index options, the defaults for the relay-log and relay-log-index options depend on the hostname. The default for relay-log is host name-relay-bin and the default for relay-log-index is hostname-relay-bin.index. Using the default introduces a problem in that if the hostname of the server changes, it will not find the relay log index file and will assume there is nothing in the relay logfiles.

After editing the my.cnf file, restart the slave server for the changes to take effect.

Connecting the Master and Slave

Now you can perform the final step in setting up basic replication: directing the slave to the master so that it knows where to replicate from. To do this, you need four pieces of information about the master:

  • A hostname
  • A port number
  • A user account on the master with replication slave privileges
  • A password for the user account

You already created a user account with the right privileges and a password when con‐ figuring the master. The hostname is given by the operating system and can’t be con‐ figured in the my.cnf file, but the port number can be assigned in my.cnf (if you do not supply a port number, the default value of 3306 will be used). The final two steps nec‐ essary to get replication up and running are to direct the slave to the master using the CHANGE MASTER TO command and then start replication using START SLAVE:

CHANGE MASTER TO
 MASTER_HOST = 'master-1',
 MASTER_PORT = 3306,
 MASTER_USER = 'repl_user',
 MASTER_PASSWORD = 'xyzzy';
-- Query OK, 0 rows affected (0.00 sec)

START SLAVE;
-- Query OK, 0 rows affected (0.15 sec)

 

Congratulations! You have now set up your first replication between a master and a slave! If you make some changes to the database on the master, such as adding new tables and filling them in, you will find that they are replicated to the slave. Try it out! Create a test database (if you do not already have one), create some tables, and add some data to the tables to see that the changes replicate over to the slave.

Observe that either a hostname or an IP address can be given to the MASTER_HOST parameter. If a hostname is given, the IP address for the hostname is retrieved by calling gethostname(3), which, depending on your configuration, could mean resolving the hostname using a DNS lookup. The steps for configuring such lookups are beyond the scope of this book.

 

摘选自:《MySQL High Availability》 p25-p29

Linux 查看 glibc 支持的版本

strings /lib/x86_64-linux-gnu/libc.so.6|grep GLIBC

Ubuntu 14.04.1 LTS 的输出为:

GLIBC_2.2.5
GLIBC_2.2.6
GLIBC_2.3
GLIBC_2.3.2
GLIBC_2.3.3
GLIBC_2.3.4
GLIBC_2.4
GLIBC_2.5
GLIBC_2.6
GLIBC_2.7
GLIBC_2.8
GLIBC_2.9
GLIBC_2.10
GLIBC_2.11
GLIBC_2.12
GLIBC_2.13
GLIBC_2.14
GLIBC_2.15
GLIBC_2.16
GLIBC_2.17
GLIBC_2.18
GLIBC_PRIVATE
GNU C Library (Ubuntu EGLIBC 2.19-0ubuntu6.3) stable release version 2.19, by Roland McGrath et al.

 

参考文章:http://blog.ithomer.net/2014/09/centos-lib64-libc-so-6version-glibc-2-14-not-found-system-to-glibc-version-is-too-low/

实用链接收藏

分享一个 MySQL 分库分表类

当一个表数据记录过大时就会出现性能瓶颈,而一般对应的解决办法是要么做分区表,要么分表,分区表就不说了,分表又分为垂直分割和水平分割,具体区别请自行搜索。一般而言,分库分表属于水平分割,按照一定的规则将数据插入到不同的表中去。而分库则可以很方便的转移数据库的压力,比如将一个很大库的分别放在不同的服务器上。

下面是我写的一个分库分表的实现:

<?php
/**
 * User: jing.liu
 * Date: 14-8-12
 * Time: 下午3:16
 */

namespace App\Model\Database;

class Config
{
    public $dsn;
    public $user;
    public $password;
    /**
     * @var string 分库分表后得到的数据库名
     */
    public $dbname;
    /**
     * @var string 分库分表后得到的表名
     */
    public $table;

    /**
     * @var array MySQL 配置数组
     */
    private static $config;

    /**
     * @var string 配置文件路径
     */
    private static $configFile = 'mysql.php';

    public function __construct($dbname, $table, $id = 0)
    {
        if (is_null(static::$config)) {
            $config = include(static::$configFile);
            static::$config = $config;
        }

        $config = static::$config;
        if (isset($config['shared']) && isset($config['shared'][$dbname])) {
            $dbconfig = $config['shared'][$dbname];
            $id = is_numeric($id) ? (int)$id : crc32($id);
            $database_id = ($id / $dbconfig['database_split'][0]) % $dbconfig['database_split'][1];
            $table_id = ($id / $dbconfig['table_split'][0]) % $dbconfig['table_split'][1];

            foreach ($dbconfig['host'] as $key => $conf) {
                list($from, $to) = explode('-', $key);
                if ($from <= $database_id && $database_id <= $to) {
                    $the_config = $conf;
                }
            }

            $this->dbname = $dbname . '_' . $database_id;
            $this->table = $table . '_' . $table_id;
        } else {
            $this->dbname = $dbname;
            $this->table = $table;
            $the_config = $config['db'][$dbname];
        }
        $c = $the_config;
        if (isset($c['unix_socket']) && $c['unix_socket']) {
            $this->dsn = sprintf('mysql:dbname=%s;unix_socket=%s', $this->dbname, $c['unix_socket']);
        } else {
            $this->dsn = sprintf('mysql:dbname=%s;host=%s;port=%s', $this->dbname, $c['host'], $c['port']);
        }
        $this->user = $c['user'];
        $this->password = $c['password'];
    }

}

Config 类就做一个事情,根据配置文件,去拿到对应的库和表的链接配置,然后客户可以根据 dsn 去链接对应的数据库。对应的配置文件如下:

<?php
/**
 * User: jing.liu
 * Date: 14-8-6
 * Time: 上午11:19
 */

$default = array(
    'unix_socket' => null,
    'host' => 'localhost',
    'port' => '3306',
    'user' => 'root',
    'password' => '',
);

$config = array(
    // 不进行分库分表的数据库
    'db' => array(
        'my_site' => $default,
    ),
    // 分库分表
    'shared' => array(
        'user' => array(
            'host' => array(
                /**
                 * 编号为 0 到 10 的库使用的链接配置
                 */
                '0-10' => $default,
                /**
                 * 编号为 11 到 28 的库使用的链接配置
                 */
                '11-28' => $default,
                /**
                 * 编号为 29 到 99 的库使用的链接配置
                 */
                '29-99' => $default,

            ),

            // 分库分表规则
            /**
             * 下面的配置对应百库百表
             * 如果根据 uid 进行分表,假设 uid 为 543234678,对应的库表为:
             *  (543234678 / 1) % 100 = 78 为编号为 78 的库
             *  (543234678 / 100) % 100 = 46 为编号为 46 的表
             */
            'database_split' => array(1, 100),
            'table_split' => array(100, 100),
        ),
    ),
);


return $config;

给出一个使用这个分库分表的例子:

<?php
/**
 * User: jing.liu
 * Date: 14-8-6
 * Time: 上午10:23
 */

namespace App\Model;

use App\Model\Database\Config;
use \PDO;

abstract class Model
{
    /**
     * @var Config
     */
    public $config;

    /**
     * @var PDO
     */
    public $connection;

    protected $dbnamePrefix;
    protected $tablePrefix;

    /**
     * @var string 分库分表后对应的表
     */
    protected $table;

    public function __construct($id)
    {
        $this->config = new Config($this->dbnamePrefix, $this->tablePrefix, $id);
        $this->connection = new Pdo($this->config->dsn, $this->config->user, $this->config->password);
        $this->table = $this->config->table;
    }

    public function update(array $data, array $where = array())
    {

    }

    public function select(array $where)
    {

    }

    public function insert(array $data)
    {

    }

    public function query($sql)
    {
        return $this->connection->query($sql);
    }
}

下面这个例子展示了如何使用上述的 Model 类:

<?php
/**
 * User: jing.liu
 * Date: 14-8-12
 * Time: 下午4:06
 */

require 'Config.php';
require 'Model.php';

use App\Model\Model;

class User extends Model
{
    protected $dbnamePrefix = 'user';
    protected $tablePrefix = 'userinfo';
}

$user = new User(4455345345);

print_r($user);

如果看官您有任何疑问或者有更好的实现,欢迎交流~

PHP 取一个整数的十位上的数值可能出现的 bug

问:如何取到一个整数的十位上的数值?

答:先除以 10 再对 10 取余。代码如下:

echo ($number / 10) % 10;

很简单是不是?但这段代码有 bug。你可以运行下面这段代码:

echo PHP_INT_MAX;
echo PHP_EOL; // 输出换行 
echo (PHP_INT_MAX / 10) % 10;
echo PHP_EOL;

输出如下:

buggggggggggeeeeee

可以看到整数 9223372036854775807 的十位数上是 0 ,而我们确得到了 2 ,这是怎么一回事呢?

我们执行下如下代码:

$a = PHP_INT_MAX;
var_dump(gettype($a), $a);
$b = $a / 10;
var_dump(gettype($b), $b);
$c = $b % 10;
var_dump(gettype($c), $c);

输出为:

RTX截图未命名

可以看到, 9223372036854775807 不能被 10 整除,$a / 10 后得到的是一个浮点数,丢失了精度,这个时候再对 10 取模得到的值就不准了。

请时刻记住 PHP 变量的类型

我们都知道 PHP 变量类型是弱类型,这样很方便,当你需要一个数字时,变量就表现得像个 Int,当你需要字符串时,变量就表现得像个 String。但方便的同时,如果不注意也会带来坑,并且这种情况带来的 bug 还很不好排查。今天我就遇到了一个。

游戏新注册用户的时候需要生成一个 uid,一般来说,在数据量小且低并发的时候直接使用 MySQL 的自增 ID 就可以生成得到唯一的 uid,但如果数据量大且并发很高,使用 MySQL 的自增 ID 就不能满足需求了。我们使用了一个叫 ukg 的东西,该服务的功能就一个,每次调用它,就给你返回一个唯一的数字,嗯,很好,刚好能满足我们生成 uid 的需求。

代码写完后,测试的过程中发现了一个 bug,设备第一次登录的时候会登录失败,第二次及以后就登录成功。排查了近 1 个小时才找到问题所在,设备第一次登录的时候,我们访问 ukg 得到一个数字,当做玩家的 uid,并且存入 MySQL,后来发现我们从 ukg 返回得到的实际上是一个 数字+空格+回车 组成的一个字符串,而空格和回车都是不可见字符串,很容易就忽略了,存入 MySQL 时,MySQL 进行了转换将空格和回车去掉然后正确存进了数据库。这样就能解释为什么第一次登录失败,第二次及以后的请求就没问题了。

定位 bug 后,解决就很容易了,从 ukg 取到数据后,将其强制转换为 Int 就 OK 了。

虽然,PHP 的语法并不要求一个变量强制其数据类型,但我们在写代码的过程中,还是需要时刻注意变量的数据类型。常见的错误还有 0 == false 相等导致的逻辑出错。如果不嫌麻烦的话,所有 == 的地方都改成 === 就不会出现这类型的 bug 了。

设计模式之装饰模式(修饰模式)

装饰模式也叫修饰模式,维基上对修饰模式的描述是:

修饰模式,是面向对象编程领域中,一种动态地往一个类中添加新的行为的设计模式。就功能而言,修饰模式相比生成子类更为灵活,这样可以给某个对象而不是整个类添加一些功能。

本文将以一个具体的例子说明为什么装饰模式比生成子类的方式更为灵活。

假设有一款城堡游戏,每个城堡有它的价值,我们有普通城堡,拥有钻石的城堡,被污染的城堡,假设普通的城堡价值为 10,拥有钻石的城堡比普通的城堡价值多 5,被污染的城堡比普通的城堡价值少 6,因此我们建了如下的类:

<?php
/**
 * 公用接口,所有物品都是有价值的
 */
abstract class Stuff
{
    abstract function getWealth();
}

class Castle extends Stuff
{
    public function getWealth()
    {
        return 10;
    }
}

class DiamondCastle extends Castle
{
    public function getWealth()
    {
        return parent::getWealth() + 5;
    }
}

class PollutedCastle extends Castle
{
    public function getWealth()
    {
        return parent::getWealth() - 6;
    }
}

看起来还不错,我们可以很方便获得钻石城堡对象也可以获得被污染的城堡对象,但是如果我们想获得即拥有钻石又被污染了的城堡对象该怎么做呢。当然可以新建一个类 DiamondPollutedCastle,但实现起来并不优雅,尤其是当后期又增加一些带有不同属性的城堡时,这种组合会更加的多,显然为每一种组合都创建一个类会使得整个系统类过多。这个时候使用装饰模式就比较合适了。

装饰模式使用组合和委托而不是只使用集成来解决功能变化的问题。Decorator 类会持有另外一个类的实例。Decorator 对象会实现与被调用对象的方法的队友的泪方法。用这种方法可以在运行是创建一系列的 Decorator 对象(《PHP 面向对象模式与实践》p167)。

下面是使用装饰模式重写的城堡游戏类:

<?php
/**
 * 公用接口,所有物品都是有价值的
 */
abstract class Stuff
{
    abstract function getWealth();
}

class Castle extends Stuff
{
    public function getWealth()
    {
        return 10;
    }
}

abstract class Decorator extends Stuff
{
    /**
     * @var Stuff
     */
    protected $stuff;
    public function __construct(Stuff $stuff)
    {
        $this->stuff = $stuff;
    }
}

class DiamondDecorator extends Decorator
{
    public function getWealth()
    {
        return $this->stuff->getWealth() + 5;
    }
}

class PollutedDecorator extends Decorator
{
    public function getWealth()
    {
        return $this->stuff->getWealth() - 6;
    }
}

我们这样来实例化城堡对象:

<?php
// 普通城堡
$generalCastle = new Castle();
// 钻石城堡
$diamondCastle = new DiamondDecorator(new Castle());
// 被污染的城堡
$pollutedCastle = new PollutedDecorator(new Castle());
// 被污染的钻石城堡
$diamondPollutedCastle = new PollutedDecorator(new DiamondDecorator(new Castle()));

通过像这样使用组合和委托,可以在运行时轻松地合并对象,对于初始化类调用的代码来说,并不需要内部是如何合并的,因为每个 Decorator 类都有 getWealth() 方法,所以无论是一个装饰器对象还是真正的 Castle 对象,调用端都可以使用 getWealth() 方法来获取一个装饰器城堡或者普通城堡的价值。