首页
统计
留言
Search
1
PHP中使用反射
995 阅读
2
phpstorm配置SFTP
940 阅读
3
Go语言——结构体
792 阅读
4
PhpStorm 使用 AI 代码生成工具 Codeium
779 阅读
5
关于PHP的垃圾回收机制
763 阅读
后端
PHP
Go
数据库
其他
前端
其他技术
生活杂谈
登录
Search
标签搜索
Laravel
Mysql
RPC
Redis
Liunx
PHP
CSS
ES
算法
开发工具
断点续传
反射
phpstorm
工具
防盗链
CURL
设计模式
面试
Nginx
搜索引擎
quhe.net
首页
栏目
后端
PHP
Go
数据库
其他
前端
其他技术
生活杂谈
页面
统计
留言
搜索到
43
篇与
后端
的结果
2022-01-15
PHP 8.1 新特性:纤程 Fiber
介绍纤程(Fiber)代表了有完整栈、可中断的功能。Fiber 可以从调用堆栈中的任何位置挂起,在 fiber 内暂停执行,直到稍后恢复 fiber。类摘要final class Fiber { /* 方法 */ public __construct(callable $callback) public start(mixed ...$args): mixed public resume(mixed $value = null): mixed public throw(Throwable $exception): mixed public getReturn(): mixed public isStarted(): bool public isSuspended(): bool public isRunning(): bool public isTerminated(): bool public static suspend(mixed $value = null): mixed public static getCurrent(): ?Fiber }Fiber::__construct — 创建一个纤程实例Fiber::start — 开始执行纤程Fiber::resume — 使用值恢复一个纤程Fiber::throw — 抛出异常恢复纤程Fiber::getReturn — 获取纤程返回的值Fiber::isStarted — 判断纤程是否启动Fiber::isSuspended — 判断纤程是否暂停Fiber::isRunning — 判断纤程是否在运行Fiber::isTerminated — 确认纤程是否已中止Fiber::suspend — 暂停当前纤程Fiber::getCurrent —获取当前执行的纤程实例纤程概要(PHP 8 >= 8.1.0)纤程(Fiber)表示一组有完整栈、可中断的功能。 纤程可以在调用堆栈中的任何位置被挂起,在纤程内暂停执行,直到稍后恢复。纤程可以暂停整个执行栈,因此该函数的直接调用者不需要改变该函数的执行方式。可以在任何地方使用Fiber::suspend()调用栈中断执行(也就是说,Fiber::suspend()的调用可以在一个深度嵌套的函数中调用,即使它不存在)。不像没有栈的生成器,每个Fiber有它自己的调用栈,允许它们在深度嵌套函数调用中暂停。一个函数声明一个中断点(即, 调用Fiber::suspends())不需要改变他的返回类型,而不像使用 yield 的函数,必须返回Generator 实例。纤程可以在任何函数调用中暂停,包括那些在 PHP VM 中被调用的函数,比如用于array\_map()的函数或者提供 Iterator 实例以被 foreach 调用的方法。纤程一旦被暂停,可以使用 Fiber::resume() 传递任意值、或者使用 Fiber::throw() 向纤程抛出一个异常以恢复运行。这个值或者异常将会在 Fiber::suspend() 中被返回(抛出)。示例:<?php $fiber = new Fiber(function (): void { $value = Fiber::suspend('fiber'); echo "Value used to resume fiber: ", $value, PHP_EOL; }); $value = $fiber->start(); echo "Value from fiber suspending: ", $value, PHP_EOL; $fiber->resume('test'); ?>输出:Value from fiber suspending: fiber Value used to resume fiber: test
2022年01月15日
51 阅读
0 评论
0 点赞
2022-01-05
什么是RPC
什么是RPCRPC的中文是“远程过程调用”,对应的英文全称是:Remote Procedure Call,可以简单理解为一个节点请求另一个节点提供的服务。请先自行思考一下什么是“本地过程调用”,可以更好的理解“远程过程调用”。知识点:RPC主要依赖于客户端与服务端建立socket链接;而HTTP REST实现通讯的代价比较高,这是RPC的一个优势体现。为什么用RPC就是因为无法在同一个进程内,或者无法在同一个服务器上通过本地调用的方式实现我们的需求。HTTP能满足需求但是不够高效,所以我们需要使用RPC。 @知乎大神的回答RPC的优势RPC能够跨多种开发工具和平台RPC能够跨语言调用RPC能够提高系统的可扩展性,解耦,提高复用RPC相较于HTTP,传输效率更高,性能消耗更小,自带负载均衡策略,自动实现服务治理RPC和HTTP对比RPC主要用于公司内部的服务调用,性能消耗低,传输效率高,服务治理方便。HTTP主要用于对外的异构环境,浏览器接口调用,APP接口调用,第三方接口调用等。@RPC和HTTP的详细对别 可以看这篇文章,不作为本篇的重点RPC的使用边界通过和HTTP的对比,我们可以倒推出RPC的边界:对外的异构环境,浏览器接口调用,APP接口调用,第三方接口调用。上述这些都不适合RPC,不知道RPC不适合做什么,比知道RPC能做什么更重要。RPC入门1:net/rpc基本构成RPC的基本构成:服务端,客户端服务端基本构成:结构体,请求结构体,响应结构体客户端基本构成:请求结构体,响应结构体代码示例rpc_service.gopackage main import ( "errors" "fmt" "log" "net" "net/http" "net/rpc" "os" ) type Arith struct { } //请求结构体 type ArithRequest struct { A int B int } //响应结构体 type ArithResponse struct { Pro int //乘积 Quo int //商 Rem int //余数 } //乘积方法 func (this *Arith) Multiply(req ArithRequest,res *ArithResponse) error{ res.Pro = req.A * req.B return nil } //除法运算方法 func (this *Arith) Divide(req ArithRequest,res *ArithResponse) error{ if req.B ==0 { return errors.New("divide by zero") } res.Quo = req.A / req.B res.Rem = req.A % req.B return nil } func main() { //注册rpc服务 rpc.Register(new(Arith)) //采用http协议作为rpc载体 rpc.HandleHTTP() lis,err := net.Listen("tcp","127.0.0.1:8095") if err!=nil { log.Fatalln("fatal error:",err) } fmt.Fprintf(os.Stdout,"%s","start connection\n") //常规启动http服务 http.Serve(lis,nil) }rpc\_client.gopackage main import ( "fmt" "log" "net/rpc" ) //算数运算请求结构体 type ArithRequest struct { A int B int } //响应结构体 type ArithResponse struct { Pro int //乘 Quo int //商 Rem int //余数 } func main() { conn,err := rpc.DialHTTP("tcp","127.0.0.1:8095") if err!=nil { log.Fatalln("dialing error:",err) } req := ArithRequest{10,20} var res ArithResponse err = conn.Call("Arith.Multiply",req,&res) //乘法运算 if err!=nil { log.Fatalln("arith error:",err) } fmt.Printf("%d * %d = %d\n",req.A,req.B,res.Pro) //除法运算 err = conn.Call("Arith.Divide",req,&res) if err!=nil { log.Fatalln("arith error:",err) } fmt.Printf("%d / %d = %d 余数是:%d",req.A,req.B,res.Quo,res.Rem) }运行结果先启动服务端,再启动客户端连接服务端//服务端console start connection //客户端console 10 * 20 = 200 10 / 20 = 0 余数是:10 RPC入门2:net/rpc/jsonrpc实现跨语言调用jsonrpc_server.gopackage main import ( "errors" "fmt" "log" "net" "net/rpc" "net/rpc/jsonrpc" "os" ) type Arith struct { } //请求结构体 type ArithRequest struct { A int B int } //响应结构体 type ArithResponse struct { Pro int //乘积 Quo int //商 Rem int //余数 } //乘积方法 func (this *Arith) Multiply(req ArithRequest,res *ArithResponse) error{ res.Pro = req.A * req.B return nil } //除法运算方法 func (this *Arith) Divide(req ArithRequest,res *ArithResponse) error{ if req.B ==0 { return errors.New("divide by zero") } res.Quo = req.A / req.B res.Rem = req.A % req.B return nil } func main() { //注册rpc服务 rpc.Register(new(Arith)) //采用http协议作为rpc载体 rpc.HandleHTTP() lis,err := net.Listen("tcp","127.0.0.1:8096") if err!=nil { log.Fatalln("fatal error:",err) } fmt.Fprintf(os.Stdout,"%s","start connection\n") //接收客户端请求 并发处理 jsonrpc for { conn,err :=lis.Accept() //接收客户端连接请求 if err!=nil { continue } //并发处理客户端请求 go func(conn net.Conn) { fmt.Fprintf(os.Stdout,"%s","new client in coming\n") jsonrpc.ServeConn(conn) }(conn) } //常规启动http服务 //http.Serve(lis,nil) }jsonrpc_client.gopackage main import ( "fmt" "log" "net/rpc/jsonrpc" ) //算数运算请求结构体 type ArithRequest struct { A int B int } //响应结构体 type ArithResponse struct { Pro int //乘 Quo int //商 Rem int //余数 } func main() { // 只有这里不一样 conn,err := jsonrpc.Dial("tcp","127.0.0.1:8096") if err!=nil { log.Fatalln("dialing error:",err) } req := ArithRequest{9,2} var res ArithResponse err = conn.Call("Arith.Multiply",req,&res) //乘法运算 if err!=nil { log.Fatalln("arith error:",err) } fmt.Printf("%d * %d = %d\n",req.A,req.B,res.Pro) //除法运算 err = conn.Call("Arith.Divide",req,&res) if err!=nil { log.Fatalln("arith error:",err) } fmt.Printf("%d / %d = %d 余数是:%d",req.A,req.B,res.Quo,res.Rem) }运行结果先启动服务端,再启动客户端连接服务端//服务端console start connection //客户端console 9 * 2 = 18 9 / 2 = 4 余数是:1 //服务端console new client in comingRPC入门3:go php跨语言调用Go作为服务端,PHP作为客户端jsonrpc_server.go:和入门2服务端的代码一样下面是PHP代码jsonrpc_client.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", 8096); $args = array('A' => 9, 'B' => 2); $r = $client->Call("Arith.Multiply", $args); printf("%d * %d = %d\n", $args['A'], $args['B'], $r['result']['Pro']); $r = $client->Call("Arith.Divide", array('A' => 9, 'B' => 2)); printf("%d / %d, Quo is %d, Rem is %d\n", $args['A'], $args['B'], $r['result']['Quo'], $r['result']['Rem']);@如何在本地启动PHP 不作为本文重点,可以看这篇文章。运行结果本地启动PHP服务:http://127.0.0.1/jsonrpc_client.php运行结果如下:9 * 2 = 18 9 / 2, Quo is 4, Rem is 1
2022年01月05日
65 阅读
0 评论
0 点赞
2021-11-30
QPS、TPS、并发用户数、吞吐量关系
1、QPSQPS Queries Per Second 是每秒查询率 ,是一台服务器 每秒能够响应的查询次数,是对一个特定的查询服务器在规定时间内 所处理流量多少的衡量标准, 即每秒的响应请求数,也即是最大吞吐能力。2、TPSTPS Transactions Per Second 也就是事务数/秒。一个事务是指一个客户机向服务器发送请求然后服务器做出反应的过程。客户机在发送请求时开始计时,收到服务器响应后结束计时,以此来计算使用的时间和完成的事务个数,3、QPS和TPS区别个人理解如下:1、Tps即每秒处理事务数,包括了用户请求服务器服务器自己的内部处理服务器返回给用户这三个过程,每秒能够完成N个这三个过程,Tps也就是N;2、Qps基本类似于Tps,但是不同的是,对于一个页面的一次访问,形成一个Tps;但一次页面请求,可能产生多次对服务器的请求,服务器对这些请求,就可计入“Qps”之中。例子:例如 :访问一个页面会请求服务器3次,一次放,产生一个“T”,产生3个“Q”例如 :一个大胃王一秒能吃10个包子,一个女孩子0.1秒能吃1个包子,那么他们是不是一样的呢?答案是否定的,因为这个女孩子不可能在一秒钟吃下10个包子,她可能要吃很久。这个时候这个大胃王就相当于TPS,而这个女孩子则是QPS。虽然很相似,但其实是不同的。4、并发数并发数(并发度):指系统同时能处理的请求数量,同样反应了系统的负载能力。这个数值可以分析机器1s内的访问日志数量来得到5、吐吞量吞吐量是指系统在单位时间内处理请求的数量,TPS、QPS都是吞吐量的常用量化指标。系统吞吐量要素一个系统的吞吐量(承压能力)与request(请求)对cpu的消耗,外部接口,IO等等紧密关联。单个request 对cpu消耗越高,外部系统接口,IO影响速度越慢,系统吞吐能力越低,反之越高。重要参数QPS(TPS),并发数,响应时间QPS(TPS):每秒钟request/事务 数量 并发数:系统同时处理的request/事务数 响应时间:一般取平均响应时间关系QPS(TPS)=并发数/平均响应时间一个系统吞吐量通常有QPS(TPS),并发数两个因素决定,每套系统这个两个值都有一个相对极限值,在应用场景访问压力下,只要某一项达到系统最高值,系统吞吐量就上不去了,如果压力继续增大,系统的吞吐量反而会下降,原因是系统超负荷工作,上下文切换,内存等等其他消耗导致系统性能下降。6、PVPV (Page View):页面访问量,即页面浏览量或点击量,用户每次刷新即被计算一次。可以统计服务一天的访问日志得到。7、UVUV (Unique Visitor):独立访客,统计1天内访问某站点的用户数。可以统计服务一天的访问日志并根据用户的唯一标识去重得到。响应时间(RT):响应时间是指系统对请求作出响应的时间,一般取平均响应时间。可以通过Nginx、Apache之类的Web Server得到。8、DAUDAU (Daily Active User),日活跃用户数量。常用于反映网站、互联网应用或网络游戏的运营情况。DAU通常统计一日(统计日)之内,登录或使用了某个产品的用户数(去除重复登录的用户),与UV概念相似9、MAUMAU (Month Active User):月活跃用户数量,指网站、app等去重后的月活跃用户数量10、系统吞吐量评估我们在做系统设计的时候就需要考虑CPU运算,IO,外部系统响应因素造成的影响以及对系统性能的初步预估。而通常情况下,我们面对需求,我们评估出来的出来QPS,并发数之外,还有另外一个维度:日pv。通过观察系统的访问日志发现,在用户量很大的情况下,各个时间周期内的同一时间段的访问流量几乎一样。比如工作日的每天早上。只要能拿到日流量图和QPS我们就可以推算日流量。通常的技术方法:1、找出系统的最高TPS和日PV,这两个要素有相对比较稳定的关系(除了放假、季节性因素影响之外)2、通过压力测试或者经验预估,得出最高TPS,然后跟进1的关系,计算出系统最高的日吞吐量。B2B中文和淘宝面对的客户群不一样,这两个客户群的网络行为不应用,他们之间的TPS和PV关系比例也不一样。11、软件性能测试的基本概念和计算公式软件做性能测试时需要关注哪些性能呢?首先,开发软件的目的是为了让用户使用,我们先站在用户的角度分析一下,用户需要关注哪些性能。对于用户来说,当点击一个按钮、链接或发出一条指令开始,到系统把结果已用户感知的形式展现出来为止,这个过程所消耗的时间是用户对这个软件性能的直观印 象。也就是我们所说的响应时间,当相应时间较小时,用户体验是很好的,当然用户体验的响应时间包括个人主观因素和客观响应时间,在设计软件时,我们就需要 考虑到如何更好地结合这两部分达到用户最佳的体验。如:用户在大数据量查询时,我们可以将先提取出来的数据展示给用户,在用户看的过程中继续进行数据检 索,这时用户并不知道我们后台在做什么。用户关注的是用户操作的相应时间。其次,我们站在管理员的角度考虑需要关注的性能点。1、 响应时间2、 服务器资源使用情况是否合理3、 应用服务器和数据库资源使用是否合理4、 系统能否实现扩展5、 系统最多支持多少用户访问、系统最大业务处理量是多少6、 系统性能可能存在的瓶颈在哪里7、 更换那些设备可以提高性能8、 系统能否支持7×24小时的业务访问再次,站在开发(设计)人员角度去考虑。1、 架构设计是否合理2、 数据库设计是否合理3、 代码是否存在性能方面的问题4、 系统中是否有不合理的内存使用方式5、 系统中是否存在不合理的线程同步方式6、 系统中是否存在不合理的资源竞争
2021年11月30日
74 阅读
0 评论
1 点赞
2021-11-12
Golang方法集的理解
先看一段代码// notifier 是一个定义了notify行为的接口 type notifier interface { notify() } // user 使用指针语义实现了notifier接口 type user struct { name string email string } // receiver为指针语义 func (u *user) notify() { fmt.Printf("Sending User Email To %s<%s>\n", u.name, u.email) } // receiver为值语义 func (u user) showUserInfo() { fmt.Printf("Name:%s\n Email:<%s>\n", u.name, u.email) } func main() { u := user{"Han", "han@email.com"} // 发生编译错误!!!!!!!!!!! sendNotification(u) } // sendNotification函数接收一个实现notifier接口的变量 func sendNotification(n notifier) { n.notify() } 代码在25行发生了编译错误// ./prog.go:32:19: cannot use u (variable of type user) as type notifier in argument to sendNotification: // user does not implement notifier (notify method has pointer receiver) 为什么会发生编译错误?1. 什么是方法集类型的 方法集 确定了该类型的 操作数 所可以 调用 的方法。每一个类型都有一个(可能为空的)方法集与之关联类型 T (值语义)的方法集,包含全部receiver为 T 的方法类型 *T (指针语义)的方法集,包含全部receiver为 T + *T 的方法注:当类型调用自己声明的方法时,不需要考虑receiver是值语义还是指针语义,可以调用全部方法。因为在调用时,编译器自动做了转换。例如:// 使用值语义声明变量u u := user{} // notify方法的receiver为指针语义 u.notify() // 编译器自动转换为 (&u).notify() // 使用指针语义声明变量u2 u2 := &user{} // showUserInfo方法的receiver为值语义 u2.showUserInfo() // 编译器自动转换为 (*u2).showUserInfo() 2. 什么是接口go语言接口是一个或多个方法签名的集合。任何类型的方法集中只要拥有该接口 对应的全部方法签名(指有相同名称、参数列表 (不包括参数名) 以及返回值列表) ,就表示该类型实现了该接口,无须在该类型上显式声明实现了哪个接口。解决理解了上面两个概念后,回到文章开头的代码中:func main() { // 使用值语义声明的user u := user{"Han", "han@email.com"} // 此时 user 类型变量的方法集中仅包含: // 值语义receiver的 showUserInfo() 方法 sendNotification(u) // 发生编译错误!!!!!!!!!!! // user类型的变量 u 没有实现 notifier 接口 // 因为拥有指针语义receiver的 notify 方法不属于 u 变量的方法集 } 修改后:func main() { // 依然使用值语义声明的user u := user{"Han", "han@email.com"} // 对u取指针,此时传递给 sendNotification 方法的参数是 *user 类型 sendNotification(&u) // 此时 *user 类型变量的方法集中包含了: // 拥有值语义receiver的 showUserInfo() 方法 // 拥有指针语义receiver的 notify() 方法 // 实现了 notifier 接口,编译器不报错 } {callout color="#f0ad4e"}可以得到以下结论:{/callout}若以值语义 T 作为receiver实现接口,不管是T类型的值,还是T类型的指针,都实现了该接口若以指针语义 *T 作为receiver实现接口,只有T类型的指针实现了该接口
2021年11月12日
103 阅读
0 评论
3 点赞
2021-11-12
Golang的panic与recover
一、panicpanic的作用就是抛出一条错误信息,从它的参数类型可以看到它可以抛出任意类型的错误信息。在函数执行过程中的某处调用了panic,则立即抛出一个错误信息,同时函数的正常执行流程终止,但是该函数中panic之前定义的defer语句将被依次执行。之后该goroutine立即停止执行。二、recoverrecover()用于将panic的信息捕捉。recover必须定义在panic之前的defer语句中。在这种情况下,当panic被触发时,该goroutine不会简单的终止,而是会执行在它之前定义的defer语句。建议每起一个goroutine,都defer统一捕获下异常谨记用defer统一捕获异常只对 当前的goroutine 有效,goroutine的异常并不会向上传递给main主函数例子package main import "fmt" import "math" func foo(a int) { defer fmt.Println("foo退出来了") defer func() { if r := recover(); r != nil { fmt.Printf("捕获到的错误:%s\n", r) } }() if a < 0 { panic("必须输入大于0的数") } fmt.Println("该数的方根为:", math.Sqrt(float64(a))) }
2021年11月12日
27 阅读
0 评论
0 点赞
1
2
3
4
...
9