Facebook的hhvm的安装和使用

HHVM是Facebook推出的用来执行PHP代码的虚拟机,它是一个PHP的JIT(Just-In-Time)编译器,同时具有产生快速代码和即时编译的优点.

首先我们在centos7下安装好hhvm的环境

yum update -y

yum install -y epel-release

yum install cpp gcc-c++ cmake3 git psmisc {binutils,boost,jemalloc,numactl}-devel \
{ImageMagick,sqlite,tbb,bzip2,openldap,readline,elfutils-libelf,gmp,lz4,pcre}-devel \
lib{xslt,event,yaml,vpx,png,zip,icu,mcrypt,memcached,cap,dwarf}-devel \
{unixODBC,expat,mariadb}-devel lib{edit,curl,xml2,xslt}-devel \
glog-devel oniguruma-devel ocaml gperf enca libjpeg-turbo-devel openssl-devel \
mariadb mariadb-server {fastlz,double-conversion,re2}-devel make -y

yum install {fribidi,libc-client,glib2}-devel -y

git clone https://github.com/facebook/hhvm -b master  hhvm  --recursive

cd hhvm

cmake3 .

make -j$(($(nproc)+1))

./hphp/hhvm/hhvm --version

make install

hhvm --version

增加hhvm的配置文件

echo "date.timezone="America/New_York"" > /etc/hhvm/php.ini

/etc/hhvm/server.ini文件内容如下

pid = /var/run/hhvm/pid

hhvm.server.ip = 0.0.0.0
hhvm.server.port = 1215
hhvm.server.type = proxygen
hhvm.server.default_document = index.php
hhvm.server.source_root = /alidata/xtgxiso/hhvm
hhvm.server.thread_count = 2
hhvm.resource_limit.max_socket = 65536
hhvm.hack.lang.look_for_typechecker = 0

hhvm.jit = true
hhvm.jit_a_size = 67108864
hhvm.jit_a_stubs_size = 22554432
hhvm.jit_global_data_size = 22554432

;logging
hhvm.log.use_syslog = false
hhvm.log.use_log_file = true
hhvm.log.file = /var/log/hhvm/error.log
hhvm.log.level = Warning
hhvm.log.always_log_unhandled_exceptions = true
hhvm.log.runtime_error_reporting_level = 8191
hhvm.repo.central.path = /var/cache/hhvm/hhvm.hhbc

hhvm.php7.all = 1

通过以上命令,我们基本完成了hhvm的安装和配置,接下来我们将hhvm做成centos下的系统服务。

touch /usr/lib/systemd/system/hhvm.service

文件内容如下:

[Unit]
Description=HipHop Virtual Machine

[Service]
ExecStart=/usr/local/bin/hhvm -c /etc/hhvm/server.ini -c /etc/hhvm/php.ini --user hhvm --mode daemon

[Install]
WantedBy=multi-user.target

然后我们就可以通过如下命令来测试服务是否配置成功

systemctl status hhvm
systemctl stop hhvm
systemctl start hhvm
systemctl restart hhvm

最后我们服务启动后,在/alidata/xtgxiso/hhvm/新建个index.php文件来测试是否成功.

<?php
echo phpinfo();

测试访问地址是:http://127.0.0.1:1215/index.php,如果能访问成功,说明安装并配置成功了.

发表在 好文推荐, 网站开发 | 标签为 | 26 条评论

golang多核的使用

对于多核编程,go是天生支持,那么我们在什么情况下应该用多核心来加速程序,而在什么情况下用单核即可呢?

现在我们用一简单的程序来说明下:

package main

import (
        "runtime"
        "fmt"
        "sync"
        "database/sql"
        _ "github.com/go-sql-driver/mysql"
	"time"
)

//定义任务队列
var waitgroup sync.WaitGroup

func xtgxiso(num int) {
        //fmt.Println(num)
        db, err := sql.Open("mysql", "root:123456@tcp(127.0.0.1:3306)/test?charset=utf8")
        if err != nil {
                fmt.Println(err)
        }
        defer db.Close()
        rows, err := db.Query("select sleep(1) as a")
        if err != nil {
                fmt.Println(err)
        }
        defer rows.Close()
        var a string
        for rows.Next() {
                err = rows.Scan(&a)
                if err != nil {
                        fmt.Println(err)
                } else {
                        //fmt.Println(a)
                }
        }
        waitgroup.Done() //任务完成,将任务队列中的任务数量-1,其实.Done就是.Add(-1)
}

