PHP-FPM配置sendmail支持PHP发信

当下常用的环境CentOS+nginx+php-fpm+mysql.

之前五四陈科学院曾有一文章是介绍环境的配置的,挖一挖水,链接在这里:

wordpress nginx php-cgi mysql在128m内存下的vps安装和优化手记[原创]

如果要使用mail功能,需要按如下步骤作:

第一步、安装sendmail

yum install sendmail

第二步、启动sendmail

service sendmail start

第三步、设置为auto start

chkconfig sendmail on

第四步、修改php-fpm.conf:

搜索sendmail,去掉那一行两头的注释如下:

<value name="sendmail_path">/usr/sbin/sendmail -t -i</value>

第五步、php-fpm reload

分享一个固定时间自动更新svn的简单shell脚本

这是一个超级简单的脚本,旨在提醒观众:在crontab最小精度只能到1分钟的情况下,可以使用这个作到1秒钟的间隔执行时间。

看代码:

#!/bin/sh
while true
do
/usr/bin/svn up /data/web/club/ #(指定的要更新的目录)
sleep 5
done
原理很简单,利用shell中sleep的单位为秒作自动执行,如果要更加小的精度,估计可以利用这个基础之上多个同时执行:) 仅猜想,标准一些的作法还是用java或者是php的sleep(毫秒级的)。

用java作bdb的方法记录二

在科学院之前的一篇文章中,介绍过普通的作BDB的方法,见:

java使用bdb手记(Berkeley DB Java api记录)

下文记录的是利用BDB的事务来完成的一个put和一个get方法的代码。其速度与原始的作方法相当。

================================================代码段==============================

/**
* @author zhen.chen
* @createTime 2010-2-20 上午10:43:31
*/
package com.renren.nuclear.storage;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import com.sleepycat.je.Cursor;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseConfig;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.Environment;
import com.sleepycat.je.EnvironmentConfig;
import com.sleepycat.je.LockMode;
import com.sleepycat.je.OperationStatus;
import com.sleepycat.je.Transaction;

public class Test {
private static Environment env;
private static Database db;
private static long cacheSize = 1000 * 1024 * 1024;
private static String path = "./bdb/";

public static void main(String[] args) {
EnvironmentConfig envConfig = new EnvironmentConfig();
envConfig.setTransactional(true);
envConfig.setAllowCreate(true);
envConfig.setCacheSize(cacheSize);
try {
env = new Environment(new File(path), envConfig);
} catch (DatabaseException e) {

}
open("nuclear");

//写入
put("aa","aaa");

//取出
get("aa");

}

/**
* 写入作
* @author www.54chen.com
* @createTime 2010-2-20 上午11:04:43
*/
public static void put(String key, String value){
// open("nuclear");
byte[] theKey = key.getBytes();
byte[] theValue = value.getBytes();
OperationStatus status = null;
Transaction transaction = null;
Cursor cursor = null;
boolean succeeded = false;
try {
transaction = env.beginTransaction(null, null);
cursor = db.openCursor(transaction, null);
status = cursor.put(new DatabaseEntry(theKey), new DatabaseEntry(theValue));
if (status != OperationStatus.SUCCESS){
// TODO
}

succeeded = true;
} catch (DatabaseException e) {

} finally {
attemptClose(cursor);
if (succeeded)
attemptCommit(transaction);
else
attemptAbort(transaction);
} }

/**
* 取出作
* @author www.54chen.com
* @createTime 2010-2-20 上午11:04:51
*/
public static List<String> get(String key){
ArrayList<String> nuclearStorageValue = new ArrayList<String>();

DatabaseEntry queryKey = new DatabaseEntry();
DatabaseEntry value = new DatabaseEntry();
queryKey.setData(key.getBytes());
Cursor cursor = null;
try {
cursor = db.openCursor(null, null);
for(OperationStatus status = cursor.getSearchKey(queryKey, value, LockMode.READ_UNCOMMITTED); status == OperationStatus.SUCCESS; status = cursor.getNextDup(queryKey,value,LockMode.RMW)) {
nuclearStorageValue.add(value.getData().toString());
} } catch (DatabaseException e) {

} finally {
attemptClose(cursor);
} return nuclearStorageValue;
}

/**
* 打开DB
* @author www.54chen.com
* @createTime 2010-2-20 上午11:01:48
*/
private static void open(String dbName) {
DatabaseConfig dbConfig = new DatabaseConfig();
dbConfig.setAllowCreate(true);
dbConfig.setTransactional(true);
try {
db = env.openDatabase(null, dbName, dbConfig);
} catch (DatabaseException e) {
e.printStackTrace();
} }

private static void attemptClose(Cursor cursor) {
try {
if (cursor != null)
cursor.close();
} catch (DatabaseException e) {
// TODO logger
} }

private static void attemptCommit(Transaction transaction) {
try {
transaction.commit();
} catch (DatabaseException e) {
attemptAbort(transaction);
// TODO logger
} }

private static void attemptAbort(Transaction transaction) {
try {
transaction.abort();
} catch (Exception e) {
// TODO logger
} }
}

