首页 > 其他分享 >GuzzleHttp 并发发起 http 请求

GuzzleHttp 并发发起 http 请求

时间:2024-06-07 17:10:50浏览次数:14  
标签:status 200 http 请求 GuzzleHttp --- 并发 测试 time

GuzzleHttp 并发发起 http 请求

系统中需要调用百度翻译接口做多语言支持,百度翻译对于不同的认证给予了不同的权益,通用文本翻译API标准版、高级版、尊享版的不同服务权益如下:

标准版 高级版 尊享版
QPS=1
支持28个语种互译
单次最长请求1000字符
免费调用量5万字符/月
QPS=10
支持 28个语种互译
单次最长请求6000字符
免费调用量100万字符/月
QPS=100
支持200+语种互译
单次最长请求6000字符
免费调用量200万字符/月

最初的实现是依次循环调用接口,组装请求参数、调用翻译接口、解析翻译结果,调试下来每秒种只能调用 5 ~ 6 次接口,高级版的 QPS = 10 都跑不满,尊享版的 QPS = 100 完全没有用武之地,系统中新支持一套语言包时,整体翻译下来需要数个小时。

考虑通过 GuzzleHttp 的并发请求进行优化,优化后可以根据不同的权益等级设置不同的并发请求数量,特别是对于尊享版本的 QPS=100,可以大幅提升翻译速度。

本地先做了模拟测试,首先部署一个 POST 请求的接口,接口的限制频率为 10次/s,将请求参数直接返回:

type TransData struct {
	Q     string `json:"q" form:"q"`
	From  string `json:"from" form:"from"`
	To    string `json:"to" form:"to"`
	AppId string `json:"app_id" form:"app_id"`
	Salt  string `json:"salt" form:"salt"`
	Sign  string `json:"sign" form:"sign"`
}

func RegisterAPIRoutes(r *gin.Engine) {

	var v1 *gin.RouterGroup

    v1 = r.Group("/v1")

	v1.Use(middlewares.LimitIP("10-S"))
	{
		v1.POST("/trans", func(c *gin.Context) {
			transData := TransData{}
			if err := c.ShouldBind(&transData); err != nil {
				fmt.Println(err.Error())
				return
			}

            // 没什么业务逻辑,所以加了 200ms 的延迟响应
			time.Sleep(time.Millisecond * 200)

			c.JSON(http.StatusOK, transData)
		})
	}
}

先按之前单条循环的方式调用接口,传入的参数按照百度翻译接口的参数组装:

$url = 'http://localhost:8000/api/v1/trans';
$client = new Client();
foreach (range(0, 9) as $i) {
    $text = '测试' . $i;
    $appId = 'app_id';
    $appKey = 'key';
    $salt = random_int(1000, 9999);
    $data = [
        'q' => $text,
        'from' => 'zh',
        'to' => 'en',
        'appid' => $appId,
        'salt' => $salt,
        'sign' => md5($appId . $text . $salt . $appKey)
    ];
    $response = $client->request('POST', $url,  [
        'http_errors' => false,
        'form_params' => $data,
    ]);
    $status = $response->getStatusCode();
    echo 'status = ' . $status . ' ---> ';
    if ($status === 200) {
        $body = $response->getBody()->getContents();
        $data = json_decode($body, true);
        echo 'i = ' . $i . ', time = ' . time() . ', 请求成功, q = ' . $data['q'] . PHP_EOL;
    } else {
        echo 'i = ' . $i . ', time = ' . time() . ', 请求失败' . PHP_EOL;
    }
}

10 次接口请求用时 2 秒多,1 秒内可以完成请求 4 次左右(和之前在系统内调试的 5 ~ 6 次差不多),执行结果:

status = 200 ---> i = 0, time = 1717747910, 请求成功, q = 测试0
status = 200 ---> i = 1, time = 1717747910, 请求成功, q = 测试1
status = 200 ---> i = 2, time = 1717747911, 请求成功, q = 测试2
status = 200 ---> i = 3, time = 1717747911, 请求成功, q = 测试3
status = 200 ---> i = 4, time = 1717747911, 请求成功, q = 测试4
status = 200 ---> i = 5, time = 1717747911, 请求成功, q = 测试5
status = 200 ---> i = 6, time = 1717747912, 请求成功, q = 测试6
status = 200 ---> i = 7, time = 1717747912, 请求成功, q = 测试7
status = 200 ---> i = 8, time = 1717747912, 请求成功, q = 测试8
status = 200 ---> i = 9, time = 1717747912, 请求成功, q = 测试9


