东东东 陈煜东的博客

分类存档: Web 开发

uwsgi log rotate按天切割日志

uwsgi和nginx的log一样都是在一个文件名上写log,无法按天进行rotate,所以单个文件的体积会越来越大,不利于log的备份。

nginx的log切分方法

在网络上研究了nginx的log切分方法,原理比较简单。

  1. 先重命名文件,mv access.log access-20150215.log
  2. 然后reload一下nginx。
  3. nginx继续在access.log上写log

我想这种方法也是应该可以应用到uwsgi上面的。但是没有那么容易。

尝试应用到uwsgi

  1. 先重命名文件,mv uwsgi.log uwsgi-20150215.log
  2. 然后reload一下uwsgi。
  3. 没有看到uwsgi.log文件

与想象中的完全不一样。发现uWSGI的reload操作根本重不会新打开log文件。这样就需要变更一下思路了。

解决方案一

参数touch-logreopen可以重新打开日志。

logto = /data/log/MODULE/uwsgi.log
touch-logreopen=/data/log/MODULE/.touchforlogrotate

touch-logreopen当指定的文件被touch过后,时间戳发生变化,会让uWSGI重新打开日志文件,并且不会终止当前的服务(不是stop+start,而是reload的概念)。

#!/bin/bash

module="module_name"
DIR=`echo $(cd "$(dirname "$0")"; pwd)`       #获取当前目录 
LOGDIR="/data/log/$module/"                   #log目录

sourcelogpath="${LOGDIR}uwsgi.log"            #log源地址
touchfile="${LOGDIR}.touchforlogrotate"       #需要touch的文件

DATE=`date -d "yesterday" +"%Y%m%d"`
destlogpath="${LOGDIR}uwsgi-${DATE}.log"     #重命名后的文件
mv $sourcelogpath $destlogpath

#echo $touchfile
touch $touchfile                             # 更新文件时间戳

然后在crontab中加入这个,让每天0点0分的时候切分日志,当然了也可以23点59分,不过要更改一下shell脚本。

00 00 * * * /data/you_module_name/bin/uwsgirotate.sh  > /dev/null 2>&1

当然了,也可以不用crontab,如果你有logrorate服务,可以往里面添加一些事件,来进行日志的滚动。

解决方法二

官方说参数log-maxsize <bytes>,可以让uWSGI的日志文件达到一定大小后重新打开。不过我记得当达到一定大小后,并没有重新写入到uwsgi.log文件中,比较奇怪。而且按日志大小来切分,感觉没有按时间来切分的好。

解决方案三

使用参数log-master,让主进程监听一些信号,当想master进程发送信号后,日志就会重新打开,这个也是要配合mv操作先更改文件名才能的。具体参考官方的使用。The Master FIFO

分类: Web 开发

手机qq WebView网页控制面板设置

现在微信的WeixinJSBridge可以控制微信内置浏览器的一些界面UI,甚至还可以调用微信的一些组件,比如摄像头、打开公众账号等等操作,那么手机QQ是否也有类似的JS接口呢?

答案是:有,但还不对外开放。

我问了手机QQ的JS接口开发人员,要想使用MobileQQ JS API是有域名权限控制的。目前还只能腾讯业务使用,将来或许会开放到QQ开放平台到,但是接口不会一下子放出来,会慢慢的放开来给普通开发者使用。看了下他们mqq的JS API接口,写的文档还不错。期待他们的对外开放。

但是,虽然不能使用手机QQ的JS API接口,但是官方还是有一些方法能够隐藏手机QQ内置浏览器的底部导航等等。具体的实现方式不能使用JS来控制,而是通过URL来控制。

定制webview

webview是什么?简单的理解就是手机QQ的内嵌浏览器吧。

使用方法

只需在url上添加一个名为_wv的参数即可配置不同的UI:

http://www.domain.com/uri?...&_wv=N

