如何更好的用好Kubernetes CSI?本文尝试从CSI简介、CSI控制器实现原理、实现示例及最佳实践4方面进行阐述。希望对您有所帮助!
一、Kubernetes CSI 简介 CSI (Container Storage Interface) 是一种标准化的接口,用于在容器编排平台(如 Kubernetes)和存储系统之间进行交互。它的设计目的是使存储插件独立于 Kubernetes 核心代码,从而简化存储系统的集成和管理。
背景和动机 在 Kubernetes 的早期阶段,存储插件(即 Volume 插件)是直接嵌入到 Kubernetes 核心代码中的。随着 Kubernetes 的发展和存储需求的增加,这种方式带来了诸多问题:
耦合性高:每次引入新的存储系统支持都需要修改 Kubernetes 核心代码,增加了维护复杂性。发布周期不同步:存储插件的发布周期与 Kubernetes 的发布周期耦合,不利于独立开发和发布。扩展性受限:无法灵活地支持多样化的存储系统。 CSI 旨在解决这些问题,通过定义标准化的接口,使存储供应商能够独立开发、发布和维护存储插件。
CSI 架构 CSI 的架构主要包括以下组件:
CSI Driver:由存储供应商提供的插件,实现了 CSI 定义的标准接口。包括 Controller Service 和 Node Service 两部分。CSI Controller:运行在 Kubernetes 控制平面,用于处理与存储卷相关的管理操作(如创建、删除、附加等)。CSI Node:运行在每个 Kubernetes 节点上,用于处理卷的挂载和卸载操作。External Provisioner:一个 Kubernetes 控制器,用于根据 PVC(PersistentVolumeClaim)动态创建存储卷。External Attacher:一个 Kubernetes 控制器,用于管理卷的附加和分离操作。External Resizer:一个 Kubernetes 控制器,用于调整卷的大小。External Snapshotter:一个 Kubernetes 控制器,用于管理卷的快照操作。 CSI 标准接口 CSI 定义了一组标准的 gRPC 接口,主要包括:
如何在 macOS 上安装 Docker Desktop Docker 是一个用于开发、部署和运行应用程序的开放平台。Docker Desktop 是 Docker 在 macOS 和 Windows 上的官方客户端,它使开发者能够轻松地在本地环境中构建、运行和共享容器化应用程序。本文将详细介绍如何在 macOS 上安装 Docker Desktop。
系统要求 在开始安装之前,请确保您的系统符合以下要求:
macOS 需要在 macOS 10.15 或更高版本上运行。至少 4GB 的 RAM。支持的文件系统格式(如 HFS+ 或 APFS)。 步骤 1:下载 Docker Desktop 安装程序 首先,前往 Docker 官方网站 下载 Docker Desktop for Mac 安装程序。
CSDN 下载地址 https://download.csdn.net/download/qcpm1983/89466044
步骤 2:安装 Docker Desktop 打开下载的 .dmg 文件
双击下载的 Docker.dmg 文件,打开安装程序。
将 Docker 图标拖动到应用程序文件夹
在打开的窗口中,将 Docker 图标拖动到应用程序文件夹。这个过程将 Docker Desktop 安装到您的系统中。
启动 Docker Desktop
代码展示
let obj_old = { name: 'Tom', age: 15, favorite: { food: 'bread', drink: 'milk' } } let obj_new = {...obj_old} console.log(obj_old === obj_new) // false console.log(obj_old.name === obj_new.name) // true console.log(obj_old.favorite === obj_new.favorite) // true 3. Array.prototype.concat()
语法:arr.concat(value0, /* … ,*/ valueN)
注:如果省略了所有 valueN 参数,则 concat 会返回调用此方法的现存数组的一个浅拷贝。
代码展示
let arr_old = [1, 2, {name: 'Tom'}] let arr_new = arr_old.concat() console.log(arr_old === arr_new) // false console.log(arr_old.name === arr_new.name) // true 4.
自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm=1001.2014.3001.5501
Django指定的模板引擎在settings.py文件中定义,代码如下:
TEMPLATES = [{
# 模板引擎,默认为Django模板
'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [], # 模板所在的目录
'APP_DIRS': True, # 是否启用APP目录
'OPTIONS': {
},
},
]
下面通过一个简单的例子,介绍如何使用模板,代码如下:
{% extends "base_generic.html" %}
{% block title %}{{ section.title }}{% endblock %}
{% block content %}
<h1>{{ section.title }}</h1>
{% for story in story_list %}
<h2>
<a href="{{ story.get_absolute_url }}">
{{ story.headline|upper }}
</a>
</h2>
<p>{{ story.tease|truncatewords:"100" }}</p>
{% endfor %}
{% endblock %}
Django模板引擎使用“{%%}”来描述Python语句区别于<HTML>标签,使用“{{}}”来描述Python变量。上面代码中的标签及说明如表7所示。
表7 Django模板引擎中的标签及说明
网关:就是网络的关口,负责请求的路由、转发、身份校验
在SpringCloud中网关的实现包括两种:
快速入门 引入依赖
路由属性 网关路由对应的Java类型是RouteDefinition,其中常见的属性有:
id:路由唯一标示uri:路由目标地址predicates:路由断言,判断请求是否符合当前路由。filters:路由过滤器,对请求或响应做特殊处理。 路由断言 Spring提供了12种基本的RoutePredicateFactory实现:
路由过滤器 网关中提供了33种路由过滤器,每种过滤器都有独特的作用。
文章目录 一、语言选择 - 英文更准确二、自洽性三、思维树四、提示词正常输出 -> 思维链 -> 自洽性 -> 思维树 进化过程五、提示词使用技巧 一、语言选择 - 英文更准确 使用 GPT 模型如果 不能得到满意的 输出结果 , 可以 尝试更换语言 , 或者 中英文混用 , 大模型知道你说的是什么 ;
不同的模型 , 针对不同的语言 , 准确率是不同的 :
ChatGPT 是针对英文训练的大模型 , 使用英文的准确性比中文要高 ;文心一言 是针对中文训练的大模型 , 使用中文的准确率比英文高 ; 另外 不同的领域的问题 , 使用不同的语言 , 也有不同的准确率 ;
偏西方领域的话题 , 使用英文准确率更高 ;偏东方领域的话题 , 使用中文准确率更高 ; 二、自洽性 使用 相同的提示词 , 使用以下两种方式 , 每次生成不同的文本结果 ;
通过多次 输入提示词 来实现 ;使用批量生成功能 , 一次性生成多个结果 ; 将生成的 多个 输出结果 呈现给 用户 , 设计一个 投票工具 , 让用户或评估者对每个生成的文本结果进行评分或投票 , 或者让 大模型 自己投票 选择最好的一个 ;
一、袖珍计算器 袖珍计算器方法主要运用到了我们高数上所学的关于e底数转化的思想,即
一种用指数函数 exp 和对数函数 ln 代替平方根函数的方法 :
1、exp函数: exp是 C 标准库 <math.h> 中的一个函数,用于计算 e 的 x 次幂,其中 e 是自然对数的底数(约为 2.71828)。
函数声明:
#include <math.h> double exp(double x); float expf(float x); long double expl(long double x); 函数用法:
#include <stdio.h> #include <math.h> int main () { double x = 0; printf("e 的 %lf 次幂是 %lf\n", x, exp(x)); printf("e 的 %lf 次幂是 %lf\n", x+1, exp(x+1)); printf("e 的 %lf 次幂是 %lf\n", x+2, exp(x+2)); return(0); } 2、log函数: log是 C 标准库 <math.
prim 出圈法,时间复杂度 O ( n 2 ) O(n^2) O(n2)
#include<iostream> #include<vector> using namespace std; #define MAX_N 5000 #define inf 100000000 struct edge{ int v,w; }; vector<edge>e[MAX_N+5]; int d[MAX_N+5],vis[MAX_N+5]; int n,m; int ans=0,cnt=0; bool prim(int s) { for(int i=0;i<=n;i++)d[i]=inf; d[s]=0; for(int i=1;i<=n;i++) { int u=0; for(int j=1;j<=n;j++) if(!vis[j]&&d[j]<d[u])u=j; vis[u]=1; ans+=d[u]; if(d[u]!=inf)cnt++; for(auto ed:e[u]) { int v=ed.v,w=ed.w; if(d[v]>w)d[v]=w; } } return n==cnt; } int main() { cin>>n>>m; for(int i=1,a,b,c;i<=m;i++) { scanf("%d%d%d",&a,&b,&c); e[a].
Qwen2的web搭建(streamlit) 千问2前段时间发布了,个人觉得千问系列是我用过最好的中文开源大模型,所以这里基于streamlit进行一个千问2的web搭建,来进行模型的测试
一、硬件要求 该文档中使用的千问模型为7B-Instruct,需要5g以上的显存,如果是轻薄本不建议进行本地测试(下图为测试时的实际显存占用)
二、环境准备 对于环境的基本要求
transformers torch streamlit sentencepiece accelerate transformers_stream_generator 上述是基础的环境准备,可以用conda创建一个新的环境来进行配置。在下载库时可以使用清华大学的镜像进行加速,如下所示
pip install transformers -i https://pypi.tuna.tsinghua.edu.cn/simple 三、模型下载 这里推荐使用huggingface镜像网站进行下载,因为在下载中断后,再次请求时会从上次中断的地方继续,而不是重新下载。
https://hf-mirror.com
以千问为例,在终端的下载请求为
huggingface-cli download --resume-download Qwen/Qwen2-7B-Instruct --local-dir ./qwen2 四、web代码编写 from transformers import AutoTokenizer,AutoModelForCausalLM import torch import streamlit as st #在侧边栏创建标题 with st.sidebar: st.markdown("qwen2") "hello world" #创建滑块,默认值为512,范围在0到1024之间 max_length = st.slider("max_length",0,1024,512,step=1) #创建标题和副标题 st.title("qwen2 chatbot") st.caption("test") #你下载到本地的模型路径 model_path = "../models/qwen2-1.5b-Instruct" #@streamlit.cache_resource 是一个用于缓存昂贵或频繁调用的资源(如大型文件、网络资源、或数据库连接)的装饰器。这个装饰器可以帮助你提高应用的性能,通过缓存那些不经常变更但加载需要大量时间或计算资源的数据。 #定义的函数来获取tokenizer和model @st.cache_resource def get_model(): tokenizer = AutoTokenizer.from_pretrained(model_path,use_fast=False) model = AutoModelForCausalLM.from_pretrained(model_path,torch_dtype=torch.float16,device_map='auto') return tokenizer,model tokenizer,model = get_model() #如果没有消息,则创建默认的消息列表 if "
1. 导入资源包 // An highlighted block var foo = 'bar'; 注:
1. import cv2: 导入OpenCV库,这是一个非常强大的计算机视觉库,用于处理图像和视频数据。
2. import tkinter as tk: 导入Tkinter库,这是Python的标准GUI库,用于创建桌面应用程序。
3. from tkinter import filedialog: 从Tkinter库中导入filedialog模块,这个模块提供了一个文件选择对话框,允许用户选择文件或目录。
4. from PIL import Image, ImageTk: 从Python Imaging Library (PIL)中导入Image和ImageTk。PIL是一个图像处理库,而ImageTk是PIL的扩展,用于在Tkinter中显示图像。
2. 初始化窗口 初始化窗口 root = tk.Tk() root.title("Cat and Dog Detector") 设置窗口大小 window_width = 800 window_height = 600 root.geometry(f"{window_width}x{window_height}") 注:
1. root = tk.Tk(): 这行代码创建了一个Tkinter窗口的根实例,通常称为root。这是所有Tkinter GUI应用程序的基础。
2. root.title(“Cat and Dog Detector”): 这行代码设置了窗口的标题,出现在窗口的标题栏上。在这个例子中,标题被设置为“Cat and Dog Detector”。
前言 c++的发展史: C++的起源可以追溯到1979年,当时Bjarne Stroustrup在贝尔实验室开始开发一种名为“C with Classes”的语言。以下是C++发展的几个关键阶段:
1979年:Bjarne Stroustrup在贝尔实验室开始开发“C with Classes”。1983年:语言正式命名为C++,添加类、继承、函数重载等特性。1985年:发布《The C++ Programming Language》一书,标志C++的正式发布。1998年:发布第一个国际标准版本C++98。2011年:发布C++11,加入自动类型推导、lambda表达式、智能指针等新特性。2014年:发布C++14,进行小幅改进。2017年:发布C++17,新增结构化绑定、std::optional等特性。2020年:发布C++20,引入概念、协程、模块等重大更新。 C++的演进不断增加新特性,提升性能和编程效率,适应现代开发需求。
一 命名空间: 在C语言中我们定义变量函数rand但是它是<stdli.b>库里面的函数,这时编译器无法区分它到底是库里面的函数还是全局变量rand,那么它们发生就会冲突,因为C语言规定定义的变量不能与库里面的函数和那32个关键字一样,如果我因为某种需求需要定义一样的变量那这时候就会出错,这时候c++中的namespace就是为了针对此类问题
namespace:
定义:namespace 是 C++ 中的一个关键字,用于定义命名空间。命名空间是一个逻辑上分组的机制,主要用来解决命名冲突问题。通过使用命名空间,可以将相同名称的标识符(如变量、函数、类等)放在不同的命名空间中,从而避免冲突。
命名空间定义:
#define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h> #include <stdlib.h> namespace bit { int rand = 10; } int main() { printf("%d", bit::rand); return 0; } 输出:
首先我们来定义一个命名空间需要使用到namespace关键字后面需要跟一个命名空间(结束后不需要加;),其中命名空间可以是函数变量类型。要访问命名空间中的成员,可以使用 :: 运算符
using命名:
我们在程序中频繁的使用命名空间里面的特定变量那需要输出多少个就需要多少个::运算符(作用域解析运算符),那c++用using可以直接访问a而不需要bit::前缀。就很好的解决了这个问题
#include <stdio.h> #include <stdlib.h> namespace bit { int rand = 10; int a = 0; } using bit::a; //using bit::rand; int main() { printf("
概览 WWDC 2024 重装升级的 SwiftUI 6.0 让 Apple 不同平台(iOS 18/macOS 15)显得愈发的冰壶玉衡、美轮美奂。
之前梦寐以求的颜色混合功能在 WWDC 24 里终于美梦成真啦!
在本篇博文中,您将学到如下内容: 概览1. 梦想成真:混合 Colors2. 混合两种以上颜色总结 相信学完本课之后,在 SwiftUI 6.0 中混合两种颜色将会变得轻而易举、小菜一碟。
那还等什么呢?让我们马上开始 Color 大“混战”吧!
Let‘s go!!!😉
1. 梦想成真:混合 Colors 何曾几时,在 SwiftUI 中我们希望有一种恣意混合多种颜色的方法。这不,在本届 WWDC 24 中苹果仿佛听到了我们秃头码农的心声。
于是乎,在 SwiftUI 6.0 中 Apple 终于为 Color 结构新增了 mix() 方法让“难关”冰解的破:
mix 方法签名很简单:我们只需传入两个需要混合的颜色、一个混合百分比(blending fraction)外加一个颜色空间(color space)即可。
值得说明的是,这里的颜色空间参数有两种选择:device 和 perceptual,默认情况下我们应该使用后者(perceptual)。因为从理论上来说,混合颜色的方式应该对人眼有意义,并且在不同设备屏幕之间是一致的。
而基于设备颜色空间(device)的混合可能产生不同的结果,这些结果也许是我们想要的,也许不是我们想要的。最佳方式是通过实验来查看实际的效果差异。
在下面的代码中,我们让粉色和蓝色以 50% 的混合度融合在了一起:
let leftColor = Color.pink let rightColor = Color.blue let mix = 0.
python爬虫篇(项目案列讲解-爬取小说) 大家谨记爬虫只是用来方便大家从互联网上检索信息,获取免费资源,不得以危害或者窃取对方资源使用为目的进行违法犯罪。牢记网络安全法。
1.爬取笔趣阁小说。 学习一下思路:
1.我们进入需要爬取到的小说界面,右键开发者工具 ,选中元素显示,然后找到需要爬取的小说章节模块在代码中的位置。
将a标签中的文本内容复制,然后ctrl+u打开源代码 ctrl+f将刚刚的文本内容复制查找是否有这个模块。(比较爽的是,刚好这里有,可以不需要去查看网络请求和script代码了)
那么我们现在可以可以来获取源代码了
import requests from lxml import etree # 网页网址(指向小说章节的那部分) url = "https://www.bige3.cc/book/3319/" #UA伪装 headers = { "User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36" } #获取源代码请求 注意参数的书写 response = requests.get(url,headers=headers) #源代码的具体编码格式建议先看一下网页中的meta设置的编码格式 meta中的charset response.encoding = 'utf-8' #赋值 webCode = response.text 编码格式的查看方式。
获取源代码之后,我们现在需要去解析一下这串源代码
选中这个章节,你现在需要做的是右键->复制->复制xPath 然后回到python代码中按照格式填写即可
实例图
复制粘贴基本成功
import requests from lxml import etree # 网页网址(指向小说章节的那部分) url = "https://www.bige3.cc/book/3319/" #UA伪装 header = { "
Tomcat高效部署与性能优化
一、引言 Apache Tomcat是一个广泛使用的开源Java Web应用服务器,它轻量级、易扩展,并支持Servlet和JSP规范。然而,随着业务的发展和用户数量的增长,Tomcat的性能和稳定性变得至关重要。本文将介绍如何高效部署Tomcat并进行性能优化,以确保Web应用的稳定运行和高效响应。
二、Tomcat高效部署 1. 环境准备
在部署Tomcat之前,需要确保服务器环境满足要求。首先,选择适合业务需求的操作系统和JDK版本。其次,检查服务器的硬件资源,如CPU、内存、磁盘和网络等,确保它们能够支撑Tomcat的运行。
2. 安装Tomcat
从官方网站下载最新版本的Tomcat,并解压到合适的目录。配置环境变量,确保Java和Tomcat能够正常启动。
3. 配置Tomcat
编辑Tomcat的配置文件(如server.xml),进行必要的配置。例如,设置合适的端口号、连接数、超时时间等。同时,可以根据需要配置虚拟主机、SSL等高级功能。
4. 部署应用
将Web应用打包成WAR文件,并放置到Tomcat的webapps目录下。Tomcat会自动解压WAR文件并部署应用。也可以通过Tomcat的管理界面进行应用的部署和管理。
5. 启动Tomcat
在命令行中进入Tomcat的bin目录,执行startup.bat(Windows)或startup.sh(Linux)脚本来启动Tomcat。
三、Tomcat性能优化 1. 调整JVM参数
JVM参数对Tomcat的性能有很大影响。可以通过编辑Tomcat的启动脚本(如catalina.sh或catalina.bat),设置合适的JVM参数,如堆大小、栈大小、垃圾回收器等。根据应用的特性和服务器的硬件资源,进行合理的调整。
2. 优化连接池
Tomcat使用连接池来管理数据库连接。可以通过配置连接池的参数来优化性能,如最大连接数、空闲连接数、连接超时时间等。根据应用的并发量和数据库的性能,进行合适的设置。
3. 启用压缩功能
启用Tomcat的压缩功能可以减小传输的数据量,提高响应速度。在server.xml中配置Connector元素,启用compression和compressionMinSize属性,并设置合适的压缩算法和压缩级别。
4. 禁用不必要的组件
Tomcat包含了许多组件和模块,但并非所有组件都是必需的。通过禁用不必要的组件,可以减少内存占用和CPU开销。在server.xml中删除或注释掉不需要的Connector、Valve等元素。
5. 监控与调优
使用Tomcat自带的监控工具(如JMX)或第三方监控工具(如Prometheus、Grafana等),对Tomcat的运行状态进行实时监控。根据监控数据,分析应用的性能瓶颈并进行调优。例如,调整线程池大小、优化数据库查询语句、增加缓存等。
前言 4.19日凌晨正准备睡觉时,突然审稿项目组的文弱同学说:Meta发布Llama 3系列大语言模型了,一查,还真是
本文以大模型开发者的视角,基于Meta官方博客的介绍:Introducing Meta Llama 3: The most capable openly available LLM to date,帮你迅速梳理下LLama的关键特征,并对比上一个版本的LLama2,且本文后续,将更新用我司paper-review数据集微调llama3的训练过程
第一部分 Meta发布Llama 3:所有大模型开发者的福音 1.1 Llama 3的性能 1.1.1 在多个榜单上超越Google的gemma 7B、Mistral 7B 此次发布的Llama 3有两个版本:8B 和 70B。由于预训练和指令微调的加强,模型在推理、代码生成和指令跟踪等方面的能力得到比较大的提高,最终在多个榜单上超越Google的gemma 7B、Mistral 7B(当然了,我还是得说一句,榜单肯定能够说明一些东西,但不代表全部)
1.1.2 一套专门的评估数据集:1800个prompt 涵盖12类任务 为了更好的评估llama3的性能,Meta开发了一套新的高质量人类评估集。该评估集包含 1,800 个prompt,涵盖 12 个关键用例:寻求建议、头脑风暴、分类、封闭式问答、编码、创意写作、提取、塑造角色/角色、开放式问答、推理、重写和总结
且为了防止模型在此评估集上过度拟合,即使Meta的建模团队也无法访问它(说白了,保证评估数据集中的数据不被模型事先学到)
下图显示了Meta针对 Claude Sonnet、Mistral Medium 和 GPT-3.5 对这些类别和提示进行人工评估的汇总结果(compared to competing models of comparable size in real-world scenarios,即PK的开源模型也都是70B左右的大小)
且llama3的预训练模型这些榜单上PK同等规模的其他模型时,亦有着相对突出的表现
1.2 Llama 3:模型架构、预训练数据、扩大预训练和指令微调 1.2.1 模型架构:继续transformer解码器架构、分组查询注意力、8K上下文 和Llama 2一样,Llama 3 继续采用相对标准的decoder-only transformer架构,但做了如下几个关键的改进
Llama 3 使用具有 128K tokens的tokenizer
jQuery jQuery操作HTML元素 增加元素 append(在内部进行尾插)
let newImg = $("<img src='///'>"); $("img").append(newImg); appendTo
let newImg = $("<img src='///'>"); newImg.appendTo($("img")); //下列方法使用方式相同 perpend(在内部进行头插)
perpendTo
after(选中元素的平级进行尾插)
insertAfter
befor(选中元素的平级进行头插)
insertBefore
删除元素 remove(将选中的元素及其子元素全部删除)
empty(将选中的元素及其子元素全部清空)
修改元素 修改文本
text(有参代表修改文本内容,无参代表获取元素文本内容)
html(有参修改内部内容,包括带有html标签的内容,无参代表获取元素html内容)
val(有参代表向input输入框添加内容,无参代表获取input输入内容)
<input name="username" id="username"/> <script> $(function(){ let username = $("#username").val();//只针对value属性,一般来说,只有input标签有value属性 }) </script> 修改属性
attr(一个参数代表取值,两个参数代表修改属性值) 增加属性
addClass(向选中的元素添加class属性) 删除属性
removeClass(删除选中元素的calss属性)
在增加calss和删除class之间切换:toggleClass()
修改样式
css(一个参数代表取值,两个参数代表修改属性值) 查找元素 基本筛选:first(),last(),eq(index)
选择第一个$(“div:first”)/$(“div”).fisrt()选择最后一个$(“div:last”)/$(“div”).last()选择指定下标$(“div:eq(1)”)/$(“div”).eq(1)选择大于指定下标$(“div:gt(1)”)选择小于指定下标$(“div:lt(1)”)选择下标为奇数$(“div:even”)选择下标为偶数$(“div:odd”) 子代选择
属于其父元素的第一个子元素的所有 元素 $("p:first-child")
属于其父元素的第一个 元素的所有 元素 $("p:first-of-type")
属于其父元素的最后一个子元素的所有 元素$("p:last-child")
属于其父元素的最后一个 元素的所有 元素$("p:last-of-type")
属于其父元素的第二个子元素的所有 元素$("
文章目录 💐线程池概念以及什么是工厂模式💐标准库中的线程池💐什么是工厂模式?💐ThreadPoolExecutor💐模拟实现线程池 💐线程池概念以及什么是工厂模式 线程的诞生是因为,频繁的创建进程太重量了(开销较大),所以引入了线程,但是呢,对于线程来讲,如果更加频繁的创建和销毁,那么开销也会慢慢的变大,所以,又引入了两种经典的方法来进一步提高:
1.协程:又称为轻量级线程,线程比较轻量是因为线程省略了分配资源的环节,而协程它在着基础上又省略了操作系统调度执行的环节,由程序员自己调度;在Java中呢,主要使用线程池,所以对于协程只是简单提一下;
2.线程池:
举一个例子:
假如我是一个很漂亮的妹子,又有许多的男生正在追我,然后我就选择了一个男生A做我男朋友,但是呢,经过一段时间之后,我就腻了,就想要和男生B谈恋爱,所以,我就和男生A提出了分手,然后和男生B培养感情,等到有了感情基础,等有了感情基础后就拿下男生B,但是,过来一段时间后,我又想和男生C谈恋爱,所以就接着重复上面的套路,先培养感情等等………
而对于上面这种换男朋友的方式,感觉效率太慢,所以,我就有了一种新的方式,在和男生A谈恋爱的同时,偷偷的和男生B、C、D等多个男生培养感情,等到我向和谁谈恋爱时,那不就是捅破一层窗户纸的事情么,就可以挑选一个直接谈恋爱,这样的效率不久高了很多么,所以,对于偷偷的和我培养感情的这群男生也就可以称为“备胎池”;
而我们的线程池也是上面这种模式,在向池中添加任务时,直接从线程池中拿线程就可以了,就不比再创建了,直接拿过来使用即可,这样也就降低了线程创建的开销;所以线程池的就是先把线程创建好,放进池子里,等到后续想要使用时,直接从池子里取;
这里就会有一个问题:为啥从线程池里面取线程比创建线程效率高?
首先,创建新的线程这个动作,是内核态+用户态相互配合完成的;
而从线程池中取这个动作,是用户态操作完成的;
所以这里就涉及到了两个新名词,什么是用户态,什么是内核态
如果一段程序是在系统内核中完成的,此时就称为内核态
如果不是,则称为用户态;
而操作系统呢,是由内核+配套的应用程序组成的,创建线程,就需要调用系统API,进入到内核中,按照内核态的方式来完成一系列的动作;
但是,为什么内核态操作的效率比较低呢?请看下图
💐标准库中的线程池 在Java中,提供了一个类——Executors创建线程池;但是,线程池对象的创建并不是直接new出来的,而是通过一个方法的返回值,返回了一个线程池对象;
public class MyThreadPool { public static void main(String[] args) { //创建一个动态的线程池 ExecutorService es = Executors.newCachedThreadPool(); es.submit(new Runnable() { @Override public void run() { System.out.println("hello"); } }); } } 创建线程池对象分为以下步骤:
1.使用Executors.newCachedThreadPool 创建出一个动态增长的线程池,为什么要用Executors.newCachedThreadPool的方式创建线程池,而不是直接new Executors?这里就涉及到了一个设计模式——工厂模式:
💐什么是工厂模式? 工厂模式:定义一个工厂类,通过调用工厂类中的不同方法来实现对象的实例化,从而创建出不同作用的对象;
举个例子,我们在创建对象时,会使用new关键字,通过构造方法来创建对象,但是使用构造方法创建对象会又很大的局限性,举个例子:假如我现在想要使用笛卡尔坐标来创建一个点对象,代码如下:
public class Point { //笛卡尔坐标系需要提供一个x,y坐标 private int x; private int y; //通过笛卡尔坐标的方式创建一个对象 public Point(int x, int y) {}; } 但是,我现在又想通过极坐标的方式创建点对象
Paywright录制工具UI
在上一篇博客中介绍了如何从0构建一款具备录制UI测试的小工具。此篇博客将从源码层面上梳理playwright录制原理。当打开playwright vscode插件时,点击录制按钮,会开启一个新浏览器,如下图所示,在新开浏览器页面上,有录制,查看等按钮。
查看vscode的源码,会看到有个recorder的folder,该folder下由react构建了一个应用的UI,执行npm run dev,在5173端口启动这样一个web应用,web应用的UI如下图所示,可以看到里面的录制按钮等和上图vscode插件大家的相同。从这里可以推断,recorder里面用react构建的componen被嵌入到了开启的浏览器中。
通过上一篇博客的介绍,我们知道,实现录制功能的核心原理是是在浏览器中注入了脚本,通过监听用户行为,并将用户行为转换为playwright的语法,从而实现录制脚本的能力。
Playwright的脚本注入
查看playwright得source code,在playwright-core/src/server/injected目录下就是注入脚本相关的内容。查看injected/recorder/recorder.ts脚本,在该脚本中在interface RecordTool中定义了大量操作页面元素的方法,例如onClick,onInput等。
在recorder.ts中,在document对象上添加了很多listener,如下图所示:
具体每个listener完成了哪些逻辑呢?以onInput为例子,下面的onInput方法的部分代码,可以看到,首先是获取Input的目标对象target,再依次判断Input的具体属性,例如时textarea,或者select,或者checkbox等。根据判断的结果返回不同的内容。
生成locator
下面是playwright中生产locator的一个function,可以看到通过注入脚本injectedScript._evaluator.begin()开始,这段代码的主要作用是为指定的HTML元素生成一个或多个唯一的CSS选择器,并返回相关的选择器和匹配的元素列表,以便用于自动化测试或其他需要唯一定位元素的场景。首先是初始化,开始评估选择器生成过程,启用ARIA缓存。如果选项中包含 forTextExpect,则会尝试为目标元素生成一个带有文本的选择器。否则,首先尝试在目标元素的父元素或影子宿主中找到符合特定角色(如按钮、链接等)的元素。然后根据是否允许多个选择器,生成一个或多个选择器,可能包含或不包含文本和CSS ID。生成locator结束后,使用Set去重生成的选择器列表,确保唯一性。最后返回结果。可以看到,为了生成合理的locator,playwright进行很多逻辑处理来保证生成locator的唯一性和合理性。
export function generateSelector(injectedScript: InjectedScript, targetElement: Element, options: GenerateSelectorOptions): { selector: string, selectors: string[], elements: Element[] } { injectedScript._evaluator.begin(); beginAriaCaches(); try { let selectors: string[] = []; if (options.forTextExpect) { let targetTokens = cssFallback(injectedScript, targetElement.ownerDocument.documentElement, options); for (let element: Element | undefined = targetElement; element; element = parentElementOrShadowHost(element)) { const tokens = generateSelectorFor(injectedScript, element, { .
功能描述 打开微信扫一扫,扫描产品上的二维码,弹出小程序,跳到“邀请用户”页面。解析二维码中的参数,自动填充到页面中的“邀请码”输入框。 操作步骤 首先,要到微信公众平台对扫普通链接二维码打开小程序功能进行配置。
找到"开发管理"-“开发设置”-“扫普通链接二维码打开小程序”
填写配置项 线上版本的二维码配置中,二维码规则和校验文件两项的配置需要后端配合。(不用填写“测试链接”)测试时,二维码规则可以自定义,校验文件不用管,但是测试链接一定要填写,测试范围选择体验版,这样的话,用微信扫描测试链接的二维码,就可以跳转到体验版小程序。
测试时,可以去草料网根据上一步中填写的测试链接生成二维码。
小程序页面内接参步骤:
onLoad(async (options) => { //onLoad参数options.q可以拿到编码加密后的二维码链接 if (options.q) { //1.将二维码链接解码 let codeStr = decodeURIComponent(options.q) //2.取出对应参数 const codeId = codeStr.match(/[?&]id=(\d+)/) devId.value = codeId ? codeId[1] : '' } })
导言 填鸭似的教育确实不行,我高中时学过集合,不知道有什么用,毫无兴趣,等到我学了一门编程语言后,才发现集合真的很有用;可以去重,可以看你有我没有的,可以看我有你没有的;列表是我最喜欢的数据结构,我最喜欢把列表和元组结合起来用,比如[(张飞,刘备集团),(贾诩,曹操集团)]。映射,也就是Python里面的字典,也是我的最爱之一!让学习变得有趣起来,就用三国混战来举例吧!
列表映射和集合出场 错误处理 //叫一个没来的将领,会发生什么? try{ String myGengeral = generalInfo['马超']; }catch(e){ print('出错:$e'); } 出错:type 'Null' is not a subtype of type 'String'
代码 void main(){ print('***************************列表**********************************'); List<String> generals = ['赵云','吕布','典韦','夏侯惇','赵云']; String liubeiGroup = generals[0]; print('刘备集团将领有:$liubeiGroup'); //典韦要保护曹操的,换曹仁出场 generals[2] = '曹仁'; print('曹操换人后,出场混战的将领有:$generals'); //孙权说:“你们又不带我玩?我要派甘宁来参加!” generals.add('甘宁'); print('孙权加人后,出场混战的将领有:$generals'); //曹操说:“像吕布这种小人,不要让他入场,必须德才兼备才有资格入场!” generals.remove('吕布'); print('移除人品差的后,出场混战的将领有:$generals'); //组委会主席汉献帝说:“让我统计下参赛选手数量,听我念名单依次入场!” print('汉献帝宣布:总决赛出场混战的将领总共有:${generals.length}位!'); for (String general in generals){ print('请入场:$general!'); } print('***************************映射**********************************'); Map<String,dynamic> generalInfo = { '名字':'赵云', '公司':'刘备集团', '性别':'男', }; print('性别有点多余'); generalInfo.remove('性别'); print(generalInfo); print('第一个出场的是${generalInfo['名字']}'); //自我介绍了说了名字么? print('${generalInfo.