============================================代码结束==================================

代码是用游标作bdb的例子,在使用的时候可以在其基础上继续进一步修改。

PS:人人网Nuclear系统的BDB引擎将可能会使用这样的作方法,目前我还在做速度上的调优,待有新的结果将第一时间公布给大家。

解读PHP开源项目中列表和hook方法:while(has_items()): Thme_ite();和apply_filters

wordpress,lilina等开源项目中,常常会看到一些莫名的代码,来无影去无踪,看上去很有意味,特地拿来分析分析,看看一般的作法。

part 1  列表 循环显示

先看代码:

<?php
$num = 0;
if(has_items()) {
while(has_items()): the_item();
} ?>
以上代码,出自某开源项目。。。

再进入the_item()函数内部,可以看到一个关键的函数:

* Returns the current item
* * @since 1.0
* * @return bool|stdClass False if item doesn't exist, otherwise returns the specified item
*/
public function current_item() {
$this->previous_item = $this->current_item;
$this->current_item = '';

$item = each($this->items);
$item = $item['value'];
if(!$item)
return false;

$this->current_item = $item;
$this->current_feed = $item->feed;

return $item;
} 这个关键的函数是each(),来看手册:

each

(PHP 3, PHP 4, PHP 5)

each --  返回数组中当前的键/值对并将数组指针向前移动一步

说明

array each ( array &array )

返回 array 数组中当前指针位置的键/值对并向前移动数组指针。键值对被返回为四个单元的数组,键名为 01keyvalue。单元 0key 包含有数组单元的键名,1value 包含有数据。

如果内部指针越过了数组的末端,则 each() 返回 FALSE

综合来看,其实就是一个做了一个游标一样的模型,利用each来作一个数组或者相似的结构,每次在显示的时候,都有对数据数组的本身作,而while里的has_item,只是作简单的判断有无而已。

part2 hook

第二种情况,是一个叫apply_filters的函数的实现。先看代码:

function the_item() {
global $lilina_items, $item;

$item = apply_filters('the_item', $lilina_items->current_item());
} 这是lilina里的一个函数,要看的是这个apply_filters方法,有一个简单的例子:

function example_hook($string, $arg1, $arg2)
//Do stuff
return $string;
} $value = apply_filters('example_hook', 'filter me', 'arg1', 'arg2');
apply_filters里面最关键的一个函数:

call_user_func_array

(PHP 4 >= 4.0.4, PHP 5)

call_user_func_array --  Call a user function given with an array of parameters

Description

mixed call_user_func_array ( callback function, array param_arr )

Call a user defined function given by function, with the parameters in param_arr.

其实这些渐渐被遗忘的函数,在高手们的使用下,对项目化的开发很有优势,随手记之。

Java线程控制器代码分享-根据cpu情况决定线程运行数量和情况

人人网海量存储系统的存储引擎部分,为了提高CPU和网络的使用情况,使用了java多线程管理并行作的方式。

在java中控制线程是一件很简单的事情,jdk提供了诸多的方法,其中比常用的两个是notify()和wait(),一个是唤醒,一个等待线程,在下面的代码中,将看到一个线程分配器,根据cpu的负载情况,自动完成对应线程的唤醒或者是等待作。整个过程是一个平滑的过程,不会因为线程的切换而导致机器负载出线锯齿。

先看一个类,读取Linux系统TOP等指令拿到系统当前负载:

import java.io.BufferedReader;
import java.io.InputStreamReader;