其中N是各种组合之和,例如N=5=1+4,表示有1和4的效果。具体的值有:

  • 1: 隐藏【底部导航】(隐藏后『返回按钮』功能变为『页面后退』, 可以配合4使用)
  • 2: 隐藏【功能菜单】
  • 4: 在隐藏【底部导航】的情况下,『返回按钮』直接关闭页面
  • 8: 隐藏【功能菜单】里的【分享给好友】项
  • 16: 隐藏【功能菜单】里的【分享到QQ空间】项
  • 32: 隐藏【功能菜单】里的【复制链接】项
  • 64: 隐藏【功能菜单】里的【查看帐号资料】项[4]
  • 128: 隐藏【功能菜单】里的【调整字体】项
  • 256: 隐藏【功能菜单】里的【用系统浏览器打开】项
  • 512: 隐藏【功能菜单】里的【用QQ浏览器打开】项
  • 1024: 锁定竖屏模式(禁用横屏)
  • 2048 [未实现]: 锁定横屏模式(禁用竖屏)
  • 4096 [iOS]: 禁用向右滑动关闭WebView的手势
  • 8192: 隐藏【功能菜单】里的【收藏】项
  • 16384:(4.7+) 隐藏【功能菜单】里的【分享到微信】
  • 32768:(4.7+) 隐藏【功能菜单】里的【分享到朋友圈】
  • 65536:(4.7+) 禁用webveiw缓存
  • 131072:(5.3+) 全屏
  • 262144:无用
  • 524288:(5.3+) activity 透明
  • 1048576:(5.3+) 忽略登录态(不种入登陆态)
  • 2097152:(5.3+) 忽略多层返回时显示”关闭”按钮

一些特殊的情况

Android下,当没有设置_wv参数时,默认为_wv=4,因此【返回按钮】行为是退出整个webview,当设置了其它不包括4的值,譬如_wv=1024(禁用横屏)则返回按钮行为是【页面回退】。

分类: Web 开发

移动开发SNS微信、QQ社交分享

Google之前出了android wear,个人感觉很时髦,有个朋友就买了个moto 360,我们一起在上面开发一个应用。

Android Wear 开发现状

现在的android wear还没能独立成为一个设备,需要依赖手机进行更多的操作。现在的apple watch也是类似,计算能力大部分还是依靠手机端。

比如手表要访问网络,需要通过蓝牙发请求到手机上,然后由手机进行网络请求,再通过蓝牙返回给手表。这意味着,你想在android wear上开发一个app,那么必须也要在手机上开发一个app,两个配套使用。这个概念类似于智能设备或智能手环,智能手环上的数据需要通过蓝牙传送给手机,手机的app做更多信息的处理。只是手表上可以有更多信息的展示和交互。

社交分享方式一 使用SDK

我们有一个需求是:将我们设备的数据分享到微信朋友圈或者是QQ空间,让用户的数据和好友分享,然后好友可以查看他的分享,关注我们的app,形成一个闭环。

一开始想到的,我们在手机上开发的app调用手机QQ、微信的SDK的,来进行分享。因为这个方法有看见过,是第一直觉。

QQ空间分享

进入到腾讯开发平台下载SDK,查看QQ分享demo

