作者归档:admin

PHP异步执行长时间任务

长时间执行的任务不适合放在PHP中执行应当放在任务调度系统或消息系统中由后台程序去执行,但是有时候还是需要在PHP中执行下长时间的任务 ,并且客户端不需要返回值。实现的思路大体如下:首先客户端发起一个不需要等待返回的请求,服务器端则需要忽略请求中断和执行时间,以免中途退出。

当有请求到达时,服务器端执行任务,在这里会产生一个文件。服务器端文件:

<?php 
//ob_start();
//header('Content-Type:text/html;charset=utf-8');
//header('Connection:close');
//flush();

ignore_user_abort(true);//忽略客户端连接中断
set_time_limit(0);//忽略页面执行超时
//Todo:do something here
sleep(10);
fopen(dirname( __FILE__ ).'\\'.time(),  "w");
&#91;/php&#93;

1.使用<a href="http://php.net/manual/en/function.fsockopen.php">fsockopen</a>来请求服务端执行任务,该方法需要自己拼凑header,包括参数等


<?php 
function triggerRequest($url, $post_data = array(), $cookie = array()){         
    $method = "GET";  //可以通过POST或者GET传递一些参数给要触发的脚本         
    $url_array = parse_url($url); //获取URL信息,以便平凑HTTP HEADER         
    $port = isset($url_array&#91;'port'&#93;)? $url_array&#91;'port'&#93; : 80;         
    $fp = fsockopen($url_array&#91;'host'&#93;, $port, $errno, $errstr, 30);         
    if (!$fp){                 
        return FALSE;         
    }         
    $getPath = $url_array&#91;'path'&#93; ."?". $url_array&#91;'query'&#93;;         
    if(!empty($post_data)){                 
        $method = "POST";         
    }         
    $header = $method . " " . $getPath;         
    $header .= " HTTP/1.1\r\n";         
    $header .= "Host: ". $url_array&#91;'host'&#93; . "\r\n "; //HTTP 1.1 Host域不能省略               
    $header .= "Connection:Close\r\n\r\n";         
    if(!empty($cookie)){                 
        $_cookie = strval(NULL);                 
        foreach($cookie as $k =--> $v){
            $_cookie .= $k."=".$v."; ";
        }
        $cookie_str =  "Cookie: " . base64_encode($_cookie) ." \r\n";//传递Cookie
        $header .= $cookie_str;
    }
    if(!empty($post_data)){
            $_post = strval(NULL);
            foreach($post_data as $k => $v){
                    $_post .= $k."=".$v."&";
            }
            $post_str  = "Content-Type: application/x-www-form-urlencoded\r\n";//POST数据
            $post_str .= "Content-Length: ". strlen($_post) ." \r\n";//POST数据的长度
            $post_str .= $_post."\r\n\r\n "; //传递POST数据
            $header .= $post_str;
    }
    fwrite($fp, $header);
    fclose($fp);
    return true;
}
$url='http://127.0.0.1/learn/asyn/server.php';
triggerRequest($url);

2.使用curl来发起请求,可以方便的设置传递参数,cookie等,比fsockopen简洁许多,需要安装php_curl扩展

<?php
 $ch = curl_init();
 $data = array('param' => '1');
 curl_setopt($ch, CURLOPT_URL, 'http://127.0.0.1/learn/asyn/server.php');
 curl_setopt($ch, CURLOPT_POST, 1);
 curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
 curl_setopt($ch, CURLOPT_TIMEOUT, 1);//1秒后立即执行
 curl_exec($ch);
 curl_close($ch);

也可以采用popen,ajax发起请求,详见参考链接。
参考链接
fsockopen函数用法
使用fscok实现异步调用PHP
PHP实现异步调用方法研究
说说php的异步请求
php curl常用的5个例子

Nginx做简单的反向代理

Nginx是一个HTTP和反向代理服务器,可做为反向代理实现负载均衡的例子,也可以作为代理服务器,也经常作为web服务器使用。如果一台服务器装有多个web服务器则必须监听多个端口,对于用户的访问将是不友好,则需要一个统一的前端来进行分发。本文只是简单的利用Nginx作为前端代理服务器,代理不同的后端服务器:IIS和Apache,实现不同的域名解析至不同的服务器。

首先更改IIS的监听端口为8000,Apache的监听端口为9000,保证Nginx占有80端口和443端口。然后更改nginx.conf添加以下内容 继续阅读

JasperReports flash打印实现

JasperReports是一个基于Java的开源报表工具,可以在ireport中进行设计后,输出HTML,PDF,Excell,OpenOffice和Word格式。JasperReports支持applet方式打印,但是flash方式却是注释掉的。在net\sf\jasperreports\flex\view\Viewer.mxml文件中

<!--
<mx:Button width="22" toolTip="Print Report" id="btnPrintReport0"
  buttonMode="true"enabled="false" >
    <mx:icon>@Embed(source='images/print.GIF')</mx:icon>
</mx:Button>
<mx:Button width="22" toolTip="Print Report" id="btnReload"
  buttonMode="true"  enabled="true" click="btnReloadClick()">
    <mx:icon>@Embed(source='images/reload.GIF')</mx:icon>
</mx:Button>
 -->

本文主要完成JasperReports的flash打印功能,是基于JasperReports Flash Viewer 4.0.0修改,参考了网络上基于3.0版本的修改。将Viewer.mxml文件上面的文字修改为

<mx:Button width="22" toolTip="Print Report" id="btnPrintReport0"
  buttonMode="true" enabled="false" click="btnPrintReport()">
   <mx:icon>@Embed(source='images/print.GIF')</mx:icon>
</mx:Button>
<!--
<mx:Button width="22" toolTip="Print Report" id="btnReload"
  buttonMode="true" enabled="true" click="btnReloadClick()">
    <mx:icon>@Embed(source='images/reload.GIF')</mx:icon>
</mx:Button>
-->

在Viewer.mxml中找到refreshPage函数,启用打印按钮,将

if (report.pageCount > 0)
{
    var pageCanvas:PageCanvas = new  PageCanvas(report.pages[pageIndex]);
    pageCanvas.scaleX = zoomRatio;
    pageCanvas.scaleY = zoomRatio;

    pageBox.visible = false;
    pageBox.removeAllChildren();
    pageBox.addChild(pageCanvas);
    pageBox.visible = true;
}

修改为

if (report.pageCount > 0)
{
    var pageCanvas:PageCanvas = new  PageCanvas(report.pages[pageIndex]);
    pageCanvas.scaleX = zoomRatio;
    pageCanvas.scaleY = zoomRatio;

    pageBox.visible = false;
    pageBox.removeAllChildren();
    pageBox.addChild(pageCanvas);
    pageBox.visible = true;
    btnPrintReport0.enabled = true;
}

在Viewer.mxml中引入net\sf\jasperreports\flex\view\PrintMananger.as文件,以支持打印

import net.sf.jasperreports.flex.view.PrintManager;

在Viewer.mxml增加打印函数btnPrintReport

            public function  btnPrintReport():void
			{
				PrintManager.printAll(jrpxmlUrl,pageFetchSize,report);
			}

net\sf\jasperreports\flex\view\PrintMananger.as文件内容

package net.sf.jasperreports.flex.view
{
	import flash.events.EventDispatcher;
	import flash.events.SecurityErrorEvent;
	import flash.events.Event;
	import flash.events.ProgressEvent;
	import mx.core.UIComponent;
	import mx.collections.IList;
	import mx.collections.ArrayCollection;
	import net.sf.jasperreports.flex.view.PageCanvas;
	import net.sf.jasperreports.flex.model.Report;
	import net.sf.jasperreports.flex.xml.ReportFactory;
	import flash.events.HTTPStatusEvent;
	import flash.net.URLLoader;
	import flash.net.URLRequest;
	import flash.events.IOErrorEvent;
	import mx.printing.FlexPrintJobScaleType;
	import mx.printing.FlexPrintJob;
	import mx.core.Application;
	import mx.containers.VBox;
	import mx.events.FlexEvent;

	public class PrintManager extends EventDispatcher
	{
		private var url:String;
        private var urlLoader:URLLoader;
		private var report:Report;
		private var pageIndex:int = 0;
		private var pageFetchSize:int = 1;

		private var componentsToBeInitialized:int = 0;
		private var pagesPrint:Array = [];

        public const LOAD_PAGE:String="loadPage";
        public const ALL_PAGE:String = "allPageLoader";

		public function PrintManager(){
            super();
        }

		public function get url():String
        {
            return url;
        }

        public function set url(value:String):void
        {
            url=value;
        }
		public function set fetchSize(fetchSize:int):void
		{
			pageFetchSize = fetchSize;
		}
        public static function printAll(url:String,fetchSize:int,report:Report):void
        {
            var pm:PrintManager = new PrintManager();
            pm.url = url;
			pm.pageFetchSize = fetchSize;
			pm.report = report;
            pm.loadAll();
        }
        private function loadAll():void
        {
            this.addEventListener(LOAD_PAGE, onLoadPage);
            this.addEventListener(ALL_PAGE, onAllPageLoaded);
			pageIndex=0;

			if (report)
			{
				var loadFlag:Boolean=false;
				while (pageIndex < report.pageCount) {
					if (!report.pages[pageIndex]) {
						loadFlag=true;
						break;
					}
					pageIndex++;
				}
			}

			if (loadFlag) {
				loadPage();
			}
            else{
                removeEventListener(LOAD_PAGE, onLoadPage);
                dispatchEvent(new Event(ALL_PAGE));
            }
        }

        private function onLoadPage(e:Event):void
        {
            pageIndex++;
            if (pageIndex < report.pageCount) {
				if(!report.pages[pageIndex]){
					loadPage();
				}
			}
            else{
                removeEventListener(LOAD_PAGE, onLoadPage);
                dispatchEvent(new Event(ALL_PAGE));
            }
        }

		public function  onAllPageLoaded(event:Event):void
		{
			var cur:int;
			var uc:UIComponent;
			var pj:*;
			if (report != null){
				pj = new FlexPrintJob();
				pj.printAsBitmap = false;
				if (pj.start()){
					cur = 0;
					while (cur < report.pageCount) {
						uc = processPage(new PageCanvas(report.pages[cur]));
						Application.application.addChild(uc);
						pj.addObject(uc);
						Application.application.removeChild(uc);
						cur++;
					};
					pj.send();
				};
			} else {

			};
		}
		private function processPage(param1:PageCanvas):UIComponent{
			var vbox:* = new VBox();
			vbox.horizontalScrollPolicy = "off";
			vbox.verticalScrollPolicy = "off";
			vbox.width = report.pageWidth;
			vbox.height = report.pageHeight;
			vbox.setStyle("backgroundColor", "#FFFFFF");
			vbox.addChild(param1);
			return (vbox);
		}

        private function loadPage():void
        {
            urlLoader=new URLLoader();
            urlLoader.addEventListener(Event.COMPLETE, completeHandler);
            urlLoader.addEventListener(Event.OPEN, openHandler);
            urlLoader.addEventListener(ProgressEvent.PROGRESS, progressHandler);
            urlLoader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
            urlLoader.addEventListener(HTTPStatusEvent.HTTP_STATUS, httpStatusHandler);
            urlLoader.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
			if (report)
			{
				var goRight:Boolean = true;
				var goLeft:Boolean = true;
				var right:int = 0;
				var left:int = 0;

				while(goRight || goLeft)
				{
					if (
						goRight
						&& left + right + 1 < pageFetchSize
						&& pageIndex + right + 1  < report.pageCount
						&& !report.pages[pageIndex + right + 1]
						)
					{
						right++;
					}
					else
					{
						goRight = false;
					}

					if (
						goLeft
						&& left + right + 1 < pageFetchSize
						&& pageIndex - left - 1 >= 0
						&& !report.pages[pageIndex - left - 1]
						)
					{
						left++;
					}
					else
					{
						goLeft = false;
					}
				}

				urlLoader.load(new URLRequest(url + "&startPage=" + (pageIndex - left) + "&endPage=" + (pageIndex + right)));
			}
			else
			{
				//first load; pageIndex == 0
				urlLoader.load(new URLRequest(url + "&page=" + pageIndex));
			}
        }

        private function completeHandler(event:Event):void
        {
			var newReport:Report = ReportFactory.create(XML(urlLoader.data));

			var i:int;
			var pages:IList;

			if (report)
			{
				pages = report.pages;
			}
			else
			{
				//first load
				report = newReport;
				pages = new ArrayCollection(new Array(report.pageCount));
			}

			for(i = newReport.startPageIndex; i <= newReport.endPageIndex; i++)
			{
				pages[i] = newReport.pages[i - newReport.startPageIndex];
			}

			report.pages = pages;

			report.startPageIndex = Math.min(report.startPageIndex, newReport.startPageIndex);
			report.endPageIndex = Math.max(report.endPageIndex, newReport.endPageIndex);

			refreshPage();
        }
		private function openHandler(event:Event):void
		{
			trace("openHandler: " + event);
		}

		private function progressHandler(event:ProgressEvent):void
		{
			trace("progressHandler loaded:" + event.bytesLoaded + " total: " + event.bytesTotal);
		}

		private function securityErrorHandler(event:SecurityErrorEvent):void
		{
			trace("securityErrorHandler: " + event);
		}

		private function httpStatusHandler(event:HTTPStatusEvent):void
		{
			trace("httpStatusHandler: " + event);
		}

		private function ioErrorHandler(event:IOErrorEvent):void
		{
			trace("ioErrorHandler: " + event);
		}

        private function refreshPage():void
        {
			if (report) {
				this.dispatchEvent(new Event(this.LOAD_PAGE));
			}
        }
	}
}

编译Main.mxml运行,即实现打印功能,但是包含图片的打印依然打印不出来。对于这种动态图片的打印添加了等待事件完成,仍能无效,不知道有没有好的实现。
参考链接:
JasperReports Library
完成Flex报表设计、生成、打印功能
Flex 4 Printing Problem with dynamic components

PHP/Java Bridge笔记

PHP/Java Bridge是基于xml的网络协议的实现,该协议使得本地脚本语言引擎( 比如PHP,Python)能够连接Java虚拟机。它比通过SOAP的RPC调用更快,更节省服务器资源;比直接的Java本地接口通信要更快和更可靠,不需要额外的组件。Java应用比PHP要多,组件和类库也比PHP要多,比如JasperReports,通过PHP/Java Bridge可以为PHP 所用,当然也可以采用其他方式进行调用,比如官方的SOAP示例。

网上的关于JavaBridge安装文章较旧,现在的JavaBridge安装已经不需要java.so或php_java.dll,只需要php安装版本大于5.0,JDK版本大于1.4.2。本文是将JavaBridge放在Tomcat下运行的,独立运行参照此文设置。 继续阅读