一种 Linux JAVA进程突然消失的原因

有时候会遇到JAVA程序在服务器上运行一段时间后进程突然没有了,查看JAVA应用的日志并没有任何的异常。

这时候应该优先想到进程是不是被操作系统Kill掉了,查看操作系统日志:

cd /var/log/
# 查看 messages 文件
cat messages* | grep java

如果看到类似如下信息说明由于操作系统内存剩余太低,JAVA进程被操作系统干掉了:

Apr 15 20:57:50 iZuwucrzorhw2yZ kernel: java invoked oom-killer: gfp_mask=0x201da, order=0, oom_score_adj=0
Apr 15 20:57:51 iZuwucrzorhw2yZ kernel: java cpuset=/ mems_allowed=0
Apr 15 20:57:51 iZuwucrzorhw2yZ kernel: CPU: 0 PID: 2793 Comm: java Not tainted 3.10.0-693.2.2.el7.x86_64 #1
Apr 15 20:57:51 iZuwucrzorhw2yZ kernel: [ 1356]     0  1356   629574    17847      94        0             0 java
Apr 15 20:57:51 iZuwucrzorhw2yZ kernel: [ 3804]     0  3804   886644    19509     113        0             0 java
Apr 15 20:57:51 iZuwucrzorhw2yZ kernel: [ 2676]     0  2676   894623    85673     278        0             0 java
Apr 15 20:57:51 iZuwucrzorhw2yZ kernel: [ 2754]     0  2754   965673    40515     362        0             0 java
Apr 15 20:57:51 iZuwucrzorhw2yZ kernel: [14208]     0 14208  3051253   108737    4500        0             0 java
Apr 15 20:57:51 iZuwucrzorhw2yZ kernel: Out of memory: Kill process 14208 (java) score 113 or sacrifice child
Apr 15 20:57:51 iZuwucrzorhw2yZ kernel: Killed process 14208 (java) total-vm:12205012kB, anon-rss:434948kB, file-rss:0kB, shmem-rss:0kB

这种问题通常发生下内存较低的服务器下,操作系统为了保护内存能够支持自己运行,将JAVA进程杀掉。

找到了运营自然好解决,通过分为2种情况:

  1. JAVA程序有内存泄露问题, 这种情况那就通过JAVA内存分析工具 定位内存泄露的位置,然后修复。
  2. 服务器内存太低, 这种情况还是升级服务器吧。

通用的程序启动/停止Shell脚本

告别Shell命令恐惧症,你需要的是这个『启动、停止Shell』脚本,下面是个php-fpm的启停脚本

#!/bin/bash

SHELL_DIR=$(cd "$(dirname "$0")";pwd)
opt="${@:2}"
echo "shell dir: $SHELL_DIR, arguments:$opt"

psid=

checkpid() {
   psid=`ps -ef | grep 'php-fpm' | grep -v 'grep' | grep -v 'sh' | awk '{print $2}' | tr '\n' ' '`
   echo "psid: $psid"
}

