nginx1.9.0+版本追加安装sticky1.25模块出现问题解决方法

问题一:

大致出现问题是因为MD5报错找不到:

1
2
3
ngx_http_sticky_misc.c: In function 「ngx_http_sticky_misc_md5」:
ngx_http_sticky_misc.c:152:15: ERROR:「MD5_DIGEST_LENGTH」 undeclared (first use in this function)
u_char hash[MD5_DIGEST_LENGTH];

解决方式:
修改在你下载解压缩之后的sticky模块文件夹中的ngx_http_sticky_misc.c文件
将这两个模块 and 包含到文件ngx_http_sticky_misc.c
下面”+”标注de地方:

1
2
3
4
5
6
7
8
9
10
11
#include <nginx.h>
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
#include <ngx_md5.h>
#include <ngx_sha1.h>
+#include <openssl/sha.h>
+#include <openssl/md5.h>
#include "ngx_http_sticky_misc.h"

问题二:

该问题大致是因为nginx1.9.0以上版本API的改变,
ngx_http_upstream_rr_peer_data_t 中类型定义从
ngx_uint_t 变为 ngx_http_upstream_rr_peer_t*,
导致sticky出现类型不匹配错误,提示如下:

1
/root/nginx-goodies-nginx-sticky-module-ng-1e96371de59f/ngx_http_sticky_module.c: In function ‘ngx_http_get_sticky_peer’: /tmp/nginx-goodies-nginx-sticky-module-ng-1e96371de59f/ngx_http_sticky_module.c:340:21: error: assignment makes pointer from integer without a cast [-Werror] iphp->rrp.current = iphp->selected_peer; ^ cc1: all warnings being treated as errors make[1]: [objs/addon/nginx-goodies-nginx-sticky-module-ng-1e96371de59f/ngx_http_sticky_module.o] Error 1 make[1]: Leaving directory `/tmp/nginx-1.9.0' make: [build] Error 2

解决方法:
修改在你下载解压缩之后的sticky模块文件夹中的ngx_http_sticky_module.c文件,具体如下(“+”标注):
添加头文件:

1
2
3
4
5
6
+#include <nginx.h>
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
#include "ngx_http_sticky_misc.h"

修改code,大概在340行左右:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/* we have a valid peer, tell the upstream module to use it */
if (peer && selected_peer >= 0) {
ngx_log_debug(NGX_LOG_DEBUG_HTTP, pc->log, 0, "[sticky/get_sticky_peer] peer found at index %i", selected_peer);
+#if defined(nginx_version) && nginx_version >= 1009000
+ iphp->rrp.current = peer;
+#else
iphp->rrp.current = iphp->selected_peer;
+#endif
+
pc->cached = 0;
pc->connection = NULL;
pc->sockaddr = peer->sockaddr;
pc->socklen = peer->socklen;
pc->name = &peer->name;
iphp->rrp.tried[n] |= m;
} else {
ngx_log_debug(NGX_LOG_DEBUG_HTTP, pc->log, 0, "[sticky/get_sticky_peer] no sticky peer selected, switch back to classic rr");
if (iphp->no_fallback) {
ngx_log_error(NGX_LOG_NOTICE, pc->log, 0, "[sticky/get_sticky_peer] No fallback in action !");

Python学习——装饰器

在学习Pthon过程中,装饰器算是一个蛮难理解的问题(对我来说,尤其是其效果看起来甚像Java中的AOP切面的织入)。

AOP采用的是Proxy模式,再利用反射机制。

Python中,函数 也是一个

对象

要理解装饰器,首先必须知道,在python中,函数也是对象,而且函数对象可以被赋值给变量,所以,通过变量也能调用该函数。

让我们用一个sample example来理解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
def shout():
print("Yes!")
shout()
# outputs: 'Yes!'
# 作为对象,可以将函数赋值给另一个对象
scream = shout
# !这里并没有使用括号:因为这里我们并不是调用函数,
# 而是将函数‘shout’赋值给‘scream’ 这意味着,
# 可以通过‘scream’ 调用 ‘shout’
scream()
# outputs: 'Yes!'
# 不仅如此,你甚至可以删除‘shout’,但是通过‘scream’依旧可以访问原有函数
del shout
try:
shout()
except NameError, e:
print e
# outputs: name 'shout' is not defined
scream()
# outputs: 'Yes!'

装饰模式有很多经典的使用场景,例如:插入日志,性能测试,事务处理等等。有了装饰器,我们就可以提取大量函数中与函数本身功能无关的代码,从而达到代码重用的目的。

一个简单的需求
现在,假设我们要增强shout()函数的功能,在调用shout()函数前后自动打印日志,但是我们不希望修改函数。这种在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)。

本质上,decorator就是一个返回函数的高阶函数。所以,我们要定义一个能打印日志的decorator,可以定义如下:

1
2
3
4
5
6
def log(func): # 接受一个函数作为参数
def wrapper(*args, **kw): # wrapper()函数的参数定义是(*args, **kw),因此,wrapper()函数可以接受任意参数的调用
print('start call %s(): ' % func.__name__)
func(*args, **kw)
print('end call %s(): ' % func.__name__)
return wrapper # 同时,返回一个参数

装饰器语法糖

在Python中,可以使用“@”语法糖(把decorator置于函数定义处)来精简装饰器的代码:

1
2
3
@log
def shout():
print("Yes!")

调用shout()函数,不仅会运行shout()函数本身,还会在运行函数shout前后各打印一行日志:

1
2
3
4
>>>shout()
start call shout():
Yes!
end call shout():

使用了“@”语法糖后,我们就不需要额外的代码来给“shout”重新赋值了,其实,@log的本质就是

1
shout=log(shout)

MQ中间件死信队列深度不断增加问题解决案例

背景:工行某分行发现小额MQ死信队列深度已超过1W,而且还一直在增加,但报文发送、接收均正常。

问题排查过程
1、检查应用日志、mq发送日志,均未发现异常。

2、查看mq死信队列信息

1
2
bash-3.2$ ./amqsbcg DEADQ QMMBFE
AMQSBCG0 - starts here
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
**********************
MQOPEN - 'DEADQ'
MQGET of message number 1
****Message descriptor****
StrucId : 'MD ' Version : 2
Report : 0 MsgType : 8
Expiry : -1 Feedback : 0
Encoding : 273 CodedCharSetId : 819
Format : 'MQDEAD '
Priority : 0 Persistence : 0
MsgId : X'414D5120514D4D424645202020202020505376A520000802'
CorrelId : X'000000000000000000000000000000000000000000000000'
BackoutCount : 0
ReplyToQ : ' '
ReplyToQMgr : 'QMMBFE '
** Identity Context
UserIdentifier : ' '
AccountingToken :
X'0000000000000000000000000000000000000000000000000000000000000000'
ApplIdentityData : ' '
** Origin Context
PutApplType : '7'
PutApplName : 'QMMBFE '
PutDate : '20120914' PutTime : '18260760'
ApplOriginData : ' '
GroupId : X'000000000000000000000000000000000000000000000000'
MsgSeqNumber : '1'
Offset : '0'
MsgFlags : '0'
OriginalLength : '-1'
**** Message ****
length - 856 bytes
00000000: 444C 4820 0000 0001 0000 0109 5359 5354 'DLH ........SYST'
00000010: 454D 2E43 4943 532E 494E 4954 4941 5449 'EM.CICS.INITIATI'
00000020: 4F4E 2E51 5545 5545 2020 2020 2020 2020 'ON.QUEUE '
00000030: 2020 2020 2020 2020 2020 2020 514D 4D42 ' QMMB'
00000040: 4645 2020 2020 2020 2020 2020 2020 2020 'FE '
00000050: 2020 2020 2020 2020 2020 2020 2020 2020 ' '
00000060: 2020 2020 2020 2020 2020 2020 0000 0111 ' ....'
00000070: 0000 0333 4D51 5452 4947 2020 0000 0006 '...3MQTRIG ....'
00000080: 5255 4E4D 5154 524D 0000 0000 0000 0000 'RUNMQTRM........'
00000090: 0000 0000 0000 0000 0000 0000 3230 3132 '............2012'
000000A0: 3039 3134 3138 3236 3037 3633 544D 2020 '091418260763TM '
000000B0: 0000 0001 3130 3238 3831 3030 3030 3139 '....102881000019'
000000C0: 5F32 2020 2020 2020 2020 2020 2020 2020 '_2 '
000000D0: 2020 2020 2020 2020 2020 2020 2020 2020 ' '
000000E0: 2020 2020 554E 4958 2E50 524F 3220 2020 ' UNIX.PRO2 '
000000F0: 2020 2020 2020 2020 2020 2020 2020 2020 ' '
00000100: 2020 2020 2020 2020 2020 2020 2020 2020 ' '
00000110: 2020 2020 2020 2020 2020 2020 2020 2020 ' '
00000120: 2020 2020 2020 2020 2020 2020 2020 2020 ' '
00000130: 2020 2020 2020 2020 2020 2020 2020 2020 ' '
00000140: 2020 2020 2020 2020 2020 2020 2020 2020 ' '
00000150: 2020 2020 0000 0006 2F62 6570 736D 6266 ' ..../bepsmbf'
00000160: 652F 6269 6E2F 6C69 622F 4D51 6372 6563 'e/bin/lib/MQcrec'
00000170: 7620 2020 2020 2020 2020 2020 2020 2020 'v '

3、检查MQDLQ结构如下

1
MQDLH

4、查到其ReasonCode为 0000 0109

5、X’00000109’ 含义为:MQFB_APPL_CANNOT_BE_STARTED
Application cannot be started.
An application processing a trigger message was unable to start the
application named in theApplIdfield of the trigger message.

6、也就是说如下程序无法启动

1
2
/bepsmbfe/bin/lib/MQcrecv
/bepsmbfe/bin/lib/MQrrecv

7、启动不了的可能原因:文件不存在、没有执行权限等。
了解到其真实原因为路径错误(正确路径为/home/bepsmbfe/bin/lib/),将其路径改正确即可解决问题。

8、解决问题的步骤
第1步: 停止相关应用
第2步: 重新定义process

1
2
3
4
#su - mqm
$runmqsc QMMBFE
DEF PROCESS(unix.pro1) APPLTYPE(UNIX) APPLICID('/home/bepsmbfe/bin/lib/MQrrecv') REPLACE
DEF PROCESS(unix.pro2) APPLTYPE(UNIX) APPLICID('/home/bepsmbfe/bin/lib/MQcrecv') REPLACE

第3步:启动相关应用
第4步:继续观察死信队列的状况,发现不再增加,问题解决。

MQ消息序号Message Sequence详解

1、什么是MQ的消息序号?

通道为每一条消息的传送分配了一个序列号,它会自动累计增值。
消息序列号由发送通道分配,是通道的一个永久属性,每当发送一条消息,消息序列号就加一。
通道的相关属性SEQWRAP 表示序号的最大值,缺省为999,999,999。序列号越界后自动归零,从头开始。
消息序列号是保证MQ消息传输不丢失、不复传的一个重要机制,通道利用消息序号来标识传送和确认的消息。

2、怎么查看通道当前的消息序号?

无论是在发送端还是接收端,在MQSC下输入如下命令,其中CURSEQNO即为当前消息序号:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ runmqsc QM
dis chs(C) all
AMQ8417: Display Channel Status details.
CHANNEL(C) XMITQ( )
CONNAME(127.0.0.1) CURRENT
CHLTYPE(RCVR) INDOUBT(NO)
LSTSEQNO(0) LSTLUWID(0000000000000000)
CURMSGS(0) CURSEQNO(3)
CURLUWID(45BF8B5021200000) STATUS(RUNNING)
LSTMSGTI(21.36.03) LSTMSGDA(2012-10-27)
MSGS(3) BYTSSENT(328)
BYTSRCVD(1783) BATCHES(7)
BATCHSZ(50) HBINT(300)
NPMSPEED(FAST) CHSTATI(21.14.41)
CHSTADA(2012-10-27) BUFSSENT(8)
BUFSRCVD(11) LONGRTS(999999999)
SHORTRTS(10) JOBNAME(00000AD500000023)
MCASTAT(RUNNING) STOPREQ(NO)

3、正常情况下的通道两端消息序号是怎样的?

正常情况下,通道两端的消息序列号或者相等或者相差为一。

4、什么原因会导致消息序号不一致?

A、通信故障:双方对前面的某一条(或一批)  消息是否发送成功理解不一致。在解决了不确定(In-doubt)  的消息后,可以用 MQSC 命令通过重置消息序号将双方调整到一致。
B、/var/mqm 使用旧的备份恢复
C、某一方MQ系统重新安装
D、队列管理器重建
E、某一方通道重建
F、某一方通道被重置

5、消息序号不一致会导致什么问题发生?

通道序号不一致会导致通道无法正常启动(即状态不是running),通道状态为retrying。

MQ日志会有错误记录,比如/var/mqm/qmgrs/队列管理器名称/errors/AMQERR01.LOG的内容:

1
AMQ9526: 通道 'SDR.TEST' 的消息序号出错 或者 Message sequence number error for channel 'SDR.TEST'

说明:
本地和远程队列管理器对下一个消息序号不一致。当希望消息序号 1 时,发送了序号为 101 的消息。
操作:
确定该不一致的原因。有可能同步信息已损坏, 或已被逆序恢复成先前的版本。如果问题不能解决, 可用 RESET CHANNEL

命令在通道的发送端人工复位此序号。

6、消息序号不一致问题发生后怎么处理?

* 方法1:在通道发送端用 MQSC 命令,重置消息序号为1,稍后通道两端消息序号会被同步为1。

示例:在发送端将消息序号重置为1(默认为1,不是0) RESET CHANNEL (C) 等于 RESET CHANNEL (C) SEQNUM(1)
注意:在连接通道的主动方重置消息序号会将双方一起调整,在被动方重置则只设置一端。因为一旦连接断开后,通道重连时双方 MCA 会将消息序号同步。
建议:在发送端和接收端同时重置消息序号,这样能快速解决序号不一致的问题。

* 方法2:在通道接收方用MQSC命令将消息序号重置为与发送端相同(“5、消息序号不一致会导致什么问题发生?” 中的日志记录了发送方通道的序号)

示例:将接收通道消息序号重置为与发送通道的101:

1
reset channel(C) seqnum(101)

7、MQ启动后是否需要重置消息序号?

MQ的消息序号是通道的一个永久属性,正常情况下,无论是重新启动队列管理器还是重新启动计算机,通道序列号都不会因此而变化,因而不需要进行重置操作。

8、常见问题

问题1:MQ 重置通道序列号不生效
请检查您重置的通道是发送通道还是接收通道,如果重置接收通道肯定只能重置接收通道的序号,并不能改变发送通道的序号,一样会消息序号出错,当然,也可以查找到发送通道的消息序号,然后将接收通道的消息序号重置成与发送通道相同的值。

具体请参考 - 6、消息序号不一致问题发生后怎么处理?

MQ中间件通道状态STATUS(RETRYING)的问题分析与解决方法

这种问题一般发送在发送端,在我们发出启动通道的命令之后,通道进入binding的状态,若网络连接畅通并且通道定义正确,它进入正常running状态,如果出现了如下的一些问题,则通道进入retrying状态。
检查通道状态示例

1
2
3
4
5
6
$ runmqsc QMgrName
dis chs(C)
AMQ8417: Display Channel Status details.
CHANNEL(C) XMITQ(QX)
CONNAME(xxx.xxx.xxx.xxx (1416)) CURRENT
CHLTYPE(SDR) STATUS(RETRYING)

原因可能有如下几种

  1. 网络连接有问题
  2. 通道定义不正确
  3. 通道两端的消息序列号(Message Sequence Number)不匹配
  4. 通道定义中的CONNAME(HostName (PortNumber))使用了主机名但是hosts文件中没有定义
  5. 接收方不能连通
  6. 接收端没有启动监听
  7. 接收端端口占用(比如其它队列管理器占用了该端口)

解决方法

  • 网络连接有问题
    检查通道定义包括网络不通,可使用 telnet 端口 测试连接
  • 通道两端的消息序列号(Message Sequence Number)不匹配
    详细请参考本站文章:Websphere MQ消息序号Message Sequence详解
  • 通道定义不正确
    检查通道配置,检查方法:
    $ runmqsc QMgrName
    dis chl(ChannelName)
  • 通道定义中的CONNAME(HostName (PortNumber))使用了主机名但是hosts文件中没有定义
    检查通道定义,检查方法:
1
2
$ runmqsc QMgrName
dis chl(ChannelName)

检查其中CONNAME是否使用了主机名,如果使用了,请检查/etc/hosts文件中是否有其定义。

  • 接收方不能连通 和 F、接收端没有启动监听
    检查方法:MQSC 中的测试通道命令PING,格式如下:
1
2
$ runmqsc QMgrName
PING CHANNEL(channel_name) [DATALEN( 16 | integer)]

其中,DATALEN 表示 PING 数据包的大小,可以用 16 字节到 32,768 字节。
PING 命令可以检查对方的队列管理器或端口监听器是否启动,也可以检查对方的通道定义是否正确。但不检查通道的通性状态。换句话说,PING CHANNEL 只检查通道能否连连通,而不检查目前是否连通。

  • 接收端端口占用
    接收端相应的队列管理器停止监听,然后检查端口是否还在监听:
1
$ netstat -an|grep 端口号