func main() {
	//记录开始时间
	start := time.Now()
        //设置最大的可同时使用线程数
        runtime.GOMAXPROCS(1)
        for i := 1; i <= 10; i++ {
                waitgroup.Add(1) //每创建一个goroutine,就把任务队列中任务的数量+1
                go xtgxiso(i)
        }
        waitgroup.Wait() //Wait()这里会发生阻塞,直到队列中所有的任务结束就会解除阻塞
	//记录结束时间
	end :=  time.Now()
	//输出执行时间,单位为秒。
	fmt.Println(end.Sub(start).Seconds())
}

这个程序是执行十次”select sleep(1) as a“.如果是顺序阻塞执行的话,执行时间肯定是10s以上,而我们用的协程不会有这种情况。

我们可以修改“runtime.GOMAXPROCS(1)”来设置最大可同时执行的线程数,对比结果发现,都是1s多点,有时多线程反而会比单线程慢些,这是为什么呢?

这是因为这个程序是IO为主的,启用多线程反而有上下文切换,所以对于以涉及IO操作的主的程序启用多线程对于加速程序意义不大,

runtime.GOMAXPROCS(1) 保证了mysql的操作(golang底层走的是非阻塞io)都在一个线程上执行,也就没有了cpu在多个线程之间来回调度

那么什么程序启用多线程呢?我们来看如下程序:

package main

import (
        "runtime"
        "fmt"
        "sync"
	"time"
)

//定义任务队列
var waitgroup sync.WaitGroup

func xtgxiso(num int) {
      	for i:=1;i<=1000000000;i++{
		num = num+i
		num = num-i
		num = num*i
		num = num/i
	}
        waitgroup.Done() //任务完成,将任务队列中的任务数量-1,其实.Done就是.Add(-1)
}

func main() {
	//记录开始时间
	start := time.Now()
        //设置最大的可同时使用的线程数
        runtime.GOMAXPROCS(1)
        for i := 1; i <= 10; i++ {
                waitgroup.Add(1) //每创建一个goroutine,就把任务队列中任务的数量+1
                go xtgxiso(i)
        }
        waitgroup.Wait() //Wait()这里会发生阻塞,直到队列中所有的任务结束就会解除阻塞 线//记录结束时间
	end :=  time.Now()
	//输出执行时间,单位为秒。
	fmt.Println(end.Sub(start).Seconds())
}

对比结果发现,多线程比单线程快,所以对于CPU的运行上,多线程运行加速效果是很明显的.

发表在 网站开发 | 标签为 | 27 条评论

使用sync.WaitGroup来同步golang程序

golang中有2种方式同步程序,一种使用channel,另一种就是sync.WaitGroup,今天我们介绍比较简单的sync.WaitGroup。

sync.WaitGroup只有3个方法,Add(),Done(),Wait()。其中Done()是Add(-1)的别名。简单的来说,使用Add()添加计数,Done()减掉一个计数,计数不为0, 阻塞Wait()的运行。
要注意的有一点。sync文档已经说明了的,在运行main函数的goroutine里运行Add()函数,在其他的goroutine里面运行Done()函数。

代码示例如下:

package main

import (
	"runtime"
	"time"
	"fmt"
	"sync"
)

//定义任务队列
var waitgroup sync.WaitGroup

func xtgxiso(num int) {
	fmt.Println(num)
	time.Sleep(1*time.Second)
	waitgroup.Done() //任务完成,将任务队列中的任务数量-1,其实.Done就是.Add(-1)
}

func main() {
	//设置最大的可同时使用的CPU核数和实际cpu核数一致
	runtime.GOMAXPROCS(runtime.NumCPU())
	for i := 0; i < 10; i++ {
		waitgroup.Add(1) //每创建一个goroutine,就把任务队列中任务的数量+1
		go xtgxiso(i)
	}
	waitgroup.Wait() //Wait()这里会发生阻塞,直到队列中所有的任务结束就会解除阻塞
	fmt.Println("finish")
}

