CuriousY A world with wonder

Create a form using React

| Comment

想用React实现一个简单的表单,其中主要的组件如下:

class App extends Component {
    render() {
        return (
            <div className="App">
                <input type="text" value="" name="username"/>
            </div>
        );
    }
}

第一坑:运行时发现输入框中输入任何字符都没有反应。原因官方文档也作了说明,简单说就是需要我们自己实现一个onChange的事件函数。(还有一个选择是仍让DOM来自己管理这些输入的值,参考官方文档


因此,参考官方文档,且为了方便代码复用,写了如下一个Input组件,然后在App组件中使用这个组件来替换原来的input

class Input extends Component {
    constructor() {
        super();
        this.handleChange = this.handleChange.bind(this);
    }

    handleChange(event) {
        this.props.value = event.target.value;
    }

    render() {
        return (
            <input {...this.props} onChange={this.handleChange}/>
        );
    }
}

class App extends Component {
    render() {
        return (
            <div className="App">
                <input type="text" value="" name="username"/>
            </div>
        );
    }
}

第二个坑出现了,运行时发现出现了如下的错误:

Uncaught TypeError: Cannot assign to read only property 'value' of object '#<Object>'

原因在于React组件的属性是只读的,因此在handleChange中不能再对value属性赋值了。

Read more

实践:使用Nginx搭建load balancer并制成docker镜像

| Comment

首先,项目在此:nginx-lb

load balance和reverse proxy的区别

首先,load balance的目的是为了均衡负载。它本身可以是一个代理,也可以不是。没有代理功能的话,那么每进来一个访问,它会redirect到某一台真正的服务器。而更多的则是有代理功能,主要是通过一台web server来代理需要负载均衡的多台服务器。通过这样一层代理可以屏蔽后面的那些服务器。所以,这种load balancer本身也是有load的,它的load主要就是代理的流量产生的网络负载,它所均衡的是后面多台服务器的计算负载。

而反向代理,就纯粹是为了代理的目的,“反向”是相对正常的放在客户端之后的代理而言的,这个代理和load balancer一样是放在服务器端之前的。反向代理的目的主要是为了隔离它身后的被它代理的服务器,它代理的服务器可以是一台也可以是多台(多台的话就兼顾了load balancer的作用)。

以上,load balance和reverse proxy的主要目的是不同的,当然它们有功能重叠的部分,且实现方式也比较类似。

使用Nginx搭建load balancer

Nginx使用起来其实还是挺容易的,这里我们只需要修改nginx.conf文件,再启动Nginx服务即可。其中关于load balance配置的部分可以参考官方文档。关键的配置是load balance策略的选择,对此官方安装包中自带了一些方法,比如round-robinleast_connip_hash等。不同的策略会导致分配负载时结果略有不同。

但这里我并没有选择官方提供的策略,而是选择了第三方的一个模块sticky。原因在于,官方提供的方法不能很好地解决一个session里面的目标服务器必须是同一个的问题。对于某些场景下session里面的连接对象变化了没什么影响,但很多场景下,如果我访问的真实服务器从一台机器突然变到了另外一台服务器上,就容易出现问题。对此,一种解决方法是使用官方的ip_hash策略,即同一个访问ip永远都是会映射到同一个目标服务器上,但这样又不是很灵活,毕竟一个ip可能是由100台不同的电脑共享的。而sticky是以session为单位来分配目标服务器的,正是我们所需要的。

Read more

Catch all the exceptions

| Comment

问题以及一个不够优雅的解决方法

之前写代码碰到过这样一个需求:假设一段程序中有三个语句要执行:

operation_one()
operation_two()
operation_three()

而这些语句都有可能抛出某种异常,我希望即使某个语句抛出异常了,之后的语句还能被执行。而把所有语句放在一个try..catch语句中是不能满足要求的,因为出现异常后,直接会进入异常处理分支,之后的语句就被跳过了,简单模拟一下就是这样的情形:

class MyException(BaseException):
    pass

def operation_one():
    print 'Operation one executed'
    raise MyException()

def operation_two():
    print 'Operation two executed'
    raise MyException()

def operation_three():
    print 'Operation three executed'
    raise MyException()

if __name__ == '__main__':
    try:
        operation_one()
        operation_two()
        operation_three()
    except MyException:
        print 'Catch MyException'

上述代码执行得到的结果为:

Operation one executed
Catch MyException

显然不能满足需求。

Read more

Reading <Docker -- 从入门到实践> - 1

| Comment

本来只是想学一下怎么写Dockerfile的,结果发现这本书写地意外的好,强烈推荐一读Docker — 从入门到实践

基本概念

对于 Linux 而言,内核启动后,会挂载 root 文件系统为其提供用户空间支持。而 Docker 镜像(Image),就相当于是一个 root 文件系统。比如官方镜像 ubuntu:14.04 就包含了完整的一套 Ubuntu 14.04 最小系统的 root 文件系统。

需要补习操作系统的知识了。


镜像构建时,会一层层构建,前一层是后一层的基础。每一层构建完就不会再发生改变,后一层上的任何改变只发生在自己这一层。比如,删除前一层文件的操作,实际不是真的删除前一层的文件,而是仅在当前层标记为该文件已删除。在最终容器运行的时候,虽然不会看到这个文件,但是实际上该文件会一直跟随镜像。因此,在构建镜像的时候,需要额外小心,每一层尽量只包含该层需要添加的东西,任何额外的东西应该在该层构建结束前清理掉。

就像堆积木一样,我有一个干净的Ubuntu系统镜像,我想在它基础上安装一些我认为常用的软件,然后形成一个新的镜像。那么这些新添加的东西就可以认为是一层。这里要注意的是在一个已经安装了很多东西的镜像上,我做一些删除操作,实际镜像的体积是不会减小的。


镜像(Image)和容器(Container)的关系,就像是面向对象程序设计中的实例一样,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等。

容器的实质是进程,但与直接在宿主执行的进程不同,容器进程运行于属于自己的独立的命名空间。因此容器可以拥有自己的 root 文件系统、自己的网络配置、自己的进程空间,甚至自己的用户 ID 空间。容器内的进程是运行在一个隔离的环境里,使用起来,就好像是在一个独立于宿主的系统下操作一样。

可以理解为docker本身实现了一套管理容器进程的方案,这套方案使各个进程拥有了独立的命名空间,然后它再把每个进程的命名空间映射到宿主的进程中。(待验证)

Read more

单独禁用pytest插件中的某个hook方法

| Comment

问题

需求很简单:我安装了一个pytest的插件,其中大部分功能都是我需要的,但唯独其中某个hook我不希望生效,该如何单独禁用掉这一个hook?

解决方法

首先尝试了Python中最常用的hack别人代码的方式:即定义一个新的函数,然后尽早地去把指定hook函数替换为这个新建的函数。比如,我想把pytest-pep8插件中的pytest_collection hook给禁用掉,那么就需要尽可能早地执行下面的代码:

from pytest_pep8 import plugin

def hacked_pytest_collection():
    pass

plugin.pytest_collection = hacked_pytest_collection

但实验下来,该方法并不好用,原因在于你很难在pytest注册各个插件的hook之前去替换掉指定的hook方法。


因此,换个思路,直接修改pytest注册的插件的hook不就可以了嘛,这个修改的代码只要在要被禁用的hook执行之前被执行即可。

比如下面这样,在自己的pytest插件的pytest_sessionstart hook中进行修改操作:

def pytest_sessionstart(session):
    # Disable pytest-pep8's `pytest_collection` hook in pytest.config
    for hook in pytest.config.hook.pytest_collection._nonwrappers:
        if hook.plugin_name == 'pep8':
            pytest.config.hook.pytest_collection._nonwrappers.remove(hook)
            break
    for hook in pytest.config.hook.pytest_collection._wrappers:
        if hook.plugin_name == 'pep8':
            pytest.config.hook.pytest_collection._wrappers.remove(hook)
            break

以上,由于pytest_sessionstart会在pytest_collection之前执行,所以轮到执行pytest_collection的hook时,pytest-pep8的该方法已经被移除掉了。

| Page 14 of 25 |