首页
统计
留言
Search
1
PHP中使用反射
996 阅读
2
phpstorm配置SFTP
940 阅读
3
Go语言——结构体
792 阅读
4
PhpStorm 使用 AI 代码生成工具 Codeium
783 阅读
5
关于PHP的垃圾回收机制
763 阅读
后端
PHP
Go
数据库
其他
前端
其他技术
生活杂谈
登录
Search
标签搜索
Laravel
Mysql
RPC
Redis
Liunx
PHP
CSS
ES
算法
开发工具
断点续传
反射
phpstorm
工具
防盗链
CURL
设计模式
面试
Nginx
搜索引擎
quhe.net
首页
栏目
后端
PHP
Go
数据库
其他
前端
其他技术
生活杂谈
页面
统计
留言
搜索到
57
篇与
quhe.net
的结果
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日
66 阅读
0 评论
0 点赞
2021-12-27
PhpStorm的8个技巧
1、大小写转换--避免重复写字母大小写切换在定义常量的时候就经常用到,如果你输入的常量是小写字母的。用鼠标选中文本,然后键盘按住Ctrl +Shift + U,这样就可以将文本中的字母全部转换为大写的字母,反之就可以转换为小写字母。快捷方便,避免重新输入。2、多个位置选中--减少重复写看以上图片,如果你操作的地方有多处是一样的,你可以这样操作:鼠标选中文本后,按住Alt + J可以同时选中相同的文本进行编辑,按住Alt +Shift+ J可以取消选中。这个快捷键的名字叫Add Selection for Next Occurrence,你可以在设置中中进行搜索,也可以修改成其他的快捷键。3、加入自定义字典--干掉错别字这个和你自己在开发中常量或变量的命名时有关,但尽量命名为相关的英文单词,这样提高代码质量。或代码中命名的词不是英文单词时,就会在该词下方出现波浪线标识,导致分不清是自己单词拼错了还是本身就不是单词,你可这样操作:鼠标选中该单词后,按住Alt + Enter 键,选择Save to project-level dictionary 就可以把讨厌的波浪号去掉啦。4、显示代码行号鼠标右键点击代码区域左边空白栏,选Show Lines Nimbers即可,相反取消选中即隐藏行号,方便程序报错时不知道选行号的程序员查看错误行。这里顺便讲一下 行号的快捷键:只需要Ctrl+G 弹出框,输入行号即可!5、代码格式优化把光标放于文件里可以格式化整个文件,或者选中要优化格式的代码,按住Ctrl + Alt + L即可自动优化所有代码格式,增强了整个代码的阅读性!6、代码模板设置,自动补全代码对于这样的操作在开发中也是经常出现的,你可以这样来操作:在Settings>Editor>Live Templates 进行自定义配置,也可配置其他需要补全的代码。设置后,假如你输入fore后点击Tab键可以自动补全foreach代码,不过现在新版的IDE都是自动的为你补全代码了,原因是自动默认了摄设置7、SSH工具--链接服务器点击Tools>Start SSH session...,在之后的弹窗中选择要链接的服务器即可直接链接,如果没有配置过服务器,可以在弹窗中选中Edit credentials进行添加服务器配置。8、FTP工具--远程改文件点击Tools>Deployment>Browse Remote Host可以直接链接远程服务器ftp。查看日志、修改配置文件非常方便,注意修改完文件要点击右上角的 ↑ 才能保存修改到服务器哦。这样你就不需要用其他的ftp工具了!9、设置单行注释符缩进位置用phpstorm给PHP代码设置//注释,默认情况下,快捷键 Ctrl + / 会将注释符 // 放在最前边, 可以通过以下设置使用 // 也跟代码一样支持缩进:File > Settings > Editor > Code Style > PHP > Code Generation 取消勾选 Line comment at first column 即可这里在穿插几个自己经常用的插件汉化插件就不必说了,看不懂英文的都会去配置,打开设置-插件的位置,搜索chinese,出来图标-汉,就是了!直接安装后重启地图插件,类似SublimeText的Mini Map插件。方便查看代码块。打开设置-插件,搜索:CodeGlance 安装,重启即可PHP文档插件:以前开发时遇到不懂的都要去查看PHP文档,现在几乎都很强大了,一个工具一个插件就能搞定很多事情,查看PHP文档,在设置的插件里搜索:chinese php document,安装,然后重启即可使用!
2021年12月27日
127 阅读
0 评论
3 点赞
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
...
4
5
6
...
12