这样我们就完成了一个简单的程序,用runtime.GOMAXPROCS(runtime.NumCPU())来控制最大协程数量,用waitgroup.Wait()来等待所有子协程完成后主协程才退出!

发表在 网站开发, 网站架构 | 标签为 , | 26 条评论

php调用go的json协议的rpc服务

我们用php代码来调用go的json协议的rpc服务。

go的服务代码:

// json_rpc_server project main.go
package main

import (
	"net/rpc"
	"net"
	"log"
	"net/rpc/jsonrpc"
)

//自己的数据类
type MyMath struct{
	
}

//加法--只能两个参数
func (mm *MyMath) Add(num map[string]float64,reply *float64) error {
    *reply = num["num1"] + num["num2"]
    return nil
}

//减法--只能两个参数
func (mm *MyMath) Sub(num map[string]string,reply *string) error {
    *reply = num["num1"] + num["num2"]
    return nil
}

func main() {
	//注册MyMath类,以代客户端调用
    rpc.Register(new(MyMath))
    listener, err := net.Listen("tcp", ":1215")
    if err != nil {
        log.Fatal("listen error:", err)
    }
    for {
        conn, err := listener.Accept()
        if err != nil {
            continue
        }
		//新协程来处理--json
        go jsonrpc.ServeConn(conn)
    }
}

php调用代码:

<?php
class JsonRPC
{
    private $conn;

    function __construct($host, $port) {
        $this->conn = fsockopen($host, $port, $errno, $errstr, 3);
        if (!$this->conn) {
            return false;
        }
    }

    public function Call($method, $params) {
        if ( !$this->conn ) {
            return false;
        }
        $err = fwrite($this->conn, json_encode(array(
                'method' => $method,
                'params' => array($params),
                'id'     => 0,
            ))."\n");
        if ($err === false)
            return false;
        stream_set_timeout($this->conn, 0, 3000);
        $line = fgets($this->conn);
        if ($line === false) {
            return NULL;
        }
        return json_decode($line,true);
    }
}

$client = new JsonRPC("127.0.0.1", 1215);
$r = $client->Call("MyMath.Add",array('num1'=>1,'num2'=>2));
var_export($r);
echo "<br/>";
$r = $client->Call("MyMath.Sub",array('num1'=>'1','num2'=>'2'));
var_dump($r);

这样我们就可以在php与go之间调用了

发表在 网站开发 | 标签为 , , | 25 条评论

golang利用反射调用类方法续

上一篇只是动态调用方法,这次我们得用反射动态获取方法和每个方法的参数.代码举例如下:

package main

import (
	"fmt"
	"reflect"
	"strconv"
)

type MyMath struct{
	a int
}


func (mm *MyMath) Add(num1 float64,num2 float64 ) float64 {
    reply := num1+num2 
	return reply
}


func (mm *MyMath) Sub(num1 float64,num2 float64 ) float64 {
    reply := num1-num2 
	return reply
}


func main() {
	m := new(MyMath)
	typ := reflect.TypeOf(m)
	//遍历方法
	for i := 0; i < typ.NumMethod(); i++ {
		method := typ.Method(i)
		mname := method.Name//方法名字
		fmt.Println("method:"+mname)
		fun := reflect.ValueOf(m).MethodByName(mname)
		ty := method.Type
		args := make([]reflect.Value, ty.NumIn()-1)
		//遍历参数
		for j:= 1; j< ty.NumIn(); j++ {
			//参数类型
			arg_type := ty.In(j).Kind().String()
			fmt.Println("参数" + strconv.Itoa(j) + ":" + arg_type)
			switch arg_type {
				case "float64":
					args[j-1] = reflect.ValueOf(float64(j))
				default:
					fmt.Println("i down'n knowon type " + arg_type)
				
			}
		}
		ret := fun.Call(args)
		fmt.Println(ret[0])
	}
	
}

这样,我们以后就可以知道一个类下有多少个方法,每个方法下有多少个参数且类型是什么,方便我们与其他弱语言相互调用的时候来处理类型!

发表在 网站开发 | 标签为 | 23 条评论

golang利用反射调用类方法

