使用redux过程中遇到Actions must be plain objects. Use custom middleware for async actions.异常的分析

原文
原因一:使用异步actions时,没有配置redux-thunk这个中间件
中间件就是一个函数,对store.dispatch方法进行了改造,在发出 Action 和执行 Reducer 这两步之间,添加了其他功能。

import { applyMiddleware, createStore } from 'redux';
import createLogger from 'redux-logger';
const logger = createLogger();

const store = createStore(
  reducer,
  applyMiddleware(logger)
);

上面代码中,redux-logger提供一个生成器createLogger,可以生成日志中间件logger。然后,将它放在applyMiddleware方法之中,传入createStore方法,就完成了store.dispatch()的功能增强。

const store = createStore(
  reducer,
  applyMiddleware(thunk, promise, logger)
);

applyMiddleware 是Redux 的原生方法,作用是将所有中间件组成一个数组,依次执行。

原因二:调用action方法时,方法体内部并没有调用dispatch。

/**
 * 加载路由或iframe
 *
 * @param menu      需要加载的菜单
 */
export function onLoadMain(menu) {
    let routeUrl = menu.routeurl;
    let url = menu.url;
    console.log("routeUrl : ",routeUrl);
    if (routeUrl) {
        weaHistory.push({pathname: routeUrl});

        document.getElementById('e9frameMain').style.visibility = 'hidden';
        document.getElementById('e9routeMain').style.display = 'block';
        // let mainframe = document.getElementById('mainFrame');
        // mainframe.src = 'about:blank';
        document.getElementById('mainFrame').src = 'about:blank';
    } else if (url && url != 'javascript:void(0);') {
        let target = menu.target || 'mainFrame';
        if ('mainFrame' != target) {
            window.open(url, target);
        } else {
            // let mainframe = document.getElementById('mainFrame');
            // mainframe.src = url;
            document.getElementById('mainFrame').src = url;
            document.getElementById('e9frameMain').style.visibility = 'visible';
            document.getElementById('e9routeMain').style.display = 'none';
        }
    }
}

以上是一个普通的function,放在action的js文件中,如果你使用了如下方式去调用这个方法,虽然能成功调用,但在配置了redux-thunk这个中间件的情况下,你发起的任何action方法,都会走thunk这个中间件,一旦方法体内没有dispatch这个方法,则会报Actions must be plain objects. Use custom middleware for async actions这个异常。

import * as themeActions from '../../../actions/theme';