说明:现在QQ移动开放平台的功能、下载已经不在QQ互联(http://connect.qq.com/)了。已经移到开发平台(http://open.qq.com/)。

微信分享朋友圈

进入微信开放平台,需要注册一下微信开放平台的账号,下载Android SDK,另外有个比较重要的调试资源签名生成工具需要安装,然后提交审核(审核的时间比较慢,要早提交),这样在分享到朋友圈的时候才会出现小尾巴。

具体的QQ分享SDK、微信朋友圈代码我就不多说了,官方有教程,网络上也有很多的教程。

问题一:手机分享必须通过用户参与才能分享

但是有个是想说明的:

从2014.3.31起QQ分享到QQ空间和微信分享到朋友圈必须经过用户的参与,不能程序(Server和app程序)通过HTTP API分享到用户的QQ空间或者微信朋友圈。

这个大家不用再苦苦寻找通过QQ分享说说的HTTP API接口了,我已经向内部人士多方确认过了。

官方网站原话:add_share和add_one_blog接口已于2014.3.31正式下线。请PC网站调用PC空间分享组件,移动端下载最新版本的SDK后调用shareToQQ或shareToQzone接口,以满足用户分享一条动态(feeds)到QQ空间的功能。

这么做的目的应该是让用户知道他们做了什么操作,并且会有什么样的结果。如果让程序自己通过HTTP API来请求服务器,那么用户完全无感知,不符合用户的感受。但是这样做就会让产品人员感到痛苦,因为让用户做最少的操作,来完成一个功能,是产品追求的。

问题二:让用户在你的体验手机上登录微信或QQ

  1. 前面说过了,手表不能独立存在,需要依靠手机,并且不是随便一个手机都能轻易连接手表的,需要在手机上安装程序才行(对于moto360来说需要最新的Andriod Wear、Google search、Googla Play)。

  2. 微信账号属于重要的私人社交账号,而且换个手机登录会有登录限制。也许你会问用你们自己的开发账号呀!那么问题就来了,那你还要让用户分享干嘛,分享不就是为了在他的好友圈产生关注,然后进入你的页面,导入新的用户,体验你的产品,形成一个闭环。

<

p>鉴于以上这些,你的产品要给用户体验,用户会容易的安装这些?会登录他自己的微信?显然不会。

社交分享方式二 二维码分享

二维码可以让用户通过相机扫描获得更多的信息,微信的扫一扫出来后也是风靡一时,现在也有很多东西是通过二维码扫描来进行的。

想象一下这个过程:

  1. 通过利用手机QQ、微信的扫描二维码得到一个URL
  2. 微信会自动打开网页,手机QQ会让用户看到URL,并需要用户确认,两者体验不同。
  3. 打开网页后,浏览信息。
  4. 分享到朋友圈或者QQ空间。
  5. 好友点开网址查看,体验,分享到自己的朋友圈、QQ空间,达成闭环。

通过二维码的方式完全解决了第一个用户需要使用体验手机登录的问题。

只是单次二维码的分享时间会比较长。

自定义微信网页的分享,调用微信JS API

在微信打开URL网页后,可以自定义分享的图文,或者调用微信的JS API来实现高级功能。

微信公众账号有一个JS API,WeixinJSBridge,例如这里,可以隐藏右上角的按钮。

官方的这个接口并没有开放给所有人使用,估计是微信公众账号放出来,但是权限没控制好吧。官方的接口不太好用,github上有一个微信JS API的封装类库,可以推荐使用一下,https://github.com/zxlie/WeixinApi

手机QQ的JS API

我问了手机QQ的JS接口,要想使用MobileQQ JS API是有域名权限控制的。目前还只能腾讯业务使用,将来或许会开放到QQ开放平台到,但是接口不会一下子放出来,会慢慢的放开。

看了下他们mqq的JS API接口,写的文档还不错。期待他们的对外开放。

二维码生成

网络上也有二维码类库,比如PHP版本的有phpqrcode,Python有qrcode、python-qrcode

总结

通过SDK和二维码分享的方式有些区别,有不同的使用场景。

区别:

  • SDK分享一般用在app分发到用户的手机上,需要用户安装app。
  • 二维码分享一般用在活动现场,用户扫描二维码直接分享。

分类: Web 开发

JS无法在非人为触发的事件下打开新窗口

本来想写个JavaScript测试脚本,自动打开新标签,去访问网页进行一个压力测试,因为JS需要去连接后台的数据,所以没办法使用ab这些工具去压测。看上去一个很简单的需求,却没办法自动实现。

使用window.open方法只能弹出一个小窗口,不能在新标签页中打开。这就有一个问题了,我当时打开的窗口有上百个,当达到一定数量后,整个chrome就崩溃了。原因我觉得应该是Windows对进程的使用空间有限制,打开一个标签后,chrome会给窗口分配一个新进程或者和其他老进程共用一个,这就会出现标签或者页面过多导致空间占用大,然后程序崩溃了。

后来想到了通过a标签,可以打开新的标签页,于是就对a标签进行事件的处理。发现同样的代码只有用户有click事件触发时,才会在新标签也中打开一个新窗口,如果使用setTimeout、setInterval这样的函数来调用,或者是在控制台里直接触发click是没办法在新标签也面中打开的,只能在小窗口中打开。也尝试了很多网络上说的方法,比如createEventtriggerdispatchEvent都不管用。

相关信息:之前看到一个文章,说Firefox等浏览器对event有一个新的属性event.isTrusted,可以查看一些这里还有这里

这里可能是浏览器对于打开新标签页有一些限制吧。不能让程序自动打开无数的标签,但是为什么可以打开新窗口却不能打开新标签页呢?

最后是通过chrome的一个插件New Tab Redirect来解决的,然后狂按ctrl+T来手工打开很多的新窗口。

相关资料

分类: Web 开发

jquery对话框弹出层无法点击输入

在开发对话框的时候使用jQuery类库来打开对话框,第一次可以输入,第二次不管怎么点击,怎么输入按键盘都无法输入。很是纳闷,纠结了很久,我一直我以为我写的代码有些问题,是不是在哪里监听了click事件。

另外还发现第二次除了无法输入文字,连F12都被捕获了,无法打开Chrome的开发者工具,F1也被捕获,但是ESC没有被捕获可以使用。

初步怀疑jQuery的类库做了什么事情,让我一直没办法点击。

后来分析z-index发现,我对话框自定义的内容z-index的值小于外层容器的值。但是只是傻傻的将input的z-index的值增加,但是并没有效果。后来不小心对我自定义对话框内容的z-index增大了一些,发现真的可以输入了。好欢喜。

这里对话框的z-index值大于遮罩层的z-index时才能点击,否者只能看见遮罩层而无法点击。

没有时间去探究为什么内层的dom没有设置z-index,外层有z-index,但是内层却不能获得焦点。很是困惑。可能哪里遗漏的吧。

分类: Web 开发

chrome修复css属性position fixed抖动

我现在使用的浏览器是Chrome版本 32.0.1664.3 m Aura,发现该版本的Chrome每次打开页面滚动页面对于position:fixed的元素会抖动,然而在重新应用position:fixed,抖动就不存在了,或者F5刷新页面后也不会抖动。

抖动的情况

我的测试代码

<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="content-type" content="text/html; charset=UTF-8">
  <title></title>
  
<style type='text/css'>
.fixbutton{
    position: fixed;
    padding: 1em;
    border: 1px solid #C8C8C8;
    background: #FF4F4F;
    bottom: 0em;
    right: 2em;
  
}

.fixedheader{
    position: fixed;
    padding: 1em;
    background: #7E7EFF;
    top : 0;
    left : 0;
    right:  0;
}
  </style>

</head>
<body>
  <div class="fixedheader">fixed header</div>

11111111<br>11111111<br>11111111<br>11111111<br>11111111<br>11111111<br>11111111<br>11111111<br>11111111<br>11111111<br>11111111<br>11111111<br>11111111<br>11111111<br>11111111<br>11111111<br>11111111<br>11111111<br>11111111<br>11111111<br>11111111<br>11111111<br>11111111<br>11111111<br>11111111<br>11111111<br>11111111<br>11111111<br>11111111<br>11111111<br>11111111<br>11111111<br>11111111<br>11111111<br>11111111<br>11111111<br>11111111<br>11111111<br>11111111<br>11111111<br>11111111<br>11111111<br>11111111<br>11111111<br>11111111<br>11111111<br>11111111<br>11111111<br>11111111<br>11111111<br>11111111<br>11111111<br>11111111<br>11111111<br>11111111<br>11111111<br>11111111<br>


<div class="fixbutton">fixed button</div>
<script src="http://code.jquery.com/jquery-1.7.2.min.js"></script>  
</body>
</html>

大家先看看视频

重新设置position发现不会抖动了。

引入jQuery1.7.2不会抖动了。但是引入1.11.0发现还是会抖动

其他浏览器的情况

我的电脑上的其他浏览器(Chromium 版本 30.0.1592.0 、Firefox 27.0.1 、 IE9)无抖动问题。

寻找其他人的浏览器,Chrome 33 无此抖动问题。

因此此问题应该是部分浏览器的问题,非全部的问题。

目前可能可以解决的方法

目前可以解决的方法我也尝试了一些,只剩下几个。

第一种,给fixed的元素添加css的样式,我试过,这个可以解决此闪动的问题。

-webkit-transform: translateZ(0);

第二种,设置css。我是在不复杂的页面做的测试。

html, body {height:100%;overflow:auto;margin: 0;}

注意,引入这个CSS可能会导致原来的页面布局出现问题,慎重。我的同事也说,这个是为了解决IE6系列的fixed抖动问题。

第三种,引入jquery1.7.2的类库。

<script src="http://code.jquery.com/jquery-1.7.2.min.js"></script>

引入jQuery的方法不知道为什么可以,但是引入了1.11.0的版本就无法解决这个问题。很奇怪。

分类: Web 开发

PHP的MVC框架模式是如何实现的

PHP的mvc框架很多,像YiiCodeIgniterThinkPHP等现在流行的框架,利用MVC模式进行web页面的开发,我们可以非常方便的编写web程序。他们的工作原理大家应该也比较感兴趣,下面我说说一个mvc框架长什么样。

路由机制

在互联网我们都是通过url提供服务,因此不同的url有不同的服务。用户访问不同的页面也就获得了不同的服务。那么我们的服务是如何通过url来区分不同的服务呢。

我们的web程序就要通过url寻找到不同的文件,进行不同的业务逻辑处理。我们的路由机制就是根据url,寻找到对应的controller,和action,然后由action进行具体的业务逻辑处理。

//定义一个controller
class UserControler extends Controller{
     //定义一个action方法,注意一定是public的
     public function index(){
          // do business code
     }
}

具体的对应规则不同的框架映射不同。以下是CodeIgniter框架的URL路由,它会尽力的尝试各种的可能,来分析URL的情况。

// 看看是否是从命令行运行的
if (php_sapi_name() == 'cli' or defined('STDIN')){
    $this->_set_uri_string($this->_parse_cli_args());
    return;
}

// 首先尝试 REQUEST_URI 这个适应大部分的情况
if ($uri = $this->_detect_uri()){
    $this->_set_uri_string($uri);
    return;
}

// 看看PATH_INFO变量是否存在?nginx需要配置
// Note: some servers seem to have trouble with getenv() so we'll test it two ways
$path = (isset($_SERVER['PATH_INFO'])) ? $_SERVER['PATH_INFO'] : @getenv('PATH_INFO');
if (trim($path, '/') != '' && $path != "/".SELF){
    $this->_set_uri_string($path);
    return;
}

// 没有PATH_INFO,看看 QUERY_STRING?
$path =  (isset($_SERVER['QUERY_STRING'])) ? $_SERVER['QUERY_STRING'] : @getenv('QUERY_STRING');
if (trim($path, '/') != ''){
    $this->_set_uri_string($path);
    return;
}

//尝试去从 $_GET 获取信息
if (is_array($_GET) && count($_GET) == 1 && trim(key($_GET), '/') != ''){
    $this->_set_uri_string(key($_GET));
    return;
}

// 尽力了,放弃了路由
$this->uri_string = '';
return;

通过上面的尝试,接下来就是如何利用路由机制加载正确的controller了。

Controller加载机制

我们来看看Codeigniter框架是如何加载到controller并且调用action的。

/system/core/Codeigniter.php中有如下的代码。Codeigniter在这之前会根据$_SERVER['PATH_INFO]里面的值来进行赋值(这个都是靠自己的设定的,默认的话CI他会有许多的if分支进行判断)。

//大约在250行
include(APPPATH.'controllers/'.$RTR->fetch_directory().$RTR->fetch_class().'.php');

$class  = $RTR->fetch_class();
$method = $RTR->fetch_method();

//大约在308行
$CI = new $class();

//大约在359行
call_user_func_array(array(&$CI, $method), array_slice($URI->rsegments, 2));

就这样,通过这个就调用到了我们的controller及其方法了,接下来就是编写自己的业务逻辑代码了。

视图view的显示

当我们的业务逻辑代码写完后,就需要页面的展示了。很多常见的MVC框架在页面的调用是这么写的。

//controller中action的方法
public function index(){
    // ... 许多的业务逻辑代码
    $data = array('name'=>'abc', 'age'=>12, .... );
    return $this->render('view/path/file.html',$data);
}

接着在视图文件view/path/file.html里写上一下代码。

<div>
姓名 : <?=$name ?>
年龄 : <?php echo $age; ?> 
</div>

这段如何将数据渲染到视图中,这段代码以前我一直很好奇,现在我明白了,我们来看看是如何实现的。

protected function render($template, array $var = array() ) 
{
    extract($var);   // 抽取数组中的变量
    ob_end_clean (); //关闭顶层的输出缓冲区内容
    ob_start ();     // 开始一个新的缓冲区
    require TEMPLATE_ROOT . $template . '.html';  //加载视图view
    $content = ob_get_contents ();             // 获得缓冲区的内容
    ob_end_clean ();           // 关闭缓冲区

    //ob_end_flush();      // 这个是直接输出缓冲区的内容了,不用再次缓存起来。
    ob_start();            //开始新的缓冲区,给后面的程序用
    return $content;       // 返回文本,此处也可以字节echo出来,并结束代码。
}

在这短短的几行代码中,全都是精华,就是这些非常重要的,全是php的内置函数,接下来我们来具体分析分析。

看看第一个extract($var)。这个函数从数组中将变量导入到当前的符号表。刚刚就将$data数组里面的name、age抽取出来,这样就可以在视图view中使用$name $age。更详细的请参考http://www.php.net/manual/zh/function.extract.php

第二个ob_end_clean()的作用是关闭顶层的缓冲区,为了是之前的程序不小心echo出的一些文字给清楚了,为了下一行的重新开辟一块缓冲区。

第三个ob_start()是开启一块新的缓冲区,为了是将视图的内容放到缓冲区。当然了,缓冲区有一定的大小,如果内容超出了缓冲区的设定值,那么会自动的发送给server。

第四个require file,这个就是第一个参数,根据自己的规则去加载视图的文件。其中文件里可以夹杂php、html的代码。你在这个render()函数声明的任何局部变量或者这里能访问到的任何全局变量,都可以在require的file文件中访问到。

第五个$content = ob_get_contents ()很重要,是为了将缓冲区的内容取出来,但不清除它。

第七个ob_start()是重新开启一个缓冲区,为了是下面的程序需要使用缓冲区。有写框架可能不用对$content的内容进行操作了,那么直接ob_end_flush()将缓冲区的内容输出出来就行了。

这个是一个很简单的展示视图的过程。如果直接使用这个不方便对视图view进行模块化,因此一些框架都不会这么直接用的。

我们从这个函数也可以看到程序有点类似程序中断保护现场的感觉。只不过中断保护现场会先保存数据,然后在返回的时候恢复回来。这里只有关闭上一个缓冲区,开启一个新的缓冲区,关闭这个缓冲哦过去,开启另外一个缓冲区。

至此,我们看到一个简单的PHP的MVC框架。如果你有兴趣可以自己开发一个MVC框架,或者更深入点的HMVC。

分类: PHP, Web 开发

Copyright © 2017 东东东 陈煜东的博客 粤ICP备13059639号-1

SITEMAP回到顶部 ↑