start() {
     echo "starting..."
     checkpid
      if [ ${#psid} -ne 0 ]; then
         echo "================================"
         echo "warn: php-fpm already started! (pid=$psid)"
         echo "================================"
      else
         echo "============ Start ============="
         echo "starting php-fpm ..."
         php-fpm $opt
         checkpid
         if [ ${#psid} -ne 0 ]; then
            echo "php-fpm started successfully!"
         else
            echo "php-fpm started failed!"
         fi
     fi
}

stop() {
   checkpid
    if [ ${#psid} -ne 0 ]; then
       echo "killing pid -> "$psid
       kill -9 $psid
         checkpid
         if [ ${#psid} -ne 0 ]; then
            echo "php-fpm stopped failed!"
         else
            echo "php-fpm stopped successfully!"
         fi
   else
      echo "================================"
      echo "warn: php-fpm is not running!"
      echo "================================"
   fi

}

status() {
   checkpid
   if [ ${#psid} -ne 0 ];  then
      echo "php-fpm is running!"
   else
      echo "php-fpm is not running!"
   fi
}

echo "running $1 command as $USER"

case "$1" in
   'start')
      start
      ;;
   'stop')
     stop
     ;;
   'restart')
     stop
     start
     ;;
   'status')
     status
     ;;
  *)
     echo "Usage: $0 {start|stop|restart|status}"
     exit 1
esac
exit 0

Mac安装Mysql Connector/ODBC失败解决办法

使用Tableau连接Mysql时,要求系统安装Mysql Connector/ODBC,引导到https://dev.mysql.com/downloads/connector/odbc/ 下载完成后发现安装失败。

正确的步骤是:

第一步:安装odbc manager
下载地址: http://www.odbcmanager.net/

第二步:安装Mysql Connector/ODBC
下载地址:https://dev.mysql.com/downloads/connector/odbc/

这样子就安装成功了。

打开Tableau连接到Mysql试试

CocosCreator接入微信登录

微信登录实际上就是 通过微信开放平台的接口获取到微信用户的基本信息添加到用户数据库中,从而实现通过微信登录功能。

查阅微信开放平台的官方文档:https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419317853&token=&lang=zh_CN

我们通过接口能够获取到的用户基本信息包括:openid (针对当前开发者账号的用户唯一标识)、nickname(用户昵称)、sex(用户性别)、province(省份)、city(城市)、country(国家)、headimgurl(头像)、privilege(特权信息)、unionid(针对同一个微信开放平台账号的用户统一标识)。用于微信登录功能这些信息已经足够了。这些信息如果获取,可以通过文档一步步反推。

需要获取基本信息API(https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID)前一步需要通过(https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code)获取到access_token 与 open_id,获取access_token 与 open_id的前置条件是需要appid、secret、code。 appid与secret两个参数都是通过创建开放平台时候生成的,获取code的文档 https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419317851&token=&lang=zh_CN 可以看出code需要通过客户端SDK获取。

所以CocosCreator接入微信登录最重要的就是如何通过微信开放平台SDK获取到code. 可以根据文档的方式集成SDK ;
Android:https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=1417751808&token=&lang=zh_CN
IOS:https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=1417694084&token=&lang=zh_CN

集成完成后唯一要做的就是通过jsb调用Native的方法,获取到code后,调用CocosCreator的JS方法将code回传到CocosCreator。 Android接入的代码如下

import AiJKit from "./ws/AiJKit";
import AppConfig from "./AppConfig";
import PlazaWeiXinLoginEvent from "./plazz/event/PlazaWeiXinLoginEvent";
import PlazaConfig from "./plazz/PlazaConfig";
import AlertWindow from "./AlertWindow";

export default class WxHelper {
    /**
     * 微信登录
     */
    public static wxLogin() {
        if (cc.sys.os == cc.sys.OS_ANDROID) {
            jsb.reflection.callStaticMethod("com/xiyoufang/aij/wx/WxHelper", "wxLogin", "()V")
        }
    }

    /**
     * 登录
     * @param code
     */
    public static onWxLogin(code) {
        cc.log("code:" + code);
        if (PlazaConfig.getInst()._aiJPro.isOpen()) {
            AiJKit.use(AppConfig.PLAZA_WS_NAME).send(new PlazaWeiXinLoginEvent(code));
        } else {
            AlertWindow.alert("提示信息", "未连接服务器,请稍后再试!");
        }
    }

}
cc['WxHelper'] = WxHelper;

点击界面的微信登录按钮,调用JS中的wxLogin方法,该方法调用Android的Java代码。Java代码如下:

package com.xiyoufang.aij.wx;

import android.util.Log;

import com.tencent.mm.opensdk.modelmsg.SendAuth;

import org.cocos2dx.javascript.AppActivity;

/**
 * Created by youfangxi@gmail.com on 2019-07-22.
 *
 * @author youfangxi@gmail.com
 */
public class WxHelper {

    public final static String WX_API_TAG = "WxApi";

    public static void print() {
        System.out.println("print from cocos creator!");
    }

    /**
     * 微信登录请求,提供给Cocos Creator调用.
     */
    public static void wxLogin() {
        SendAuth.Req req = new SendAuth.Req();
        req.scope = "snsapi_userinfo";
        req.state = "0";
        AppActivity.getInstance().wxApi.sendReq(req);  //发送登录请求
        Log.i(WX_API_TAG, req.state);
    }

}

还有一个重要的部分就是获取微信回调的结果,Java代码:

package net.yw365.jiong.wxapi;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;

import com.tencent.mm.opensdk.constants.ConstantsAPI;
import com.tencent.mm.opensdk.modelbase.BaseReq;
import com.tencent.mm.opensdk.modelbase.BaseResp;
import com.tencent.mm.opensdk.modelmsg.SendAuth;
import com.tencent.mm.opensdk.openapi.IWXAPIEventHandler;
import com.xiyoufang.aij.wx.WxHelper;

import org.cocos2dx.javascript.AppActivity;
import org.cocos2dx.lib.Cocos2dxJavascriptJavaBridge;

/**
 * Created by youfangxi@gmail.com on 2019-07-22.
 *
 * @author youfangxi@gmail.com
 */
public class WXEntryActivity extends Activity implements IWXAPIEventHandler {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        AppActivity.getInstance().wxApi.handleIntent(getIntent(), this);
    }

    /**
     * 
     * @param baseReq 请求报文
     */
    @Override
    public void onReq(BaseReq baseReq) {
        Log.i(WxHelper.WX_API_TAG, "onReq:" + baseReq.getClass().getSimpleName());
    }

    /**
     * 接收响应
     *
     * @param baseResp 响应报文
     */
    @Override
    public void onResp(BaseResp baseResp) {
        Log.i(WxHelper.WX_API_TAG, "onResp:" + baseResp.getClass().getSimpleName());
        switch (baseResp.errCode) {
            case BaseResp.ErrCode.ERR_OK:
                switch (baseResp.getType()) {
                    case ConstantsAPI.COMMAND_SENDAUTH: {   //授权响应
                        final String code = ((SendAuth.Resp) baseResp).code;
                        AppActivity.getInstance().runOnGLThread(new Runnable() {
                            @Override
                            public void run() {
                                //将code 传递到cocoscreator.
//                                Cocos2dxJavascriptJavaBridge.evalString("cc.log(\"code:" + code + "\")");
                                Cocos2dxJavascriptJavaBridge.evalString("cc.WxHelper.onWxLogin(\"" + code + "\")");
                            }
                        });
                        break;
                    }
                    case ConstantsAPI.COMMAND_SENDMESSAGE_TO_WX: {  //发送信息到微信的响应

                        break;
                    }
                    default:
                        break;
                }
                break;
            case BaseResp.ErrCode.ERR_AUTH_DENIED:
                break;
            case BaseResp.ErrCode.ERR_USER_CANCEL:
                break;
            case BaseResp.ErrCode.ERR_SENT_FAILED:
                break;
            case BaseResp.ErrCode.ERR_BAN:
                break;
            case BaseResp.ErrCode.ERR_COMM:
                break;
            case BaseResp.ErrCode.ERR_UNSUPPORT:
                break;
        }
        finish();   //关闭当前界面
    }
}

可以看出获取到Code后通过Cocos2dxJavascriptJavaBridge.evalString(“cc.WxHelper.onWxLogin(\”” + code + “\”)”);方式将code回传到CocosCreator的cc.WxHelper.onWxLogin方法。
此处需要特别注意的就是WxHelper这个JS里的 cc[‘WxHelper’] = WxHelper; 这句一定要加上不能喽,否则Cocos2dxJavascriptJavaBridge.evalString(“cc.WxHelper.onWxLogin(\”” + code + “\”)”);将执行失败。

获取到Code后剩下的就是将Code传给游戏后端,后端调用另外两个API接口获取到用户信息并且保存到数据库中。

完整代码可以查看我的开源项目:https://gitee.com/xiyoufang/aij

Use PyCharm Debug Your Airflow DAGS

Usually we debug DAG through the shell command, which looks like

airflow test dag_id task_id date

But this is not an efficient way to debug, It cannot add breakpoints. So we need to analyze through a large number of debugging logs. can not be very friendly to watch the values of the variables at that time.

We can use PyCharm to debug airflow DAG, just like debugging regular python scripts, to improve our debugging efficiency.

We just need to add the following code:

if __name__ == '__main__':
    dag.cli()

Configure the debugging configuration like in the screenshot, And run the main function. Now we can debug using breakpoints.

Just enjoy.

列的方式聚合同一字段不同的值

使用Group By 语句进行聚合操作时输出的方式都是以行的方式呈现,例如:

SELECT T.MESSAGE_TYPE AS MESSAGE_TYPE, COUNT(1) AS COUNT
FROM TABLE_A T
WHERE T.CREATED_AT = '2019-03-22 10:20:20'
GROUP BY T.MESSAGE_TYPE;
效果图

但是通常我们更希望以列的方式展现在表格中,这样更为直观,那么可以改写成如下SQL:

SELECT COUNT(DECODE(T.MESSAGE_TYPE, 'AT+CMD', 1, NULL)) AS AT_CMD,
COUNT(DECODE(T.MESSAGE_TYPE, 'AT+CFG', 1, NULL)) AS AT_CFG,
COUNT(DECODE(T.MESSAGE_TYPE, 'AT+QRY', 1, NULL)) AS AT_QRY
FROM TABLE_A T
WHERE T.CREATED_AT = '2019-03-22 10:20:20';

效果图

Airflow执行失败后添加微信告警

Airflow默认带有邮件了Slack告警方式,都是不太符合国情,要想第一时间收到告警信息,肯定是微信最为方便。
只需添加几行代码就能给Airflow带上微信模板信息推送功能,先决条件是需要申请微信公众号与开通模板信息功能。

一、安装 weixin-python的工具包

pip intall weixin-python

二、在DAG上添加执行失败回调与回调方法

def send_wechat(context):
    mp = WeixinMP(app_id='APP_ID', app_secret='APP_SECRET')
    mp.template_send(template_id='TEMPLATE_ID',
                     touser='USER_ID', data={
            "first": {
                "value": u"ARIFLOW执行告警!",
            },
            "content": {
                "value": u"ARIFLOW的DAG执行出错"
            },
            "occurtime": {
                "value": str(datetime.datetime.now())
            }
        })
on_failure_callback=send_wechat

收工。

CentOS使用pyenv优雅的安装Python3.6.8

由于CentOS默认安装了Python2.7,有时候我们需要Python3.6或者其他Python版本,这时候就需要升级Python2.7到Python其他版本,网上一大堆教程异常复杂。

第一步、安装pyenv

如果没有安装git,则安装

yum install git	

安装后执行命令,下载pyenv

git clone git://github.com/yyuu/pyenv.git ~/.pyenv

第二步、修改.bashrc

在末尾添加如下内容

export PYENV_ROOT="$HOME/.pyenv"
export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init -)"

添加后执行

source ~/.bashrc

第三步、安装开发工具

yum groupinstall "Development Tools"  -y
yum install -y python-devel libevent-devel python-pip gcc xz-devel openssl-devel readline-devel sqlite-devel bzip2-devel

第四步、查看可用的Python列表,并选择安装

pyenv install --list
pyenv install 3.6.8

第五步、切换Python版本

pyenv versions
pyenv global 3.6.8
pyenv rehash


可以通过的 pyenv global 版本号 反复切换Python。该方法同样适用MacOS。