以前不知反射的用法,那是一直在写弱语言,现在用了go之后才知道反射的用途之一就是动态调用.

php动态调用代码一般是这样的:

?php
class MyMath{

    public function Add($num1,$num2){
        return $num1+$num2;
    }
}
$class_name = "MyMath";
$method_name = "Add";

$class = new $class_name();
$num = call_user_func(array($class,$method_name),1,2);
var_dump($num);

而在go中是不行的,应该是利用反射,代码举例如下:

package main

import (
	"fmt"
	"reflect"
)

//自己的数据类
type MyMath struct{
	a int
}

//加法
func (mm *MyMath) Add(num1 float64,num2 float64 ) float64 {
    reply := num1+num2 
	return reply
}


func main() {
	m := new(MyMath)
	add := reflect.ValueOf(m).MethodByName("Add")
	args := make([]reflect.Value, 2)
	args[0] = reflect.ValueOf(1.0)
	args[1] = reflect.ValueOf(2.0)
	ret := add.Call(args)
	fmt.Println(ret[0])
}

现在我们知道在go中大致反射是怎么用了.

发表在 网站开发 | 标签为 | 25 条评论

golang版本的json协议的rpc服务

前面两篇文章,数据传输都是用的gob编码,这次我们以json来举例.

服务端代码 :

package main

import (
	"net/rpc"
	"net"
	"log"
	"net/rpc/jsonrpc"
)

//自己的数据类
type MyMath struct{
	
}

//加法--只能两个参数
func (mm *MyMath) Add(num map[string]int,reply *int) error {
    *reply = num["num1"] + num["num2"]
    return nil
}

//减法--只能两个参数
func (mm *MyMath) Sub(num map[string]int,reply *int) error {
    *reply = num["num1"] - num["num2"]
    return nil
}

func main() {
	//注册MyMath类,以代客户端调用
    rpc.Register(new(MyMath))
    listener, err := net.Listen("tcp", ":1215")
    if err != nil {
        log.Fatal("listen error:", err)
    }
    for {
        conn, err := listener.Accept()
        if err != nil {
            continue
        }
		//新协程来处理--json
        go jsonrpc.ServeConn(conn)
    }
}

客户端代码:

package main

import (
	"fmt"
	"log"
	"net/rpc/jsonrpc"
)

func main() {
	//连接服务--json
    client, err := jsonrpc.Dial("tcp", "127.0.0.1:1215")
    if err != nil {
        log.Fatal("dialing:", err)
    }
    var reply int
	var num = make(map[string]int)
	num["num1"] = 3
	num["num2"] = 2
	//调用远程MyMath的Add方法,也只能是三个参数
    err = client.Call("MyMath.Add",num,&reply)
    if err != nil {
        log.Fatal("arith error:", err)
    }
	//输出结果
    fmt.Println(reply)
	//调用远程MyMath的Sub方法,也只能是三个参数
	err = client.Call("MyMath.Sub",num,&reply)
    if err != nil {
        log.Fatal("arith error:", err)
    }
	//输出结果
    fmt.Println(reply)
	client.Close()
}

这样,我们基本对go语言http,tcp,json三个协议的rpc服务有了一个基本的了解.

发表在 网站开发 | 标签为 , | 28 条评论

golang版本的tcp协议的rpc服务

服务端代码:

package main

import (
	"net/rpc"
	"net"
	"log"
)

//自己的数据类
type MyMath struct{
	
}

//加法--只能两个参数
func (mm *MyMath) Add(num map[string]int,reply *int) error {
    *reply = num["num1"] + num["num2"]
    return nil
}

//减法--只能两个参数
func (mm *MyMath) Sub(num map[string]int,reply *int) error {
    *reply = num["num1"] - num["num2"]
    return nil
}

func main() {
	//注册MyMath类,以代客户端调用
    rpc.Register(new(MyMath))
    listener, err := net.Listen("tcp", ":1215")
    if err != nil {
        log.Fatal("listen error:", err)
    }
    for {
        conn, err := listener.Accept()
        if err != nil {
            continue
        }
		//新协程来处理
        go rpc.ServeConn(conn)
    }
}

客户端代码:

package main

import (
	"net/rpc"
	"net"
	"log"
)

