当前位置: 首页 > news >正文

用PHP异步协程控制python爬虫脚本,实现多协程分布式爬取

背景

公司需要爬取指定网站的产品数据。但是个人对python的多进程和协程不是特别熟悉。所以,想通过php异步协程,发起爬取url请求控制python爬虫脚本,达到分布式爬取的效果。

准备

  • 1.准备一个mongodb数据库用于存放爬取数据
  • 2.引入flask包,方便php通过调用url发起请求控制脚本
  • 3.引入selenium、BeautifulSoup4、webdriver等python包
  • 4.使用php的swoole异步协程发送url请求

python爬虫脚本

from bs4 import BeautifulSoup
from selenium import webdriver
from selenium.webdriver.common.by import By
import time
from flask import Flask,jsonify,request
from pymongo import MongoClient
from datetime import datetime
from gevent import pywsgi
import sys
import requests
import re
from tornado.httpserver import HTTPServer
from tornado.wsgi import WSGIContainer
from tornado.ioloop import IOLoop#创建一个服务,赋值给APP
app = Flask(__name__)@app.route('/get_ic_product_list',methods=['post']) #指定接口访问的路径,支持什么请求方式get,post
def get_ic_product_list():# url = 'https://www.ic.com/psen/ClassList2.aspx?id=3'# url = 'https://www.ic.com/ClassList2.aspx?id=3'chrome_options = webdriver.ChromeOptions()chrome_options.add_argument('--no-sandbox')chrome_options.add_argument('--headless')chrome_options.add_argument('--disable-gpu')chrome_options.add_argument('--ignore-certificate-errors')browser = webdriver.Chrome()conn = MongoClient('192.168.0.143', 27017)try:uri = request.form.get('url')# 是否按照零库存处理 yes-是 no-否isContinue = request.form.get('is_continue_stock', 'yes')# uri = sys.argv[1]# isContinue = sys.argv[2]if uri == '':return jsonify({'status': 0, 'msg': 'uri is null'})browser.get(uri)browser.implicitly_wait(3)time.sleep(2)# 连接mongodatabase = conn.iccollection = database.ic_product# 时间格式化nowtime = datetime.now().strftime('%Y-%m-%d %H:%M:%S')spiderTime = datetime.now().strftime('%Y%m%d')spidertime = int(spiderTime)# 总页数pageTag = browser.find_element(By.CLASS_NAME, 'P_Page')total_page = pageTag.text.strip().split('/')[1]# 分类列表,不按照stock=0处理cateMap = ['21', '43', '175','179','184','241','250','268','306','317','325','343','397','394','409','467','547','556','606','638','657','678','1150','1158','2315','2383']if total_page:t_page = int(total_page)if t_page > 20:totalPage = 55else:totalPage = t_pageelse:totalPage = 20stop = 0soup = BeautifulSoup(browser.page_source, 'html.parser')tables = soup.findAll(name="table", attrs={"class": "prilist"})if len(tables) > 0:for table in tables:# 阶梯价price_tb = table.findAll('tr')price = [tb.text.strip() for tb in price_tb]# 行数据cols = table.parent.parentitem = [col.text.strip() for col in cols]time.sleep(2)print('==========item==========')print(item)# 库存为0,终止程序stock = item[11]if stock == '-':stop += 1continuepos = item[3].find('Promotion')if pos != -1:sku = str(item[3].replace('(Promotion)', ''))else:sku = item[3]# 格式化数据collection.insert_one({'datasheet': str(item[0]),'img_url': str(item[1]),'productDescEn': str(item[2]),'part': sku,'manufacturer': str(item[5]),'description': str(item[7]),'pdfLinkUrl': str(item[10]),'availability': str(item[11]),'price': price,'created_at': nowtime,'spider_time': spidertime,'sync_time': 20010101,'updated_at': '2001-01-01 00:00:00',})# 接口请求在clickfor i in range(totalPage):print('==========i==========')print(i)browser.find_element(By.XPATH, './/div[@id="Pager1"]/a[9]').click()soup = BeautifulSoup(browser.page_source, 'html.parser')tables = soup.findAll(name="table", attrs={"class": "prilist"})print('==========stop==========')print(stop)if stop>10 and isContinue=='yes':breakif len(tables) < 1:return jsonify({'status': 0, 'msg': 'product list empty'})print('==========tables==========')print(tables)mongoData = []for table in tables:# 阶梯价price_tb = table.findAll('tr')price = [tb.text.strip() for tb in price_tb]# 行数据cols = table.parent.parentitem = [col.text.strip() for col in cols]time.sleep(2)print('==========item==========')print(item)# 库存为0,终止程序stock = item[11]if stock == '-':stop += 1continuepos = item[3].find('Promotion')if pos != -1:sku = str(item[3].replace('(Promotion)', ''))else:sku = item[3]# 格式化数据mongoData.append({'datasheet': str(item[0]),'img_url': str(item[1]),'productDescEn': str(item[2]),'part': sku,'manufacturer': str(item[5]),'description': str(item[7]),'pdfLinkUrl': str(item[10]),'availability': str(item[11]),'price': price,'created_at': nowtime,'spider_time': spidertime,'sync_time': 20010101,'updated_at': '2001-01-01 00:00:00',})# 插入mongomongoData and collection.insert_many(mongoData)print('================mongoData================')print(mongoData)del price, tablestime.sleep(5)return jsonify({'status': 1,'msg': 'ok'})except Exception as error:return jsonify({'status':0,'msg':error})finally:conn.close()browser.close()if __name__ == '__main__':app.run(host='0.0.0.0',port=8005,debug=True)          # 启动服务器# Tornado启动服务
# s = HTTPServer(WSGIContainer(app))
# s.listen(8005) # 监听 8080 端口
# IOLoop.current().start()

