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 条评论

php7嵌入式开发–重新创建cli

php7嵌入式开发–环境配置基础上,我们新建个embed1.c,代码如下:

#include <stdio.h>
#include <sapi/embed/php_embed.h>
int main(int argc, char *argv[]) {
    zend_file_handle    script;
    if ( argc <= 1 ) {
        fprintf(stderr, "Usage: %s <filename.php> <arguments>\n", argv[0]);
        return -1;
    }
    

    script.type             = ZEND_HANDLE_FP;  
    script.filename         = argv[1];  
    script.opened_path      = NULL;  
    script.free_filename    = 0;  

    if ( !(script.handle.fp = fopen(script.filename, "rb")) ) {
        fprintf(stderr, "Unable to open: %s\n", argv[1]);
        return -1;
    }
    argc --;
    argv ++;
    PHP_EMBED_START_BLOCK(argc, argv)
        php_execute_script(&script TSRMLS_CC);
    PHP_EMBED_END_BLOCK()
    return 0;
}

编译命令如下:

gcc -I /usr/local/include/php/ -I /usr/local/include/php/main/ -I /usr/local/include/php/Zend/ -I /usr/local/include/php/TSRM/ -lphp7 -o embed1 embed1.c

执行如下测试

这样,我们就可以通过embed1执行PHP源代码了,相当于php下的cli.

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

php7嵌入式开发–环境配置

1:我们先生成libphp7.so库.在编译的时候加上–enable-embed

wget http://cn2.php.net/distributions/php-7.0.11.tar.gz
tar -zxvf php-7.0.11.tar.gz
cd php-7.0.11
configure --enable-embed
make
make install

生成的libphp7.so文件应该在/usr/local/lib/目录下,需要的头文件应该在 /usr/local/include/php目录下.

2:编写C文件embed.c

#include "sapi/embed/php_embed.h"
 
int main(int argc, char * argv[]){
    PHP_EMBED_START_BLOCK(argc,argv);
    char * script = " print 'Hello World!';";
    zend_eval_string(script, NULL,"Simple Hello World App" TSRMLS_CC);
    PHP_EMBED_END_BLOCK();
    return 0;
}

3:编译生成目标程序

gcc -I /usr/local/include/php/ -I /usr/local/include/php/main/ -I /usr/local/include/php/Zend/ -I /usr/local/include/php/TSRM/ -lphp7 -o embed embed.c

4:运行程序

现在我们大概可以了解php7的嵌入式开发了

发表在 好文推荐, 网站架构 | 标签为 , | 19 条评论

lsof 一切皆文件

lsof(list open files)是一个查看当前系统文件的工具。在linux环境下,任何事物都以文件的形式存在,通过文件不仅仅可以访问常规数据,还可以访问网络连接和硬件。如传输控制协议 (TCP) 和用户数据报协议 (UDP) 套接字等,系统在后台都为该应用程序分配了一个文件描述符,该文件描述符提供了大量关于这个应用程序本身的信息.

lsof打开的文件可以是:
    普通文件
    目录
    网络文件系统的文件
    字符或设备文件
    (函数)共享库
    管道,命名管道
    符号链接
    网络文件(例如:NFS file、网络socket,unix域名socket)
    还有其它类型的文件,等等

命令示例

查找打开某个文件的进程
sof /usr/local/php7/var/log/php-fpm.log

列出某个用户打开的文件信息
lsof -u nginx

列出某个程序进程所打开的文件信息
lsof -c php

通过某个进程号显示该进程打开的文件
lsof -p 1234

列出所有的网络连接
lsof -i

列出所有tcp 网络连接信息
lsof -i tcp

列出谁在使用某个端口
lsof -i :3306

列出某个用户的所有活跃的网络端口
lsof -a -u nginx -i

记录到此,方便以后查询!

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

lex和yacc学习1

Lex(Lexical Analyzar 词法分析生成器),Yacc(Yet Another Compiler Compiler编译器代码生成器)是Unix下十分重要的词法分析,语法分析的工具。经常用于语言分析,公式编译等广泛领域,详细内容可参考http://dinosaur.compilertools.net/.
先来看最简单的例子(xtgxiso.l)

%{
#include "stdio.h"
%}
%%
[/n]                  ;
[0-9]+                printf("Int:%s/n",yytext);
.                     printf("Unknown:%c/n",yytext[0]);
%%

执行如下命令

到此我们会对lex有个直观的用法了解
1.定义Lex描述文件
2.通过lex工具解析成lex.yy.c文件
3.使用cc编译lex.yy.c生成可执行程序

现在我们来写一个相对比较完整的例子

%{
#include "stdio.h"
%}
%%
[/n]                  ;
[0-9]+                printf("Int:%s/n",yytext);
.                     printf("Unknown:%c/n",yytext[0]);
%%

int main()
{
    yylex();
    return 0;
}