//自己的数据类
type MyMath struct{
	
}

//加法--只能两个参数
func (mm *MyMath) Add(num map[string]int,reply *int) error {
    *reply = num["num1"] + num["num2"]
    return nil
}

//减法--只能两个参数
func (mm *MyMath) Sub(num map[string]int,reply *int) error {
    *reply = num["num1"] - num["num2"]
    return nil
}

func main() {
	//注册MyMath类,以代客户端调用
    rpc.Register(new(MyMath))
    listener, err := net.Listen("tcp", ":1215")
    if err != nil {
        log.Fatal("listen error:", err)
    }
    for {
        conn, err := listener.Accept()
        if err != nil {
            continue
        }
		//新协程来处理
        go rpc.ServeConn(conn)
    }
}

对比之现http协议的区别,服务端采用了TCP协议,然后需要自己控制连接,当有客户端连接上来后,我们需要把这个连接交给rpc来处理,而客户端唯一的区别一个是DialHTTP,一个是Dial(tcp),其他处理一模一样.

发表在 网站开发 | 标签为 , | 27 条评论

golang版本的http协议的rpc服务

Rpc就是一个远程调用协议,采用客户端调用服务端的方式来实现一些功能。现在我们以http协议来做一个rpc服务为例

package main

import (
	"net/rpc"
	"net"
	"log"
	"net/http"
)

//自己的数据类
type MyMath struct{
	
}

//加法--只能两个参数--方法名第一个字母必须大写
func (mm *MyMath) Add(num map[string]int,reply *int) error {
    *reply = num["num1"] + num["num2"]
    return nil
}

//减法--只能两个参数--方法名第一个字母必须大写写
func (mm *MyMath) Sub(num map[string]int,reply *int) error {
    *reply = num["num1"] - num["num2"]
    return nil
}

func main() {
	//注册MyMath类,以代客户端调用
    rpc.Register(new(MyMath))
    rpc.HandleHTTP()
    l, e := net.Listen("tcp", ":1215")
    if e != nil {
        log.Fatal("listen error:", e)
    }
    http.Serve(l, nil)
}

这样我们就完成了一个简单的http版本的rpc服务端,现在我们来看客户端怎么来调用上面的服务.

package main

import (
	"net/rpc"
	"fmt"
	"log"
	
)

func main() {
	//连接服务
    client, err := rpc.DialHTTP("tcp", "127.0.0.1:1215")
    if err != nil {
        log.Fatal("dialing:", err)
    }
    var reply int
	var num = make(map[string]int)
	num["num1"] = 3
	num["num2"] = 2
	//调用远程MyMath的Add方法,也只能是三个参数
    err = client.Call("MyMath.Add",num,&reply)
    if err != nil {
        log.Fatal("arith error:", err)
    }
	//输出结果
    fmt.Println(reply)
	//调用远程MyMath的Sub方法,也只能是三个参数
	err = client.Call("MyMath.Sub",num,&reply)
    if err != nil {
        log.Fatal("arith error:", err)
    }
	//输出结果
    fmt.Println(reply)
	client.Close()
}

以上我们就完成了一个http协议的rpc的实现和调用,后续我们会以tcp协议的来举例!

发表在 网站开发 | 标签为 , | 26 条评论

Golang中嵌入php

前面两章我们都是在C语言中做的嵌入,现在我们来做个golang中嵌入php的简单demo,原理是一样的.代码如下:

package main
// #cgo CFLAGS: -I /usr/local/include/php/ -I /usr/local/include/php/main/ -I /usr/local/include/php/Zend/ -I /usr/local/include/php/TSRM/ 
// #cgo LDFLAGS: -lphp5
// #include "sapi/embed/php_embed.h"
/*
int print() {
    PHP_EMBED_START_BLOCK(0,NULL);
    char * script = "echo 123;";
    zend_eval_string(script,NULL,"Simple Hello World App" TSRMLS_CC);
    PHP_EMBED_END_BLOCK();
    return 1;
}
*/
import "C"


func main() {
    C.print()
}

执行结果如下:

这只是最简单的demo,后续我们可以用go做http server,然后用php来处理请求!

发表在 网站开发, 网站架构 | 标签为 , | 22 条评论