php协程调用curl请求封装

在laravel 框架下创建command服务
此处有用到redis队列的,有不熟悉的请阅读redis相关资料

<?php
/*** 爬取IC任务*/
namespace App\Console\Commands;use App\Models\IcProductCategoryModel;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Redis;
use Swoole\Timer;class SpiderIcProductList extends Command
{/*** The name and signature of the console command.** @var string*/protected $signature = 'spider:ic_product';/*** The console command description.** @var string*/protected $description = '每天定时爬取IC网站数据';/*** Create a new command instance.** @return void*/public function __construct(){parent::__construct();}/*** Execute the console command.** @return int*/public function handle(){$this->getProduct();return 0;}public function getProduct(){/*** ic product_num> 0, 分类约345个* 目前15min【900 * 1000】爬取10个分类,一天能爬取960个分类,一天能爬2次*/Timer::tick(60 * 15 * 1000,function (){$categoryModel = new IcProductCategoryModel();$key = $categoryModel::getCategoryQueue();echo 'time:' .date('H:i:s') .space();if (Redis::lLen($key) > 0) {$data = Redis::lRange($key,0,9);foreach ($data as $categoryId){go(function () use ($categoryId){$client = new \Swoole\Client(SWOOLE_SOCK_TCP);// 尝试与指定 TCP 服务端建立连接(IP和端口号需要与服务端保持一致,超时时间为0.5秒)if ($client->connect("127.0.0.1", 9505, 0.5)) {// 建立连接后发送内容$data = ['service' => 'RemoteApiSpider','method' => 'getProduct','param' =>  ['category_id' => $categoryId,'platform' => PLATFORM_IC,],];$sendData = jsonE($data);echo  'send=' .$sendData  .' ,'. date('H:i:s')   .space();$client->send($sendData);// 打印接收到的消息echo $client->recv();// 关闭连接$client->close();} else {echo "connect failed.";}});}Redis::lTrim($categoryModel::getCategoryQueue(),10,-1);unset($data);}});echo 'spider product end time:' .date('H:i:s') .space();}
}

创建服务端Server

<?phpnamespace App\Console\Commands;use App\Services\RemoteApiSpider;
use App\Services\TcpService;
use App\Services\TestService;
use Swoole\Database\MysqliConfig;
use Swoole\Database\MysqliPool;
use Swoole\Server;class Swoole extends Base
{protected $ws;/*** The name and signature of the console command.** @var string*/protected $signature = 'swoole {action} {--d}';/*** The console command description.** @var string*/protected $description = 'swoole service command';/*** Create a new command instance.** @return void*/public function __construct(){parent::__construct();}/*** Execute the console command.** @return int*/public function handle(){global $argv;$action = $this->argument('action');$argv[0] = 'wk';$argv[1] = $action;$argv[2] = $this->option('d') ? '-d' : '';$this->startServer();return 0;}public function startServer(){$server = new \Swoole\Server("127.0.0.1", 9505);// 设置异步任务的工作进程数量$log_path = storage_path() . '/logs/server.log';$server->set(['daemonize' => true,'log_file' =>  $log_path,'task_worker_num' => 50,]);//收到请求时触发$server->on('receive', function(\Swoole\Server $server, $fd, $from_id, $data) {//投递异步任务$task_id = $server->task($data);echo "异步任务投递成功: id=$task_id , recv={$data} " .his() . space();$server->send($fd, "task_id[{$task_id}],recv={$data},数据已接收,处理中... " .his() . space());});// 处理异步任务$server->on('Task', array($this, 'onTask'));//        $server->on('task', function (\Swoole\Server $server, $task_id, $from_id, $data) {
//            echo "新的待处理异步任务[id=$task_id] ".his() . space();
//            /**
//             $data = ['data' => [],'code' => '200','msg' => 'false']
//             */
//            $service = new RemoteApiSpider();
//            $service->setDebug(true);
//            if($data && is_string($data)){
//                $data = jsonD($data);
//            }
//            $ret = false;
//            if($data['code']==200 && $data['msg'] == 'ok'){
//                if(isset($data['data']['category_id']) && $data['data']['category_id']){
//                    $categoryId = $data['data']['category_id'];
//                    $ret = $service->getProduct($categoryId);
//                }
//            }
//            // todo 处理异步任务
//            // 返回任务执行的结果
//            $msg = "task_id[{$task_id}] ,category_id:" .$data . " ,ret:".$ret . " ," . his() . space();
//            $server->finish($msg);
//        });// 处理异步任务的结果$server->on('finish', function (\Swoole\Server $server, $task_id, $data) {echo "异步任务[$task_id] 处理完成: $data ". his() . space();});$server->start();}public function process(){$server = new TcpService();// 定义连接建立回调函数$server->onConnect = function ($conn) {echo "onConnect -- accepted " . stream_socket_get_name($conn, true) . "\n";};// 定义收到消息回调函数$server->onMessage = function ($conn, $msg) {echo "onMessage --" . $msg . "\n";$service = new RemoteApiSpider();$service->setDebug(true);$content = $service->getProduct($msg);fwrite($conn, "received " . $msg . "\n");};// 定义连接关闭回调函数$server->onClose = function ($conn) {echo "onClose --" . stream_socket_get_name($conn, true) . "\n";};// 启动服务器主进程$server->run();}public function onTask($serv, $task_id, $from_id, $data){//传类名、方法、参数实现公共使用try {if (!$data) {throw new \Exception(' task_id=' . $task_id . ',暂无需要处理的task任务', 400);}if (is_string($data)) {$data = jsonD($data);}echo 'recv: ' . jsonE($data) . space();$className = "App\Services\\{$data['service']}";if (!class_exists($className)) {throw new \Exception(' task_id=' . $task_id . ',未找到服务类名', 401);}$class = new $className;$func = $data['method'];if(isset($data['param'])){$ret = $class->$func($data['param']);}else{$ret = $class->$func();}return jsonE($ret);} catch (\Exception $e) {$msg = ' task_id=' . $task_id . ' ,error: ' . $e->getMessage();echo jsonE($msg) . space();}$serv->finish($msg);//每分钟执行一下service下的start方法,发送二维数组请求给tcp服务端,服务端调用service下的process方法实现业务逻辑//服务端拿到数据后,处理后结果格式:['code'=> 200,'msg'=> 'ok','data' => maxid]
//        $data = [
//            'service' => 'TestService',
//            'method' => 'process',
//            'param' => ['debug'=> true,'send' =>'sucess'],
//        ];//暂时留空 2.3部分会完善}}

curl发送异步请求调用python脚本

<?php
/*** 爬取接口封装*/
namespace App\Services;use App\Library\Oneyac\Oneyac;
use App\Models\HqchipProductCategoryModel;
use App\Models\HqchipProductMongoModel;
use App\Models\IcProductCategoryModel;
use App\Models\IcProductMongoModel;
use App\Models\LcscProductCategoryModel;
use App\Models\LcscProductMongoModel;
use App\Models\OneyacProductCategoryModel;
use App\Models\OneyacProductMongoModel;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Redis;class RemoteApiSpider extends Base
{/*** 根据分类循环分页爬取产品列表* @param $data* @return int* @throws \GuzzleHttp\Exception\GuzzleException*/public function getProduct($data){$cateId = 0;$categoryId = $data['category_id'] ?? $cateId;$platform = $data['platform'] ?? PLATFORM_LCSC;$platform = strtolower(trim($platform));$categoryId = (int)$categoryId;switch ($platform) {case PLATFORM_IC:$cateModel = new IcProductCategoryModel();Redis::hDel($cateModel::getCategoryListKey(),$categoryId);//TODO 调用python API接口爬取产品数据//s2 20分钟处理4个 与 s1 20分钟处理6个 $header = [];$header['Content-Type'] = 'application/json;charset=UTF-8';$query_param['page'] = 1;$res = $this->getIcProduct($categoryId, $platform,$query_param, $header);$res && $cateId = $categoryId;break;}return $cateId;}/*** 获取ic产品信息* @param $categoryId* @param $platform* @param $query_param* @param $header* @return bool* @throws \GuzzleHttp\Exception\GuzzleException*/public function getIcProduct($categoryId, $platform, $query_param, $header){$module =  'product_list';$this->getPlatformConf(PLATFORM_IC,$module);$msg = ' ' . $platform . '_' . $module . ' ';echo $msg. ' ,category_id:' . $categoryId . ' spidering' .space();try {//$uri = 'https://www.ic.com/psen/ClassList2.aspx?id=3';$uri = $this->base_uri . $this->uri . '?id=' . $categoryId;# todo 3的倍数推入s2,其他推入s1$config = config('ic');# 重点:此处可以部署到不同服务器进行爬取,我这里只部署了2台服务,s1与s2if(($categoryId%3)===0){//s2$curl = $config['spider_uri']['s2'] ?: 'http://192.168.0.124:8005/get_ic_product_list';}else{//s1$curl = $config['spider_uri']['s1'] ?: 'http://192.168.0.127:8005/get_ic_product_list';}$response = curlPost($curl,['url' => $uri,'is_continue_stock' => 'yes']);$flag = false;if(isset($response) && !empty($response)){$response = is_array($response) ? $response : jsonD($response);$flag = ($response['msg']=='ok' && $response['status']==1);}if ($flag) {$this->debug && Log::info($msg . 'category_id:' . $categoryId . ' ,mongo-batch-insert:' . $flag);} else {throw new \Exception($msg.'ic爬虫异常,请检查python爬虫');}unset($response);return  $flag;}catch (\Exception $e){Log::error($msg .'爬取产品异常,异常原因:'.$e->getMessage());return false;}}

nginx配置

server {listen 8080;server_name spidertest.test *.spidertest.test;root "D:/WWW/spidertest/";location / {proxy_pass http://127.0.0.1:8005;}charset utf-8;
}
  • 注:
    1.在不同服务器上部署python爬虫脚本,并配置nginx服务。配置完成后安装下面命令启动脚本。接下来开启异步多线程执行python爬虫脚本即可。
    2.记得启动swoole服务。命令如下:
php artisan swoole start

运行

python脚本启动
在这里插入图片描述

postman测试:
![在这里插入图片描述](https://img-blog.csdnimg.cn/c24295c2a30c4f79a377856df4bb9767.png

php启动协程命令:
在这里插入图片描述

爬取回来的数据结果:在这里插入图片描述

相关文章:

用PHP异步协程控制python爬虫脚本,实现多协程分布式爬取

背景 公司需要爬取指定网站的产品数据。但是个人对python的多进程和协程不是特别熟悉。所以&#xff0c;想通过php异步协程&#xff0c;发起爬取url请求控制python爬虫脚本&#xff0c;达到分布式爬取的效果。 准备 1.准备一个mongodb数据库用于存放爬取数据2.引入flask包&a…...

VUE3写后台管理(3)

VUE3写后台管理&#xff08;3&#xff09; 1.环境1.node2.vite3.Element-plus4.vue-router5.element icon6.less7.vuex8.vue-demi9.mockjs10.axios11.echarts 2.首页1.布局Main2.头部导航栏CommonHeader3.左侧菜单栏CommonLeft4.首页Home1.从后端获取数据显示到前端table的三种…...

机器学习笔记之最优化理论与算法(十二)无约束优化问题——共轭梯度法

机器学习笔记之最优化理论与方法——共轭梯度法 引言回顾&#xff1a;共轭方向法的重要特征线性共轭梯度法共轭方向公式的证明过程 关于线搜索公式中参数的化简关于线搜索公式中步长部分的化简关于线搜索公式中共轭方向系数的化简参数化简的目的 非线性共轭梯度法(FR,PRP方法)关…...

JVM中的java同步互斥工具应用演示及设计分析

1.火车站售票系统仿真 某火车站目前正在出售火车票&#xff0c;共有50张票&#xff0c;而它有3个售票窗口同时售票&#xff0c;下面设计了一个程序模拟该火车站售票&#xff0c;通过实现Runnable接口实现&#xff08;模拟网络延迟&#xff09;。 伪代码&#xff1a; Ticket类…...

数据治理-数据质量

实现数据质量的前提就是数据本身是可靠和可信的。 导致数据质量低下的因素 组织缺乏对低质量数据影响的理解&#xff0c;缺乏规划、孤岛式系统设计、不一致的开发过程、不完整的文档、缺乏标准或缺乏治理等。 所有组织都会遇到与数据质量有关的问题。数据质量需要跨职能的承诺…...

[sqoop]hive3.1.2 hadoop3.1.1安装sqoop1.4.7

参考: Hadoop3.2.4Hive3.1.2sqoop1.4.7安装部署_hadoop sqoop安装_alicely07的博客-CSDN博客 一、安装 1、解压 tar -zxvf sqoop-1.4.7.bin__hadoop-2.6.0.tar.gz -C /home/data_warehouse/module mv sqoop-1.4.7.bin__hadoop-2.6.0 sqoop-1.4.72、配置文件 sqoop-env.s…...

js事件的详细介绍

11.事件 1.什么是事件 js属于事件驱动编程,把驱动,执行,调用通过一些交互,触发一些函数事件:发起-->执行绑定事件-->触发事件on 绑定 emit触发 off解绑2.事件分类 鼠标事件 点击事件 onclick 双击事件 ondblclick 按下事件 onmousedown 抬起事件 onmouseup 鼠标进…...

虚幻4学习笔记(12)操控导入的角色、动画蓝图、播放蒙太奇和打包、角色重定向

虚幻4学习笔记 操控导入的角色设置鼠标旋转关掉动态模糊 动画蓝图、播放蒙太奇和打包角色走路奔跑动画shift 奔跑F 跳舞移动打断 跳舞 打包角色重定向姿势调整解决跑步 腿分太开隐藏剑 B站UP谌嘉诚课程&#xff1a;https://www.bilibili.com/video/BV164411Y732 操控导入的角色…...

hive with tez:无法从链中的任何提供者加载aws凭据

环境信息 hadoop 3.1.0 hive-3.1.3 tez 0.9.1 问题描述 可以从hadoop命令行正确地访问s3a uri。我可以创建外部表和如下命令&#xff1a; create external table mytable(a string, b string) location s3a://mybucket/myfolder/; select * from mytable limit 20; 执行正…...

Ubuntu修改静态IP、网关和DNS的方法总结

Ubuntu修改静态IP、网关和DNS的方法总结 ubuntu系统&#xff08;其他debian的衍生版本好像也可以&#xff09;修改静态IP有以下几种方法。&#xff08;搜索总结&#xff0c;可能也不太对&#xff09; /etc/netplan (use) Ubuntu 18.04开始可以使用netplan配置网络&#xff0…...

Eureka服务器注册

一。Eureka服务器注册 1.pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://mav…...

Windows安装GPU版本的pytorch详细教程

文章目录 chatGLM2-6B安装教程正式安装 chatGLM2-6B ChatGLM2-6B版本要装pytorch2.0&#xff0c;而且要2.0.1 &#xff0c;因此CUDA不能用12.0 &#xff0c;也不能用10.0&#xff0c;只能用11.x 版本。 安装教程 pip install直接下载安装 官网&#xff1a; https://pytorch.…...

理解Kruskal算法的前提----深入理解并查集【超简单~】

并查集的实现思路 并查集主要分为两个部分&#xff1a;第一部分就是需要找到点对应的祖宗节点&#xff0c;第二部分&#xff0c;是要将属于同一个集合节点的祖宗节点进行统一&#xff0c;也就是结合操作。 Find函数实现 // parent数组用来存储下标值所对应的父节点值 // 比如…...

Jenkins+Gitee+Docker+Ruoyi项目前后端分离部署

前言 描述&#xff1a;本文主要是用来记录 如何用标题上的技术&#xff0c;部署到云服务器上通过ip正常访问。 一、总览 1.1、Docker做的事 拉取 mysql 镜像拉取 redis 镜像拉取 jdk 镜像拉取 nginx 镜像 解释说明&#xff1a;前端项目的打包文件放在 nginx容器运行。后端…...

笙默考试管理系统-MyExamTest----codemirror(23)

笙默考试管理系统-MyExamTest----codemirror&#xff08;23&#xff09; 目录 笙默考试管理系统-MyExamTest----codemirror&#xff08;23&#xff09; 一、 笙默考试管理系统-MyExamTest 二、 笙默考试管理系统-MyExamTest 三、 笙默考试管理系统-MyExamTest 四、 笙…...

重学Java (一) 泛型

1. 前言 泛型编程自从 Java 5.0 中引入后已经超过15个年头了。对于现在的 Java 码农来说熟练使用泛型编程已经是家常便饭的事情了。所以本文就在不对泛型的基础使用在做说明了。 如果你还不会使用泛型的话&#xff0c;可以参考下面两个链接 Java 泛型详解The Java™ Tutorial…...

Docker 部署 Redis 服务

拉取最新版本的 Redis 镜像: $ sudo docker pull redis:latest在本地预先创建好 data 目录和 conf/redis.conf 文件。 使用以下命令来运行 Redis 容器: $ sudo docker run -itd --name redis --privilegedtrue -p 6379:6379 -v /home/ubuntu/docker/redis/data:/data -v /ho…...

阿里云产品试用系列-负载均衡 SLB

阿里云负载均衡&#xff08;Server Load Balancer&#xff0c;简称SLB&#xff09;是云原生时代应用高可用的基本要素。通过将流量分发到不同的后端服务来扩展应用系统的服务吞吐能力&#xff0c;消除单点故障并提升应用系统的可用性。阿里云SLB包含面向4层的网络型负载均衡NLB…...

drf 对象级权限

drf 对象级权限 Django REST Framework&#xff08;DRF&#xff09;提供了对象级别权限&#xff08;Object-level permissions&#xff09;来控制特定对象的访问权限。 简单来说&#xff1a;通过视图类中的self.get_object(pk)得到一个obj对象(视图对象)&#xff0c;在与requ…...

八大排序(二)--------冒泡排序

本专栏内容为&#xff1a;八大排序汇总 通过本专栏的深入学习&#xff0c;你可以了解并掌握八大排序以及相关的排序算法。 &#x1f493;博主csdn个人主页&#xff1a;小小unicorn ⏩专栏分类&#xff1a;八大排序汇总 &#x1f69a;代码仓库&#xff1a;小小unicorn的代码仓库…...

Linux相关概念和易错知识点(42)(TCP的连接管理、可靠性、面临复杂网络的处理)

目录 1.TCP的连接管理机制&#xff08;1&#xff09;三次握手①握手过程②对握手过程的理解 &#xff08;2&#xff09;四次挥手&#xff08;3&#xff09;握手和挥手的触发&#xff08;4&#xff09;状态切换①挥手过程中状态的切换②握手过程中状态的切换 2.TCP的可靠性&…...

跨链模式:多链互操作架构与性能扩展方案

跨链模式&#xff1a;多链互操作架构与性能扩展方案 ——构建下一代区块链互联网的技术基石 一、跨链架构的核心范式演进 1. 分层协议栈&#xff1a;模块化解耦设计 现代跨链系统采用分层协议栈实现灵活扩展&#xff08;H2Cross架构&#xff09;&#xff1a; 适配层&#xf…...

C++.OpenGL (20/64)混合(Blending)

混合(Blending) 透明效果核心原理 #mermaid-svg-SWG0UzVfJms7Sm3e {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-icon{fill:#552222;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-text{fill…...

Caliper 负载(Workload)详细解析

Caliper 负载(Workload)详细解析 负载(Workload)是 Caliper 性能测试的核心部分,它定义了测试期间要执行的具体合约调用行为和交易模式。下面我将全面深入地讲解负载的各个方面。 一、负载模块基本结构 一个典型的负载模块(如 workload.js)包含以下基本结构: use strict;/…...

Rust 开发环境搭建

环境搭建 1、开发工具RustRover 或者vs code 2、Cygwin64 安装 https://cygwin.com/install.html 在工具终端执行&#xff1a; rustup toolchain install stable-x86_64-pc-windows-gnu rustup default stable-x86_64-pc-windows-gnu ​ 2、Hello World fn main() { println…...

提升移动端网页调试效率:WebDebugX 与常见工具组合实践

在日常移动端开发中&#xff0c;网页调试始终是一个高频但又极具挑战的环节。尤其在面对 iOS 与 Android 的混合技术栈、各种设备差异化行为时&#xff0c;开发者迫切需要一套高效、可靠且跨平台的调试方案。过去&#xff0c;我们或多或少使用过 Chrome DevTools、Remote Debug…...

文件上传漏洞防御全攻略

要全面防范文件上传漏洞&#xff0c;需构建多层防御体系&#xff0c;结合技术验证、存储隔离与权限控制&#xff1a; &#x1f512; 一、基础防护层 前端校验&#xff08;仅辅助&#xff09; 通过JavaScript限制文件后缀名&#xff08;白名单&#xff09;和大小&#xff0c;提…...

从零手写Java版本的LSM Tree (一):LSM Tree 概述

&#x1f525; 推荐一个高质量的Java LSM Tree开源项目&#xff01; https://github.com/brianxiadong/java-lsm-tree java-lsm-tree 是一个从零实现的Log-Structured Merge Tree&#xff0c;专为高并发写入场景设计。 核心亮点&#xff1a; ⚡ 极致性能&#xff1a;写入速度超…...

StarRocks 全面向量化执行引擎深度解析

StarRocks 全面向量化执行引擎深度解析 StarRocks 的向量化执行引擎是其高性能的核心设计&#xff0c;相比传统行式处理引擎&#xff08;如MySQL&#xff09;&#xff0c;性能可提升 5-10倍。以下是分层拆解&#xff1a; 1. 向量化 vs 传统行式处理 维度行式处理向量化处理数…...

JavaScript 标签加载

目录 JavaScript 标签加载script 标签的 async 和 defer 属性&#xff0c;分别代表什么&#xff0c;有什么区别1. 普通 script 标签2. async 属性3. defer 属性4. type"module"5. 各种加载方式的对比6. 使用建议 JavaScript 标签加载 script 标签的 async 和 defer …...