Time: 00:02.100, Memory: 34.00 MB

再改为并发的方式发起请求:

$url = 'http://localhost:8000/api/v1/trans';
$appId = 'app_id';
$appKey = 'key';

$promises = [];
$client = new Client();
foreach (range(0, 9) as $i) {
    $text = '测试' . $i;
    $salt = random_int(1000, 9999);
    $data = [
        'q' => $text,
        'from' => 'zh',
        'to' => 'en',
        'appid' => $appId,
        'salt' => $salt,
        'sign' => md5($appId . $text . $salt . $appKey)
    ];
    $promises[] = $client->postAsync($url, [
        'http_errors' => false,
        'form_params' => $data,
    ]);
}

$responses  = Utils::unwrap($promises);
foreach (range(0, 9) as $i) {
    $status = $responses[$i]->getStatusCode();
    echo 'status = ' . $status . ' ---> ';
    if ($status === 200) {
        $body = $responses[$i]->getBody()->getContents();
        $data = json_decode($body, true);
        echo 'i = ' . $i . ', time = ' . time() . ', 请求成功, q = ' . $data['q'] . PHP_EOL;
    } else {
        echo 'i = ' . $i . ', time = ' . time() . ', 请求失败' . PHP_EOL;
    }
}

10 次接口请求用时不到 1 秒,大幅减少了等待时间,执行结果:

status = 200 ---> i = 0, time = 1717748427, 请求成功, q = 测试0
status = 200 ---> i = 1, time = 1717748427, 请求成功, q = 测试1
status = 200 ---> i = 2, time = 1717748427, 请求成功, q = 测试2
status = 200 ---> i = 3, time = 1717748427, 请求成功, q = 测试3
status = 200 ---> i = 4, time = 1717748427, 请求成功, q = 测试4
status = 200 ---> i = 5, time = 1717748427, 请求成功, q = 测试5
status = 200 ---> i = 6, time = 1717748427, 请求成功, q = 测试6
status = 200 ---> i = 7, time = 1717748427, 请求成功, q = 测试7
status = 200 ---> i = 8, time = 1717748427, 请求成功, q = 测试8
status = 200 ---> i = 9, time = 1717748427, 请求成功, q = 测试9

Time: 00:00.309, Memory: 34.00 MB

将并发的数量由 10 改为 15,(两次循环处的 9 改为 14):