handleClick(e) {

        const {actions}=this.props;
        let urlParams= {
            iframeUrl: e.key,
            routeUrl:e.item.props.routeurl
        }

        actions.updateMenusUrl(urlParams);*/
        let menuInfo={
            routeurl:e.item.props.routeurl,
            url: e.key,
            target:'mainFrame'
        }
        //通过派生action的形式来调用这个方法
        actions. onLoadMain(menuInfo);

function mapStateToProps(state) {
    const {middleTheme} = state;

    return {
        backEndMenuUrl: middleTheme.get('backEndMenuUrl'),
        columnMenuInfo:middleTheme.get('columnMenuInfo')
    }
}

function mapDispatchToProps(dispatch) {
    return {
        actions: bindActionCreators(themeActions, dispatch)
    };
}

module.exports = connect(mapStateToProps, mapDispatchToProps)(E9LeftMenusTree);

所以常规function最好通过import导入的形式去调用就OK了,如下:

import {onLoadMain} from '../../../actions/theme';

handleClick(e) {

        const {actions}=this.props;
        let urlParams= {
            iframeUrl: e.key,
            routeUrl:e.item.props.routeurl
        }

        actions.updateMenusUrl(urlParams);*/
        let menuInfo={
            routeurl:e.item.props.routeurl,
            url: e.key,
            target:'mainFrame'
        }
        //常规import组件形式调用function
        onLoadMain(menuInfo);
    }

最后分享下我在解决这个问题中参考的阮大大的关于redux中间件的介绍:Redux中间件和异步操作

memcached 启动参数 和 stat 参数详解

/usr/local/bin/memcached -u root -c 50000 -t 8 -m 256 -l 192.168.1.101 -p 11211 -d
-p <num>      监听的TCP端口(默认: 11211)
-U <num>      监听的UDP端口(默认: 11211, 0表示不监听)
-s <file>     用于监听的UNIX套接字路径(禁用网络支持)
-a <mask>     UNIX套接字访问掩码,八进制数字(默认:0700)
-l <ip_addr>  监听的IP地址。(默认:INADDR_ANY,所有地址)
-d            作为守护进程来运行
-r            最大核心文件限制
-u <username> 设定进程所属用户。(只有root用户可以使用这个参数)
-m <num>      所有slab class可用内存的上限,以MB为单位。(默认:64MB)
              (译者注:也就是分配给该memcached实例的内存大小。)
-M            内存用光时报错。(不会删除数据)
-c <num>      最大并发连接数。(默认:1024)
-k            锁定所有内存页。注意你可以锁定的内存上限。
              试图分配更多内存会失败的,所以留意启动守护进程时所用的用户可分配的内存上限。
              (不是前面的 -u <username> 参数;在sh下,使用命令"ulimit -S -l NUM_KB"来设置。)
-v            提示信息(在事件循环中打印错误/警告信息。)
-vv           详细信息(还打印客户端命令/响应)
-vvv          超详细信息(还打印内部状态的变化)
-h            打印这个帮助信息并退出
-i            打印memcached和libevent的许可
-P <file>     保存进程ID到指定文件,只有在使用 -d 选项的时候才有意义
-f <factor>   不同slab class里面的chunk大小的增长倍率。(默认:1.25)
              (译者注:每个slab class里面有相同数量个slab page,每个slab page里面有chunk,且在当前slab class内的chunk大小固定。
              而不同slab class里的chunk大小不一致,具体差异就是根据这个参数的倍率在增长,直到分配的内存用尽。)
-n <bytes>    chunk的最小空间(默认:48)
              (译者注:chunk数据结构本身需要消耗48个字节,所以一个chunk实际消耗的内存是n+48。)
-L            尝试使用大内存页(如果可用的话)。提高内存页尺寸可以减少"页表缓冲(TLB)"丢失次数,提高运行效率。
              为了从操作系统获得大内存页,memcached会把全部数据项分配到一个大区块。
-D <char>     使用 <char> 作为前缀和ID的分隔符
              这个用于按前缀获得状态报告。默认是":"(冒号)
              如果指定了这个参数,则状态收集会自动开启;如果没指定,则需要用命令"stats detail on"来开启。
-t <num>      使用的线程数(默认:4)
-R            每个连接可处理的最大请求数
-C            禁用CAS
-b            设置后台日志队列的长度(默认:1024)
-B            绑定协议 - 可能值:ascii,binary,auto(默认)
-I            重写每个数据页尺寸。调整数据项最大尺寸
echo -e "stats\nquit\n" | /usr/bin/nc 192.168.1.101 11211
STAT pid 20487               ## memcache 进程PID
STAT uptime 1977637         ## 自memcache启动以来,服务器运行秒数
STAT time 1461202739       ## 服务器当前unix时间戳
STAT version 1.4.21      ## memcache 服务器版本
STAT libevent 1.4.13-stable      ## libevent 版本
STAT pointer_size 64      ##  架构(32 或 64 位)
STAT rusage_user 150.835069      ## 进程累计用户时间
STAT rusage_system 249.086133      ## 进程累计系统时间
STAT curr_connections 10      ## 当前打开连接数
STAT total_connections 5509      ## 自memcache启动以来,打开的连接总数
STAT connection_structures 11      ## 服务器分配的连接结构数
STAT reserved_fds 40      ## 
STAT cmd_get 8913248      ## 自memcache启动以来,执行get命令总数
STAT cmd_set 123382      ## 自memcache启动以来,执行set命令总数
STAT cmd_flush 0      ## 自memcache启动以来,执行flush命令总数
STAT cmd_touch 0      ## 自memcache启动以来,执行touch_all命令总数
STAT get_hits 8913074      ## 自memcache启动以来,get命中次数
STAT get_misses 174      ## 自memcache启动以来,get未命中次数
STAT delete_misses 0      ## 自memcache启动以来,delete未命中次数
STAT delete_hits 0      ## 自memcache启动以来,delete命中次数
STAT incr_misses 0      ## 自memcache启动以来,incr未命中次数
STAT incr_hits 0      ## 自memcache启动以来,incr命中次数
STAT decr_misses 0      ## 自memcache启动以来,decr未命中次数
STAT decr_hits 0      ## 自memcache启动以来,decr命中次数
STAT cas_misses 0      ## 自memcache启动以来,cas未命中次数
STAT cas_hits 0      ## 自memcache启动以来,cas命中次数
STAT cas_badval 0      ## 使用擦拭次数
STAT touch_hits 0      ## 自memcache启动以来,touch命中次数
STAT touch_misses 0      ## 自memcache启动以来,touch未命中次数
STAT auth_cmds 0      ##
STAT auth_errors 0      ##
STAT bytes_read 111225505      ## memcached服务器从网络读取的总的字节数
STAT bytes_written 3621054898      ## memcached服务器发送到网络的总的字节数
STAT limit_maxbytes 33554432      ## memcached服务缓存允许使用的最大字节数(分配的内存数)
STAT accepting_conns 1      ## 目前接受的链接数
STAT listen_disabled_num 0      ##
STAT threads 8      ## 被请求的工作线程的总数量
STAT conn_yields 0      ## 连接操作主动放弃数目
STAT hash_power_level 16      ##
STAT hash_bytes 524288      ##
STAT hash_is_expanding 0      ##
STAT malloc_fails 0      ## 
STAT bytes 384154      ## 存储item字节数(当前存储占用的字节数)
STAT curr_items 856      ## item个数(当前存储的数据总数)
STAT total_items 123382      ## item总数(启动以来存储的数据总数)
STAT expired_unfetched 0      ##
STAT evicted_unfetched 0      ##
STAT evictions 0      ## LRU释放的对象数目。为了给新的数据项目释放空间,从缓存移除的缓存对象的数目。比如超过缓存大小时根据LRU算法移除的对象,以及过期的对象
STAT reclaimed 0      ## 已过期的数据条目来存储新数据的数目
STAT crawler_reclaimed 0      ##
STAT lrutail_reflocked 0      ##
END

判断一个点是否在一个多边形里

“判断一个点是否在一个多边形里”,一开始以为是个挺难的问题,但Google了一下之后发现其实蛮简单,所用到的算法叫做“Ray-casting Algorithm”,中文应该叫“光线投射算法”,这是维基百科的描述:[维基百科]

简单地说可以这么判断:从这个点引出一根“射线”,与多边形的任意若干条边相交,累计相交的边的数目,如果是奇数,那么点就在多边形内,否则点就在多边形外。

阅读详细

from collections import namedtuple
from pprint import pprint as pp
import sys
 
Pt = namedtuple('Pt', 'x, y')               # Point
Edge = namedtuple('Edge', 'a, b')           # Polygon edge from a to b
Poly = namedtuple('Poly', 'name, edges')    # Polygon
 
_eps = 0.00001
_huge = sys.float_info.max
_tiny = sys.float_info.min
 
def rayintersectseg(p, edge):
    ''' takes a point p=Pt() and an edge of two endpoints a,b=Pt() of a line segment returns boolean
    '''
    a,b = edge
    if a.y > b.y:
        a,b = b,a
    if p.y == a.y or p.y == b.y:
        p = Pt(p.x, p.y + _eps)
 
    intersect = False
 
    if (p.y > b.y or p.y < a.y) or ( p.x > max(a.x, b.x)):
        return False
 
    if p.x < min(a.x, b.x): intersect = True else: if abs(a.x - b.x) > _tiny:
            m_red = (b.y - a.y) / float(b.x - a.x)
        else:
            m_red = _huge
        if abs(a.x - p.x) > _tiny:
            m_blue = (p.y - a.y) / float(p.x - a.x)
        else:
            m_blue = _huge
        intersect = m_blue >= m_red
    return intersect
 
def _odd(x): return x%2 == 1
 
def ispointinside(p, poly):
    ln = len(poly)
    return _odd(sum(rayintersectseg(p, edge)
                    for edge in poly.edges ))
 
def polypp(poly):
    print "\n  Polygon(name='%s', edges=(" % poly.name
    print '   ', ',\n    '.join(str(e) for e in poly.edges) + '\n    ))'
 
if __name__ == '__main__':
    polys = [
      Poly(name='square', edges=(
        Edge(a=Pt(x=0, y=0), b=Pt(x=10, y=0)),
        Edge(a=Pt(x=10, y=0), b=Pt(x=10, y=10)),
        Edge(a=Pt(x=10, y=10), b=Pt(x=0, y=10)),
        Edge(a=Pt(x=0, y=10), b=Pt(x=0, y=0))
        )),
      Poly(name='square_hole', edges=(
        Edge(a=Pt(x=0, y=0), b=Pt(x=10, y=0)),
        Edge(a=Pt(x=10, y=0), b=Pt(x=10, y=10)),
        Edge(a=Pt(x=10, y=10), b=Pt(x=0, y=10)),
        Edge(a=Pt(x=0, y=10), b=Pt(x=0, y=0)),
        Edge(a=Pt(x=2.5, y=2.5), b=Pt(x=7.5, y=2.5)),
        Edge(a=Pt(x=7.5, y=2.5), b=Pt(x=7.5, y=7.5)),
        Edge(a=Pt(x=7.5, y=7.5), b=Pt(x=2.5, y=7.5)),
        Edge(a=Pt(x=2.5, y=7.5), b=Pt(x=2.5, y=2.5))
        )),
      Poly(name='strange', edges=(
        Edge(a=Pt(x=0, y=0), b=Pt(x=2.5, y=2.5)),
        Edge(a=Pt(x=2.5, y=2.5), b=Pt(x=0, y=10)),
        Edge(a=Pt(x=0, y=10), b=Pt(x=2.5, y=7.5)),
        Edge(a=Pt(x=2.5, y=7.5), b=Pt(x=7.5, y=7.5)),
        Edge(a=Pt(x=7.5, y=7.5), b=Pt(x=10, y=10)),
        Edge(a=Pt(x=10, y=10), b=Pt(x=10, y=0)),
        Edge(a=Pt(x=10, y=0), b=Pt(x=2.5, y=2.5))
        )),
      Poly(name='exagon', edges=(
        Edge(a=Pt(x=3, y=0), b=Pt(x=7, y=0)),
        Edge(a=Pt(x=7, y=0), b=Pt(x=10, y=5)),
        Edge(a=Pt(x=10, y=5), b=Pt(x=7, y=10)),
        Edge(a=Pt(x=7, y=10), b=Pt(x=3, y=10)),
        Edge(a=Pt(x=3, y=10), b=Pt(x=0, y=5)),
        Edge(a=Pt(x=0, y=5), b=Pt(x=3, y=0))
        )),
      ]
    testpoints = (Pt(x=5, y=5), Pt(x=5, y=8),
                  Pt(x=-10, y=5), Pt(x=0, y=5),
                  Pt(x=10, y=5), Pt(x=8, y=5),
                  Pt(x=10, y=10))
 
    print "\n TESTING WHETHER POINTS ARE WITHIN POLYGONS"
    for poly in polys:
        polypp(poly)
        print '   ', '\t'.join("%s: %s" % (p, ispointinside(p, poly))
                               for p in testpoints[:3])
        print '   ', '\t'.join("%s: %s" % (p, ispointinside(p, poly))
                               for p in testpoints[3:6])
        print '   ', '\t'.join("%s: %s" % (p, ispointinside(p, poly))
                               for p in testpoints[6:])
public static class RayCastingAlgorithm {
        public static bool IsWithin(Point pt, IList<Point> polygon, bool noneZeroMode) {
            int ptNum = polygon.Count();
            if (ptNum < 3) {
                return false;
            }
            int j = ptNum - 1;
            bool oddNodes = false;
            int zeroState = 0;
            for (int k = 0; k < ptNum; k++) { Point ptK = polygon[k]; Point ptJ = polygon[j]; if (((ptK.Y > pt.Y) != (ptJ.Y > pt.Y)) && (pt.X < (ptJ.X - ptK.X) * (pt.Y - ptK.Y) / (ptJ.Y - ptK.Y) + ptK.X)) { oddNodes = !oddNodes; if (ptK.Y > ptJ.Y) {
                        zeroState++;
                    }
                    else {
                        zeroState--;
                    }
                }
                j = k;
            }
            return noneZeroMode?zeroState!=0:oddNodes;
        }
    }

mysql主从配置

开启mysql只读模式:

FLUSH TABLES WITH READ LOCK;
SET GLOBAL read_only = 1;

查看主数据库状态:
show master status; //可以查看主mysql状态

备份主数据库:

mysqldump -h -ujbbwbu -p jbbwb > jbbwb.sql

导入备份文件到从数据库:

source jbbwb.sql

设置同步点:
change master to
-> master_host=’192.168.78.128′,
-> master_user=’test’,
-> master_password=’MyPass1!’,
-> master_log_file=’mysql-bin.000001′,
-> master_log_pos=1244;

start slave;

show slave status \G;

关闭mysql只读模式:
SET GLOBAL read_only = 0;
UNLOCK TABLES;