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。

Airflow的DAG并行度控制

DAG中有两个初始化配置 concurrency 和 max_active_runs
concurrency:表示一个DAG,在同一时间点最大可以运行多少个Task。
max_active_runs:表示一个DAG,在同一时间点最多可以被运行几个。

假如我们一个DAG同一时间只能被运行一次,那么一定要指明 max_active_runs = 1
如果我们DAG中有10个Task,我们如果希望10个Task可以在触发后可以同时执行,那么我们的concurrency需要不小于10才行,若小于10,那么会有任务需要等待之前的任务执行完成才会开始执行。

就像上图,一个DAG被同时触发了2次,但是同时只有1个DAG的2个Task执行,另外一个DAG的两个Task被堵塞,等待资源的释放。

CentOS上Airflow安装部署流程

第一步、安装python开发包,用于编译Python第三方模块

yum install python-devel

第二步、安装Python的Mysql客户端模块

pip install mysqlclient

第三步、安装airflow、包括加密组件、用户登录组件

export AIRFLOW_HOME=~/airflow
export SLUGIFY_USES_TEXT_UNIDECODE=yes
pip install apache-airflow
pip install apache-airflow[mysql]
pip install apache-airflow[crypto]
pip install apache-airflow[password]

第四步、修改airflow.conf

# The executor class that airflow should use. Choices include
# SequentialExecutor, LocalExecutor, CeleryExecutor, DaskExecutor, KubernetesExecutor
executor = LocalExecutor
# The SqlAlchemy connection string to the metadata database.
# SqlAlchemy supports many different database engine, more information
# their website
sql_alchemy_conn = mysql://root:xxxxxx@localhost:3306/airflow

第五步、彻底卸载Mariadb,安装Mysql5.7
彻底卸载老的,卸载前请备份数据

mysqldump -u root -p --all-databases > alldb.sql
yum remove mysql MySQL-server MySQL-shared MySQL-shared-compat MariaDB-server MariaDB-client
rpm -qa|grep mariadb
rpm -e mariadb-embedded-devel-5.5.60-1.el7_5.x86_64
rpm -e mariadb-devel-5.5.60-1.el7_5.x86_64
rpm -e mariadb-embedded-5.5.60-1.el7_5.x86_64
rpm -e --nodeps mariadb-libs-5.5.60-1.el7_5.x86_64

添加Mysql5.7的yum

vim /etc/yum.repos.d/mysql-community.repo
wget https://repo.mysql.com//mysql80-community-release-el7-2.noarch.rpm
rpm -Uvh mysql80-community-release-el7-2.noarch.rpm
yum install mysql-community-server
systemctl start mysqld
systemctl status mysqld

mysql-community.repo内容如下:
[mysql57-community]
name=MySQL 5.7 Community Server
baseurl=http://repo.mysql.com/yum/mysql-5.7-community/el/7/$basearch/
enabled=1
gpgcheck=0
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-mysql

上面步骤后Mysql5.7安装完成!
查看Mysql安装后的临时密码,并修改密码,并创建数据库airflow

grep 'temporary password' /var/log/mysqld.log
mysql -uroot -p
ALTER USER 'root'@'localhost' IDENTIFIED BY 'xxxxxx'
FLUSH  PRIVILEGES;
CREATE DATABASE IF NOT EXISTS airflow  DEFAULT CHARSET utf8;

修改my.cnf

vim /etc/my.cnf

添加 explicit_defaults_for_timestamp = 1
重启Mysql

systemctl restart mysqld

第六步、初始化airflow数据库、启动web

airflow initdb

第七步、配置web登录

[webserver]
authenticate = True
auth_backend = airflow.contrib.auth.backends.password_auth

执行下面python初始化一个用户

import airflow
from airflow import models, settings
from airflow.contrib.auth.backends.password_auth import PasswordUser
user = PasswordUser(models.User())
user.username = 'youfangxi'
user.email = '951868171@qq.com'
user.password = 'xxxxxx'
# 超级管理员添加 user.superuser = True
session = settings.Session()
session.add(user)
session.commit()
session.close()
exit()

启动过程中若遇到MySQLdb ImportError: libmysqlclient.so.18  问题,可通过下面步骤解决

yum provides */libmysqlclient.so.18

查看那些包提供 libmysqlclient.so.18 然后安装

yum install mysql-community-libs-compat-5.7.25-1.el7.x86_64

这样最基本的一个带用户认证的airflow就基本安装完成!