// foreach (range(0, 9) as $i) {
foreach (range(0, 14) as $i) {

执行结果也符合 10次/s 的频率限制:

status = 200 ---> i = 0, time = 1717750047, 请求成功, q = 测试0
status = 200 ---> i = 1, time = 1717750047, 请求成功, q = 测试1
status = 200 ---> i = 2, time = 1717750047, 请求成功, q = 测试2
status = 200 ---> i = 3, time = 1717750047, 请求成功, q = 测试3
status = 200 ---> i = 4, time = 1717750047, 请求成功, q = 测试4
status = 200 ---> i = 5, time = 1717750047, 请求成功, q = 测试5
status = 200 ---> i = 6, time = 1717750047, 请求成功, q = 测试6
status = 200 ---> i = 7, time = 1717750047, 请求成功, q = 测试7
status = 200 ---> i = 8, time = 1717750047, 请求成功, q = 测试8
status = 200 ---> i = 9, time = 1717750047, 请求成功, q = 测试9
status = 429 ---> i = 10, time = 1717750047, 请求失败
status = 429 ---> i = 11, time = 1717750047, 请求失败
status = 429 ---> i = 12, time = 1717750047, 请求失败
status = 429 ---> i = 13, time = 1717750047, 请求失败
status = 429 ---> i = 14, time = 1717750047, 请求失败

Time: 00:00.310, Memory: 34.00 MB

接口频率限制改为 100次/s

v1.Use(middlewares.LimitIP("100-S"))

并发数量也改为 100 次:

foreach (range(0, 99) as $i) {

100 次的接口请求用时 1 秒钟:

status = 200 ---> i = 0, time = 1717750526, 请求成功, q = 测试0
status = 200 ---> i = 1, time = 1717750526, 请求成功, q = 测试1
status = 200 ---> i = 2, time = 1717750526, 请求成功, q = 测试2
status = 200 ---> i = 3, time = 1717750526, 请求成功, q = 测试3
.
.
.
status = 200 ---> i = 94, time = 1717750526, 请求成功, q = 测试94
status = 200 ---> i = 95, time = 1717750526, 请求成功, q = 测试95
status = 200 ---> i = 96, time = 1717750526, 请求成功, q = 测试96
status = 200 ---> i = 97, time = 1717750526, 请求成功, q = 测试97
status = 200 ---> i = 98, time = 1717750526, 请求成功, q = 测试98
status = 200 ---> i = 99, time = 1717750526, 请求成功, q = 测试99

Time: 00:00.951, Memory: 36.00 MB

标签:status,200,http,请求,GuzzleHttp,---,并发,测试,time
From: https://www.cnblogs.com/zhpj/p/18237543/guzzlehtp-and-issue-an-http-request-z1xzemg

相关文章

  • Maui+blazor中使用https时信任所有证书
    Maui中的Android使用https时信任所有证书前言最近使用Maui+blazor写了一个Androidapp,需要调用webapi接口,同时需要用websock与服务器通信,在使用http和https中遇到一些问题httpAndroid默认禁止http,想要使用http需要在Platforms\Android目录下找到AndroidManifest.xml文件,然后......
  • FastAPI-4:异步、并发和Starlette
    4异步、并发和Starlette本章关注FastAPI的底层Starlette库,尤其是它对异步处理的支持。在概述了Python中“同时做更多事情”的多种方法后,您将看到Python中较新的async和await关键字是如何融入Starlette和FastAPI的。4.1StarletteFastAPI的大部分网络代码都基于TomChristie......
  • 高并发下使用Redis分布式锁确保接口执行唯一性【重点】
    摘要:本文将介绍如何使用Redis分布式锁来确保在高并发环境下,确保某个接口只有一个线程能够执行。通过使用Redis的SETNX命令,我们可以实现一个简单的分布式锁,从而避免多个线程同时访问共享资源。一、背景在高并发的系统中,为了保证数据的一致性和完整性,我们经常需要对某些接口......
  • Http协议详解之三次握手
    HTTP的三次握手在计算机网络中,HTTP(HyperTextTransferProtocol,超文本传输协议)是用于在客户端和服务器之间传输超文本的协议。尽管HTTP本身是一个无状态的应用层协议,但它通常依赖于TCP(TransmissionControlProtocol,传输控制协议)来确保数据的可靠传输。TCP是一种面向连接的......
  • HTTP Status 400 – Bad Request
    1.问题2.原因org.apache.juli.logging.DirectJDKLog:log|ErrorparsingHTTPrequestheaderNote:furtheroccurrencesofHTTPheaderparsingerrorswillbeloggedatDEBUGlevel.java.lang.IllegalArgumentException:Requestheaderistoolargeat......
  • 并发的三⼤特性
    1.学习内容:并发的三⼤特性2.详解:并发编程的三大特性包括原子性、可见性和有序性。1.原子性:原子操作是不可再分的单一操作,要么全部执行成功,要么全部执行失败,不会被中断。在并发编程中,原子性确保多个线程对共享变量进行操作时不会产生数据不一致的情况。2.可见性:可见性指......
  • 调用文心一言API询问httpx的使用方法2
    [importrequestsimportjsondefget_access_token():url="https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=输入自己的id&client_secret=输入自己id的密码"payload=json.dumps("")headers={"Content-Typ......
  • Spark读取http数据写入hive
    http请求hutool,json转换数据写入到rdd,之后转换为sparksqlSparkSessionspark=SparkSession.builder().master("yarn").appName("json2hive").config("hive.exec.dynamic.partition","true").config("......
  • 调用文心一言API询问httpx的使用方法
    importrequestsimportjsondefget_access_token():url="https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=输入自己的id&client_secret=输入自己id的密码"payload=json.dumps("")headers={"Content-Type......
  • 互联网大厂的缓存策略:抵抗超高并发的秘密武器,已开源!
    大家好,我是冰河~~最近,有小伙伴私信我:冰哥,我最近出去面试,面试官问我如何设计缓存能让系统在百万级别流量下仍能平稳运行,我当时没回答上来。接着,面试官问我之前的项目是怎么使用缓存的,我说只是缓存了一些数据。当时确实想不到缓存还有哪些用处,估计这次面试是挂了。冰哥,你可以给我讲......