/**
* 节点的cpu 内存 磁盘空间 情况
* * @author zhen.chen
* */
public class NodeLoadView {

/**
* 获取cpu使用情况
* * @return
* @throws Exception
*/
public double getCpuUsage() throws Exception {
double cpuUsed = 0;

Runtime rt = Runtime.getRuntime();
Process p = rt.exec("/usr/bin/uptime");// 调用系统的“top"命令
String[] strArray = null;
BufferedReader in = null;
try {
in = new BufferedReader(new InputStreamReader(p.getInputStream()));
String str = null;
while ((str = in.readLine()) != null) {
strArray = str.split("load average: ");
strArray = strArray[1].split(",");
cpuUsed = Double.parseDouble(strArray[0]);
} } catch (Exception e) {
e.printStackTrace();
} finally {
in.close();
} return cpuUsed;
}

/**
* 内存监控
* * @return
* @throws Exception
*/
public double getMemUsage() throws Exception {

double menUsed = 0;
Runtime rt = Runtime.getRuntime();
Process p = rt.exec("top -b -n 1");// 调用系统的“top"命令

BufferedReader in = null;
try {
in = new BufferedReader(new InputStreamReader(p.getInputStream()));
String str = null;
String[] strArray = null;

while ((str = in.readLine()) != null) {
int m = 0;

if (str.indexOf(" R ") != -1) {// 只分析正在运行的进程,top进程本身除外 &&
//
// System.out.println("------------------3-----------------");
strArray = str.split(" ");
for (String tmp : strArray) {
if (tmp.trim().length() == 0)
continue;

if (++m == 10) {
// 9)--第10列为mem的使用百分比(RedHat 9)

menUsed += Double.parseDouble(tmp);

}
}

}
} } catch (Exception e) {
e.printStackTrace();
} finally {
in.close();
} return menUsed;
}

/**
* 获取磁盘空间大小
* * @return
* @throws Exception
*/
public double getDeskUsage() throws Exception {
double totalHD = 0;
double usedHD = 0;
Runtime rt = Runtime.getRuntime();
Process p = rt.exec("df -hl");// df -hl 查看硬盘空间

BufferedReader in = null;
try {
in = new BufferedReader(new InputStreamReader(p.getInputStream()));
String str = null;
String[] strArray = null;
while ((str = in.readLine()) != null) {
int m = 0;
// if (flag > 0) {
// flag++;
strArray = str.split(" ");
for (String tmp : strArray) {
if (tmp.trim().length() == 0)
continue;
++m;
// System.out.println("----tmp----" + tmp);
if (tmp.indexOf("G") != -1) {
if (m == 2) {
// System.out.println("---G----" + tmp);
if (!tmp.equals("") && !tmp.equals("0"))
totalHD += Double.parseDouble(tmp.substring(0,
tmp.length() - 1)) * 1024;

}
if (m == 3) {
// System.out.println("---G----" + tmp);
if (!tmp.equals("none") && !tmp.equals("0"))
usedHD += Double.parseDouble(tmp.substring(0,
tmp.length() - 1)) * 1024;

}
} if (tmp.indexOf("M") != -1) {
if (m == 2) {
// System.out.println("---M---" + tmp);
if (!tmp.equals("") && !tmp.equals("0"))
totalHD += Double.parseDouble(tmp.substring(0,
tmp.length() - 1));

}
if (m == 3) {
// System.out.println("---M---" + tmp);
if (!tmp.equals("none") && !tmp.equals("0"))
usedHD += Double.parseDouble(tmp.substring(0,
tmp.length() - 1));
// System.out.println("----3----" + usedHD);
} }

}

// }
} } catch (Exception e) {
e.printStackTrace();
} finally {
in.close();
} return (usedHD / totalHD) * 100;
} //
//    public static void main(String[] args) throws Exception {
//        NodeLoadView cpu = new NodeLoadView();
//        System.out
//                .println("---------------cpu used:" + cpu.getCpuUsage() + "%");
//        System.out
//                .println("---------------mem used:" + cpu.getMemUsage() + "%");
//        System.out
//                .println("---------------HD used:" + cpu.getDeskUsage() + "%");
//        System.out.println("------------jvm监控----------------------");
//        Runtime lRuntime = Runtime.getRuntime();
//        System.out.println("--------------Free Momery:" + lRuntime.freeMemory()
//                + "K");
//        System.out.println("--------------Max Momery:" + lRuntime.maxMemory()
//                + "K");
//        System.out.println("--------------Total Momery:"
//                + lRuntime.totalMemory() + "K");
//        System.out.println("---------------Available Processors :"
//                + lRuntime.availableProcessors());
//    }
再来看关键的一个类,THreadScheduler:

import java.util.Map;

import org.apache.log4j.Logger;

import test.NodeLoadView;
public class ThreadScheduler {
private static Logger logger = Logger.getLogger(ThreadScheduler.class.getName());
private Map<String, Thread> runningThreadMap;
private Map<String, Thread> waitingThreadMap;
private boolean isFinished = false;
private int runningSize;

public ThreadScheduler (Map<String, Thread> runningThreadMap, Map<String, Thread> waitingThreadMap) {
this.runningThreadMap = runningThreadMap;
this.waitingThreadMap = waitingThreadMap;
this.runningSize = waitingThreadMap.size();
}

/**
* 开始调度线程
* @author zhen.chen
* @createTime 2010-1-28 上午11:04:52
*/
public void schedule(){
long sleepMilliSecond = 1 * 1000;
int allowRunThreads = 15;
// 一次启动的线程数,cpuLoad变大时以此值为参考递减
int allowRunThreadsRef = 15;
double cpuLoad = 0;// 0-15
NodeLoadView load = new NodeLoadView();

while (true) {
try {
cpuLoad = load.getCpuUsage();
} catch (Exception e1) {
e1.printStackTrace();
} // cpuLoad低 启动的线程多
allowRunThreads = (int) Math.floor(allowRunThreadsRef - cpuLoad);
// threads不能为0
if (allowRunThreads < 1) {
allowRunThreads = 1;
} if (allowRunThreads > allowRunThreadsRef) {
allowRunThreads = allowRunThreadsRef;
} if (logger.isDebugEnabled()) {
logger.debug("[ThreadScheduler]running Thread:" + runningThreadMap.size() + "; waiting Thread:" + waitingThreadMap.size() + "; cpu:" + cpuLoad + " allowRunThreads:" + allowRunThreads);
}

// 检查runningSize个线程的情况,满足条件则启动
for (int x = 0; x < runningSize; x++) {
if (waitingThreadMap.get(x+"") != null) {
if (allowRunThreadsRef <= runningThreadMap.size()) {
break;
} synchronized (waitingThreadMap.get(x+"")) {
if (!waitingThreadMap.get(x+"").isAlive()) {
waitingThreadMap.get(x+"").start();
}else{
waitingThreadMap.get(x+"").notify();
} }
runningThreadMap.put(x+"", waitingThreadMap.get(x+""));
waitingThreadMap.remove(x+"");
} }
// 检查runningSize个线程的情况,满足条件则暂停
for (int x = 0; x < runningSize; x++) {
if (runningThreadMap.size() <= allowRunThreads) {
break;
} if (runningThreadMap.get(x+"") != null) {
synchronized (runningThreadMap.get(x+"")) {
try {
if (runningThreadMap.get(x+"").isAlive()) {
runningThreadMap.get(x+"").wait();
}else{
continue;
} } catch (InterruptedException e) {
e.printStackTrace();
} }
waitingThreadMap.put(x+"", runningThreadMap.get(x));
runningThreadMap.remove(x+"");
} }
// 全部跑完,返回
if (waitingThreadMap.size() == 0 && runningThreadMap.size() == 0) {
if (logger.isDebugEnabled()) {
logger.debug("[ThreadScheduler] over.total Threads size:" + runningSize);
this.isFinished = true;
return;
} // 使主while循环慢一点
try {
Thread.sleep(sleepMilliSecond);
} catch (InterruptedException e1) {
e1.printStackTrace();
} }

}

public boolean isFinished() {
return isFinished;
} } 这个类的作用:

1.接收runningThreadMap和waitingThreadMap两个map,里面对应存了运行中的线程实例和等待中的线程实例。

2.读cpu情况,自动判断要notify等待中的线程还是wait运行中的线程。

3.两个map都结束,退出。(必须runningThreadMap内部的Thread自己将runningThreadMap对应的Thread remove掉)

如何使用:

public class TestThread {
public static class Runner extends Thread {
public Runner(int j, Map<String, Thread> threadMap) {

}
public void run() {
// TODO 你的逻辑 完成后需要从threadMap中remove掉
} }

public static void main(String[] args) {
// 运行中的线程
Map<String, Thread> threadMap = new HashMap<String, Thread>();
// 正在等待中的线程
Map<String, Thread> waitThreadMap = new HashMap<String, Thread>();
for (int j = 0; j < args.length; j++) {
Thread t = new Runner(j, threadMap);
waitThreadMap.put(j + "", t);
}

ThreadScheduler threadScheduler = new ThreadScheduler(threadMap, waitThreadMap);
threadScheduler.schedule();
if (threadScheduler.isFinished() == false) {
//没能正常结束
} }
}