前端技术
HTML
CSS
Javascript
前端框架和UI库
VUE
ReactJS
AngularJS
JQuery
NodeJS
JSON
Element-UI
Bootstrap
Material UI
服务端和客户端
Java
Python
PHP
Golang
Scala
Kotlin
Groovy
Ruby
Lua
.net
c#
c++
后端WEB和工程框架
SpringBoot
SpringCloud
Struts2
MyBatis
Hibernate
Tornado
Beego
Go-Spring
Go Gin
Go Iris
Dubbo
HessianRPC
Maven
Gradle
数据库
MySQL
Oracle
Mongo
中间件与web容器
Redis
MemCache
Etcd
Cassandra
Kafka
RabbitMQ
RocketMQ
ActiveMQ
Nacos
Consul
Tomcat
Nginx
Netty
大数据技术
Hive
Impala
ClickHouse
DorisDB
Greenplum
PostgreSQL
HBase
Kylin
Hadoop
Apache Pig
ZooKeeper
SeaTunnel
Sqoop
Datax
Flink
Spark
Mahout
数据搜索与日志
ElasticSearch
Apache Lucene
Apache Solr
Kibana
Logstash
数据可视化与OLAP
Apache Atlas
Superset
Saiku
Tesseract
系统与容器
Linux
Shell
Docker
Kubernetes
[SSH隧道MySQL安全访问策略]的搜索结果
这里是文章列表。热门标签的颜色随机变换,标签颜色没有特殊含义。
点击某个标签可搜索标签相关的文章。
点击某个标签可搜索标签相关的文章。
转载文章
...,高可用,高性能,高安全,易运维,易部署,易接入等能力。 3、功能设计与实现:对架构设计的底层代码级别实现。如公共核心类,接口实现,应用发现规则、接口变更等。 技术经理 人生就是不断上升的过程,你已经到达经理的层次了。如今的你,需要不断提高领导力,需要定期召开团队会议讨论问题。 首先我们要更加自信,在工作中显示自己的功力,给讲话增添力量。如:“本次项目虽然有很大的困难,我们也需苦战到底。当然示先垂范,身先士卒,方能成功!” 技术经理有时候也可能叫系统分析员,一些小公司可能会整个公司或者部门有一个技术经理。技术经理承担的角色主要是系统分析、架构搭建、系统构建、代 码走查等工作,如果说项目经理是总统,那么技术经理就是总理。当然不是所有公司都是这样的,有些公司项目经理是不管技术团队的,只做需求、进度和同客户沟 通,那么这个时候的项目经理就好像工厂里的跟单人员了,这种情况在外包公司比较多。对于技术经理来说,着重于技术方面,你需要知道某种功能用哪些技术合 适,需要知道某项功能需要多长的开发时间等。同时,技术经理也应该承担提高团队整体技术水平的工作。 你需要和大家站在一起,因为人们也都有解决问题的能力,更需要有以下的能力与责任: 1、任务管理:开发工作量评估、定立开发流程、分配和追踪开发任务 2、质量管理:代码review、开发风险判断/报告/协调解决 3、效率提升:代码底层研发和培训、最佳代码实践规范总结与推广、自动化生产工具、自动化部署工具 4、技术能力提升:招聘面试、试题主拟、新人指导、项目复盘与改进 技术总监 如果一个研发团队超过20人,有多条产品线或业务量很大,这时已经有多个技术经理在负责每个业务,这时需要一位技术总监。 主要职责: 1、组建平台研发部,与架构师共建软件公共平台,方便各条产品业务线研发。 2、通过技术平台、通过高一层的职权,管理和协调公司各个部门与本部门各条线。现在每个产品线都应该有合格的技术经理和高级程序员。 结语:我们相信,每个人都能成为IT大神。现在开始,找个师兄带你入门,让你的学习之路不再迷茫。 这里推荐我们的前端学习交流圈:784783012,里面都是学习前端的从最基础的HTML+CSS+JS【炫酷特效,游戏,插件封装,设计模式】到移动端HTML5的项目实战的学习资料都有整理,送给每一位前端小伙伴。 最新技术,与企业需求同步。好友都在里面学习交流,每天都会有大牛定时讲解前端技术! 点击:前端技术分享 本篇文章为转载内容。原文链接:https://blog.csdn.net/webDk/article/details/88917912。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2024-05-10 13:13:48
755
转载
转载文章
...,这是一个很好的开发策略。 不用常规的 Ubiquity,Lubuntu 20.04 使用的是 Calamares 安装程序 在新版本中使用了全新的 Calamares 安装程序,取代了其它 Ubuntu 官方版本使用的 Ubiquity 安装程序。 整个安装过程在大约能在 10 分钟内完成,比之前 Lubuntu 的版本稍微快一些。 由于镜像文件附带了预先安装的基本应用程序,所以你可以很快就可以完成系统的完全配置。 不要直接从 Lubuntu 18.04 升级到 Lubuntu 20.04 通常,你可以将 Ubuntu 从一个 LTS 版本升级到另一个 LTS 版本。但是 Lubuntu 团队建议不要从 Lubuntu 18.04 升级到 20.04。他们建议重新安装,这才是正确的。 Lubuntu 18.04 使用 LXDE 桌面,20.04 使用 LXQt。由于桌面环境的巨大变化,从 18.04 升级到 20.04 将导致系统崩溃。 更多的 KDE 和 Qt 应用程序 下面是在这个新版本中默认提供的一些应用程序,正如我们所看到的,并非所有应用程序都是轻量级的,而且大多数应用程序都是基于 Qt 的。 甚至使用的软件中心也是 KDE 的 Discover,而不是 Ubuntu 的 GNOME 软件中心。 ◈ Ark – 归档文件管理器◈ Bluedevil – 蓝牙连接管理◈ Discover 软件中心 – 包管理系统◈ FeatherPad – 文本编辑器◈ FireFox – 浏览器◈ K3b – CD/DVD 刻录器◈ Kcalc – 计算器◈ KDE 分区管理器 – 分区管理工具◈ LibreOffice – 办公套件(Qt 界面版本)◈ LXimage-Qt – 图片查看器及截图制作◈ Muon – 包管理器◈ Noblenote – 笔记工具◈ PCManFM-Qt – 文件管理器◈ Qlipper – 剪贴板管理工具◈ qPDFview – PDF 阅读器◈ PulseAudio – 音频控制器◈ Qtransmission – BT 下载工具(Qt 界面版本)◈ Quassel – IRC 客户端◈ ScreenGrab – 截屏制作工具◈ Skanlite – 扫描工具◈ 启动盘创建工具 – USB 启动盘制作工具◈ Trojita – 邮件客户端◈ VLC – 媒体播放器◈ MPV 视频播放器 测试 Lubuntu 20.04 LTS LXQt 版 Lubuntu 的启动时间不到一分钟,虽然是从 SSD 启动的。 LXQt 目前需要的内存比基于 Gtk+ 2 的 LXDE 稍微多一点,但是另一种 Gtk+ 3 工具包也需要更多的内存。 在重新启动之后,系统以非常低的内存占用情况运行,大约只有 340 MB(按照现代标准),比 LXDE 多 100 MB。 LXQt 不仅适用于硬件较旧的用户,也适用于那些希望在新机器上获得简约经典体验的用户。 桌面布局看起来类似于 KDE 的 Plasma 桌面,你觉得呢? 在左下角有一个应用程序菜单,一个用于显示固定和活动的应用程序的任务栏,右下角有一个系统托盘。 Lubuntu 的 LXQt 版本可以很容易的定制,所有的东西都在菜单的首选项下,大部分的关键项目都在 LXQt “设置”中。 值得一提的是,LXQt 在默认情况下使用流行的 Openbox 窗口管理器。 与前三个发行版一样,20.04 LTS 附带了一个默认的黑暗主题 Lubuntu Arc,但是如果不适合你的口味,可以快速更换,也很方便。 就日常使用而言,事实证明,Lubuntu 20.04 向我证明,其实每一个 Ubuntu 的分支版本都完全没有问题。 结论 Lubuntu 团队已经成功地过渡到一个现代的、依然轻量级的、极简的桌面环境。LXDE 看起来被遗弃了,迁移到一个活跃的项目也是一件好事。 我希望 Lubuntu 20.04 能够让你和我一样热爱,如果是这样,请在下面的评论中告诉我。请继续关注! via: https://itsfoss.com/lubuntu-20-04-review/ 作者:Dimitrios Savvopoulos 选题:lujun9972 译者:qfzy1233 校对:wxy 本文由 LCTT 原创编译,Linux中国 荣誉推出 本篇文章为转载内容。原文链接:https://blog.csdn.net/weixin_39539807/article/details/111619265。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-05-17 18:52:15
318
转载
转载文章
...or是使用主机缓冲区访问目标的访问器,它使访问的数据可以在主机上使用。通过构建Host Accessor可以将数据同步回主机,除此之外还可以通过销毁缓冲区将数据同步回主机。 buf是存储数据的缓冲区。 host_accessor b(buf,read_only); 除此之外还可以将buf设置为局部变量,当系统超出buf生存期,buf被销毁,数据也将转移到主机中。 矢量相加源代码 根据上面的知识,这里展示了利用DPC++实现矢量相加的代码。 //第一行在jupyter中指明了该cpp文件的保存位置%%writefile lab/vector_add.cppinclude <CL/sycl.hpp>using namespace sycl;int main() {const int N = 256;// 初始化两个队列并打印std::vector<int> vector1(N, 10);std::cout<<"\nInput Vector1: "; for (int i = 0; i < N; i++) std::cout << vector1[i] << " ";std::vector<int> vector2(N, 20);std::cout<<"\nInput Vector2: "; for (int i = 0; i < N; i++) std::cout << vector2[i] << " ";// 创建缓存区buffer vector1_buffer(vector1);buffer vector2_buffer(vector2);// 提交矢量相加任务queue q;q.submit([&](handler &h) {// 为缓存区创建访问器accessor vector1_accessor (vector1_buffer,h);accessor vector2_accessor (vector2_buffer,h);h.parallel_for(range<1>(N), [=](id<1> index) {vector1_accessor[index] += vector2_accessor[index];});});// 创建主机访问器将设备中数据拷贝到主机当中host_accessor h_a(vector1_buffer,read_only);std::cout<<"\nOutput Values: ";for (int i = 0; i < N; i++) std::cout<< vector1[i] << " ";std::cout<<"\n";return 0;} 运行结果 统一共享内存 (Unified Shared Memory USM) 统一共享内存是一种基于指针的方法,是将CPU内存和GPU内存进行统一的虚拟化方法,对于C++来说,指针操作内存是很常规的方式,USM也可以最大限度的减少C++移植到DPC++的代价。 下图显示了非USM(左)和USM(右)的程序员开发视角。 类型 函数调用 说明 在主机上可访问 在设备上可访问 设备 malloc_device 在设备上分配(显式) 否 是 主机 malloc_host 在主机上分配(隐式) 是 是 共享 malloc_shared 分配可以在主机和设备之间迁移(隐式) 是 是 USM语法 初始化: int data = malloc_shared<int>(N, q); int data = static_cast<int >(malloc_shared(N sizeof(int), q)); 释放 free(data,q); 使用共享内存之后,程序将自动在主机和运算设备之间隐式移动数据。 数据依赖 使用USM时,要注意数据之间的依赖关系以及事件之间的依赖关系,如果两个线程同时修改同一个内存区,将产生不可预测的结果。 我们可以使用不同的选项管理数据依赖关系: 内核任务中的 wait() 使用 depends_on 方法 使用 in_queue 队列属性 wait() q.submit([&](handler &h) {h.parallel_for(range<1>(N), [=](id<1> i) { data[i] += 2; });}).wait(); // <--- wait() will make sure that task is complete before continuingq.submit([&](handler &h) {h.parallel_for(range<1>(N), [=](id<1> i) { data[i] += 3; });}); depends_on auto e = q.submit([&](handler &h) { // <--- e is event for kernel taskh.parallel_for(range<1>(N), [=](id<1> i) { data[i] += 2; });});q.submit([&](handler &h) {h.depends_on(e); // <--- waits until event e is completeh.parallel_for(range<1>(N), [=](id<1> i) { data[i] += 3; });}); in_order queue property queue q(property_list{property::queue::in_order()}); // <--- this will make sure all the task with q are executed sequentially 练习1:事件依赖 以下代码使用 USM,并有三个提交到设备的内核。每个内核修改相同的数据阵列。三个队列之间没有数据依赖关系 为每个队列提交添加 wait() 在第二个和第三个内核任务中实施 depends_on() 方法 使用 in_order 队列属性,而非常规队列: queue q{property::queue::in_order()}; %%writefile lab/usm_data.cppinclude <CL/sycl.hpp>using namespace sycl;static const int N = 256;int main() {queue q{property::queue::in_order()};//用队列限制执行顺序std::cout << "Device : " << q.get_device().get_info<info::device::name>() << "\n";int data = static_cast<int >(malloc_shared(N sizeof(int), q));for (int i = 0; i < N; i++) data[i] = 10;q.parallel_for(range<1>(N), [=](id<1> i) { data[i] += 2; });q.parallel_for(range<1>(N), [=](id<1> i) { data[i] += 3; });q.parallel_for(range<1>(N), [=](id<1> i) { data[i] += 5; });q.wait();//wait阻塞进程for (int i = 0; i < N; i++) std::cout << data[i] << " ";std::cout << "\n";free(data, q);return 0;} 执行结果 练习2:事件依赖 以下代码使用 USM,并有三个提交到设备的内核。前两个内核修改了两个不同的内存对象,第三个内核对前两个内核具有依赖性。三个队列之间没有数据依赖关系 %%writefile lab/usm_data2.cppinclude <CL/sycl.hpp>using namespace sycl;static const int N = 1024;int main() {queue q;std::cout << "Device : " << q.get_device().get_info<info::device::name>() << "\n";//设备选择int data1 = malloc_shared<int>(N, q);int data2 = malloc_shared<int>(N, q);for (int i = 0; i < N; i++) {data1[i] = 10;data2[i] = 10;}auto e1 = q.parallel_for(range<1>(N), [=](id<1> i) { data1[i] += 2; });auto e2 = q.parallel_for(range<1>(N), [=](id<1> i) { data2[i] += 3; });//e1,e2指向两个事件内核q.parallel_for(range<1>(N),{e1,e2}, [=](id<1> i) { data1[i] += data2[i]; }).wait();//depend on e1,e2for (int i = 0; i < N; i++) std::cout << data1[i] << " ";std::cout << "\n";free(data1, q);free(data2, q);return 0;} 运行结果 UMS实验 在主机中初始化两个vector,初始数据为25和49,在设备中初始化两个vector,将主机中的数据拷贝到设备当中,在设备当中并行计算原始数据的根号值,然后将data1_device和data2_device的数值相加,最后将数据拷贝回主机当中,检验最后相加的和是否是12,程序结束前将内存释放。 %%writefile lab/usm_lab.cppinclude <CL/sycl.hpp>include <cmath>using namespace sycl;static const int N = 1024;int main() {queue q;std::cout << "Device : " << q.get_device().get_info<info::device::name>() << "\n";//intialize 2 arrays on hostint data1 = static_cast<int >(malloc(N sizeof(int)));int data2 = static_cast<int >(malloc(N sizeof(int)));for (int i = 0; i < N; i++) {data1[i] = 25;data2[i] = 49;}// STEP 1 : Create USM device allocation for data1 and data2int data1_device = static_cast<int >(malloc_device(N sizeof(int),q));int data2_device = static_cast<int >(malloc_device(N sizeof(int),q));// STEP 2 : Copy data1 and data2 to USM device allocationq.memcpy(data1_device, data1, sizeof(int) N).wait();q.memcpy(data2_device, data2, sizeof(int) N).wait();// STEP 3 : Write kernel code to update data1 on device with sqrt of valueauto e1 = q.parallel_for(range<1>(N), [=](id<1> i) { data1_device[i] = std::sqrt(25); });auto e2 = q.parallel_for(range<1>(N), [=](id<1> i) { data2_device[i] = std::sqrt(49); });// STEP 5 : Write kernel code to add data2 on device to data1q.parallel_for(range<1>(N),{e1,e2}, [=](id<1> i) { data1_device[i] += data2_device[i]; }).wait();// STEP 6 : Copy data1 on device to hostq.memcpy(data1, data1_device, sizeof(int) N).wait();q.memcpy(data2, data2_device, sizeof(int) N).wait();// verify resultsint fail = 0;for (int i = 0; i < N; i++) if(data1[i] != 12) {fail = 1; break;}if(fail == 1) std::cout << " FAIL"; else std::cout << " PASS";std::cout << "\n";// STEP 7 : Free USM device allocationsfree(data1_device, q);free(data1);free(data2_device, q);free(data2);// STEP 8 : Add event based kernel dependency for the Steps 2 - 6return 0;} 运行结果 本篇文章为转载内容。原文链接:https://blog.csdn.net/MCKZX/article/details/127630566。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-07-22 10:28:50
321
转载
ElasticSearch
...是sql数据库(比如mysql):你可能会想到在对应字段上使用field1 like '%?%',?即用户输出的关键词 比如你的底层数据库用的是mongo:你可能会想到在对应字段上使用db.collection.find({ "field1": { $regex: /aaa/ } })做查询,aaa即用户输入的关键词 比如你的底层数据库用的是elasticsearch:那厉害了,专业全文搜索神奇,全文搜索或搜索相关的需求使用elasticsearch绝对是最合适的选择 比如你的底层数据库用的是hive、impala、clickhouse等大数据计算引擎:鸟枪换炮,其实用作全文索引和搜索的场景并不合适,你可能依旧会使用sql数据库那样用like做交互 2. 方案选择 调研之后,可能会发现对于数据量相对大一点的搜索场景,在当下流行的数据库或计算引擎中,elasticsearch是其中最合适的解决方案。 无论是sql的like、还是mongo的regex,在线上环境下,数据量较多的情况下,都不是很高效的查询,甚至有的公司的dba会禁止在线上使用类似的查询语法。 与elasticsearch是“亲戚”的,大家还常提到lucene、solr,但是无论从现在的发展趋势还是公司运维人才的储备(不得不说当下的运维人才中,对es熟悉的人才会更多一些),elasticsearch是相对较合适的选择。 一些大数据计算引擎,其实更多的适合OLAP场景。当然也完全可以使用,因为比如clickhouse、starrocks等的查询速度已经发展的非常快。但你会发现在中文分词搜索上,实现起来有一定困扰。 所以,如果你不差机器,首选方案还是elasticsearch。 3. elasticsearch的适用场景 3.1 经典的日志搜索场景 提到elasticsearch不得不提到它的几个好朋友: 一些公司里经常用elasticsearch来收集日志,然后用kibana来展示和分析。 展开来说,举个例子,你的app打印日志打印到了线上日志文件,当app出现故障你需要做定位筛查的时候,可能需要登录线上机器用grep命令各种查看。 但如果你不差机器资源,可以搭建上述架构,app的日志会被收集到elasticsearch中,最终你可以在kibana中查看日志,kibana里面可以很方面的做各种筛查操作。 这个流畅大概是这样的: 3.2 通用搜索场景 但是没有上图的beats、logstash、kibana,elasticsearch可以自己工作吗?完全可以的! elasticsearch也支持单机部署,数据规模不是很大的情况下,表现也是不错的。所以,你也不用担心因为自己机器资源不够而对elasticsearch望而却步。当然,单机部署的情况下,更多的适合自己玩,对于可靠性的要求就不能太苛刻了。 如果你在用宝塔,那你可以在宝塔面板,左侧“软件商店”中直接找到elasticsearch,并“没有痛苦”的安装。 本篇文章主要讨论选型,所以不涉及安装细节。 3.2.1 性能顾虑 上面提到了“表现”,其实性能只是elasticsearch的一个方面,主要你的机器资源足够(机器资源?对,包括你的机器个数,elasticsearch可以非常方便的横向扩展,以及单机的配置,cpu+内存,内存越高越好,elasticsearch比较吃内存!),它一定会给你很好的性能反应。试想,公司里的app打印线上日志的行数其实可比一般业务系统产生的订单数量要大很多很多,elasticsearch都可以常在日志的实时分析,所以如果你要做通用场景,而且机器资源不是问题,这是完全行得通的。 3.2.2 易用性和可玩性 此外,在使用elasticsearch的时候,会有很多的可玩性。这里不引经据典,呈现很多elasticsearch官方文章的列举优秀特性(当然,确实很优秀!)。 这里举几个例子: (1)中文分词:第一章提到的其它引擎几乎很难实现,elasticsearch对分词器的支持是原生的,因为elasticsearch天生就为全文索引而生,elasticsearch的汉语名字就是“弹性搜索”。这家伙可是专门搞搜索的! 有的朋友可能不了解分词器,比如你的一个字段里存储“今天我要吃冰激凌”,在分词器的加持下,es最终会存储为“今天|我|要|吃|冰激凌”,并且使用倒排索引的形式进行存储。当你搜索“冰激凌”的时候,可以很快的反馈回来。 关于elasticsearch的原理,这里不展开说明,分词器和倒排索引是elasticsearch的最基本的概念。如果有不了解的朋友,可以自行百度一下。而且这两个概念,与elasticsearch其实不挂钩,是搜索中的通用概念。 关于倒排索引,其核心表现如下图: 如果你要用mysql、mongo实现中文分词,这......其实挺麻烦的,可能在后面的版本支持中会实现的很好,但在当前的流行版本中,它们对中文分词是不够友好的。 mysql5.7之后支持外挂第三方分词器,支持中文分词。而在数据量较大的情况下,mysql的多机器部署几乎很难实现,elasticsearch可以很容易的水平扩展。 mongo支持西方语言的分词,但不支持中文、日语、汉语等东方语言,你需要在自己的逻辑代码中实现分词器。 ngram分词,你看看效果:依旧是“今天我要吃冰激凌”,ngram二元分词后即将得到结果“今天、天我、我要、要吃、吃冰、冰激、激凌”。这....,那你搜索冰激凌就搜不出来!咋办呢,当然可以使用三元分词。但是更好的解决方案还是中文分词器,但它们原生并不支持的。 (2)自定义排名场景:比如你的搜索“冰激凌”,结果中返回了有10条,这10条应该有你想对它指定的顺序。最简单的就是用默认的得分,但是如果你想人为干预这个得分怎么办? elasticsearch支持function_score功能(可以不用,这个是增强功能),es会在计算最终得分之前回调这个你指定的function_score回调函数,传入原始得分、行的原始数据,你可以在里面做计算,比如查询其它参考表、或查看是否是广告位,以得到新的score返回给用户。 function_scrore的功能不展开描述,是一个在自定义得分场景下十分有用又简单易用的功能!下面是一个使用示例,不仅如此,它是支持自定义函数的,自由度非常高。 (3)文本高亮:你用mysql或mongo也可以实现,比如用户搜索“冰激凌”,你只需要在逻辑代码中对“冰激凌”替换为“<span class='highlight-term'>冰激凌</span>”,然后前端做样式即可。但如果用户搜索了“好吃的冰激凌”咋办呢?还有就是英文大小写的场景,用户搜索"MAIN",那结果及时匹配到了“main”(小写的),这个单词是否应该高亮呢?也许这时候你会用业务代码实现toLowerCase下基于位置下标的匹配。 挺麻烦的吧,elasticsearch,自动可以返回高亮字段!并且可以自由指定高亮的html前后标签。 (4)实在太多了....这家伙天生为索引而生,而且版本还在不断地迭代。不差机器的话,用用吧! 4. 退而求其次 4.1 普通数据库 尽管elasticsearch在搜索场景下,是非常好用的利器!但是它比较消耗机器资源,如果你的数据规模并不大,而且想快速实现功能。你可以使用mysql或mongo来代替,完全没有问题。 技术是为了解决特定业务场景下的问题,结合当前手头的资源,适合自己的才是最好的。也许你搞了一个单机器的elasticsearch,单机器内存只有2G,它的表现并不会比mysql、mongo来的好。 当然,如果你为了使用上边提到的一些优秀的独有的特性,那elasticsearch一定还是最佳选择! 对于mysql(关系型数据库)和mongo(文档数据库)的区别这里不展开描述了,但对于搜索而言,两种都合适。有时候选型也不用很纠结,其实都是差不太多的东西,适合自己的、自己熟悉的、运维起来顺手的,就是最好的。 4.2 普通数据库实现中文分词搜索的原理 尽管mysql在5.7以后支持外挂第三方分词器,mongo在截止目前的版本中也不支持中文分词(你可能会看到一些文章中说可以指定language为chinese,但其实会报错的)。 其实当你选择普通数据库,你就不得不在逻辑代码中自己实现一套索引分词+搜索分词逻辑。 索引分词+搜索分词?为什么分开写,如果你有用过elasticsearch或solr,你会知道,在指定字段的时候,需要指定index分词器和search分词器。 下面以mongo为例做简要说明。 4.2.1 index分词器 意思是当数据“索引”截断如何分词。首先,这里必须要承认,数据之后存储了,才能被查询。在搜索中,这句话可以换成是“数据只有被索引了,才能被搜索”。 这时候请求打过来了,要索引一条数据,其中某字段是“今天我要吃冰激凌”,分词后得到“今天|我|要|吃|冰激凌”,这个就可以入库了。 如果你使用elasticsearch或solr,这个过程是自动的。如果你使用不支持外观分词器的常规数据库,这个过程你就要手动了,并把分词后的结果用空格分开(最好使用空格,因为西方语言的分词规则就是按空格拆分,以及逗号句号),存入数据库的一个待搜索的字段上。 效果如下图: 本站的其它博文中有介绍IKAnalyzer:https://www.52itw.com/java/6268.html 4.2.2 search分词器 当用户的查询请求打过来,用户输入了“好吃的冰激凌”,分词后得到“好吃|冰激凌”(“的”作为停用词stopwords,被自动忽略了,IKAnalyzer可以指定停用词表)。 于是这时候就回去上图的数据库表里面搜索“好吃 冰激凌”(与index分词器结果统一,还是用空格分隔)。 当然,对于mongo而言,你需要事先开启全文索引db.xxx.ensureIndex({content: "text"}),xxx是集合名,content是字段名,text是全文索引的标识。 mongo搜索的时候用这个语法:db.xxx.find( { $text: { $search: "好吃 冰激凌" } },{ score: { $meta: "textScore" } }).sort( { score: { $meta: "textScore" } } ) 4.2.3 索引库和存储库分开 为了减少单表的大小,为了让普通的列表查询、普通筛选可以跑的更快,你可以对原有的数据原封不动的做一张表。 然后对于搜索场景,再单独对需要被搜索的字段单独拎一张表出来! 然后二者之间做增量信号同步或定时差额同步,可能会有延迟,这个就看你能容忍多长时间(悄悄告诉你,elasticsearch也需要指定这个refresh时间,一般是1s到几秒、甚至分钟级。当然,二者的这个时间对饮的底层目的是不一样的)。 这样,搜索的时候先查询搜索库,拿到一个指针id的列表,然后拿到指针id的列表区存储里把数据一次性捞出来。当然,也是支持分页的,你查询搜索库其实也是普通的数据库查询嘛,支持分页参数的。 4.3 存储库和索引库的延伸阅读 很多有名的开源软件也是使用的存储库与索引库分离的技术方案,如apache atlas: apache atlas对于大数据领域的数据资产元数据管理、数据血缘上可谓是专家,也涉及资产搜索的特性,它的实现思路就是:从搜索库中做搜索、拿到key、再去存储库中做查询。 搜索库:上图右下角,可以看到使用的是elasticsearch、solr或lucene,多个选一个 存储库:上图左下角,可以看到使用的是Cassandra、HBase或BerkeleyDB,多个选一个 虽然apache atlas在只有搜索库或只有存储库的时候也可以很好的工作,但只针对于数据量并不大的场景。 搜索库,擅长搜索!存储库,擅长海量存储!搜索库多样化搜索,然后去存储库做点查。 当你的数据达到海量的时候,es+hbase也是一种很好的解决方案,不在这里展开说明了。
2024-01-27 17:49:04
537
admin-tim
转载文章
...送,送到通用的数据库访问函数中: using System;using System.Data;using System.Data.SqlClient;using System.Configuration;using System.Collections.Generic;using WestGarden.Model;namespace WestGarden.Web{public partial class Default3 : System.Web.UI.Page{protected void Page_Load(object sender, EventArgs e){IList<CategoryInfo> catogories = new List<CategoryInfo>();string connectionString = ConfigurationManager.ConnectionStrings["NetShopConnString"].ConnectionString;string cmdText = "SELECT CategoryId, Name, Descn FROM Category";SqlDataReader rdr = ExecuteReader(connectionString, CommandType.Text, cmdText);while (rdr.Read()){CategoryInfo category = new CategoryInfo(rdr.GetString(0), rdr.GetString(1), rdr.GetString(2));catogories.Add(category);}rdr.Close();ddlCategories.DataSource = catogories;ddlCategories.DataTextField = "Name";ddlCategories.DataValueField = "CategoryId";ddlCategories.DataBind();}public static SqlDataReader ExecuteReader(string connectionString, CommandType cmdType, string cmdText){SqlCommand cmd = new SqlCommand();SqlConnection conn = new SqlConnection(connectionString);try{cmd.Connection = conn;cmd.CommandType = cmdType;cmd.CommandText = cmdText;conn.Open();//如果创建了 SqlDataReader 并将 CommandBehavior 设置为 CloseConnection,//则关闭 SqlDataReader 会自动关闭此连接SqlDataReader rdr = cmd.ExecuteReader(CommandBehavior.CloseConnection);return rdr;}catch{conn.Close();throw;}//finally//{// conn.Close();//} }} } 这个通用数据库访问函数可以进一步完善如下: using System;using System.Data;using System.Data.SqlClient;using System.Configuration;using System.Collections.Generic;using WestGarden.Model;namespace WestGarden.Web{public partial class Default4 : System.Web.UI.Page{protected void Page_Load(object sender, EventArgs e){IList<CategoryInfo> catogories = new List<CategoryInfo>();string connectionString = ConfigurationManager.ConnectionStrings["NetShopConnString"].ConnectionString;string cmdText = "SELECT CategoryId, Name, Descn FROM Category";SqlDataReader rdr = ExecuteReader(connectionString, CommandType.Text, cmdText,null);while (rdr.Read()){CategoryInfo category = new CategoryInfo(rdr.GetString(0), rdr.GetString(1), rdr.GetString(2));catogories.Add(category);}rdr.Close();ddlCategories.DataSource = catogories;ddlCategories.DataTextField = "Name";ddlCategories.DataValueField = "CategoryId";ddlCategories.DataBind();}public static SqlDataReader ExecuteReader(string connectionString, CommandType cmdType, string cmdText, params SqlParameter[] commandParameters){SqlCommand cmd = new SqlCommand();SqlConnection conn = new SqlConnection(connectionString);try{//cmd.Connection = conn;//cmd.CommandType = cmdType;//cmd.CommandText = cmdText;//conn.Open();PrepareCommand(cmd, conn, null, cmdType, cmdText, commandParameters);//如果创建了 SqlDataReader 并将 CommandBehavior 设置为 CloseConnection,//则关闭 SqlDataReader 会自动关闭此连接。SqlDataReader rdr = cmd.ExecuteReader(CommandBehavior.CloseConnection);cmd.Parameters.Clear();return rdr;}catch{conn.Close();throw;}//finally//{// conn.Close();//} }private static void PrepareCommand(SqlCommand cmd, SqlConnection conn, SqlTransaction trans, CommandType cmdType, string cmdText, SqlParameter[] cmdParms){if (conn.State != ConnectionState.Open)conn.Open();cmd.Connection = conn;cmd.CommandText = cmdText;if (trans != null)cmd.Transaction = trans;cmd.CommandType = cmdType;if (cmdParms != null){foreach (SqlParameter parm in cmdParms)cmd.Parameters.Add(parm);} }} } 因为重点在过程,在结构,代码都比较简单,唯一值得一提的是SqlConnection的关闭问题,在最后比较完善的数据库访问函数中(这是SQLHelper中的源代码),没有使用using()结构,也没有显示关闭,主要原因是调用ExecuteReader方法时,使用了参数 CommandBehavior 并将其设置为 CloseConnection: SqlDataReader rdr = cmd.ExecuteReader(CommandBehavior.CloseConnection); 根据MSDN的说法:如果创建了 SqlDataReader 并将 CommandBehavior 设置为 CloseConnection,则关闭 SqlDataReader 会自动关闭此连接。 参考网址:http://msdn.microsoft.com/zh-cn/library/y6wy5a0f(v=vs.80).aspx 版权所有©2012,WestGarden.欢迎转载,转载请注明出处.更多文章请参阅博客http://www.cnblogs.com/WestGarden/ 转载于:https://www.cnblogs.com/WestGarden/archive/2012/06/04/2533560.html 本篇文章为转载内容。原文链接:https://blog.csdn.net/weixin_33697898/article/details/94471782。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-03-18 20:09:36
89
转载
转载文章
...算法、自然语言处理、安全、游戏开发、IOS等。 面试难度中等,会有手写sql、算法、linux命令的环节。 松果出行 松果出行主要业务是构建国内县域城市交通出行网络,目前主要是共享电单车和共享新能源汽车服务。目前业务已经覆盖全国21个省,5000个县。 福利待遇属于行业中等,五险一金、年终奖等,没有补充医疗保险。 招聘岗位很多,以JAVA为主,各种级别都有。也有物联网、传感器硬件相关的岗位。 小桔科技 目前研发团队主要做推荐、搜索系统,注册地在大连。 福利待遇行业中等,五险一金、年终奖,没有补充医疗保险。 招聘岗位包括JAVA、PHP、搜索算法、前端、数仓等。 理想汽车 智能电动车品牌,这两年在行业内名气比较大。 福利待遇行业中等偏上,六险一金、交通补贴等。 招聘岗位很多,以JAVA为主,各种级别都有。另外也招聘PaaS平台研发、搜索、车载语音、大数据等。 参加过理想汽车面试的同学反馈面试体验不太好,面试官没有耐心,给大家一个参考。 狮桥 智慧物流+普惠金融融资租赁业务。 福利待遇中等偏下,五险一金、年终奖,没有补充医疗保险。 招聘岗位主要是JAVA开发。 领创集团 海外金融业务,主要做印度市场。 福利待遇中等偏下,六险一金,年终奖,工作节奏慢。 招聘岗位主要是JAVA,招聘岗位主要是java。 面试过的同学反馈体验比较好,面试官比较nice,有手写代码环节。 总结 今天主要推荐了望京的16家值得加入的互联网公司,事实上,望京区域的互联网公司和其他科技公司至少有几百家,由于个人精力有限,主要梳理了业界比较知名和自己熟悉的公司。相信还有好多非常不错的公司值得加入,欢迎大家跟我交流讨论。 欢迎关注个人公众号,一起学习进步 本篇文章为转载内容。原文链接:https://blog.csdn.net/zjj2006/article/details/121412370。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-01-11 22:59:19
529
转载
.net
...以及跨模块的依赖解析策略。这对于构建大型分布式系统尤其有用,因为它允许开发者在不影响现有业务逻辑的前提下,实现更复杂的依赖关系管理。 值得注意的是,谷歌也在其开源项目中大力推广依赖注入的理念。例如,Flutter团队推出了一套名为GetIt的新一代DI库,它不仅支持多种平台(Web、Mobile、Desktop),还提供了更为简洁的API设计。相比传统的Dagger或Hilt,GetIt更适合小型项目或快速原型开发,其轻量化的特点使得开发者能够迅速上手并提升生产力。 与此同时,国内的一些技术社区也开始关注这一领域的发展趋势。例如,InfoQ最近发表了一篇深度解读文章,分析了国内企业在采用DI模式时面临的挑战,特别是如何平衡灵活性与稳定性之间的关系。文章指出,尽管DI能够显著改善代码结构,但在实际落地过程中仍需谨慎权衡,尤其是在高并发场景下,不恰当的配置可能导致资源浪费甚至系统崩溃。 综上所述,无论是国际巨头还是本土企业,都在积极拥抱依赖注入技术,并探索适合自身需求的最佳实践。对于开发者而言,持续关注行业动态和技术演进,及时调整学习方向,无疑是保持竞争力的关键所在。
2025-05-07 15:53:50
40
夜色朦胧
NodeJS
...统时必须格外注意数据安全与隐私保护。一方面,企业需要严格遵守数据收集、存储和传输的相关规定;另一方面,还需加强技术手段,如加密通信、匿名化处理等,以防止敏感信息泄露。正如某网络安全专家所言:“技术本身没有善恶之分,关键在于如何正确使用。”因此,在追求技术创新的同时,企业应当始终将合规性和安全性放在首位,确保技术进步真正造福于社会。 总之,Node.js 和 WebSocket 技术的应用前景十分广阔,但同时也面临着诸多挑战。只有不断探索新技术、新方法,同时坚守法律底线和社会责任,才能让这一技术更好地服务于各行各业的发展需求。
2025-05-06 16:24:48
72
清风徐来
Mahout
...术底子,来灵活地调整策略,让一切都能无缝对接,发挥出最大的效用。就像是做菜,得看食材的新鲜度,再搭配合适的调料,才能做出让人满意的美味佳肴一样。所以,别死板地照搬方案,得因地制宜,因材施教,这样才能确保我们的工作既高效又有效。
2024-09-06 16:26:39
59
月影清风
ZooKeeper
本文针对ZooKeeper中常见的CommitQueueFullException问题,从分布式协调机制出发,深入分析其触发原因,包括队列管理不当、高并发及网络延迟等。文章提出优化策略,如调整队列大小、客户端请求合并、异步API应用,并强调在高并发场景下需关注硬件资源配置。通过实践证明,合理优化可有效缓解CommitQueueFullException,确保ZooKeeper稳定运行。
2025-03-16 15:37:44
10
林中小径
ElasticSearch
...战演练 代码中的应对策略 除了调整配置,我们还可以通过编写脚本来监控和处理NodeNotActiveException。比如,下面这段Java代码展示了如何捕获异常并记录日志: java import org.elasticsearch.client.RestHighLevelClient; import org.elasticsearch.client.RestClient; import org.elasticsearch.client.indices.CreateIndexRequest; import org.elasticsearch.client.indices.CreateIndexResponse; public class ElasticSearchExample { public static void main(String[] args) { RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http"))); try { CreateIndexRequest request = new CreateIndexRequest("test_index"); CreateIndexResponse response = client.indices().create(request, RequestOptions.DEFAULT); System.out.println("Index created: " + response.isAcknowledged()); } catch (Exception e) { if (e instanceof ClusterBlockException) { System.err.println("Cluster block detected: " + e.getMessage()); } else { System.err.println("Unexpected error: " + e.getMessage()); } } finally { try { client.close(); } catch (IOException ex) { System.err.println("Failed to close client: " + ex.getMessage()); } } } } 这段代码的作用是在创建索引时捕获可能发生的异常,并根据异常类型采取不同的处理方式。如果遇到ClusterBlockException,我们可以选择延迟重试或者其他补偿措施。 --- 6. 总结与反思 成长路上的一课 通过这次经历,我深刻体会到,作为一名开发者,不仅要掌握技术细节,还要学会从实际问题出发,找到最优解。NodeNotActiveException这个错误看着不起眼,但其实背后有不少门道呢!比如说,你的服务器硬件是不是有点吃不消了?集群那边有没有啥小毛病没及时发现?还有啊,咱们平时运维的时候是不是也有点松懈了?这些都是得好好琢磨的地方! 最后,我想说的是,技术学习的过程就像爬山一样,有时候会遇到陡峭的山坡,但只要坚持下去,总能看到美丽的风景。希望这篇文章能给大家带来一些启发和帮助!如果还有其他疑问,欢迎随时交流哦~
2025-03-14 15:40:13
64
林中小径
转载文章
...者提供了低级别的硬件访问权限,可以创建更复杂的3D图形和实时渲染效果,对于消除类游戏这类对响应速度有较高要求的游戏来说具有重大意义。 此外,游戏设计中的AI算法也是值得关注的方向。例如,运用深度学习和强化学习技术优化消除类游戏的智能提示系统,能有效提高玩家体验并延长游戏生命周期。一篇发表在“自然”杂志子刊上的论文就研究了AI在连连看等消除类游戏中的应用,展示了通过机器学习预测最佳消除路径的可能性。 总的来说,在继续深入实践HTML、CSS、JavaScript基础开发的同时,紧跟Web技术前沿进展,结合先进的编程语言、图形处理技术和AI算法,将有助于开发者打造出更为丰富、流畅且富有挑战性的消除类游戏产品,不断满足日益增长的用户体验需求。
2023-06-08 15:26:34
516
转载
转载文章
...的${key}属性被访问了)return value},set: function (newValue) {console.log(监听到obj对象的${key}属性被设置值)value = newValue} })})obj.name = 'kobe'obj.age = 30console.log(obj.name)console.log(obj.age)/ 监听到obj对象的name属性被设置值监听到obj对象的age属性被设置值监听到obj对象的name属性被访问了kobe监听到obj对象的age属性被访问了30/ 属性描述符监听对象的缺点: 首先,Object.defineProperty 设计的初衷,不是为了去监听截止一个对象中所有的属性的 我们在定义某些属性的时候,初衷其实是定义普通的属性,但是后面我们强行将它变成了数据属性描述符 其次,如果我们想监听更加丰富的操作,比如新增属性、删除属性,那么 Object.defineProperty 是无能为力的 所以我们要知道,存储数据描述符设计的初衷并不是为了去监听一个完整的对象 Ps: 原来的对象是 数据属性描述符,通过 Object.defineProperty 变成了 访问属性描述符 2. Proxy基本使用 在ES6中,新增了一个Proxy类,这个类从名字就可以看出来,是用于帮助我们创建一个代理的: 也就是说,如果我们希望监听一个对象的相关操作,那么我们可以先创建一个代理对象(Proxy对象) 之后对该对象的所有操作,都通过代理对象来完成,代理对象可以监听我们想要对原对象进行哪些操作 将上面的案例用 Proxy 来实现一次: 首先,我们需要 new Proxy 对象,并且传入需要侦听的对象以及一个处理对象,可以称之为 handler; const p = new Proxy(target, handler) 其次,我们之后的操作都是直接对 Proxy 的操作,而不是原有的对象,因为我们需要在 handler 里面进行侦听 const obj = {name: 'why',age: 18}const objProxy = new Proxy(obj, {// 获取值时的捕获器get: function (target, key) {console.log(监听到obj对象的${key}属性被访问了)return target[key]},// 设置值时的捕获器set: function (target, key, newValue) {console.log(监听到obj对象的${key}属性被设置值)target[key] = newValue} })console.log(objProxy.name)console.log(objProxy.age)objProxy.name = 'kobe'objProxy.age = 30console.log(obj.name)console.log(obj.age)/ 监听到obj对象的name属性被访问了why监听到obj对象的age属性被访问了18监听到obj对象的name属性被设置值监听到obj对象的age属性被设置值kobe30/ 2.1 Proxy 的 set 和 get 捕获器 如果我们想要侦听某些具体的操作,那么就可以在 handler 中添加对应的捕捉器(Trap) set 和 get 分别对应的是函数类型 set 函数有四个参数: target:目标对象(侦听的对象) property:将被设置的属性 key value:新属性值 receiver:调用的代理对象 get 函数有三个参数 target:目标对象(侦听的对象) property:被获取的属性 key receiver:调用的代理对象 2.2 Proxy 所有捕获器 (13个) handler.getPrototypeOf() Object.getPrototypeOf 方法的捕捉器 handler.setPrototypeOf() Object.setPrototypeOf 方法的捕捉器 handler.isExtensible() Object.isExtensible 方法的捕捉器 handler.preventExtensions() Object.preventExtensions 方法的捕捉器 handler.getOwnPropertyDescriptor() Object.getOwnPropertyDescriptor 方法的捕捉器 handler.defineProperty() Object.defineProperty 方法的捕捉器 handler.ownKeys() Object.getOwnPropertyNames 方法和 Object.getOwnPropertySymbols 方法的捕捉器 handler.has() in 操作符的捕捉器 handler.get() 属性读取操作的捕捉器 handler.set() 属性设置操作的捕捉器 handler.deleteProperty() delete 操作符的捕捉器 handler.apply() 函数调用操作的捕捉器 handler.construct() new 操作符的捕捉器 const obj = {name: 'why',age: 18}const objProxy = new Proxy(obj, {// 获取值时的捕获器get: function (target, key) {console.log(监听到obj对象的${key}属性被访问了)return target[key]},// 设置值时的捕获器set: function (target, key, newValue) {console.log(监听到obj对象的${key}属性被设置值)target[key] = newValue},// 监听 in 的捕获器has: function (target, key) {console.log(监听到obj对象的${key}属性的in操作)return key in target},// 监听 delete 的捕获器deleteProperty: function (target, key) {console.log(监听到obj对象的${key}属性的delete操作)delete target[key]} })// in 操作符console.log('name' in objProxy)// delete 操作delete objProxy.name/ 监听到obj对象的name属性的in操作true监听到obj对象的name属性的delete操作/ 2.3 Proxy 的 construct 和 apply 到捕捉器中还有 construct 和 apply,它们是应用于函数对象的 function foo() {console.log('调用了 foo')}const fooProxy = new Proxy(foo, {apply: function (target, thisArg, argArray) {console.log(对 foo 函数进行了 apply 调用)target.apply(thisArg, argArray)},construct: function (target, argArray, newTarget) {console.log(对 foo 函数进行了 new 调用)return new target(...argArray)} })fooProxy.apply({}, ['abc', 'cba'])new fooProxy('abc', 'cba')/ 对 foo 函数进行了 apply 调用调用了 foo对 foo 函数进行了 new 调用调用了 foo/ 3. Reflect 3.1 Reflect 的作用 Reflect 也是 ES6 新增的一个 API,它是一个对象,字面的意思是反射 Reflect 的作用: 它主要提供了很多操作 JavaScript 对象的方法,有点像 Object 中操作对象的方法 比如 Reflect.getPrototypeOf(target) 类似于 Object.getPrototypeOf() 比如 Reflect.defineProperty(target, propertyKey, attributes) 类似于 Object.defineProperty() 如果我们有 Object 可以做这些操作,那么为什么还需要有Reflect这样的新增对象呢? 这是因为在早期的 ECMA 规范中没有考虑到这种对 对象本身 的操作如何设计会更加规范,所以将这些 API 放到了 Object上面 但是 Object 作为一个构造函数,这些操作实际上放到它身上并不合适 另外还包含一些类似于 in、delete 操作符,让 JS 看起来是会有一些奇怪的 所以在 ES6 中新增了 Reflect,让我们这些操作都集中到了 Reflect 对象上 那么 Object 和 Reflect 对象之间的 API 关系,可以参考 MDN 文档: 比较 Reflect 和 Object 方法 3.2 Reflect 的常见方法 Reflect中有哪些常见的方法呢?它和Proxy是一一对应的,也是13个 Reflect.getPrototypeOf(target) 类似于 Object.getPrototypeOf() Reflect.setPrototypeOf(target, prototype) 设置对象原型的函数. 返回一个 Boolean, 如果更新成功,则返回 true Reflect.isExtensible(target) 类似于 Object.isExtensible() Reflect.preventExtensions(target) 类似于 Object.preventExtensions() , 返回一个 Boolean Reflect.getOwnPropertyDescriptor(target, propertyKey) 类似于 Object.getOwnPropertyDescriptor() , 如果对象中存在该属性,则返回对应的属性描述符, 否则返回 undefined Reflect.defineProperty(target, propertyKey, attributes) 和 Object.defineProperty() 类似, 如果设置成功就会返回 true Reflect.ownKeys(target) 返回一个包含所有自身属性(不包含继承属性)的数组 (类似于 Object.keys(), 但不会受 enumerable 影响) Reflect.has(target, propertyKey) 判断一个对象是否存在某个属性,和 in 运算符 的功能完全相同 Reflect.get(target, propertyKey[, receiver]) 获取对象身上某个属性的值,类似于 target[name] Reflect.set(target, propertyKey, value[, receiver]) 将值分配给属性的函数,返回一个 Boolean,如果更新成功,则返回 true Reflect.deleteProperty(target, propertyKey) 作为函数的 delete 操作符,相当于执行 delete target[name] Reflect.apply(target, thisArgument, argumentsList) 对一个函数进行调用操作,同时可以传入一个数组作为调用参数。和 Function.prototype.apply() 功能类似 Reflect.construct(target, argumentsList[, newTarget]) 对构造函数进行 new 操作,相当于执行 new target(...args) 3.3 Reflect 的使用 那么我们可以将之前Proxy案例中对原对象的操作,都修改为Reflect来操作 const obj = {name: 'why',age: 18}const objProxy = new Proxy(obj, {get: function (target, key) {console.log(监听到obj对象的${key}属性被访问了)return Reflect.get(target, key)// return target[key] // 对原来对象进行了直接操作},set: function (target, key, newValue) {console.log(监听到obj对象的${key}属性被设置值)Reflect.set(target, key, newValue)// target[key] = newValue // 对原来对象进行了直接操作} })objProxy.name = 'kobe'console.log(objProxy.name)/ 监听到obj对象的name属性被设置值监听到obj对象的name属性被访问了kobe/ 3.4 Receiver的作用 我们发现在使用getter、setter的时候有一个receiver的参数,它的作用是什么呢? 如果我们的源对象(obj)有 setter 、getter 的访问器属性,那么可以通过 receiver 来改变里面的 this const obj = {_name: 'why',get name() {return this._name // 不使用receiver, _name属性的操作不会被objProxy代理,因为this指向obj},set name(newValue) {this._name = newValue} }const objProxy = new Proxy(obj, {get: function (target, key, receiver) {// receiver 是创建出来的代理对象console.log('get 方法被访问-------', key, receiver)console.log(objProxy === receiver) // truereturn Reflect.get(target, key, receiver)},set: function (target, key, newValue, receiver) {Reflect.set(target, key, newValue, receiver)} })objProxy.name = 'kobe'console.log(objProxy.name) // kobe/ get 方法被访问------- name { _name: 'kobe', name: [Getter/Setter] }trueget 方法被访问------- _name { _name: 'kobe', name: [Getter/Setter] }truekobe/ 3.5 Reflect 的 construct function Student(name, age) {this.name = namethis.age = age}function Teacher() {}const stu = new Student('why', 18)console.log(stu)console.log(stu.__proto__ === Student.prototype)/ Student { name: 'why', age: 18 }true/// 执行 Student 函数中的内容,但是创建出来的对象是 Teacher 对象const teacher = Reflect.construct(Student, ['why', 18], Teacher)console.log(teacher)console.log(teacher.__proto__ === Teacher.prototype)/ Teacher { name: 'why', age: 18 }true/ 4. 响应式 4.1 什么是响应式? 先来看一下响应式意味着什么?我们来看一段代码: m 有一个初始化的值,有一段代码使用了这个值; 那么在 m 有一个新的值时,这段代码可以自动重新执行 let m = 0// 一段代码console.log(m)console.log(m 2)console.log(m 2)m = 200 上面的这样一种可以自动响应数据变量的代码机制,我们就称之为是响应式的 对象的响应式 4.2 响应式函数设计 首先,执行的代码中可能不止一行代码,所以我们可以将这些代码放到一个函数中: 那么问题就变成了,当数据发生变化时,自动去执行某一个函数; 但是有一个问题:在开发中是有很多的函数的,如何区分一个函数需要响应式,还是不需要响应式呢? 很明显,下面的函数中 foo 需要在 obj 的 name 发生变化时,重新执行,做出相应; bar 函数是一个完全独立于 obj 的函数,它不需要执行任何响应式的操作; // 对象的响应式const obj = {name: 'why',age: 18}function foo() {const newName = obj.nameconsole.log('你好啊,李银河')console.log('Hello World')console.log(obj.name)}function bar() {console.log('普通的其他函数')console.log('这个函数不需要有任何的响应式')}obj.name = 'kobe' // name 发生改变时候 foo 函数执行 响应式函数的实现 watchFn 如何区分响应式函数? 这个时候我们封装一个新的函数 watchFn 凡是传入到 watchFn 的函数,就是需要响应式的 其他默认定义的函数都是不需要响应式的 / 封装一个响应式的函数 /let reactiveFns = []function watchFn(fn) {reactiveFns.push(fn)}// 对象的响应式const obj = {name: 'why',age: 18}watchFn(function foo() {const newName = obj.nameconsole.log('你好啊,李银河')console.log('Hello World')console.log(obj.name)})watchFn(function demo() {console.log(obj.name, 'demo function ---------')})function bar() {console.log('普通的其他函数')console.log('这个函数不需要有任何的响应式')}obj.name = 'kobe' // name 发生改变时候 foo 函数执行reactiveFns.forEach((fn) => {fn()}) 4.3 响应式依赖的收集 目前收集的依赖是放到一个数组中来保存的,但是这里会存在数据管理的问题: 在实际开发中需要监听很多对象的响应式 这些对象需要监听的不只是一个属性,它们很多属性的变化,都会有对应的响应式函数 不可能在全局维护一大堆的数组来保存这些响应函数 所以要设计一个类,这个类用于管理某一个对象的某一个属性的所有响应式函数: 相当于替代了原来的简单 reactiveFns 的数组; class Depend {constructor() {this.reactiveFns = []}addDepend(reactiveFn) {this.reactiveFns.push(reactiveFn)}notify() {this.reactiveFns.forEach((fn) => {fn()})} }const depend = new Depend()function watchFn(fn) {depend.addDepend(fn)}// 对象的响应式const obj = {name: 'why', // depend 对象age: 18 // depend 对象}watchFn(function foo() {const newName = obj.nameconsole.log('你好啊,李银河')console.log('Hello World')console.log(obj.name)})watchFn(function demo() {console.log(obj.name, 'demo function ---------')})function bar() {console.log('普通的其他函数')console.log('这个函数不需要有任何的响应式')}obj.name = 'kobe'depend.notify() 4.4 监听对象的变化 那么接下来就可以通过之前的方式来监听对象的变化: 方式一:通过 Object.defineProperty 的方式(vue2采用的方式); 方式二:通过 new Proxy 的方式(vue3采用的方式); 我们这里先以Proxy的方式来监听 class Depend {constructor() {this.reactiveFns = []}addDepend(reactiveFn) {this.reactiveFns.push(reactiveFn)}notify() {this.reactiveFns.forEach((fn) => {fn()})} }const depend = new Depend()function watchFn(fn) {depend.addDepend(fn)}// 对象的响应式const obj = {name: 'why', // depend 对象age: 18 // depend 对象}// 监听对象的属性变化:Proxy(vue3)/Object.defineProperty(vue2)const objProxy = new Proxy(obj, {get: function (target, key, receiver) {return Reflect.get(target, key, receiver)},set: function (target, key, newValue, receiver) {Reflect.set(target, key, newValue, receiver)depend.notify()} })watchFn(function foo() {const newName = objProxy.nameconsole.log('你好啊,李银河')console.log('Hello World')console.log(objProxy.name)})watchFn(function demo() {console.log(objProxy.name, 'demo function ---------')})objProxy.name = 'kobe'objProxy.name = 'james'/ 你好啊,李银河Hello Worldkobekobe demo function ---------你好啊,李银河Hello Worldjamesjames demo function ---------/ 4.5 对象的依赖管理 目前是创建了一个 Depend 对象,用来管理对于 name 变化需要监听的响应函数: 但是实际开发中我们会有不同的对象,另外会有不同的属性需要管理; 如何可以使用一种数据结构来管理不同对象的不同依赖关系呢? 在前面我们刚刚学习过 WeakMap,并且在学习 WeakMap 的时候我讲到了后面通过 WeakMap 如何管理这种响应式的数据依赖: 实现 可以写一个 getDepend 函数专门来管理这种依赖关系 / 封装一个获取depend的函数 /const taregtMap = new WeakMap()function getDepend(target, key) {// 根据target对象获取mapconst map = taregtMap.get(target)if (!map) {map = new Map()taregtMap.set(target, map)}// 根据key获取depend对象const depend = map.get(key)if (!depend) {depend = new Depend()map.set(key, depend)}return depend}// 监听对象的属性变化:Proxy(vue3)/Object.defineProperty(vue2)const objProxy = new Proxy(obj, {get: function (target, key, receiver) {return Reflect.get(target, key, receiver)},set: function (target, key, newValue, receiver) {Reflect.set(target, key, newValue, receiver)const depend = getDepend(target, key)depend.notify()} }) 正确的依赖收集 我们之前收集依赖的地方是在 watchFn 中: 但是这种收集依赖的方式我们根本不知道是哪一个 key 的哪一个 depend 需要收集依赖; 只能针对一个单独的 depend 对象来添加你的依赖对象; 那么正确的应该是在哪里收集呢?应该在我们调用了 Proxy 的 get 捕获器时 因为如果一个函数中使用了某个对象的 key,那么它应该被收集依赖 / 封装一个响应式函数 /let activeReactviceFn = nullfunction watchFn(fn) {activeReactviceFn = fnfn()activeReactviceFn = null}/ 封装一个获取depend的函数 /const taregtMap = new WeakMap()function getDepend(target, key) {// 根据target对象获取maplet map = taregtMap.get(target)if (!map) {map = new Map()taregtMap.set(target, map)}// 根据key获取depend对象let depend = map.get(key)if (!depend) {depend = new Depend()map.set(key, depend)}return depend}// 监听对象的属性变化:Proxy(vue3)/Object.defineProperty(vue2)const objProxy = new Proxy(obj, {get: function (target, key, receiver) {// 根据 target key 获取对应的 depnedconst depend = getDepend(target, key)// 给 depend 对象中添加响应式函数activeReactviceFn && depend.addDepend(activeReactviceFn)return Reflect.get(target, key, receiver)},set: function (target, key, newValue, receiver) {Reflect.set(target, key, newValue, receiver)const depend = getDepend(target, key)depend.notify()} }) 4.6 对 Depend 重构 两个问题: 问题一:如果函数中有用到两次 key,比如 name,那么这个函数会被收集两次 问题二:我们并不希望将添加 reactiveFn 放到 get 中,因为它是属于 Depend 的行为 所以我们需要对 Depend 类进行重构: 解决问题一的方法:不使用数组,而是使用 Set 解决问题二的方法:添加一个新的方法,用于收集依赖 // 保存当前需要收集的响应式函数let activeReactviceFn = nullclass Depend {constructor() {this.reactiveFns = new Set()}depend() {if (activeReactviceFn) {this.reactiveFns.add(activeReactviceFn)} }addDepend(reactiveFn) {this.reactiveFns.add(reactiveFn)}notify() {this.reactiveFns.forEach((fn) => {fn()})} }// 对象的响应式const obj = {name: 'why', // depend 对象age: 18 // depend 对象}/ 封装一个响应式函数 /function watchFn(fn) {activeReactviceFn = fnfn()activeReactviceFn = null}/ 封装一个获取depend的函数 /const taregtMap = new WeakMap()function getDepend(target, key) {// 根据target对象获取maplet map = taregtMap.get(target)if (!map) {map = new Map()taregtMap.set(target, map)}// 根据key获取depend对象let depend = map.get(key)if (!depend) {depend = new Depend()map.set(key, depend)}return depend}// 监听对象的属性变化:Proxy(vue3)/Object.defineProperty(vue2)const objProxy = new Proxy(obj, {get: function (target, key, receiver) {// 根据 target key 获取对应的 depnedconst depend = getDepend(target, key)// 给 depend 对象中添加响应式函数depend.depend()return Reflect.get(target, key, receiver)},set: function (target, key, newValue, receiver) {Reflect.set(target, key, newValue, receiver)const depend = getDepend(target, key)depend.notify()} })watchFn(function () {console.log(objProxy.name, '--------------')console.log(objProxy.name, '++++++++++++++')})objProxy.name = 'kobe'/ why --------------why ++++++++++++++kobe --------------kobe ++++++++++++++/ 4.7 创建响应式对象 目前的响应式是针对于obj一个对象的,我们可以创建出来一个函数,针对所有的对象都可以变成响应式对象 / 保存当前需要收集的响应式函数 /let activeReactviceFn = null/ 依赖收集类 /class Depend {constructor() {this.reactiveFns = new Set()}depend() {if (activeReactviceFn) {this.reactiveFns.add(activeReactviceFn)} }addDepend(reactiveFn) {this.reactiveFns.add(reactiveFn)}notify() {this.reactiveFns.forEach((fn) => {fn()})} }/ 封装一个响应式函数 /function watchFn(fn) {activeReactviceFn = fnfn()activeReactviceFn = null}/ 封装一个获取depend的函数 /const taregtMap = new WeakMap()function getDepend(target, key) {// 根据target对象获取maplet map = taregtMap.get(target)if (!map) {map = new Map()taregtMap.set(target, map)}// 根据key获取depend对象let depend = map.get(key)if (!depend) {depend = new Depend()map.set(key, depend)}return depend}/ 创建响应式对象函数 /function reactive(obj) {// 监听对象的属性变化:Proxy(vue3)/Object.defineProperty(vue2)return new Proxy(obj, {get: function (target, key, receiver) {// 根据 target key 获取对应的 depnedconst depend = getDepend(target, key)// 给 depend 对象中添加响应式函数depend.depend()return Reflect.get(target, key, receiver)},set: function (target, key, newValue, receiver) {Reflect.set(target, key, newValue, receiver)const depend = getDepend(target, key)depend.notify()} })}const info = reactive({address: '广州市',height: 1.88})watchFn(() => {console.log(info.address, '---')})info.address = '北京市' 4.8 Vue2 响应式原理 前面所实现的响应式的代码,其实就是 Vue3 中的响应式原理: Vue3 主要是通过 Proxy 来监听数据的变化以及收集相关的依赖的 Vue2 中通过 Object.defineProerty的方式来实现对象属性的监听 可以将 reactive 函数进行如下的重构: 在传入对象时,我们可以遍历所有的 key,并且通过属性存储描述符来监听属性的获取和修改 在 setter 和 getter 方法中的逻辑和前面的 Proxy 是一致的 / 保存当前需要收集的响应式函数 /let activeReactviceFn = null/ 依赖收集类 /class Depend {constructor() {this.reactiveFns = new Set()}depend() {if (activeReactviceFn) {this.reactiveFns.add(activeReactviceFn)} }addDepend(reactiveFn) {this.reactiveFns.add(reactiveFn)}notify() {this.reactiveFns.forEach((fn) => {fn()})} }/ 封装一个响应式函数 /function watchFn(fn) {activeReactviceFn = fnfn()activeReactviceFn = null}/ 封装一个获取depend的函数 /const taregtMap = new WeakMap()function getDepend(target, key) {// 根据target对象获取maplet map = taregtMap.get(target)if (!map) {map = new Map()taregtMap.set(target, map)}// 根据key获取depend对象let depend = map.get(key)if (!depend) {depend = new Depend()map.set(key, depend)}return depend}/ 创建响应式对象函数 /function reactive(obj) {Object.keys(obj).forEach((key) => {let value = obj[key]Object.defineProperty(obj, key, {get: function () {const dep = getDepend(obj, key)dep.depend()return value},set: function (newValue) {value = newValueconst dep = getDepend(obj, key)dep.notify()} })})return obj}const info = reactive({address: '广州市',height: 1.88})watchFn(() => {console.log(info.address, '---')})info.address = '北京市' 本篇文章为转载内容。原文链接:https://blog.csdn.net/wanghuan1020/article/details/126774033。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-01-11 12:37:47
679
转载
转载文章
...日志数据,提取出某日访问百度次数最多的那个IP 解决思想: hash分解+ 分而治之 + 归并 把这一天访问百度的日志中的IP取出来,逐个写入到一个大文件中。注意到IP是32位的,最多有2^32个IP。同样可以采用hash映射的方法,比如模1024,把整个大文件映射为1024个小文件。 再找出每个小文中出现频率最大的IP(可以采用hash_map进行频率统计,然后再找出频率最大的几个)及相应的频率。 然后再在这1024组最大的IP中,找出那个频率最大的IP,即为所求。 问题5 海量数据分布在100台电脑中,想个办法高效统计出这批数据的TOP10。 解决思想: 分而治之 + 归并。 注意TOP10是取最大值或最小值。如果取频率TOP10,就应该先hash分解。 在每台电脑上求出TOP10,采用包含10个元素的堆完成(TOP10小,用最大堆,TOP10大,用最小堆)。比如求TOP10大,我们首先取前10个元素调整成最小堆,如果发现,然后扫描后面的数据,并与堆顶元素比较,如果比堆顶元素大,那么用该元素替换堆顶,然后再调整为最小堆。最后堆中的元素就是TOP10大。 求出每台电脑上的TOP10后,然后把这100台电脑上的TOP10组合起来,共1000个数据,再利用上面类似的方法求出TOP10就可以了。 问题6 在2.5亿个整数中找出不重复的整数,内存不足以容纳这2.5亿个整数。 解决思路1 : hash 分解+ 分而治之 + 归并 2.5亿个int数据hash到1024个小文件中a0~a1023,如果某个小文件大小还大于内存,进行多级hash。每个小文件读进内存,找出只出现一次的数据,输出到b0~b1023。最后数据合并即可。 解决思路2 : 2-Bitmap 如果内存够1GB的话,采用2-Bitmap(每个数分配2bit,00表示不存在,01表示出现一次,10表示多次,11无意义)进行,共需内存2^322bit=1GB内存。然后扫描这2.5亿个整数,查看Bitmap中相对应位,如果是00变01,01变10,10保持不变。所描完事后,查看bitmap,把对应位是01的整数输出即可。 注意,如果是找出重复的数据,可以用1-bitmap。第一次bit位由0变1,第二次查询到相应bit位为1说明是重复数据,输出即可。 问题7 一共有N个机器,每个机器上有N个数。每个机器最多存O(N)个数并对它们操作。如何找到N^2个数中的中数? 解决思想1 : hash分解 + 排序 按照升序顺序把这些数字,hash划分为N个范围段。假设数据范围是2^32 的unsigned int 类型。理论上第一台机器应该存的范围为0~(2^32)/N,第i台机器存的范围是(2^32)(i-1)/N~(2^32)i/N。hash过程可以扫描每个机器上的N个数,把属于第一个区段的数放到第一个机器上,属于第二个区段的数放到第二个机器上,…,属于第N个区段的数放到第N个机器上。注意这个过程每个机器上存储的数应该是O(N)的。 然后我们依次统计每个机器上数的个数,一次累加,直到找到第k个机器,在该机器上累加的数大于或等于(N^2)/2,而在第k-1个机器上的累加数小于(N^2)/2,并把这个数记为x。那么我们要找的中位数在第k个机器中,排在第(N^2)/2-x位。然后我们对第k个机器的数排序,并找出第(N^2)/2-x个数,即为所求的中位数的复杂度是O(N^2)的。 解决思想2: 分而治之 + 归并 先对每台机器上的数进行排序。排好序后,我们采用归并排序的思想,将这N个机器上的数归并起来得到最终的排序。找到第(N^2)/2个便是所求。复杂度是O(N^2 lgN^2)的。 2 Trie树+红黑树+hash_map 这里Trie树木、红黑树或者hash_map可以认为是第一部分中分而治之算法的具体实现方法之一。 问题1 上千万或上亿数据(有重复),统计其中出现次数最多的钱N个数据。 解决思路: 红黑树 + 堆排序 如果是上千万或上亿的int数据,现在的机器4G内存可以能存下。所以考虑采用hash_map/搜索二叉树/红黑树等来进行统计重复次数。 然后取出前N个出现次数最多的数据,可以用包含N个元素的最小堆找出频率最大的N个数据。 问题2 1000万字符串,其中有些是重复的,需要把重复的全部去掉,保留没有重复的字符串。请怎么设计和实现? 解决思路:trie树。 这题用trie树比较合适,hash_map也应该能行。 问题3 一个文本文件,大约有一万行,每行一个词,要求统计出其中最频繁出现的前10个词,请给出思想,给出时间复杂度分析。 解决思路: trie树 + 堆排序 这题是考虑时间效率。 1. 用trie树统计每个词出现的次数,时间复杂度是O(nlen)(len表示单词的平准长度)。 2. 然后找出出现最频繁的前10个词,可以用堆来实现,前面的题中已经讲到了,时间复杂度是O(nlg10)。 总的时间复杂度,是O(nle)与O(nlg10)中较大的哪一个。 问题4 搜索引擎会通过日志文件把用户每次检索使用的所有检索串都记录下来,每个查询串的长度为1-255字节。假设目前有一千万个记录,这些查询串的重复读比较高,虽然总数是1千万,但是如果去除重复和,不超过3百万个。一个查询串的重复度越高,说明查询它的用户越多,也就越热门。请你统计最热门的10个查询串,要求使用的内存不能超过1G。 解决思想 : trie树 + 堆排序 采用trie树,关键字域存该查询串出现的次数,没有出现为0。最后用10个元素的最小推来对出现频率进行排序。 3 BitMap或者Bloom Filter 3.1 BitMap BitMap说白了很easy,就是通过bit位为1或0来标识某个状态存不存在。可进行数据的快速查找,判重,删除,一般来说适合的处理数据范围小于82^32。否则内存超过4G,内存资源消耗有点多。 问题1 已知某个文件内包含一些电话号码,每个号码为8位数字,统计不同号码的个数。 解决思路: bitmap 8位最多99 999 999,需要100M个bit位,不到12M的内存空间。我们把0-99 999 999的每个数字映射到一个Bit位上,所以只需要99M个Bit==12MBytes,这样,就用了小小的12M左右的内存表示了所有的8位数的电话 问题2 2.5亿个整数中找出不重复的整数的个数,内存空间不足以容纳这2.5亿个整数。 解决思路:2bit map 或者两个bitmap。 将bit-map扩展一下,用2bit表示一个数即可,00表示未出现,01表示出现一次,10表示出现2次及以上,11可以暂时不用。 在遍历这些数的时候,如果对应位置的值是00,则将其置为01;如果是01,将其置为10;如果是10,则保持不变。需要内存大小是2^32/82=1G内存。 或者我们不用2bit来进行表示,我们用两个bit-map即可模拟实现这个2bit-map,都是一样的道理。 3.2 Bloom filter Bloom filter可以看做是对bit-map的扩展。 参考july大神csdn文章 Bloom Filter 详解 4 Hadoop+MapReduce 参考引用july大神 csdn文章 MapReduce的初步理解 Hadoop框架与MapReduce模式 转载请注明本文地址: 大数据——海量数据处理的基本方法总结 本篇文章为转载内容。原文链接:https://blog.csdn.net/hong2511/article/details/80842704。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2024-03-01 12:40:17
541
转载
转载文章
...个图标,点击图标进行ssh配置: ssh yourname@yourip -A 然后右键选择在当前窗口进行连接: 除此之外,还可以设置免费登录,具体可参考这篇文章。 当然,也可以使用windows本地的WSL2,vscode连接WSL还需要安装WSL和Dev Containers这两个插件。 在服务器端执行code .会自动安装vscode server,安装位置在用户的根目录下: 3. 安装FFI Navigator 由于TVM是由Python和C++混合开发,且大多数的IDE仅支持在同一种语言中查找函数定义,因此对于跨语言的FFI 调用,即Python跳转到C++或者C++跳转到Python,vscode是做不到的。虽然解决这个问题在技术上可能非常具有挑战性,但我们可以通过构建一个与FFI注册码模式匹配并恢复必要信息的项目特定分析器来解决这个问题,FFI Navigator就这样诞生了,作者仍然是陈天奇博士。 安装方式如下: 建议使用源码安装git clone https://github.com/tqchen/ffi-navigator.git 安装python依赖cd ffi-navigator/pythonpython setyp.py install vscode需要安装FFI Navigator插件,直接搜索安装即可(安装到服务器端)。 最后需要在.vscode/setting.json进行配置,内容如下: {"python.analysis.extraPaths": ["${workspaceFolder}/python"], // 添加额外导入路径, 告诉pylance自定义的python库在哪里"ffi_navigator.pythonpath": "/home/liyanpeng/anaconda3/envs/tvmenv/bin/python", // 配置FFI Navigator"python.defaultInterpreterPath": "/home/liyanpeng/anaconda3/envs/tvmenv/bin/python","files.associations": {"type_traits": "cpp","fstream": "cpp","thread": "cpp",".tcc": "cpp"} } 更详细内容可以参考项目链接。 结束语 对于vscode的使用技巧及C/C++相关的配置,这里不再详细的介绍了,感兴趣的小伙伴们可以了解下。 本篇文章为转载内容。原文链接:https://blog.csdn.net/qq_42730750/article/details/126723224。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-12-12 20:04:26
87
转载
转载文章
...关系进行映射,那么当访问ObjA的时候,顺便访问这个额外的指针,从这个指针指向的位图就能找到被ObjA引用的分区B对应的内存块。通常我们只需要判定位图里面对应的位是否有1,有的话则认为发生了引用。 class CardTable: public CHeapObj<mtGC> {friend class VMStructs;public:typedef uint8_t CardValue;// All code generators assume that the size of a card table entry is one byte.// They need to be updated to reflect any change to this.// This code can typically be found by searching for the byte_map_base() method.STATIC_ASSERT(sizeof(CardValue) == 1);protected:// The declaration order of these const fields is important; see the// constructor before changing.const MemRegion _whole_heap; // the region covered by the card tableconst size_t _page_size; // page size used when mapping _byte_mapsize_t _byte_map_size; // in bytesCardValue _byte_map; // the card marking arrayCardValue _byte_map_base;// Some barrier sets create tables whose elements correspond to parts of// the heap; the CardTableBarrierSet is an example. Such barrier sets will// normally reserve space for such tables, and commit parts of the table// "covering" parts of the heap that are committed. At most one covered// region per generation is needed.static constexpr int max_covered_regions = 2;// The covered regions should be in address order.MemRegion _covered[max_covered_regions];// The last card is a guard card; never committed.MemRegion _guard_region;inline size_t compute_byte_map_size(size_t num_bytes);enum CardValues {clean_card = (CardValue)-1,dirty_card = 0,CT_MR_BS_last_reserved = 1};// a word's worth (row) of clean card valuesstatic const intptr_t clean_card_row = (intptr_t)(-1);// CardTable entry sizestatic uint _card_shift;static uint _card_size;static uint _card_size_in_words;size_t last_valid_index() const {return cards_required(_whole_heap.word_size()) - 1;}private:void initialize_covered_region(void region0_start, void region1_start);MemRegion committed_for(const MemRegion mr) const;public:CardTable(MemRegion whole_heap);virtual ~CardTable() = default;void initialize(void region0_start, void region1_start);// Barrier set functions.// Initialization utilities; covered_words is the size of the covered region// in, um, words.inline size_t cards_required(size_t covered_words) const {assert(is_aligned(covered_words, _card_size_in_words), "precondition");return covered_words / _card_size_in_words;}// Dirty the bytes corresponding to "mr" (not all of which must be// covered.)void dirty_MemRegion(MemRegion mr);// Clear (to clean_card) the bytes entirely contained within "mr" (not// all of which must be covered.)void clear_MemRegion(MemRegion mr);// Return true if "p" is at the start of a card.bool is_card_aligned(HeapWord p) {CardValue pcard = byte_for(p);return (addr_for(pcard) == p);}// Mapping from address to card marking array entryCardValue byte_for(const void p) const {assert(_whole_heap.contains(p),"Attempt to access p = " PTR_FORMAT " out of bounds of "" card marking array's _whole_heap = [" PTR_FORMAT "," PTR_FORMAT ")",p2i(p), p2i(_whole_heap.start()), p2i(_whole_heap.end()));CardValue result = &_byte_map_base[uintptr_t(p) >> _card_shift];assert(result >= _byte_map && result < _byte_map + _byte_map_size,"out of bounds accessor for card marking array");return result;}// The card table byte one after the card marking array// entry for argument address. Typically used for higher bounds// for loops iterating through the card table.CardValue byte_after(const void p) const {return byte_for(p) + 1;}void invalidate(MemRegion mr);// Provide read-only access to the card table array.const CardValue byte_for_const(const void p) const {return byte_for(p);}const CardValue byte_after_const(const void p) const {return byte_after(p);}// Mapping from card marking array entry to address of first wordHeapWord addr_for(const CardValue p) const {assert(p >= _byte_map && p < _byte_map + _byte_map_size,"out of bounds access to card marking array. p: " PTR_FORMAT" _byte_map: " PTR_FORMAT " _byte_map + _byte_map_size: " PTR_FORMAT,p2i(p), p2i(_byte_map), p2i(_byte_map + _byte_map_size));// As _byte_map_base may be "negative" (the card table has been allocated before// the heap in memory), do not use pointer_delta() to avoid the assertion failure.size_t delta = p - _byte_map_base;HeapWord result = (HeapWord) (delta << _card_shift);assert(_whole_heap.contains(result),"Returning result = " PTR_FORMAT " out of bounds of "" card marking array's _whole_heap = [" PTR_FORMAT "," PTR_FORMAT ")",p2i(result), p2i(_whole_heap.start()), p2i(_whole_heap.end()));return result;}// Mapping from address to card marking array index.size_t index_for(void p) {assert(_whole_heap.contains(p),"Attempt to access p = " PTR_FORMAT " out of bounds of "" card marking array's _whole_heap = [" PTR_FORMAT "," PTR_FORMAT ")",p2i(p), p2i(_whole_heap.start()), p2i(_whole_heap.end()));return byte_for(p) - _byte_map;}CardValue byte_for_index(const size_t card_index) const {return _byte_map + card_index;}// Resize one of the regions covered by the remembered set.void resize_covered_region(MemRegion new_region);// Card-table-RemSet-specific things.static uintx ct_max_alignment_constraint();static uint card_shift() {return _card_shift;}static uint card_size() {return _card_size;}static uint card_size_in_words() {return _card_size_in_words;}static constexpr CardValue clean_card_val() { return clean_card; }static constexpr CardValue dirty_card_val() { return dirty_card; }static intptr_t clean_card_row_val() { return clean_card_row; }// Initialize card sizestatic void initialize_card_size();// Card marking array base (adjusted for heap low boundary)// This would be the 0th element of _byte_map, if the heap started at 0x0.// But since the heap starts at some higher address, this points to somewhere// before the beginning of the actual _byte_map.CardValue byte_map_base() const { return _byte_map_base; }virtual bool is_in_young(const void p) const = 0;}; class G1CardTable : public CardTable {friend class VMStructs;friend class G1CardTableChangedListener;G1CardTableChangedListener _listener;public:enum G1CardValues {g1_young_gen = CT_MR_BS_last_reserved << 1,// During evacuation we use the card table to consolidate the cards we need to// scan for roots onto the card table from the various sources. Further it is// used to record already completely scanned cards to avoid re-scanning them// when incrementally evacuating the old gen regions of a collection set.// This means that already scanned cards should be preserved.//// The merge at the start of each evacuation round simply sets cards to dirty// that are clean; scanned cards are set to 0x1.//// This means that the LSB determines what to do with the card during evacuation// given the following possible values://// 11111111 - clean, do not scan// 00000001 - already scanned, do not scan// 00000000 - dirty, needs to be scanned.//g1_card_already_scanned = 0x1};static const size_t WordAllClean = SIZE_MAX;static const size_t WordAllDirty = 0;STATIC_ASSERT(BitsPerByte == 8);static const size_t WordAlreadyScanned = (SIZE_MAX / 255) g1_card_already_scanned;G1CardTable(MemRegion whole_heap): CardTable(whole_heap), _listener() {_listener.set_card_table(this);}static CardValue g1_young_card_val() { return g1_young_gen; }static CardValue g1_scanned_card_val() { return g1_card_already_scanned; }void verify_g1_young_region(MemRegion mr) PRODUCT_RETURN;void g1_mark_as_young(const MemRegion& mr);size_t index_for_cardvalue(CardValue const p) const {return pointer_delta(p, _byte_map, sizeof(CardValue));}// Mark the given card as Dirty if it is Clean. Returns whether the card was// Clean before this operation. This result may be inaccurate as it does not// perform the dirtying atomically.inline bool mark_clean_as_dirty(CardValue card);// Change Clean cards in a (large) area on the card table as Dirty, preserving// already scanned cards. Assumes that most cards in that area are Clean.inline void mark_range_dirty(size_t start_card_index, size_t num_cards);// Change the given range of dirty cards to "which". All of these cards must be Dirty.inline void change_dirty_cards_to(CardValue start_card, CardValue end_card, CardValue which);inline uint region_idx_for(CardValue p);static size_t compute_size(size_t mem_region_size_in_words) {size_t number_of_slots = (mem_region_size_in_words / _card_size_in_words);return ReservedSpace::allocation_align_size_up(number_of_slots);}// Returns how many bytes of the heap a single byte of the Card Table corresponds to.static size_t heap_map_factor() { return _card_size; }void initialize(G1RegionToSpaceMapper mapper);bool is_in_young(const void p) const override;}; 以位为粒度的位图能准确描述每一个字的引用关系,但是一个位通常包含的信息太少,只能描述2个状态:引用还是未引用。实际应用中JVM在垃圾回收的时候需要更多的状态,如果增加至一个字节来描述状态,则位图需要256KB的空间,这个数字太大,开销占了25%。所以一个可能的做法位图不再描述一个字,而是一个区域,JVM选择512字节为单位,即用一个字节描述512字节的引用关系。选择一个区域除了空间利用率的问题之外,实际上还有现实的意义。我们知道Java对象实际上不是一个字能描述的(有一个参数可以控制对象最小对齐的大小,默认是8字节,实际上Java在JVM中还有一些附加信息,所以对齐后最小的Java对象是16字节),很多Java对象可能是几十个字节或者几百个字节,所以用一个字节描述一个区域是有意义的。但是我没有找到512的来源,为什么512效果最好?没有相应的数据来支持这个数字,而且这个值不可以配置,不能修改,但是有理由相信512字节的区域是为了节约内存额外开销。按照这个值,1MB的内存只需要2KB的额外空间就能描述引用关系。这又带来另一个问题,就是512字节里面的内存可能被引用多次,所以这是一个粗略的关系描述,那么在使用的时候需要遍历这512字节。 再举一个例子,假设有两个对象B、C都在这512字节的区域内。为了方便处理,记录对象引用关系的时候,都使用对象的起始位置,然后用这个地址和512对齐,因此B和C对象的卡表指针都指向这一个卡表的位置。那么对于引用处理也有可有两种处理方法:·处理的时候会以堆分区为处理单位,遍历整个堆分区,在遍历的时候,每次都会以对象大小为步长,结合卡表,如果该卡表中对应的位置被设置,则说明对象和其他分区的对象发生了引用。具体内容在后文中介绍Refine的时候还会详细介绍。·处理的时候借助于额外的数据结构,找到真正对象的位置,而不需要从头开始遍历。在后文的并发标记处理时就使用了这种方法,用于找到第一个对象的起始位置。在G1除了512字节粒度的卡表之外,还有bitMap,例如使用bitMap可以描述一个分区对另外一个分区的引用情况。在JVM中bitMap使用非常多,例如还可以描述内存的分配情况。 在G1除了512字节粒度的卡表之外,还有bitMap,例如使用bitMap可以描述一个分区对另外一个分区的引用情况。在JVM中bitMap使用非常多,例如还可以描述内存的分配情况。G1在混合收集算法中用到了并发标记。在并发标记的时候使用了bitMap来描述对象的分配情况。例如1MB的分区可以用16KB(16KB×ObjectAlignmentInBytes×8=1MB)来描述,即16KB额外的空间。其中ObjectAlignmentInBytes是8字节,指的是对象对齐,第二个8是指一个字节有8位。即每一个位可以描述64位。例如一个对象长度对齐之后为24字节,理论上它占用3个位来描述这个24字节已被使用了,实际上并不需要,在标记的时候只需要标记这3个位中的第一个位,再结合堆分区对象的大小信息就能准确找出。其最主要的目的是为了效率,标记一个位和标记3个位相比能节约不少时间,如果对象很大,则更划算。这些都是源码的实现细节,大家在阅读源码时需要细细斟酌。 本篇文章为转载内容。原文链接:https://blog.csdn.net/qq_16500963/article/details/132133125。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-12-16 20:37:50
246
转载
转载文章
...载操作符以及控制属性访问等。 构造器 , 构造器是面向对象编程中的一个重要概念,它指的是当创建一个类的新实例时所调用的方法。在Python中,构造器通常指__init__()方法,该方法在实例化对象时自动执行,用于设置对象的初始状态或执行必要的初始化操作。例如,在上述文章中,类RoundFloatManual中的__init__(self, val)就是构造器,用于接收浮点数并将其四舍五入为两位小数。 重载操作符 , 重载操作符是指在面向对象编程中,重新定义或扩展某个操作符的行为。在Python中,可以通过实现对应的操作符特殊方法来重载操作符,比如实现__add__()方法以改变加法运算符\ +\ 的行为。在示例类Time60中,通过覆盖__add__()方法使得两个时间对象相加时,会返回一个新的Time60对象,其小时和分钟之和为两个输入对象的相应值之和,这就是对加法操作符的重载。
2023-04-19 14:30:42
132
转载
转载文章
...以及一些对用户帐号的安全保护措施,下图的菜单非常简要的说明了个人中心的功能。个人觉得最值得关注的就是密保设置和修改头像,因为之前没有处理过类似的问题,本文主要记录对头像的处理过程以及思考,希望给碰到类似问题的苦逼程序员一点借鉴。 个人中心整体功能一览 2,头像处理xmind 叽歪一句,个人碰到问题的时候,首先会分析问题,在分析问题的基础上,得到整体的解决方案,然后一步步分解步骤,去实现,首先奉上我的解决方案,也许不是最优的,但是按照个人的知识和技能水平,绝对是可以实现的。 修改头像mind 3,实现步骤 按照我的mind,首先是上传图片,先上效果图,然后给出实现的代码。首先是整体的结构图,做的比较丑,别喷哥··· 修改头像整体效果图 下面按照mind一步步实现, 首先:点击修改头像,弹出一个层, 第一步:弹出上传图片的层,上传图片到服务器 对实现细节不感冒的屌丝可以看看代码(结合哥的mind看可以事半功倍): 分层实现细节 Html结构层这个可以免了,一般都可以弄出来 Js连接层 首先是弹出一个上传图片的层,然后上传图片到服务器端。 $("editHead").bind("click", function () { showUploadDiv(); }); function showUploadDiv() { $("uploadMsg").empty(); $.fancybox({ type:'inline', width:400, href:'uploadUserHead' }); }//fancybox弹出层 上传的处理代码 Servlet服务端处理层(commonupload实现)服务器端处理代码 上传的处理代码 $(function () { $("uploadFrom").ajaxForm({ beforeSubmit:checkImg, error:function(data,status){ alert(status+' , '+data); $("uploadMsg").html('上传文件超过1M!'); }, success:function (data,status) { try{ var msg = $.parseJSON(data); if (msg.code == 200) { //如果成功提交 javascript:$.fancybox.close(); $("uploadUserHead").hide(); var data = msg.object; $("editImg").attr("src", data.path).show(); $("preview1").attr("src", data.path).show(); $(".zoom").show(); $("width").val(data.width); $("height").val(data.height); $("oldImgPath").val(data.realPath); $("imgFileExt").val(data.fileExt); var api, jcrop_api, boundx, boundy; $('editImg').Jcrop({ onChange:updatePreview, onSelect:updatePreview, aspectRatio:1, bgOpacity:0.5, bgColor:'white', addClass:'jcrop-light' }, function () { api = this; api.setSelect([130, 65, 130 + 350, 65 + 285]); api.setOptions({ bgFade:true }); api.ui.selection.addClass('jcrop-selection'); var bounds = this.getBounds(); boundx = bounds[0]; boundy = bounds[1]; jcrop_api = this; }); function updatePreview(c) { if (parseInt(c.w) > 0) { var rx = 80 / c.w; var ry = 80 / c.h; $('preview1').css({ width:Math.round(rx boundx) + 'px', height:Math.round(ry boundy) + 'px', marginLeft:'-' + Math.round(rx c.x) + 'px', marginTop:'-' + Math.round(ry c.y) + 'px' }); } jQuery('x').val(c.x); jQuery('y').val(c.y); jQuery('x2').val(c.x2); jQuery('y2').val(c.y2); jQuery('w').val(c.w); jQuery('h').val(c.h); } } if (msg.code == 204) { $("uploadMsg").html(msg.msg); } }catch (e){ $("uploadMsg").html('上传文件超过1M!'); } } }); }); //服务器端处理代码 String tempSavePath = ConfigurationUtils.get("user.resource.dir"); //上传的图片零时保存路径 String tempShowPath = ConfigurationUtils.get("user.resource.url"); //用户保存的头像路径 if(tempSavePath.equals("/img")) { tempSavePath=sc.getRealPath("/")+tempSavePath; } Msg msg = new Msg(); msg.setCode(204); msg.setMsg("上传头像失败!"); String type = request.getParameter("type"); if (!Strings.isNullOrEmpty(type) && type.equals("first")) { request.setCharacterEncoding("utf-8"); DiskFileItemFactory factory = new DiskFileItemFactory(); ServletFileUpload servletFileUpload = new ServletFileUpload(factory); try { List items = servletFileUpload.parseRequest(request); Iterator iterator = items.iterator(); while (iterator.hasNext()) { FileItem item = (FileItem) iterator.next(); if (!item.isFormField()) { { File tempFile = new File(item.getName()); File saveTemp = new File(tempSavePath+"/tempImg/"); String getItemName=tempFile.getName(); String fileName = UUID.randomUUID()+"." +getItemName.substring(getItemName.lastIndexOf(".") + 1, getItemName.length()); File saveDir = new File(tempSavePath+"/tempImg/", fileName); //如果目录不存在,创建。 if (saveTemp.exists() == false) { if (!saveTemp.mkdir()) { // 创建失败 saveTemp.getParentFile().mkdir(); saveTemp.mkdir(); } else { } } if (saveDir.exists()) { log.info("存在同名文件···"); saveDir.delete(); } item.write(saveDir); log.info("上传头像成功!"+saveDir.getName()); msg.setCode(200); msg.setMsg("上传头像成功!"); Image image = new Image(); BufferedImage bufferedImage = null; try { bufferedImage = ImageIO.read(saveDir); } catch (IOException e) { e.printStackTrace(); } image.setHeight(bufferedImage.getHeight()); image.setWidth(bufferedImage.getWidth()); image.setPath(tempShowPath+ "/tempImg/" + fileName); log.info(image.getPath()); image.setRealPath(tempSavePath+"/tempImg/"+ fileName); image.setFileExt(fileName.substring(fileName.lastIndexOf(".") + 1, fileName.length())); msg.setObject(image); } } else { log.info("" + item.getFieldName()); } } } catch (Exception ex) { log.error("上传用户头像图片异常!"); ex.printStackTrace(); } finally { AppHelper.returnJsonAjaxForm(response, msg); } } 上传成功后,可以看到照片和照片的预览效果。看图: 上传头像之后的效果 Friday, October 05, 2012 第二步:编辑和保存头像 选中图中的区域,保存头像,就完成头像的修改。 修改之后的效果入下: 修改之后的头像(因为传了一张动态图片,得到的跟上图有些不同) 实现细节: 首先用了一个js控件:Jcrop,有兴趣的屌丝可以去搜一下,然后,利用上传之后的图片和之前的选定区域,完成了一个截图,保存为用户的头像。 连接层的js: $("saveHead").bind("click", function () { var width = $("width").val(); var height = $("height").val(); var oldImgPath = $("oldImgPath").val(); var imgFileExt = $("imgFileExt").val(); var x = $('x').val(); var y = $('y').val(); var w = $('w').val(); var h = $('h').val(); $.ajax({ url:'/imgCrop', type:'post', data:{x:x, y:y, w:w, h:h, width:width, height:height, oldImgPath:oldImgPath, fileExt:imgFileExt}, datatype:'json', success:function (msg) { if (msg.code == 200) { $("avatar").attr("src", msg.object); forword('/nav', 'index'); } else { alert(msg.msg); } } }); }); function checkImg() { //限制上传文件的大小和后缀名 var filePath = $("input[name='uploadImg']").val(); if (!filePath) { $("uploadMsg").html("请选择上传文件!").show(); return false; } else { var extStart = filePath.lastIndexOf("."); var ext = filePath.substring(extStart, filePath.length).toUpperCase(); if (ext != ".PNG" && ext != ".GIF" && ext != ".JPG") { $("uploadMsg").html("图片限于png,gif,jpg格式!").show(); return false; } } return true; } 服务器端处理代码: String savePath = ConfigurationUtils.get("user.resource.dir"); //上传的图片保存路径 String showPath = ConfigurationUtils.get("user.resource.url"); //显示图片的路径 if(savePath.equals("/img")) { savePath=sc.getRealPath("/")+savePath; } int userId = AppHelper.getUserId(request); String userName=AppHelper.getUserName(request); Msg msg = new Msg(); msg.setCode(204); msg.setMsg("剪切图片失败!"); if (userId <= 0) { msg.setMsg("请先登录"); return; } // 用户经过剪辑后的图片的大小 Integer x = (int)Float.parseFloat(request.getParameter("x")); Integer y = (int)Float.parseFloat(request.getParameter("y")); Integer w = (int)Float.parseFloat(request.getParameter("w")); Integer h = (int)Float.parseFloat(request.getParameter("h")); //获取原显示图片路径 和大小 String oldImgPath = request.getParameter("oldImgPath"); Integer width = (int)Float.parseFloat(request.getParameter("width")); Integer height = (int)Float.parseFloat(request.getParameter("height")); //图片后缀 String imgFileExt = request.getParameter("fileExt"); String foldName="/"+ DateUtils.nowDatetoStrToMonth()+"/"; String imgName = foldName + UUID.randomUUID()+userName + "." + imgFileExt; //组装图片真实名称 String createImgPath = savePath + imgName; //进行剪切图片操作 ImageCut.abscut(oldImgPath,createImgPath, xwidth/300, yheight/300, wwidth/300, hheight/300); File f = new File(createImgPath); if (f.exists()) { msg.setObject(imgName); //把显示路径保存到用户信息下面。 UserService userService = userServiceProvider.get(); int rel = userService.updateUserAvatar(userId, showPath+imgName); if (rel >= 1) { msg.setCode(200); msg.setMsg("剪切图片成功!"); log.info("剪切图片成功!"); //记录日志,更新session log(showPath+imgName,userName); UserObject userObject= userService.getUserObject(userName); request.getSession().setAttribute("userObject", userObject); if (userObject != null && Strings.isNullOrEmpty(userObject.getHeadDir())) userObject.setHeadDir("/images/geren_right_01.jpg"); } else { msg.setCode(204); msg.setMsg("剪切图片失败!"); log.info("剪切图片失败!"); } } AppHelper.returnJson(response, msg); File file=new File(oldImgPath); boolean deleteFile= file.delete(); if(deleteFile==true) { log.info("删除原来图片成功"); } / 图像切割(改) @param srcImageFile 源图像地址 @param dirImageFile 新图像地址 @param x 目标切片起点x坐标 @param y 目标切片起点y坐标 @param destWidth 目标切片宽度 @param destHeight 目标切片高度 / public static void abscut(String srcImageFile, String dirImageFile, int x, int y, int destWidth, int destHeight) { try { Image img; ImageFilter cropFilter; // 读取源图像 BufferedImage bi = ImageIO.read(new File(srcImageFile)); int srcWidth = bi.getWidth(); // 源图宽度 int srcHeight = bi.getHeight(); // 源图高度 if (srcWidth >= destWidth && srcHeight >= destHeight) { Image image = bi.getScaledInstance(srcWidth, srcHeight, Image.SCALE_DEFAULT); // 改进的想法:是否可用多线程加快切割速度 // 四个参数分别为图像起点坐标和宽高 // 即: CropImageFilter(int x,int y,int width,int height) cropFilter = new CropImageFilter(x, y, destWidth, destHeight); img = Toolkit.getDefaultToolkit().createImage(new FilteredImageSource(image.getSource(), cropFilter)); BufferedImage tag = new BufferedImage(destWidth, destHeight, BufferedImage.TYPE_INT_RGB); Graphics g = tag.getGraphics(); g.drawImage(img, 0, 0, null); // 绘制缩小后的图 g.dispose(); // 输出为文件 ImageIO.write(tag, "JPEG", new File(dirImageFile)); } } catch (Exception e) { e.printStackTrace(); } } 最后一个处理的比较好的地方就是图片的存储路径问题: 我在服务器端的nginx中做了一个图片的地址映射,把图片放到了跟程序不同的路径中,每次存储图片都是存到图片路径中,客户端拿到图片的地址确实经过nginx映射过的地址。 还有就是关于限制上传图片的大小的问题: 我在服务器端显示了资源的最大大小为1M,当上传的资源超过1M,服务器自动报错413,通过异常处理,可以在客户端得到正确的提示信息。 4,总结优点和不足。 关于修改头像,这么做下来确实达到了目的,用户可以从容的修改头像,性能也还可以。但是,上传图片的大小判断是依靠服务器端来判断的,等待的时间比较久,改进的方向是使用flash控件来限制,使用flash来上传,也不会出现弹出层,这样比较大众化,更容易为用户接受一点。我会不断改进。 本篇文章为转载内容。原文链接:https://blog.csdn.net/weixin_39849287/article/details/111489534。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-07-18 10:58:17
268
转载
转载文章
...号,大大提升了社区的安全性和便利性,充分展示了MNIST数据集训练模型在实际场景中的高效应用。 此外,针对跨国文化背景下的数字识别差异问题,有研究团队正着手构建包含多元书写风格的全球手写数字数据库,以期通过更全面的数据训练,提升各类设备对手写数字识别的普适性和准确性。同时,也有科研人员积极探索新的图像预处理技术和网络优化算法,如超分辨率技术、注意力机制等,进一步提高识别系统的鲁棒性和精度。 值得注意的是,云端训练与边缘计算的结合正在为OpenMV等嵌入式设备提供强大的后盾支持。例如,阿里云IoT部门最近推出的云端-边缘协同训练方案,允许用户在云端完成大规模数据训练后,将轻量化模型部署至OpenMV等终端设备上,既保证了模型性能,又降低了设备存储和计算压力,对于推动智能硬件在数字识别领域的广泛应用具有深远意义。 总之,在当今AI技术蓬勃发展的大背景下,OpenMV作为微型计算机视觉平台的角色愈发重要,其在手写数字识别项目中的实践不仅体现了技术的先进性,也昭示着未来物联网设备智能化的发展趋势。
2024-01-10 08:44:41
282
转载
Golang
...力。嘿,你有没有试过访问这个地址:http://localhost:8080/username?当你这么做的时候,Go 这家伙就会偷偷摸摸地给你派来一个小帮手——一个协程,专门负责处理你的请求。而且更贴心的是,它完全不用你去管什么线程池那些听起来就头大的复杂玩意儿,简直是太省心了吧! 当然了,光靠协程还不够。为了确保程序的健壮性,我们需要合理地利用通道(channel)来进行通信。比如下面这个简单的生产者-消费者模型: go package main import ( "fmt" "time" ) func producer(ch chan<- int) { for i := 0; i < 5; i++ { ch <- i fmt.Println("Produced:", i) time.Sleep(500 time.Millisecond) } close(ch) } func consumer(ch <-chan int) { for num := range ch { fmt.Println("Consumed:", num) } } func main() { ch := make(chan int) go producer(ch) consumer(ch) } 在这个例子中,producer函数向通道发送数据,而consumer函数从通道接收数据。用这种方法,咱们就能又优雅又稳妥地搞定多线程里的同步难题,还不用担心被死锁给缠上。 --- 3. 内存管理 GC的奥秘 接下来谈谈内存管理。Go的垃圾回收器(GC)是它的一大亮点。就像用老式工具编程一样,C/C++这种传统语言就得让程序员自己动手去清理内存,稍不留神,就可能搞出内存泄漏,或者戳到那些讨厌的野指针,简直让人头大!而Go则完全解放了我们的双手,它会自动帮你清理不再使用的内存。 不过,GC也不是万能的。有时候,如果你对性能要求特别高,可能会遇到GC停顿的问题。为了解决这个问题,Go团队一直在优化GC算法。最新版本中引入了分代GC(Generational GC),大幅降低了停顿时间。 那么,我们在实际开发中应该如何减少GC的压力呢?最直接的方法就是尽量避免频繁的小对象分配。比如,我们可以复用一些常见的结构体,而不是每次都新建它们: go type Buffer struct { data []byte } func NewBuffer(size int) Buffer { return &Buffer{data: make([]byte, size)} } func (b Buffer) Reset() { b.data = b.data[:0] } func main() { buf := NewBuffer(1024) for i := 0; i < 100; i++ { buf.Reset() // 使用buf... } } 在这个例子中,我们通过Reset()方法复用了同一个Buffer实例,而不是每次都调用make([]byte, size)重新创建一个新的切片。这样可以显著降低GC的压力。 --- 4. 网络优化 TCP/IP的实战 再来说说网络优化。Go的net包提供了强大的网络编程支持,无论是HTTP、WebSocket还是普通的TCP/UDP,都能轻松搞定。特别是对那些高性能服务器而言,怎么才能又快又稳地搞定海量连接,这简直就是一个绕不开的大难题啊! 举个例子,假设我们要实现一个简单的HTTP长连接服务器。传统的做法可能是监听端口,然后逐个处理请求。但这种方式效率不高,特别是在高并发场景下。Go提供了一个更好的解决方案——使用net/http包的Serve方法: go package main import ( "log" "net/http" ) func handler(w http.ResponseWriter, r http.Request) { w.Write([]byte("Hello, World!")) } func main() { http.HandleFunc("/", handler) log.Fatal(http.ListenAndServe(":8080", nil)) } 这段代码看起来很简单,但它实际上已经具备了处理大量并发连接的能力。为啥呢?就是因为Go语言里的http.Server自带了一个超级能打的“工具箱”,里面有个高效的连接池和请求队列,遇到高并发的情况时,它就能像一个经验丰富的老司机一样,把各种请求安排得明明白白,妥妥地hold住场面! 当然,如果你想要更底层的控制,也可以直接使用net包来编写TCP服务器。比如下面这个简单的TCP回显服务器: go package main import ( "bufio" "fmt" "net" ) func handleConnection(conn net.Conn) { defer conn.Close() reader := bufio.NewReader(conn) for { message, err := reader.ReadString('\n') if err != nil { fmt.Println("Error reading:", err) break } fmt.Print("Received:", message) conn.Write([]byte(message)) } } func main() { listener, err := net.Listen("tcp", ":8080") if err != nil { fmt.Println("Error listening:", err) return } defer listener.Close() fmt.Println("Listening on :8080...") for { conn, err := listener.Accept() if err != nil { fmt.Println("Error accepting:", err) continue } go handleConnection(conn) } } 在这个例子中,我们通过listener.Accept()不断接受客户端连接,并为每个连接启动一个协程来处理请求。这种模式非常适合处理大量短连接的场景。 --- 5. 代码结构 模块化与可扩展性 最后,我们来聊聊代码结构。一个高性能的服务器不仅仅依赖于语言特性,还需要良好的设计思路。Go语言特别推崇把程序分成小块儿来写,就像搭积木一样,每个功能都封装成独立的小模块或包。这样不仅修 bug 的时候方便找问题,写代码的时候也更容易看懂,以后想加新功能啥的也简单多了。 比如,假设我们要开发一个分布式任务调度系统,可以按照以下方式组织代码: go // tasks.go package task type Task struct { ID string Name string Param interface{} } func NewTask(id, name string, param interface{}) Task { return &Task{ ID: id, Name: name, Param: param, } } // scheduler.go package scheduler import "task" type Scheduler struct { tasks []task.Task } func NewScheduler() Scheduler { return &Scheduler{ tasks: make([]task.Task, 0), } } func (s Scheduler) AddTask(t task.Task) { s.tasks = append(s.tasks, t) } func (s Scheduler) Run() { for _, t := range s.tasks { fmt.Printf("Executing task %s\n", t.Name) // 执行任务逻辑... } } 通过这种方式,我们将任务管理和调度逻辑分离出来,使得代码更加清晰易懂。同时,这样的设计也方便未来扩展新的功能,比如添加日志记录、监控指标等功能。 --- 6. 总结与展望 好了,到这里咱们就差不多聊完了如何用Go语言进行高性能服务器开发。说实话,写着这篇文章的时候,我脑海里突然蹦出大学时那股子钻研劲儿,感觉就像重新回到那些熬夜敲代码的日子了,整个人都热血上头!Go这门语言真的太带感了,简单到没话说,效率还超高,稳定性又好得没话说,简直就是程序员的救星啊! 不过,我也想提醒大家一句:技术再好,最终还是要服务于业务需求。不管你用啥法子、说啥话,老老实实问问自己:“这招到底管不管用?是不是真的解决问题了?”这才是真本事! 希望这篇文章对你有所帮助,如果你有任何疑问或者想法,欢迎随时留言讨论!让我们一起继续探索Go的无限可能吧!
2025-04-23 15:46:59
39
桃李春风一杯酒
Netty
...手,保证咱们的信息能安全顺利地到达目的地。 java RetryHandler retryHandler = new RetryHandler(maxRetries); ctx.pipeline().addFirst(retryHandler); 上面这段代码展示了如何添加一个重试处理器到Netty的管道中,让它在遇到错误时自动重试。 4. 总结与展望 经过这一番探讨,相信大家已经对Netty及其在大数据流处理平台中的应用有了更深入的理解。Netty可不只是个工具库啊,它更像是个靠谱的小伙伴,陪着咱们一起在高性能网络编程的大海里劈波斩浪、寻宝探险! 当然,Netty也有它的局限性。比如说啊,遇到那种超级复杂的业务场景,你可能就得绞尽脑汁写一堆专门定制的代码,不然根本搞不定。还有呢,这门技术的学习难度有点大,刚上手的小白很容易觉得晕头转向,不知道该怎么下手。但我相信,只要坚持实践,总有一天你会爱上它。 未来,随着5G、物联网等新技术的发展,大数据流处理的需求将会更加旺盛。而Netty凭借其卓越的性能和灵活性,必将在这一领域继续发光发热。所以,不妨大胆拥抱Netty吧,它会让你的开发之旅变得更加精彩! 好了,今天的分享就到这里啦!如果你有任何疑问或者想法,欢迎随时交流。记住,编程之路没有终点,只有不断前进的脚步。加油,朋友们!
2025-04-26 15:51:26
46
青山绿水
转载文章
...实现了只要指定一张 Mysql 数据库中的表,就可以自动生成 bootstrap 样式的管理后台界面。支持列表展示、搜索、删除、批量删除、文本框、时间控件等等一切基础功能。再以后涉及管理后台的功能,只需要在这个基础上改造就行了,人力投入降低了很多,风格也得到了统一。这个工具现在在我们团队内部仍然还在广泛地使用。 还有个故事我也讲过,就是老大分配给我一个图片下载的任务。我不局限于完成完成任务,而且还把文件系统、磁盘工作原理都深入整理了一遍,就是这篇《Linux文件系统十问》 03 转战搜狗 2013 下半年的时候,我第一次感受到了工作岗位的震荡。我还专注解决某一个 bug,花了不少精力都还没查到 bug 的原因。这时候,部门助理突然招呼我们所有人都下楼,在银科腾讯的 Image 印象店集合。在那里,见到了腾讯的总裁 Martin。这还是第一次离大老板只有一米远的距离。 所有人都是一脸困惑,突然把大家召集下来是干嘛呢。原来就在几个小时前,腾讯总办已经和搜狗达成了协议。腾讯收购搜狗的一部分股份,并把我们连人带业务一起注入到了搜狗。 没想到,是老板用一种更牛逼的方式帮我把 bug 给解决了。 14 年 1 月正式到了搜狗以后,我们没有继续做搜索了。而是内部 Transfer 到了另外一个部门。做起了搜狗网址导航、搜狗手机助手、搜狗浏览器等业务。我也是从那个时间点,开始带团队的,也是从那以后慢慢开始从个人贡献者到带团队集体输出的角色的转变。 在搜狗工作的这 7 年的时间里,我仍然也是延续之前的风格。不拘泥于完成工作中的产品需求,以及老大交付的任务。而是主动去探索各种项目中有价值的事情。 比如在手机助手的推广中,我琢磨了新用户的安装流程的各个环节后,找出影响用户安装率提升的关键因素。然后对新版本安装包采用了多种技术方案,将单用户获取成本削减了20%+,这一年下来就是千万级别的成本节约。 我们还主动在手机助手的搜索模块中应用了简单的学习算法。采用了用户协同,标签相似,点击反馈等方法将手机助手的搜索转化率提升了数个百分点。 除了用技术提升业务以外,我还结合工作中的问题进行了很多的深度技术思考。 如有一次我们自己维护了一个线上的redis(当时工程部还没有redis平台,redis服务要业务自己维护)。为了优化性能,我把后端的请求由短连接改成了长连接。虽然看效果性能确实是优化了,但是我的思考并没有停止。我们所有的后端机都会连接这个redis。这样在这个redis实例上可能得有6000多条并发连接存在。我就开始疑惑,Linux 最多能有多少个TCP连接呢,我这 6000 条长连接会不会把这个服务器玩坏? 再比如,我们组的服务器遭遇过几次连接相关的线上问题。其中一次是因为端口紧张而导致 CPU 消耗飙升。后来我又深入研究了一下。 最近,由于 Docker 的广泛应用。底层的网络工作方式已经在悄悄地发生变化了。所以我又开辟了一个网络虚拟化的坑,来一点一点地填。 现在我们的「开发内功修炼」公众号和 Github 就是在作为一个我和大家分享我的技术思考的一个窗口。 04 重回腾讯 时隔 7 年,我又以一种奇特的方式变回了腾讯人的身份。 腾讯再一次收购了搜狗的股份,这一次不再是控股,而是全资。 在离开腾讯的这 7 年多的时间里,腾讯的内部技术工作方式已经发生了翻天覆地的变化。 所以在刚转回腾讯的这一段时间里,我花了大量的精力来熟悉腾讯基于 tRPC 的各种技术生态。除了工作日,也投入了不少周末的精力。 05 再叨叨几句 最后,水文里挤干货,通过我今天的文章我想给大家分享这么几点经验。 第一,是要学会抬头看路,选择一个好的赛道进去。我非常庆幸我当年从广电赛道切换到了互联网,获得了更大的舞台。不过其实我自己在这点上做的也不是特别好,2013年底入职搜狗前拒绝了字节大把期权的offer,要不然我我早就财务自由了。 第二,不要光被动接收领导的指令干活。要主动积极思考项目中哪些地方是待改进的,想到了你就去做。领导都非常喜欢积极主动的员工。我自己也是喜欢招一些能主动思考,积极推进的同学。这些人能创造意外的价值。 第三,工作中除了业务以外还要主动技术的深度思考。毕竟技术仍然是开发的立命之本。在晋升考核的时候,业务数据做的再好也代替不了技术实力的核心位置。把工作中的技术点总结一下,在公司内分享出来。不涉及机密的话在外网分享一下更好。对你自己,对你的团队,都是好事。 技术交流群 最近有很多人问,有没有读者交流群,想知道怎么加入。 最近我创建了一些群,大家可以加入。交流群都是免费的,只需要大家加入之后不要随便发广告,多多交流技术就好了。 目前创建了多个交流群,全国交流群、北上广杭深等各地区交流群、面试交流群、资源共享群等。 有兴趣入群的同学,可长按扫描下方二维码,一定要备注:全国 Or 城市 Or 面试 Or 资源,根据格式备注,可更快被通过且邀请进群。 ▲长按扫描 往期推荐 武大94年博士年薪201万入职华为!学霸日程表曝光,简直降维打击! 腾讯三面:40亿个QQ号码如何去重? 我被开除了。。只因为看了骂公司的帖子 如果你喜欢本文, 请长按二维码,关注 Hollis. 转发至朋友圈,是对我最大的支持。 点个 在看 喜欢是一种感觉 在看是一种支持 ↘↘↘ 本篇文章为转载内容。原文链接:https://blog.csdn.net/hollis_chuang/article/details/121738393。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-02-06 11:38:24
232
转载
站内搜索
用于搜索本网站内部文章,支持栏目切换。
知识学习
实践的时候请根据实际情况谨慎操作。
随机学习一条linux命令:
sed -i 's/old_text/new_text/g' file.txt
- 替换文件中所有旧文本为新文本。
推荐内容
推荐本栏目内的其它文章,看看还有哪些文章让你感兴趣。
2023-04-28
2023-08-09
2023-06-18
2023-04-14
2023-02-18
2023-04-17
2024-01-11
2023-10-03
2023-09-09
2023-06-13
2023-08-07
2023-03-11
历史内容
快速导航到对应月份的历史文章列表。
随便看看
拉到页底了吧,随便看看还有哪些文章你可能感兴趣。
时光飞逝
"流光容易把人抛,红了樱桃,绿了芭蕉。"