int yywrap()
{
    return 1;
}

执行如下命令

这次编译没有加ll选项,那是因为加了main函数.

现在我们来大致了解lex的描述文件结构,
一般可以分为<定义部分><规则部><用户子程序部分>。其中规则部分是必须的,定义和用户子程序部分是任选的

1:定义部分起始于 %{ 符号,终止于 %} 符号,其间可以是包括include语句、声明语句在内的C语句。这部分跟普通C程序开头没什么区别

2:规则部分起始于”%%”符号,终止于”%%”符号,其间则是词法规则。词法规则由模式和动作两部分组成。模式部分可以由任意的正则表达式组成,动作部分是由C语言语句组成,这些语句用来对所匹配的模式进行相应处理。需要注意的是,lex将识别出来的单词存放在yytext[]字符数据中,因此该数组的内容就代表了所识别出来的单词的内容.

3:用户子程序部分,可以包含用C语言编写的子程序,而这些子程序可以用在前面的动作中,这样就可以达到简化编程的目的

Lex内部变量和函数

yytext char * 当前匹配的字符串

yyleng int 当前匹配的字符串长度

yyin FILE * lex当前的解析文件,默认为标准输出

yyout FILE * lex解析后的输出文件,默认为标准输入

yylineno int 当前的行数信息

ECHO #define ECHO fwrite(yytext, yyleng, 1, yyout) 也是未匹配字符的默认动作

int yylex(void) 调用Lex进行词法分析

int yywrap(void) 在文件(或输入)的末尾调用。如果函数的返回值是1,就停止解析

Lex其实就是词法分析器,通过配置文件*.l,依据正则表达式逐字符去顺序解析文件,并动态更新内存的数据解析状态。不过Lex只有状态和状态转换能力。因为它没有堆栈,它不适合用于剖析外壳结构。而yacc增加了一个堆栈,并且能够轻易处理像括号这样的结构。Lex善长于模式匹配,如果有更多的运算要求就需要yacc了.

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

swoole2.0试用说明

    Swoole在2.0开始内置协程的能力,提供了具备协程能力IO接口.最好的好处是开发者可以以同步编码的方式达到异步IO的效果.

    Swoole2.0安装要求

  •      php版本要求:>= 5.5, 暂不支持PHP7
  •      基于swoole_server或者swoole_http_server进行开发,目前支持在onRequet, onReceive, onConnect回调中使用协程

    安装过程

  1. 先安装第三方的异步Redis库https://github.com/redis/hiredis   以支持redis操作
  2. 启用–enable-async-redis –enable-coroutine    

    安装常见问题

      1.找不到libhiredis.so.0.13 ,确保安装了hiredis前提下.增加/etc/ld.so.conf.d/lib.conf文件。内容”/usr/local/lib”.然后运行ldconfig命令

      2.gcc 4.4下如果在编译swoole的时候(即make阶段),出现gcc warning

        dereferencing pointer ‘v.327’ does break strict-aliasing rules、dereferencing type-punned pointer will break strict-aliasing rules

        请手动编辑Makefile,将CFLAGS = -Wall -pthread -g -O2替换为CFLAGS = -Wall -pthread -g -O2 -fno-strict-aliasing,

        然后重新编译make clean;make;make install

    安装完成后,请用php -m 来确认swoole是否安装成功.

    第一个代码示例.

<?php

$serv = new swoole\http\server("0.0.0.0", 1215);

$serv->set([
        'worker_num' => 1,
]);

$serv->on('Request', function($request, $response) {
    $response->header("X-Server", "Swoole");
    $cli = new Swoole\Coroutine\Http\Client('127.0.0.1', 80);
    $cli->setHeaders([
        'Host' => "test.xtgxiso.cn",
        "User-Agent" => 'Chrome/49.0.2587.3',
        'Accept' => 'text/html,application/xhtml+xml,application/xml',
        'Accept-Encoding' => 'gzip',
    ]);
    $cli->set([ 'timeout' => 2]);
    $cli->get('/sleep1.php');
    $str =  $cli->body;
    $cli->close();
 
    $response->end("<h1>Hello Swoole!</h1>".$str);
});

$serv->start();

    代码大致的功能是,以一个进程启动一个http server,访问一个url.这个url会sleep(1)

 模拟并发

<script src="http://123.56.145.253:1215/?id=1"></script>
<script src="http://123.56.145.253:1215/?id=2"></script>
<script src="http://123.56.145.253:1215/?id=3"></script>
<script src="http://123.56.145.253:1215/?id=4"></script>
<script src="http://123.56.145.253:1215/?id=5"></script>
<?php
echo "123";

   访问的效果

通过这个示例,我们已经可以明白swoole2.0的好处了。原来为了实现高并发,用的是IO复用,是各种回调。现在内置了协程,实现了高性能的同时代码看起来也是同步的!期待swoole2.0越来越强大.

   

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