前端技术
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
[类型系统 ]的搜索结果
这里是文章列表。热门标签的颜色随机变换,标签颜色没有特殊含义。
点击某个标签可搜索标签相关的文章。
点击某个标签可搜索标签相关的文章。
Javascript
...pt中的一个常见错误类型,特别是在处理异步操作的时候。比如fetch请求、文件上传下载、定时器这些地方都可能遇到它。它就像是一个警报器,告诉你某件事中途被中断了。 举个简单的例子: javascript const controller = new AbortController(); const signal = controller.signal; setTimeout(() => { console.log('定时器触发了!'); }, 3000); controller.abort(); // 中断定时器 console.log(signal.reason); // 输出 "AbortError: The operation was aborted." 在这个例子中,我们创建了一个AbortController实例,并通过调用它的abort()方法来中断定时器。嘿,瞧瞧最后一行输出啊!这告诉我们出问题了,是个“AbortError”,简单说就是有某个操作被强行中断啦。 --- 二、AbortError的实际应用场景 说到AbortError的应用场景,我觉得最典型的就是网络请求了。你有没有过这样的经历?比如你在网页上点了个下载按钮,想看个大图或者视频啥的。刚点完没多久,就觉得“这速度也太磨叽了吧!再等下去我都快睡着了”,然后一狠心就直接取消了操作。哎呀,这就像是服务器那边正拼了命地给你打包数据呢,结果你这边的浏览器直接甩出一句:“兄弟,不用忙活了,我不等了!””这就是AbortError发挥作用的地方。 让我们来看一段代码: javascript async function fetchData() { const controller = new AbortController(); const signal = controller.signal; try { const response = await fetch('https://example.com/large-file', { signal }); console.log('数据已成功获取'); } catch (error) { if (error.name === 'AbortError') { console.log('请求被用户取消'); } else { console.error('发生了其他错误:', error); } } // 取消请求 controller.abort(); } fetchData(); 在这段代码里,我们使用AbortController来管理一个网络请求。如果用户决定取消请求,我们就调用controller.abort(),这时fetch函数会抛出一个AbortError。嘿嘿,简单来说呢,就是咱们逮住这个错误,看看它是不是个“AbortError”,如果是的话,就用一种超优雅的方式把它处理了,不搞什么大惊小怪的。 --- 三、AbortError与其他错误的区别 说到错误,难免要和其他错误比较一番。比如说嘛,就有人会好奇地问:“AbortError跟一般的错误到底有啥不一样呀?”说实话呢,这个问题我也琢磨了好久好久,头都快想大了! 首先,AbortError是一种特殊的错误类型,专门用于表示操作被人为中断的情况。其实很多小错误啊,就是程序员自己不小心搞出来的,像打字打错了变量名,或者一激动让数组越界了之类的,都是挺常见的乌龙事件。简单来说呢,这俩的区别就是——AbortError就像是个“计划内”的小插曲,咱们事先知道它可能会发生,也能提前做好准备去应对;但普通的错误嘛,就好比是突然从天而降的小麻烦,压根儿没得防备,让人措手不及! 举个例子: javascript function divide(a, b) { if (b === 0) { throw new Error('除数不能为零'); } return a / b; } try { console.log(divide(10, 0)); // 抛出普通错误 } catch (error) { console.error(error.message); // 输出 "除数不能为零" } 在这个例子中,divide函数因为传入了非法参数(即分母为0)而抛出了一个普通错误。而如果我们换成AbortError呢? javascript const controller = new AbortController(); function process() { setTimeout(() => { console.log('处理完成'); }, 5000); } process(); controller.abort(); // 中断处理 这里虽然也有中断操作的意思,但并没有抛出任何错误。这就像是说,AbortError不会自己偷偷跑出来捣乱,得咱们主动去点那个abort()按钮才行。就好比你得自己动手去按开关,灯才不会自己亮起来一样。 --- 四、深入探讨AbortError的优缺点 说到优点嘛,我觉得AbortError最大的好处就是它让我们的代码更加健壮和可控。比如说啊,在面对一堆同时涌来的请求时, AbortError 就像一个神奇的开关,能帮我们把那些没用的请求一键关掉,这样就不会白白浪费资源啦!对了,它还能帮咱们更贴心地照顾用户体验呢!比如说,当用户等得花儿都快谢了,就给个机会让他们干脆放弃这事儿,省得干着急。 但是呢,凡事都有两面性。AbortError也有它的局限性。首先,它只适用于那些支持AbortSignal接口的操作,比如fetch、XMLHttpRequest之类。如果你尝试在一个不支持AbortSignal的操作上使用它,那就会直接报错。另外啊,要是随便乱用 AbortError 可不好,比如说老是取消请求的话,系统可能就会被折腾得够呛,负担越来越重,你说是不是? 说到这里,我想起了之前开发的一个项目,当时为了优化性能,我给每个API请求都加了AbortController,结果发现有时候会导致页面加载速度反而变慢了。后来经过反复调试,我才意识到,频繁地取消请求其实是得不偿失的。所以啊,大家在使用AbortError的时候一定要权衡利弊,不能盲目追求“安全”。 --- 五、总结与展望 总的来说,AbortError是一个非常实用且有趣的错误类型。它不仅能让我们更轻松地搞定那些乱七八糟的异步任务,还能让代码变得更好懂、更靠谱!不过,就像任何工具一样,它也需要我们在实践中不断摸索和完善。 未来,随着前端开发越来越复杂,我相信AbortError会有更多的应用场景。不管是应对一大堆同时进行的任务,还是让咱们跟软件互动的时候更顺畅、更开心,它都绝对是我们离不开的得力助手!所以,各位小伙伴,不妨多尝试用它来解决实际问题,说不定哪天你会发现一个全新的解决方案呢! 好了,今天的分享就到这里啦。希望能给大家打开一点思路,也期待大家在评论区畅所欲言,分享你的想法!最后,祝大家coding愉快,早日成为编程界的高手!
2025-03-27 16:22:54
107
月影清风
ElasticSearch
...控制,是一种用于管理系统权限的方法。在Elasticsearch中,RBAC允许管理员根据特定的角色定义用户的权限,例如允许某些用户只能读取特定类型的文档或执行特定的操作。这种方法有助于简化权限管理并提高系统的安全性。
2025-05-12 15:42:52
100
星辰大海
Go-Spring
...提供了error类型,用于表示可能发生的错误。Hey, 你知道GoSpring怎么玩儿的嘛?它把错误处理这个事儿做得超有创意的!它不仅让咱们能更灵活地处理各种小状况,还特别注意保护咱们的安全感。怎么做到的呢?就是通过接口和那些具体的错误类型,就像是给错误贴上了标签,这样咱们就能更精准地识别和应对问题了。这下,无论是小故障还是大难题,都能被咱们轻松搞定,是不是感觉整个程序都活灵活现起来了呢? 示例代码: go package main import ( "fmt" "net/http" "os" ) func main() { http.HandleFunc("/", func(w http.ResponseWriter, r http.Request) { if err := processRequest(r); err != nil { writeError(err) } }) err := http.ListenAndServe(":8080", nil) if err != nil { fmt.Println("Server start error:", err) os.Exit(1) } } func processRequest(req http.Request) error { // 示例错误处理 return errors.New("Request processing failed") } func writeError(err error) { // 日志记录错误 log.Error(err) } 在这个例子中,我们定义了一个简单的HTTP服务器,其中包含了错误处理逻辑。如果在处理请求时遇到错误,processRequest函数会返回一个error对象。哎呀,兄弟!这事儿得这么干:首先,咱们得动用 writeError 这个功能,把出错的提示给记到日记本里头去。要是服务器启动的时候遇到啥问题,那咱们就别藏着掖着,直接把错误的信息给大伙儿瞧一瞧,这样大家也好知道哪儿出了岔子,好及时修修补补。 2. 日志记录的最佳实践 日志记录是监控系统健康状况、追踪错误来源以及优化应用性能的关键手段。哎呀,你懂的,GoSpring这个家伙可厉害了!它能跟好多不同的日志工具玩得转,比如那个基础的log,还有那个火辣辣的zap。想象一下,就像是你有好多不同口味的冰淇淋可以选择,无论是奶油味、巧克力味还是草莓味,GoSpring都能给你完美的体验。而且,它还能让你自己来调调口味,比如你想让日志多一些颜色、或者想让它在特定的时候特别响亮,GoSpring都能满足你,真的超贴心的! 示例代码: go package main import ( "log" "os" "go.uber.org/zap" ) func main() { // 初始化日志器 sugarLogger := zap.NewExample().Sugar() defer sugarLogger.Sync() http.HandleFunc("/", func(w http.ResponseWriter, r http.Request) { sugarLogger.Info("Processing request", zap.String("method", r.Method), zap.String("path", r.URL.Path)) }) err := http.ListenAndServe(":8080", nil) if err != nil { sugarLogger.Fatal("Server start error", zap.Error(err)) } } 在这个例子中,我们使用了go.uber.org/zap库来初始化日志器。咱们用个俏皮点的糖糖(Sugar())功能做了一个小版的日志记录工具,这样就能更轻松地往里面塞进各种日志信息了。就像是给日记本添上了便利贴,想记录啥就直接贴上去,简单又快捷!当服务器启动失败时,日志器会自动记录错误信息并结束程序执行。 3. 结合错误处理与日志记录的最佳实践 在实际应用中,错误处理和日志记录通常是紧密相连的。正确的错误处理策略应该包括: - 异常捕获:确保捕获所有潜在的错误,并适当处理或记录它们。 - 上下文信息:在日志中包含足够的上下文信息,帮助快速定位问题根源。 - 日志级别:根据错误的严重程度选择合适的日志级别(如INFO、ERROR)。 - 错误重试:对于可以重试的操作,实现重试机制,并在日志中记录重试尝试。 示例代码: go package main import ( "context" "math/rand" "time" "go.uber.org/zap" ) func main() { rand.Seed(time.Now().UnixNano()) ctx, cancel := context.WithTimeout(context.Background(), 5time.Second) defer cancel() for i := 0; i < 10; i++ { err := makeNetworkCall(ctx) if err != nil { zap.Sugar().Errorf("Network call %d failed: %s", i, err) } else { zap.Sugar().Infof("Network call %d succeeded", i) } time.Sleep(1 time.Second) } } func makeNetworkCall(ctx context.Context) error { time.Sleep(time.Duration(rand.Intn(10)) time.Millisecond) return fmt.Errorf("network call failed after %d ms", rand.Intn(10)) } 在这个例子中,我们展示了如何在一个循环中处理网络调用,同时利用context来控制调用的超时时间。在每次调用失败时,我们记录详细的错误信息和调用次数。这种做法有助于在出现问题时快速响应和诊断。 结论 通过上述实践,我们可以看到GoSpring如何通过结构化错误处理和日志记录来提升应用的健壮性和维护性。哎呀,兄弟!如果咱们能好好执行这些招数,那可真是大有裨益啊!不仅能大大缩短遇到问题时,咱们得花多少时间去修复,还能省下一大笔银子呢!更棒的是,还能让咱们团队里的小伙伴们,心往一处想,劲往一处使,互相理解,配合得天衣无缝。这感觉,就像是大家在一块儿打游戏,每个人都有自己的角色,但又都为了一个共同的目标而努力,多带劲啊!哎呀,你知道吗?当咱们的应用越做越大,用GoSpring的那些工具和好方法,简直就是如虎添翼啊!这样咱就能打造出一个既稳如泰山又快如闪电,还特别容易打理的系统。想象一下,就像给你的小花园施肥浇水,让每一朵花都长得茁壮又美丽,是不是感觉棒极了?所以啊,别小看了这些工具和最佳实践,它们可是你建大事业的得力助手!
2024-07-31 16:06:44
278
月下独酌
.net
... 4.1 注册类型时搞错了 有时候我们会不小心把类型注册错了。比如: csharp services.AddTransient(); // 想注册MockService,却写成了Service 结果就是,无论你在代码中怎么尝试,拿到的永远是Service而不是MockService。其实这个坑挺容易被忽略的,毕竟编译器又不报错,一切都看起来风平浪静,直到程序跑起来的时候,问题才突然冒出来,啪叽一下给你整一个大 surprise! 我的建议是,尽量使用常量或者枚举来定义服务名称,这样可以减少拼写错误的风险: csharp public static class ServiceNames { public const string MockService = "MockService"; public const string RealService = "RealService"; } services.AddTransient(ServiceNames.MockService, typeof(MockService)); 4.2 生命周期设置不当 另一个常见的问题是生命周期设置错误。比如说,你要是想弄个单例服务,结果不小心把它设成了 Transient,那每次调用的时候都会新生成一个实例。这就好比你本来想让一个人负责一件事,结果每次都换个人来干,不仅效率低得让人崩溃,搞不好还会出大乱子呢! csharp // 错误示范 services.AddTransient(); // 正确示范 services.AddSingleton(); 记住,单例模式适用于那些无状态或者状态不重要的场景。嘿,想象一下,你正在用一个数据库连接池这种“有状态”的服务,要是把它搞成单例模式,那可就热闹了——多个线程或者任务同时去抢着用它,结果就是互相踩脚、搞砸事情,什么竞争条件啦、数据混乱啦,各种麻烦接踵而至。就好比大家伙儿都盯着同一个饼干罐子,都想伸手拿饼干,但谁也没个规矩,结果不是抢得太猛把罐子摔了,就是谁都拿不痛快。所以啊,这种情况下,还是别让单例当这个“独裁者”了,分清楚责任才靠谱! 4.3 忘记注册依赖 有时候,我们可能会忘记注册某些依赖项。比如: csharp public class SomeClass { private readonly IAnotherService _anotherService; public SomeClass(IAnotherService anotherService) { _anotherService = anotherService; } } 如果IAnotherService没有被注册到DI容器中,那么在运行时就会抛出异常。为了避免这种情况,你可以使用AddScoped或AddTransient来确保所有依赖都被正确注册。 --- 5. 探讨与总结 通过今天的讨论,我们可以看到,虽然依赖注入能够极大地提高代码的质量和可维护性,但它并不是万能的。设置搞错了,那可就麻烦大了,小到一个单词拼错了,大到程序跑偏、东西乱套,什么幺蛾子都可能出现。 我的建议是,在使用DI框架时要多花时间去理解和实践。不要害怕犯错,因为正是这些错误教会了我们如何更好地编写代码。同时,也要学会利用工具和日志来帮助自己排查问题。 最后,我想说的是,编程不仅仅是解决问题的过程,更是一个不断学习和成长的过程。希望大家能够在实践中找到乐趣,享受每一次成功的喜悦! 好了,今天的分享就到这里啦,如果你有任何疑问或者想法,欢迎随时留言交流哦!😄
2025-05-07 15:53:50
44
夜色朦胧
Hive
...么多小文件堆在一起,系统就会变得特别卡,整体性能直线下降,简直像路上突然挤满了慢吞吞的小汽车,堵得不行!要解决这个问题嘛,咱们可以先把文件用GZIP压缩一下,弄个小“压缩包”,然后再把它丢进Hive里头去。 下面是一段示例代码,展示了如何创建一个支持GZIP格式的外部表: sql -- 创建数据库 CREATE DATABASE IF NOT EXISTS log_db; -- 切换到数据库 USE log_db; -- 创建外部表并指定GZIP格式 CREATE EXTERNAL TABLE IF NOT EXISTS logs ( id STRING, timestamp STRING, message STRING ) ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' STORED AS TEXTFILE -- 注意这里使用TEXTFILE而不是默认的SEQUENCEFILE LOCATION '/path/to/gzipped/files'; 看到这里,你可能会问:“为什么这里要用TEXTFILE而不是SEQUENCEFILE?”这是因为Hive默认不支持直接读取GZIP格式的数据,所以我们需要手动调整存储格式。此外,还需要确保你的Hadoop集群已经启用了GZIP解压功能。 3.2 BZIP2的高阶玩法 接下来轮到BZIP2登场了。相比于GZIP,BZIP2的压缩比更高,但它也有一个明显的缺点:解压速度较慢。因此,BZIP2更适合用于那些访问频率较低的大规模静态数据集。 下面这段代码展示了如何创建一个支持BZIP2格式的分区表: sql -- 创建数据库 CREATE DATABASE IF NOT EXISTS archive_db; -- 切换到数据库 USE archive_db; -- 创建分区表并指定BZIP2格式 CREATE TABLE IF NOT EXISTS archives ( file_name STRING, content STRING ) PARTITIONED BY (year INT, month INT) STORED AS RCFILE -- RCFILE支持BZIP2压缩 TBLPROPERTIES ("orc.compress"="BZIP2"); 需要注意的是,在这种情况下,你需要确保Hive的配置文件中启用了BZIP2支持,并且相关的JAR包已经正确安装。 --- 四、实战经验分享 踩过的坑与学到的东西 在这个过程中,我遇到了不少挫折。比如说吧,有次我正打算把一个GZIP文件塞进Hive里,结果系统直接给我整了个报错,说啥解码器找不着。折腾了半天才发现,哎呀,原来是服务器上那个GZIP工具的老版本太不给劲了,跟最新的Hadoop配不上,闹起了脾气!于是,我赶紧联系运维团队升级了相关依赖,这才顺利解决问题。 还有一个教训是关于文件命名规范的。一开始啊,我老是忘了在压缩完的文件后面加“.gz”或者“.bz2”这种后缀名,搞得 Hive 一脸懵逼,根本分不清文件是啥类型的,直接就报错不认账了。后来我才明白,那些后缀名可不只是个摆设啊,它们其实是给文件贴标签的,告诉你这个文件是啥玩意儿,是图片、音乐,还是什么乱七八糟的东西。 --- 五、总结与展望 总的来说,虽然Hive对GZIP和BZIP2的支持有限,但这并不意味着我们不能利用它们的优势。相反,只要掌握了正确的技巧,我们完全可以在这两者之间找到平衡点,满足不同的业务需求。 最后,我想说的是,作为一名数据工程师,我们不应该被工具的限制束缚住手脚。相反,我们应该敢于尝试新事物,勇于突破常规。毕竟,正是这种探索精神,推动着整个行业不断向前发展! 好了,今天的分享就到这里啦。如果你也有类似的经历或者想法,欢迎随时跟我交流哦~再见啦!
2025-04-19 16:20:43
45
翡翠梦境
转载文章
...t;!-- 配置默认系统拦截器栈 --><interceptor-ref name="defaultStack"/><!-- param子元素配置了DocUploadAction类中savePath属性值为/upload --><param name="savePath">/upload</param><result>/showFile.jsp</result><!-- 指定input逻辑视图,即不符合上传要求,被fileUpload拦截器拦截后,返回的视图页面 --><result name="input">/uploadFile.jsp</result></action> <action name="docDownload" class="nuc.sw.action.DocDownloadAction"><!-- 指定结果类型为stream --><result type="stream"><!-- 指定下载文件的文件类型 text/plain表示纯文本 --><param name="contentType">application/msword,text/plain</param><!-- 指定下载文件的入口输入流 --><param name="inputName">inputStream</param><!-- 指定下载文件的处理方式与文件保存名 attachment表示以附件形式下载,也可以用inline表示内联即在浏览器中直接显示,默认值为inline --><param name="contentDisposition">attachment;filename="${downloadFileName}"</param><!-- 指定下载文件的缓冲区大小,默认为1024 --><param name="bufferSize">40960</param></result></action><action name="loginAction" class="nuc.sw.action.LoginAction" method="loginMethod"><result name="loginOK">/uploadFile.jsp</result><result name="loginFail">/login.jsp</result><result name="input">/login.jsp</result></action> </package></struts> /20171105_shiyan_upanddown/WebContent/login.jsp <%@ page language="java" contentType="text/html; charset=UTF-8"pageEncoding="UTF-8"%><%@ taglib prefix="s" uri="/struts-tags" %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>登录页</title><s:head/></head><body><s:actionerror/><s:fielderror fieldName="err"></s:fielderror><s:form action="loginAction" method="post"> <s:textfield label="用户名" name="username"></s:textfield><s:password label="密码" name="password"></s:password><s:submit value="登陆"></s:submit></s:form></body></html> /20171105_shiyan_upanddown/WebContent/showFile.jsp <%@ page language="java" contentType="text/html; charset=UTF-8"pageEncoding="UTF-8"%><%@ taglib prefix="s" uri="/struts-tags" %><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>显示上传文档</title></head><body><center><font style="font-size:18px;color:red">上传者:<s:property value="name"/></font><table width="45%" cellpadding="0" cellspacing="0" border="1"><tr><th>文件名称</th><th>上传者</th><th>上传时间</th></tr><s:iterator value="uploadFileName" status="st" var="doc"><tr><td align="center"><a href="docDownload.action?downPath=upload/<s:property value="doc"/>"><s:property value="doc"/> </a></td><td align="center"><s:property value="name"/></td><td align="center"><s:date name="createTime" format="yyyy-MM-dd HH:mm:ss"/></td></tr></s:iterator></table></center></body></html> /20171105_shiyan_upanddown/WebContent/uploadFile.jsp <%@ page language="java" contentType="text/html; charset=UTF-8"pageEncoding="UTF-8"%><%@ taglib prefix="s" uri="/struts-tags" %><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>多文件上传</title></head><body><center><s:form action="docUpload" method="post" enctype="multipart/form-data"><s:textfield name="name" label="姓名" size="20"/><s:file name="upload" label="选择文档" size="20"/><s:file name="upload" label="选择文档" size="20"/><s:file name="upload" label="选择文档" size="20"/><s:submit value="确认上传" align="center"/></s:form></center></body></html> 本篇文章为转载内容。原文链接:https://blog.csdn.net/qq_34101492/article/details/78811741。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-11-12 20:53:42
141
转载
转载文章
...启他,但是会出现其他类型的运行错误。 本实验,我们创建一个Pod尝试分配超过其限制的内存,下面的这个Pod的配置文档,它申请50M的内存, 内存限制设置为100M。 memory-request-limit-2.yaml apiVersion: v1kind: Podmetadata:name: memory-demo-2spec:containers:- name: memory-demo-2-ctrimage: vish/stressresources:requests:memory: 50Milimits:memory: "100Mi"args:- -mem-total- 250Mi- -mem-alloc-size- 10Mi- -mem-alloc-sleep- 1s 在配置文件里的args段里,可以看到容器尝试分配250M的内存,超过了限制的100M。 创建Pod: kubectl create -f https://k8s.io/docs/tasks/configure-pod-container/memory-request-limit-2.yaml --namespace=mem-example 查看Pod的详细信息: kubectl get pod memory-demo-2 --namespace=mem-example 这时候,容器可能会运行,也可能会被杀掉。如果容器还没被杀掉,重复之前的命令直至 你看到这个容器被杀掉: NAME READY STATUS RESTARTS AGEmemory-demo-2 0/1 OOMKilled 1 24s 查看容器更详细的信息: kubectl get pod memory-demo-2 --output=yaml --namespace=mem-example 这个输出显示了容器被杀掉因为超出了内存限制。 lastState:terminated:containerID: docker://65183c1877aaec2e8427bc95609cc52677a454b56fcb24340dbd22917c23b10fexitCode: 137finishedAt: 2017-06-20T20:52:19Zreason: OOMKilledstartedAt: null 本实验里的容器可以自动重启,因此kubelet会再去启动它。输入多几次这个命令看看它是怎么 被杀掉又被启动的: kubectl get pod memory-demo-2 --namespace=mem-example 这个输出显示了容器被杀掉,被启动,又被杀掉,又被启动的过程: stevepe@sperry-1:~/steveperry-53.github.io$ kubectl get pod memory-demo-2 --namespace=mem-exampleNAME READY STATUS RESTARTS AGEmemory-demo-2 0/1 OOMKilled 1 37sstevepe@sperry-1:~/steveperry-53.github.io$ kubectl get pod memory-demo-2 --namespace=mem-exampleNAME READY STATUS RESTARTS AGEmemory-demo-2 1/1 Running 2 40s 查看Pod的历史详细信息: kubectl describe pod memory-demo-2 --namespace=mem-example 这个输出显示了Pod一直重复着被杀掉又被启动的过程: ... Normal Created Created container with id 66a3a20aa7980e61be4922780bf9d24d1a1d8b7395c09861225b0eba1b1f8511... Warning BackOff Back-off restarting failed container 查看集群里节点的详细信息: kubectl describe nodes 输出里面记录了容器被杀掉是因为一个超出内存的状况出现: Warning OOMKilling Memory cgroup out of memory: Kill process 4481 (stress) score 1994 or sacrifice child 删除Pod: kubectl delete pod memory-demo-2 --namespace=mem-example 配置超出节点能力范围的内存申请 内存的申请和限制是针对容器本身的,但是认为Pod也有容器的申请和限制是一个很有帮助的想法。 Pod申请的内存就是Pod里容器申请的内存总和,类似的,Pod的内存限制就是Pod里所有容器的 内存限制的总和。 Pod的调度策略是基于请求的,只有当节点满足Pod的内存申请时,才会将Pod调度到合适的节点上。 在这个实验里,我们创建一个申请超大内存的Pod,超过了集群里任何一个节点的可用内存资源。 这个容器申请了1000G的内存,这个应该会超过你集群里能提供的数量。 memory-request-limit-3.yaml apiVersion: v1kind: Podmetadata:name: memory-demo-3spec:containers:- name: memory-demo-3-ctrimage: vish/stressresources:limits:memory: "1000Gi"requests:memory: "1000Gi"args:- -mem-total- 150Mi- -mem-alloc-size- 10Mi- -mem-alloc-sleep- 1s 创建Pod: kubectl create -f https://k8s.io/docs/tasks/configure-pod-container/memory-request-limit-3.yaml --namespace=mem-example 查看Pod的状态: kubectl get pod memory-demo-3 --namespace=mem-example 输出显示Pod的状态是Pending,因为Pod不会被调度到任何节点,所有它会一直保持在Pending状态下。 kubectl get pod memory-demo-3 --namespace=mem-exampleNAME READY STATUS RESTARTS AGEmemory-demo-3 0/1 Pending 0 25s 查看Pod的详细信息包括事件记录 kubectl describe pod memory-demo-3 --namespace=mem-example 这个输出显示容器不会被调度因为节点上没有足够的内存: Events:... Reason Message------ -------... FailedScheduling No nodes are available that match all of the following predicates:: Insufficient memory (3). 内存单位 内存资源是以字节为单位的,可以表示为纯整数或者固定的十进制数字,后缀可以是E, P, T, G, M, K, Ei, Pi, Ti, Gi, Mi, Ki.比如,下面几种写法表示相同的数值:alue: 128974848, 129e6, 129M , 123Mi 删除Pod: kubectl delete pod memory-demo-3 --namespace=mem-example 如果不配置内存限制 如果不给容器配置内存限制,那下面的任意一种情况可能会出现: 容器使用内存资源没有上限,容器可以使用当前节点上所有可用的内存资源。 容器所运行的命名空间有默认内存限制,容器会自动继承默认的限制。集群管理员可以使用这个文档 LimitRange来配置默认的内存限制。 内存申请和限制的原因 通过配置容器的内存申请和限制,你可以更加有效充分的使用集群里内存资源。配置较少的内存申请, 可以让Pod跟任意被调度。设置超过内存申请的限制,可以达到以下效果: Pod可以在负载高峰时更加充分利用内存。 可以将Pod的内存使用限制在比较合理的范围。 清理 删除命名空间,这会顺便删除命名空间里的Pod。 kubectl delete namespace mem-example 译者:NickSu86 原文链接 本篇文章为转载内容。原文链接:https://blog.csdn.net/Aria_Miazzy/article/details/99694937。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-12-23 12:14:07
498
转载
转载文章
...符号引用,有助于增强类型安全性和提升性能。 同时,随着JIT即时编译器的发展,如GraalVM项目,其先进的动态编译技术能更高效地将字节码转换为机器码,使得Java应用程序执行效率大幅提升。对于Class文件内部结构的理解,有助于我们更好地利用这些新特性和工具进行优化配置。 此外,随着微服务、容器化和云原生架构的普及,Class文件在服务启动速度和资源占用上的优化也显得尤为重要。例如,通过提前解析和验证Class文件以减少运行时开销,或者采用Ahead-of-Time(AOT)编译技术将部分Class文件直接编译成本地代码,从而提升系统启动速度和降低内存使用。 另外,对于安全领域,深入理解Class文件结构有助于分析恶意字节码攻击手段,以及如何通过虚拟机层面的安全防护措施来避免有害类文件的加载执行。例如,最新的Java版本不断强化类加载验证机制,防止非法或恶意篡改的Class文件危害系统安全。 综上所述,随着Java技术栈的持续演进,Class文件这一基础而又关键的概念,在实际开发和运维过程中仍具有极高的研究价值和实战意义,值得开发者们密切关注和深入探索。
2024-01-09 17:46:36
646
转载
转载文章
...数组的元素是复杂数据类型时,我们还需要在其析构函数中正确释放内存。 真正的智能指针:shared_ptr auto_ptr和unique_ptr都有或多或少的缺陷,因此C++11还推出了shared_ptr,这也是目前工程内使用最多最广泛的智能指针,他使用引用计数(感觉有参考Objective-C的嫌疑),实现对同一块内存可以有多个引用,在最后一个引用被释放时,指向的内存才释放,这也是和unique_ptr最大的区别。 另外,使用shared_ptr过程中有几点需要注意: 构造shared_ptr的方法,如下示例代码所示,我们尽量使用shared_ptr构造函数或者make_shared的方式创建shared_ptr,禁止使用裸指针赋值的方式,这样会shared_ptr难于管理指针的生命周期。 // 使用裸指针赋值构造,不推荐,裸指针被释放后,shared_ptr就野了,不能完全控制裸指针的生命周期,失去了智能指针价值int p = new int(10);shared_ptr<int>sp = p;delete p; // sp将成为野指针,使用sp将crash// 将裸指针作为匿名指针传入构造函数,一般做法,让shared_ptr接管裸指针的生命周期,更安全shared_ptr<int>sp1(new int(10));// 使用make_shared,推荐做法,更符合工厂模式,可以连代码中的所有new,更高效;方法的参数是用来初始化模板类shared_ptr<int>sp2 = make_shared<int>(10); 禁止使用指向shared_ptr的裸指针,也就是智能指针的指针,这听起来就很奇怪,但开发中我们还需要注意,使用shared_ptr的指针指向一个shared_ptr时,引用计数并不会加一,操作shared_ptr的指针很容易就发生野指针异常。 shared_ptr<int>sp = make_shared<int>(10);cout << sp.use_count() << endl; //输出1shared_ptr<int> sp1 = &sp;cout << (sp1).use_count() << endl; //输出依然是1(sp1).reset(); //sp成为野指针cout << sp << endl; //crash 使用shared_ptr创建动态数组,在介绍unique_ptr时我们就讲过创建动态数组,而shared_ptr同样可以做到,不过稍微复杂一点,如下代码所示,除了要显示指定析构方法外(因为默认是T的析构函数,不是T[]),另外对外的数据类型依然是shared_ptr<T>,非常有迷惑性,看不出来是数组,最后不能直接使用下标读写数组,要先get()获取裸指针才可以使用下标。所以,不推荐使用shared_ptr来创建动态数组,尽量使用unique_ptr,这可是unique_ptr为数不多的优势了。 template <typename T>shared_ptr<T> make_shared_array(size_t size) {return shared_ptr<T>(new T[size], default_delete<T[]>());}shared_ptr<int>sp = make_shared_array(10); //看上去是shared<int>类型,实际上是数组sp.get()[0] = 100; //不能直接使用下标读写数组元素,需要通过get()方法获取裸指针后再操作 用shared_ptr实现多态,在我们使用裸指针时,实现多态就免不了定义虚函数,那么用shared_ptr时也不例外,不过有一处是可以省下的,就是析构函数我们不需要定义为虚函数了,如下面代码所示: class A {public:~A() {cout << "dealloc A" << endl;} };class B : public A {public:~B() {cout << "dealloc B" << endl;} };int main(int argc, const char argv[]) {A a = new B();delete a; //只打印dealloc Ashared_ptr<A>spa = make_shared<B>(); //析构spa是会先打印dealloc B,再打印dealloc Areturn 0;} 循环引用,笔者最先接触引用计数的语言就是Objective-C,而OC中最常出现的内存问题就是循环引用,如下面代码所示,A中引用B,B中引用A,spa和spb的强引用计数永远大于等于1,所以直到程序退出前都不会被退出,这种情况有时候在正常的业务逻辑中是不可避免的,而解决循环引用的方法最有效就是改用weak_ptr,具体可见下一章。 class A {public:shared_ptr<B> b;};class B {public:shared_ptr<A> a;};int main(int argc, const char argv[]) {shared_ptr<A> spa = make_shared<A>();shared_ptr<B> spb = make_shared<B>();spa->b = spb;spb->a = spa;return 0;} //main函数退出后,spa和spb强引用计数依然为1,无法释放 刚柔并济:weak_ptr 正如上一章提到,使用shared_ptr过程中有可能会出现循环引用,关键原因是使用shared_ptr引用一个指针时会导致强引用计数+1,从此该指针的生命周期就会取决于该shared_ptr的生命周期,然而,有些情况我们一个类A里面只是想引用一下另外一个类B的对象,类B对象的创建不在类A,因此类A也无需管理类B对象的释放,这个时候weak_ptr就应运而生了,使用shared_ptr赋值给一个weak_ptr不会增加强引用计数(strong_count),取而代之的是增加一个弱引用计数(weak_count),而弱引用计数不会影响到指针的生命周期,这就解开了循环引用,上一章最后的代码使用weak_ptr可改造为如下代码。 class A {public:shared_ptr<B> b;};class B {public:weak_ptr<A> a;};int main(int argc, const char argv[]) {shared_ptr<A> spa = make_shared<A>();shared_ptr<B> spb = make_shared<B>();spa->b = spb; //spb强引用计数为2,弱引用计数为1spb->a = spa; //spa强引用计数为1,弱引用计数为2return 0;} //main函数退出后,spa先释放,spb再释放,循环解开了使用weak_ptr也有需要注意的点,因为既然weak_ptr不负责裸指针的生命周期,那么weak_ptr也无法直接操作裸指针,我们需要先转化为shared_ptr,这就和OC的Strong-Weak Dance有点像了,具体操作如下:shared_ptr<int> spa = make_shared<int>(10);weak_ptr<int> spb = spa; //weak_ptr无法直接使用裸指针创建if (!spb.expired()) { //weak_ptr最好判断是否过期,使用expired或use_count方法,前者更快spb.lock() += 10; //调用weak_ptr转化为shared_ptr后再操作裸指针}cout << spa << endl; //20 智能指针原理 看到这里,智能指针的用法基本介绍完了,后面笔者来粗浅地分析一下为什么智能指针可以有效帮我们管理裸指针的生命周期。 使用栈对象管理堆对象 在C++中,内存会分为三部分,堆、栈和静态存储区,静态存储区会存放全局变量和静态变量,在程序加载时就初始化,而堆是由程序员自行分配,自行释放的,例如我们使用裸指针分配的内存;而最后栈是系统帮我们分配的,所以也会帮我们自动回收。因此,智能指针就是利用这一性质,通过一个栈上的对象(shared_ptr或unique_ptr)来管理一个堆上的对象(裸指针),在shared_ptr或unique_ptr的析构函数中判断当前裸指针的引用计数情况来决定是否释放裸指针。 shared_ptr引用计数的原理 一开始笔者以为引用计数是放在shared_ptr这个模板类中,但是细想了一下,如果这样将shared_ptr赋值给另一个shared_ptr时,是怎么做到两个shared_ptr的引用计数同时加1呢,让等号两边的shared_ptr中的引用计数同时加1?不对,如果还有第二个shared_ptr再赋值给第三个shared_ptr那怎么办呢?或许通过下面的类图便清楚个中奥秘。 [ boost中shared_ptr与weak_ptr类图 ] 我们重点关注shared_ptr<T>的类图,它就是我们可以直接操作的类,这里面包含裸指针T,还有一个shared_count的对象,而shared_count对象还不是最终的引用计数,它只是包含了一个指向sp_counted_base的指针,这应该就是真正存放引用计数的地方,包括强应用计数和弱引用计数,而且shared_count中包含的是sp_counted_base的指针,不是对象,这也就意味着假如shared_ptr<T> a = b,那么a和b底层pi_指针指向的是同一个sp_counted_base对象,这就很容易做到多个shared_ptr的引用计数永远保持一致了。 多线程安全 本章所说的线程安全有两种情况: 多个线程操作多个不同的shared_ptr对象 C++11中声明了shared_ptr的计数操作具有原子性,不管是赋值导致计数增加还是释放导致计数减少,都是原子性的,这个可以参考sp_counted_base的源码,因此,基于这个特性,假如有多个shared_ptr共同管理一个裸指针,那么多个线程分别通过不同的shared_ptr进行操作是线程安全的。 多个线程操作同一个shared_ptr对象 同样的道理,既然C++11只负责sp_counted_base的原子性,那么shared_ptr本身就没有保证线程安全了,加入两个线程同时访问同一个shared_ptr对象,一个进行释放(reset),另一个读取裸指针的值,那么最后的结果就不确定了,很有可能发生野指针访问crash。 作者:腾讯技术工程 https://mp.weixin.qq.com/s?__biz=MjM5ODYwMjI2MA==&mid=2649743462&idx=1&sn=c9d94ddc25449c6a0052dc48392a33c2&utm_source=tuicool&utm_medium=referralmp.weixin.qq.com 本篇文章为转载内容。原文链接:https://blog.csdn.net/weixin_31467557/article/details/113049179。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-02-24 18:25:46
142
转载
Mahout
...面临着数据量大、数据类型多样、数据更新速度快等挑战。然而,正是这些挑战也为AI技术的应用提供了广阔的舞台。通过深度学习、自然语言处理、强化学习等AI技术,实时光流分析系统能够实现对复杂市场动态的深度理解与预测,比如预测股票价格波动、识别欺诈交易、优化投资组合等。这些应用不仅提高了金融决策的效率和准确性,也促进了金融市场的透明度和公平性。 AI在金融风控中的应用 在金融风控领域,实时光流分析与AI的结合尤为显著。通过对社交媒体情绪分析、网络公开信息挖掘、用户行为轨迹追踪等多维度数据的实时分析,金融机构能够更早地发现潜在的信用风险和欺诈行为。例如,通过分析用户的网络活动模式,AI系统可以识别出异常行为,进而采取预警措施,有效防范金融犯罪。此外,AI还可以通过预测模型帮助银行和信贷机构评估贷款申请人的信用风险,实现自动化审批流程,提高服务效率。 未来趋势与展望 随着5G、物联网、边缘计算等新技术的普及,实时光流分析与AI在金融领域的应用将更加广泛。未来,金融机构将能够实时处理海量的物联网设备产生的数据,实现智能资产管理、个性化金融服务等创新应用。同时,随着法律法规的完善和技术标准的统一,实时光流分析与AI在金融行业的应用将更加规范和成熟,为金融市场的健康发展提供坚实的技术支撑。 实时光流分析与AI在金融领域的深度融合,正引领着金融科技创新的新潮流,不仅推动了金融行业的数字化转型,也为全球经济的可持续发展注入了新的活力。随着技术的不断进步与应用场景的不断拓展,这一领域的发展前景无疑充满了无限可能。
2024-09-06 16:26:39
60
月影清风
转载文章
...获文件 , 此文件的类型必须与 Libpcap 类型一致 , 如果设置了文件, 与此同时就应该设置 device 为 NULL,默认值为 NULL/ int sk_buff_size; / 表示的是数据接口 sk_buff 的大小 .sk_buff 是Linux 内核中一个重要的数据结构, 是用来进行数据包排队操作的 , 默认值为 168/ int dev_addon; / 表示在数据结构 sk_buff 中用于网络接口上信息的字节数. 如果是 -1( 默认值 ),那么 Libnids 会根据不同的网络接口进行修正 / void (syslog) (); / 是一个函数指针 , 默认值为nids_syslog() 函数 . 在 syslog函数中可以检测入侵攻击 , 如网络扫描攻击 , 也可以检测一些异常情况, 如无效 TCP 标记 / int syslog_level; / 表示日志等级 , 默认值是LOG_ALERT/ int scan_num_hosts; / 表示一个哈西表的大小 ,( 此哈西表用来存储端口扫描信息) 表示 Libnids 将要检测的同时扫描的端口数据 . 如果其值为 0,Libnids将不提供端口扫描功能 . 默认值 256/ int scan_delay; / 表示在扫描检测中 , 俩端口扫描的间隔时间, 以毫秒来计算 , 缺省值为 3000/ int scan_num_ports; / 表示相同源地址必须扫描的 TCP 端口数目 , 默认值为10/ void (no_mem) (char ); / 是一个函数指针 , 当Libnids 发生内存溢出时被调用/ int (ip_filter) (); / 是一个函数指针 , 此函数可以用来分析IP 数据包 , 当有 IP 数据包到达时 , 此函数就被调用. 如果此函数返回非零值 , 此数据包就被处理 ;如果返回零 , 此 IP 数据包就被丢弃. 默认值为 nids_ip_filter 函数 , 总是返回 1./ char pcap_filter; / 表示过滤规则 , 即Libpcap 的过滤规则 , 默认值为 NULL,表示捕获所有数据包 . 可以在此设置过滤规则 , 只捕获感兴趣的开发包/ int promisc; / 表示网卡模式 , 如果是非零, 就把此网卡设置为混杂模式 ; 否则 , 设为非混杂模式 . 默认值为1/ int one_loop_less; / 初始值为 0/ int pcap_timeout; / 表示捕获数据返回的时间 , 以毫秒计算. 实际上它表示的就是 Libpcap 函数中的 pcap_open_live函数的 timeout 参数 , 默认值 1024/ }; / 返回值 : 调用成功返回 1,失败返回 0 参 数 : 无 功 能 : 对 Libnids 初始化, 这是所有设计基于 Libnids 的程序最开始调用的函数 . 它的主要内容包括打开网络接口 , 打开文件 , 编译过滤规则 , 判断网络链路层类型, 进行必要的初始化工作 / int nids_init (void); / 返回值 : 无 参 数 : 回调函数名字 功 能 : 注册一个能够检测所有 IP 数据包的回调函数, 包括 IP 碎片 .e.g nids_register_ip_frag(ip_frag_function); void ip_frag_function(struct ip a_packet,int len) a_packet 表示接收的IP 数据包 len 表示接收的数据包长度 此回调函数可以检测所有的IP 数据包 , 包括 IP 碎片 / void nids_register_ip_frag (void ()); // / 返回值 : 无 参 数 : 回调函数名字 功 能 : 注册一个回调函数 , 此回调函数可以接收正常的IP 数据包 .e.g nids_register_ip_frag(ip_frag_function); void ip_frag_function(struct ip a_packet) a_packet 表示接收的IP 数据包 此回调函数可以接收正常的IP 数据包 , 并在此函数中对捕获数到的 IP数据包进行分析 . / void nids_register_ip (void ()); // / 返回值 : 无 参 数 : 回调函数 功 能 : 注册一个 TCP 连接的回调函数. 回调函数的类型定义如下 : void tcp_callback(struct tcp_stream ns,void param) ns 表示一个TCP 连接的所有信息 , param 表示要传递的参数信息 , 可以指向一个 TCP连接的私有数据 此回调函数接收的TCP 数据存放在 half_stream 的缓存中 , 应该马上取出来 ,一旦此回调函数返回 , 此数据缓存中存储的数据就不存在 了 .half_stream 成员 offset描述了被丢弃的数据字节数 . 如果不想马上取出来 , 而是等到存储一定数量的数据之后再取出来, 那么可 以使用函数nids_discard(struct tcp_stream ns, int num_bytes)来处理 . 这样回调函数返回时 ,Libnids 将丢弃缓存数据之前 的 num_bytes 字节的数据 .如果不调用 nids_discard()函数 , 那么缓存数据的字节应该为 count_new 字节 . 一般情况下, 缓存中的数据 应该是count-offset 字节 / void nids_register_tcp (void ()); / 返回值 : 无 参 数 : 回调函数 功 能 : 注册一个分析 UDP 协议的回调函数, 回调函数的类型定义如下 : void udp_callback(struct tuple4 addr,char buf,int len,struct ip iph) addr 表示地址端口信息buf 表示 UDP 协议负载的数据内容 len表是 UDP 负载数据的长度 iph 表示一个IP 数据包 , 包括 IP 首部 ,UDP 首部以及UDP 负载内容 / void nids_register_udp (void ()); / 返回值 : 无 参 数 : 表示一个 TCP 连接 功 能 : 终止 TCP 连接 . 它实际上是调用 Libnet的函数进行构造数据包 , 然后发送出去 / void nids_killtcp (struct tcp_stream ); / 返回值 : 无 参 数 : 参数 1 一个 TCP 连接 参数 2 个数 功 能 : 丢弃参数 2 字节 TCP 数据 , 用于存储更多的数据 / void nids_discard (struct tcp_stream , int); / 返回值 : 无 参 数 : 无 功 能 : 运行 Libnids, 进入循环捕获数据包状态. 它实际上是调用 Libpcap 函数 pcap_loop()来循环捕获数据包 / void nids_run (void); / 返回值 : 调用成功返回文件描述符 ,失败返回 -1 参 数 : 无 功 能 : 获得文件描述符号 / int nids_getfd (void); / 返回值 : 调用成功返回个数 ,失败返回负数 参 数 : 表示捕获数据包的个数 功 能 : 调用 Libpcap 中的捕获数据包函数pcap_dispatch() / int nids_dispatch (int); / 返回值 : 调用成功返回 1,失败返回 0 参 数 : 无 功 能 : 调用 Libpcap 中的捕获数据包函数pcap_next() / int nids_next (void); extern struct nids_prm nids_params; /libnids.c定以了一个全部变量 , 其定义和初始值在 nids_params/ extern char nids_warnings[]; extern char nids_errbuf[]; extern struct pcap_pkthdr nids_last_pcap_header; struct nids_chksum_ctl { / 描述的是计算校验和 , 用于决定是否计算校验和/ u_int netaddr; / 表示地址 / u_int mask; / 表示掩码 / u_int action; / 表示动作 , 如果是NIDS_DO_CHKSUM, 表示计算校验和; 如果是 NIDS_DONT_CHKSUM, 表示不计算校验和 / u_int reserved; / 保留未用 / }; / 返回值 : 无 参 数 : 参数 1 表示 nids_chksum_ctl 列表 参数 2 表示列表中的个数 功 能 : 决定是否计算校验和 . 它是根据数据结构nids_chksum_ctl 中的action 进行决定的 , 如果所要计算的对象不在列表中 , 则必须都要计算校验和 / extern void nids_register_chksum_ctl(struct nids_chksum_ctl , int); endif / _NIDS_NIDS_H / 本篇文章为转载内容。原文链接:https://blog.csdn.net/xieqb/article/details/7681968。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-02-08 17:36:31
310
转载
Sqoop
...入到Hadoop生态系统中,或者反过来把Hadoop中的数据导出到关系型数据库里。对我来说,这简直就是个救星啊!毕竟我天天都要跟一堆 structured data(结构化数据)打交道,没有它,我的日子能过得下去才怪呢! 不过呢,事情并没有想象中那么顺利。话说有一次我用 Sqoop 做数据迁移的时候,发现了个让人挠头的问题——只要碰到某些特别的数据处理任务,作业就突然“罢工”了,也不知道是啥原因。这事儿可把我给整郁闷了,我都觉得自己的水平挺过关的了,没想到被一个看起来超简单的题目给绊住了,真是有点糗啊! 示例代码: bash sqoop import \ --connect jdbc:mysql://localhost:3306/mydatabase \ --username root \ --password mypassword \ --table employees \ --target-dir /user/hadoop/employees 这段代码看起来挺正常的,但我后来发现,当表中的数据量过大或者存在一些复杂的约束条件时,Sqoop就表现得不太友好。 --- 二、Sqoop作业失败的背后 接下来,让我们一起深入探讨一下这个问题。说实话,刚开始接触Sqoop那会儿,我对它是怎么工作的压根儿没弄明白,稀里糊涂的。我以为只要配置好连接信息,然后指定源表和目标路径就行了。但实际上,Sqoop并不是这么简单的工具。 当我第一次遇到作业失败的情况时,内心是崩溃的。屏幕上显示的错误信息密密麻麻,但仔细一看,其实都是些常见的问题。打个比方啊,Sqoop这家伙一碰到一些特别的符号,比如空格或者换行符,就容易“翻车”,直接给你整出点问题来。还有呢,有时候因为网络卡了一下,延迟太高,Sqoop就跟服务器说拜拜了,连接就这么断了,挺烦人的。 有一次,我在尝试将一张包含大量JSON字段的表导出到HDFS时,Sqoop直接报错了。我当时就在心里嘀咕:“为啥别的工具处理起来轻轻松松的事儿,到Sqoop这儿就变得这么棘手呢?”后来,我一咬牙,开始翻遍各种资料,想着一定要找出个解决办法来。 思考与尝试: 经过一番研究,我发现Sqoop默认情况下并不会对数据进行深度解析,这意味着如果数据本身存在问题,Sqoop可能无法正确处理。所以,为了验证这个假设,我又做了一次测试。 bash sqoop import \ --connect jdbc:mysql://localhost:3306/mydatabase \ --username root \ --password mypassword \ --table problematic_table \ --fields-terminated-by '\t' \ --lines-terminated-by '\n' 这次我特意指定了分隔符和换行符,希望能避免之前遇到的那些麻烦。嘿,没想到这次作业居然被我搞定了!中间经历了不少波折,不过好在最后算是弄懂了个中奥秘,也算没白费功夫。 --- 三、透明性的重要性 Sqoop到底懂不懂我的需求? 说到Sqoop的透明性,我觉得这是一个非常重要的概念。所谓的透明性嘛,简单来说,就是Sqoop能不能明白咱们的心思,然后老老实实地按咱们想的去干活儿,不添乱、不出错!显然,在我遇到的这些问题中,Sqoop的表现并不能让人满意。 举个例子来说,假设你有一个包含多列的大表,其中某些列的数据类型比较复杂(例如数组、嵌套对象等)。在这种情况下,Sqoop可能会因为无法正确识别这些数据类型而失败。更糟糕的是,它并不会给出明确的提示,而是默默地报错,让你一头雾水。 为了更好地应对这种情况,我在后续的工作中加入了更多的调试步骤。比如说啊,你可以先用describe这个命令去看看表的结构,确保所有的字段都乖乖地被正确识别了;接着呢,再用--check-column这个选项去瞅一眼,看看有没有重复的记录藏在里面。这样一来,虽然增加了工作量,但至少能减少不必要的麻烦。 示例代码: bash sqoop job --create my_job \ -- import \ --connect jdbc:mysql://localhost:3306/mydatabase \ --username root \ --password mypassword \ --table employees \ --check-column id \ --incremental append \ --last-value 0 这段代码展示了如何创建一个增量作业,用于定期更新目标目录中的数据。通过这种方式,可以有效避免一次性加载过多数据带来的性能瓶颈。 --- 四、总结与展望 与Sqoop共舞 总的来说,尽管Sqoop在某些场景下表现得不尽人意,但它依然是一个强大的工具。通过不断学习和实践,我相信自己能够更加熟练地驾驭它。未来的计划里,我特别想试试一些更酷的功能,比如说用Sqoop直接搞出Avro文件,或者把Spark整进来做分布式计算,感觉会超级带劲! 最后,我想说的是,技术这条路从来都不是一帆风顺的。遇到困难并不可怕,可怕的是我们因此放弃努力。正如那句话所说:“失败乃成功之母。”只要保持好奇心和求知欲,总有一天我们会找到属于自己的答案。 如果你也有类似的经历,欢迎随时交流!我们一起进步,一起成长! --- 希望这篇文章对你有所帮助,如果有任何疑问或者想要了解更多细节,请随时告诉我哦!
2025-03-22 15:39:31
94
风中飘零
转载文章
...数据结构呢? 在许多类型的程序的设计中,数据结构的选择是一个基本的设计考虑因素。许多大型系统的构造经验表明,系统实现的困难程度和系统构造的质量都严重的依赖于是否选择了最优的数据结构。 许多时候,确定了数据结构后,算法就容易得到了。有些时候事情也会反过来,我们根据特定算法来选择数据结构与之适应。不论哪种情况,选择合适的数据结构都是非常重要的。 选择了数据结构,算法也随之确定,是数据而不是算法是系统构造的关键因素。这种洞见导致了许多种软件设计方法和程序设计语言的出现,面向对象的程序设计语言就是其中之一。 也就是说,选定数据结构往往是解决问题的核心,比如我们做一道算法题,往往就要先确定数据结构,再根据这个数据结构去思考怎么解题。 如果没有数据结构的基础知识,也就没有谈算法的意义了,很多时候即使你会使用一些封装好的编程api,但你却不知道其背后的实现原理,比如hashmap,linkedlist这些Java里的集合类,实际上都是JDK封装好的基础数据结构。 如何学习数据结构 第一次接触 我第一次接触数据结构这门课还是4年前,那这时候我在准备考研,专业课考的就是数据结构与算法,作为一个非科班的小白,对这个东西可以说是一窍不通。 这个时候的我只有一点点c语言的基础,基本上可以忽略不计,所以小白同学也可以按照这个思路进行学习。 数据结构基本上是考研的必考科目,所以我一开始使用的是考研的复习书籍,《天勤数据结构》和《王道数据结构》这两个家的书都是专门为计算机考研服务的,可以直接百度,这两本书对于我这种小白来说居然都是可以看懂的,所以,用来入门也是ok的。 入门学习阶段 最早的时候我并没有直接看书,而是先打算先看视频,因为视频更好理解呀,找视频的办法就是百度,于是当时找到的最好资源就是《郝斌的数据结构》这个视频应该是很早之前录制的了,但是对于小白来说是够用的,特别基础,讲的很仔细。 从最开始的数组、线性表,再讲到栈和队列,以及后面更复杂的二叉树、图、哈希表,大概有几十个视频,那个时候正值暑假,我按照每天一个视频的进度看完了,看的时候还得时不时地实践一下,更有助于理解。 看完了这个系列的视频之后,我又转战开始啃书了,视频里讲的都是数据结构的基础,而书上除了基础之外,还有一些算法题目,比如你学完了线性表和链表之后,书上就会有相关的算法题,比如数组的元素置换,链表的逆置等等,这些在日后看来很容易的题目,当时把我难哭了。 好在大部分题目是有讲解的,看完讲解之后还能安抚一下我受伤的心灵。 记住这本书,我在考研之前翻了至少有三四遍。 强化学习阶段 完成了第一波视频+书籍的学习之后,我们应该已经对数据结构有了初步的了解了,对一些简单的数据结构算法也应该有所了解了,比如栈的入栈和出栈,队列的进队和出队,二叉树的先序遍历和后续遍历、层次遍历,图的最短路径算法,深度优先遍历等等。 有了一定的基础之后,我们需要对哪方面进行强化学习呢? 那就要看你学习数据结构的目的是什么了,比如你学习数据结构是为了能做算法题,那么接下来你应该重点去学习算法方面的知识,后续我们也将有一篇新的文章来讲怎么学习算法,敬请期待。 当然,我当时主要是复习考研,所以还是针对专业课的历年真题来复习,像我们的卷子中就考察了很多关于哈希表、最短路径算法、KMP算法、赫夫曼算法以及最短路径算法的应用。 对于考卷上的一些知识点,我觉得掌握的并不是很好,于是又买了《王道数据结构》以及一些并没有什么卵用的书回来看,再次强化了基础。 并且,由于我们的复试通常会考察一些比较经典的算法问题,所以我又花了很多时间去学习这些算法题,这些题目并非数据结构的基础算法,所以在之前的书和视频中可能找不到答案。 于是我又在网上搜到了另一个系列视频《小甲鱼的数据结构视频》里面除了讲解数据结构之外,还讲解了更多经典的算法题,比如八皇后问题,汉诺塔问题,马踏棋盘,旅行商问题等,这些问题对于新手来说真的是很头大的,使用视频学习确实效果更佳。 实践阶段 纸上得来终觉浅,绝知此事要躬行。 众所周知,算法题和数学题一样,需要多加练习,而且考研的时候必须要手写算法,于是我就经常在纸上写(抄)算法,你还别说,就算是抄,多抄几次也有助于理解。 很多基础的算法,比如层次遍历,深度优先遍历和广度优先遍历,多写几遍更有助理解,再比如稍微复杂一点的迪杰斯特拉算法,不多写几遍你可真记不住。 除了在纸上写之外,更好的办法自然是在电脑上敲了,写Java的使用Java写,写C++ 的用C++ 写,总之用自己擅长的语言实现就好,尴尬的是我当时只会c,所以就只好老老实实地用devc++写简单的c语言程序了。 至此,我们也算是学会了数据结构的基础知识了,至少知道每个数据结构的特性,会写常见的数据结构算法,甚至偶尔还能掏出一个八皇后出来。 推荐资源 书籍 《天勤数据结构》 《王道数据结构》 如果你要考研的话,这两本书可不要错过 严蔚敏《数据结构C语言版》 这本书是大学本科计算机专业常用的教科书,年代久远,可以看看,官方也有配套的教学视频 《大话数据结构》 官方教材大家都懂的,比较不接地气,这本书对于很多新手来说是更适合入门的书籍。 《数据结构与算法Java版》 如果你是学Java的,想有一本Java语言描述的数据结构书籍,可以试试这本,但是这本书显然比较复杂,不适合入门使用。 视频 《郝斌数据结构》 这个视频上文有提到过,年代比较久远,但是入门足够了。 《小甲鱼数据结构与算法》 这个视频比较新,更加全面,有很多关于经典算法的教程,作者也入驻了B站,有兴趣也可以到B站看他的视频。 总结 关于数据结构的学习,我们就讲到这里了,如果还有什么疑问也可以到我公众号里找我探讨,虽然我们提到了算法,但是这里只关注一些基础的数据结构算法,后续会有关于“怎么学算法“的文章推出,敬请期待。 本篇文章为转载内容。原文链接:https://blog.csdn.net/a724888/article/details/104586757。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-09-12 23:35:52
135
转载
ElasticSearch
...earch的日志分析系统。一切看起来都很顺利,数据导入、索引创建啥的都没问题。但当我尝试对某些节点进行操作时,突然蹦出了这么一行错误: org.elasticsearch.cluster.block.ClusterBlockException: blocked by: [SERVICE_UNAVAILABLE/2/no active shards]; 当时我心里那个急啊!赶紧去查文档,发现这是NodeNotActiveException的表现之一。简单说吧,就好比某个关键的小哥突然“罢工”了,可能是因为它内存不够用,或者网络断了啥的,结果整个团队的工作都乱套了,没法正常运转了。 我当时就纳闷了:“这不是应该自动恢复吗?为啥还要报错呢?”后来才明白,虽然ElasticSearch确实有自我修复机制,但有时候我们需要手动干预才能让它恢复正常。 --- 2. 理解背后的逻辑 为什么会出现这种问题? 在深入了解之前,我觉得有必要先搞清楚这个异常的根本原因。其实NodeNotActiveException并不是什么特别复杂的概念,它主要出现在以下几种情况: - 节点宕机:某个节点由于硬件故障或者网络问题离线了。 - 磁盘空间不足:如果某个节点的磁盘满了,ElasticSearch会自动将其标记为不可用。 - 配置错误:比如分配给节点的资源不够,导致其无法启动。 对于我来说,问题出在第二个点上——磁盘空间不足。我当时为了省钱,给服务器分配的空间少得可怜,结果没多久就发现磁盘直接爆满,把自己都吓了一跳!于是ElasticSearch很生气,直接把该节点踢出了集群。 --- 3. 解决方案一 扩容磁盘空间 既然问题找到了,那就动手解决吧!首先,我决定先扩展磁盘容量。这一步其实很简单,只要登录服务器,增加磁盘大小就行。具体步骤如下: bash 查看当前磁盘状态 df -h 扩展磁盘(假设你已经购买了额外的存储) sudo growpart /dev/xvda 1 sudo resize2fs /dev/xvda1 完成后记得重启ElasticSearch服务: bash sudo systemctl restart elasticsearch 重启之后,神奇的事情发生了——我的节点重新上线了!不过这里有个小技巧分享给大家:如果你不确定扩容是否成功,可以通过以下命令检查磁盘使用情况: bash df -h 看到磁盘空间变大了,心里顿时舒坦了不少。 --- 4. 解决方案二 调整ElasticSearch配置 当然啦,仅仅扩容还不够,还需要优化ElasticSearch的配置文件。特别是那些容易导致内存不足或磁盘占用过高的参数,比如indices.memory.index_buffer_size和indices.store.throttle.max_bytes_per_sec。修改后的配置文件大概长这样: yaml cluster.routing.allocation.disk.threshold_enabled: true cluster.routing.allocation.disk.watermark.low: 85% cluster.routing.allocation.disk.watermark.high: 90% cluster.routing.allocation.disk.watermark.flood_stage: 95% cluster.info.update.interval: 30s 这些设置的意思是告诉ElasticSearch,当磁盘使用率达到85%时开始警告,达到90%时限制写入,超过95%时完全停止操作。这样可以有效避免再次出现类似的问题。 --- 5. 实战演练 代码中的应对策略 除了调整配置,我们还可以通过编写脚本来监控和处理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
65
林中小径
转载文章
...锁? Mysql索引类型和区别,事务的隔离级别和事务原理 Spring scope 和设计模式 Sql优化 三面 fullgc的时候会导致接口的响应速度特别慢,该如何排查和解决? 项目内存或者CPU占用率过高如何排查? ConcurrentHashmap原理 数据库分库分表 MQ相关,为什么kafka这么快,什么是零拷贝? 小算法题 http和https协议区别,具体原理 四面(Leader) 手画自己项目的架构图,并且针对架构和中间件提问 印象最深的一本技术书籍是什么? 五面(HR) 没什么过多的问题,主要就是聊了一下自己今后的职业规划,告知了薪资组成体系等等。 插播一条福利!!!最近整理了一套1000道面试题的文档(详细内容见文首推荐文章),以及大厂面试真题,和最近看的几本书。 需要刷题和跳槽的朋友,这些可以免费赠送给大家,帮忙转发文章,宣传一下,后台私信【面试】免费领取! 小天:好像问了两次看书的情况诶?现在面试还问这个? 程序员H:是啊,幸亏之前为了弄懂JVM还看了两本书,不然真不知道说啥了! 小天:看来,我也要找几本书去看了,感情没看过两本书都不敢跳槽了! 程序员H:对了,还有简历,告诉你一个捷径 简历尽量写好一些,项目经验突出: 1、自己的知识广度和深度 2、自身的优势 3、项目的复杂性和难度以及指标 4、自己对于项目做的贡献或者优化 程序员H:唉~这还不能走可怎么办呀!你说,我把主管打一顿,是不是马上就可以走了? 小天:... 查看全文 http://www.taodudu.cc/news/show-3387369.html 相关文章: 阿里菜鸟面经 Java后端开发 社招三年 已拿offer 阿里 菜鸟网络(一面) 2021年阿里菜鸟网络春招实习岗面试分享,简历+面试+面经全套资料! 阿里菜鸟国际Java研发面经(三面+总结):JVM+架构+MySQL+Redis等 2021年3月29日 阿里菜鸟实习面试(一面)(含部分总结) mongodb 子文档排序_猫鼬101:基础知识,子文档和人口简介 特征工程 计算方法Gauss-Jordan消去法求线性方程组的解 使用(VAE)生成建模,理解可变自动编码器背后的数学原理 视觉SLAM入门 -- 学习笔记 - Part2 带你入门nodejs第一天——node基础语法及使用 python3数据结构_Python3-数据结构 debezium-connect-oracle使用 相关数值分析多种算法代码 android iphone treeview,Android之IphoneTreeView带组指示器的ExpandableListView效果 nginx rewrite功能使用 3-3 OneHot编码 JavaWeb:shiro入门小案例 MySQL的定义、操作、控制、查询语言的用法 MongoDB入门学习(三):MongoDB的增删查改 赋值、浅复制和深复制解析 以及get/set应用 他是吴恩达导师,被马云聘为「达摩院」首座 Jordan 标准型定理 列主元的Gauss-Jordan消元法-python实现 Jordan 块的几何 若尔当型(The Jordan form) 第七章 其他神经网络类型 解决迁移系统后无法配置启用WindowsRE环境的问题 宝塔面板迁移系统盘/www到数据盘/home 使用vmware vconverter从物理机迁移系统到虚拟机P2V 本篇文章为转载内容。原文链接:https://blog.csdn.net/m0_62695120/article/details/124510157。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-03-08 20:01:49
69
转载
转载文章
... comment '类型',numinput int(10) comment '进货量',numstore int(10) comment '库存量'); 修改字段名 bid bno alter table book1 change bid bno int(4); 修改顺序 pub 放到author后面 alter table book1 modify pub varchar(50) after author; 修改数据类型 bno int(4) -->int(10) alter table book1 modify bno int(10); 删除字段 alter table book1 drop 字段名; 删除表 drop table 表名; 插入语句 insert into book1(bno,bname,author,type) values(1001,'斗破苍穹','天蚕土豆','玄幻');insert into book1(bno,bname,author,type) values(1002,'全职高手','蝴蝶兰','网游竞技');insert into book1(bno,bname,author,type) values(1003,'鬼吹灯','天下霸唱','恐怖');insert into book1(bno,bname,author,type)values(1004,'西游记','吴承恩','4大名著');insert into book1(bno,bname,author,type)values(1005,'java基础','王克晶','达内学习手册'); update语句 把1005号书,修改成'天线宝宝',作者不详,类型少儿 把1004号书修改成'天龙八部',作者金庸,类型武侠 update book1 set bname="天线宝宝",author="作者不详",type="少儿" where bno=1005; 删除类型是'恐怖'的所有书籍 删除全表记录 删除表格 修改book名称为book_item rename table book to book_item; 在表格尾部添加字段price double(7,2) alter table book_item add price double(7,2); 把price字段的位置放到author之后 alter table book_item modify price double(7,2) after author; 把表中存在的数据添加价格,每本书都在100~1000之间,自定 update book_item set price=199 where bno=1001; 修改1001的价格为500元 把所有字段的null字段补全 update book_item set pub="达内出版社",numinput=500,numstore=100 where pub is null; 删除价格小于150的所有条目 删除所有数据 SQL分类 数据定义语言 DDL 重点 数据操纵语言 DML 重点 增 删 改 数据查询语言 DQL select 查 事务控制语言 TCL 数据库控制语言 DCL 数据定义语言 DDL - 负责数据结构定义,与创建数据库对象的语言- 常用create alter drop- DDL不支持事务,DDL语句执行之后,不能回滚 数据操纵语言 DML - 对数据库中更改数据操作的语句- select insert update delete--> CRUD 增删改查- 通常把select相关操作,单独出来,称之为DQL- DML支持事务,在非自动提交模式时,可以利用rollback回滚操作. 数据查询语言 DQL - 筛选,分组,连表查询 面试重点 TCL 和 DCL - 事务控制语句TCL- 负责实现数据库中事务支持的语言,commit rollback savepoint等指令- DCL数据库控制语言- 管理数据库的授权,角色控制等,grant(授权),revoke(取消授权) 练习: 案例:创建一张表customer(顾客) create table customer(cid int(4) primary key comment '顾客编号',cname varchar(50) comment '顾客姓名',sex char(5) comment '顾客性别',address varchar(50) comment '地址',phone varchar(11) comment '手机',email varchar(50) comment '邮箱'); show create table customer; 插入5条数据 insert into customer values(1001,'小明','男','楼上18号','123','123@163.com');insert into customer values(1002,'小红','女','楼上17号','1234','1234@163.com');insert into customer values(1003,'老王','男','楼上18号隔壁','1234','1234@163.com');insert into customer values(1004,'老宋','男','楼上17号隔壁','1234','1234@163.com');insert into customer values(1005,'小马','女','楼上17号隔壁','1234','1234@163.com'); -1 修改一条数据的姓名 小红的姓名 -2 修改一条数据的性别 老王的性别 -3 修改一条数据的电话 1001号的电话 -4 修改一条数据的邮箱 邮箱为123@163.com,改成323@163.com -5 查询性别为 男的所有数据 select from customer where sex="男"; -6 自定义DDL操作的需求,5道题,可以同上面book表的操作 数据库数据类型 主要包括5大类 整数类型 int, big int 浮点数类型 double decimal 字符串类型 char varchar text 日期类型 date datetime timestamp time year... 其他数据类型 set.... 字符串 - char(固定长度) 定长字符串 最多255个字节- 定多少长度,就占用多少长度- 多了放不进去,少了用空格补全- 不认识内容尾部的空格- varchar(最大长度) 变长字符串 最大65535字节,但是使用一般不超过255- 只要不超过定的长度,都可以放进去- 以内容真实长度为准- 认识内容尾部的空格- text 最大65535字节- blob 大数据对象,以二进制(字节)的方式存储 整数 tinyint 1字节 smallint 2字节 int 4字节 bigint 8字节 int(6)影响的是查询时显示长度(zerofill)不影响数据的保存长度 create table t1(id1 int,id2 int(5)); insert into t1 values(111111,111111); alter table t1 modify id1 int zerofill; alter table t1 modify id2 int(5) zerofill; insert into t1 values (1,1); float 4字节 double 8字节 double(8,2) 可能会产生精度的缺失 10.0/3 3.3333333336 decimal 不会缺失精度,但是使用的时候需要指定总长度和小数位数 日期 - date 年月日- time 时分秒- datetime 年月日时分秒,到9999年,而且需要手动输入,如果没有手动输入,就显示null.- timestamp 年月日时分秒,在没有数据手动插入时,自动填入当前时间.最大值2038- bigint 1970-1-1 0:0:0 格林威治时间 案例:创建表t,字段d1 date,d2 time,d3 datetime,d4 timestamp create table t(id int,d1 date,d2 time,d3 datetime,d4 timestamp);insert into t (d1,d2) values ('1910-01-10','12:32:12');insert into t values(1,'2018-12-21','15:12:00','1995-02-10 12:08:12','2030-10-10 15:19:32');insert into t values(2,'3018-01-25','15:12:34','9234-12-31 12:12:12','2030-12-31 12:12:12');insert into t values(2,'3018-01-25','15:12:34','9999-12-31 23:59:59','2030-12-31 12:12:12'); 练习 创建人物表,插入,修改,查询 create table person(id int(4) primary key,name varchar(50),age int(3));insert into person values(1,"梅超风",36);insert into person values(2,"洪七公",96);insert into person values(3,"杨过",40);insert into person values(4,"令狐冲",28);insert into person values(5,"张三丰",100);insert into person values(6,"张翠山",27);insert into person values(7,"张无忌",27);insert into person values(8,"赵敏",18);insert into person values(9,"独孤求败",250);insert into person values(10,"楚留香",36);1.案例:修改张三丰的name为刘备,id为11update person set name="刘备",id=11 where name="张三丰";2.案例:修改2号人物的的name为夏侯渊update person set name="夏侯渊" where id=2;3.案例:根据条件修改person表中的数据,修改id是6的数据中,姓名改为'任我行', 年龄改为39update person set name="任我行",age=39 where id=6;4.案例:修改姓名是‘楚留香'的数据,把id改为20,年龄改为19update person set id=20,age=19 where name="楚留香";5.案例:把person所有的数据的年龄全部改为20 update person set age=20;6.案例:修改id为7的数据,把id改为100,姓名改为杨过,年龄改为21update person set id=100,name="杨过",age=21 where id=7;7.案例:修改姓名是独孤求败,把年龄改为35update person set age=35 where name="独孤求败";8.案例:修改id=8的信息,把姓名改为房玄龄update person set name="房玄龄" where id=8;9.案例 :修改id为20并且年龄为20的人的姓名为刘德华(郑少秋也行)提示 where...and...update person set name="郑少秋" where id=20 and age=20; 查询 没有条件的简单查询 select from 表名;查询表中所有的数据 select from person; select from t; select from emp; select from dept; 查询某些列中的值 select name as '姓名' from person; select name as '姓名',age as '年龄' from person; select id as '编号',name as '姓名',age as '年龄' from person; 学习过程的编程习惯select from 表; 工作中的编程习惯select id,name,age from person; 查询emp表中所有员工的姓名,上级领导的编号,职位,工资 select ename,mgr,job,sal from emp; 查询emp表中所有员工的编号,姓名,所属部门编号,工资 select empno,ename,deptno,sal from emp; 查询dept表中所有部门的名称和地址 select dname,loc from dept; 如果忘记了mysql的用户名和密码怎么办 卸载重新装 不重装软件如何修改密码 1.停止mysql服务 2.cmd中输入一个命令 mysqld --skip-grant-tables; -通过控制台,开启了一个mysql服务 3.开启一个新的cmd -mysql -u root -p 可以不使用密码进入数据库 show databases;----mysql 5. use mysql; 6. update user set password=password('新密码') where user="root"; 7. 关闭mysqld这个服务/进程 8. 重启mysql服务 作业 mysql02,一天的代码重新敲一遍,熟悉emp和dept列名 本篇文章为转载内容。原文链接:https://blog.csdn.net/sinat_41915844/article/details/79770973。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2024-02-16 12:44:07
545
转载
转载文章
...,使用DBMS_AQ系统包进行相应的操作,是Oracle的默认组件,只要安装了Oracle数据库就可以使用。使用AQ可以在多个Oracle数据库、Oracle与Java、C等系统中进行数据传输。 下面分步骤说明如何创建Oracle AQ 1. 创建消息负荷payload Oracle AQ中传递的消息被称为有效负荷(payloads),格式可以是用户自定义对象或XMLType或ANYDATA。本例中我们创建一个简单的对象类型用于传递消息。 create type demo_queue_payload_type as object (message varchar2(4000)); 2. 创建队列表 队列表用于存储消息,在入队时自动存入表中,出队时自动删除。使用DBMS_AQADM包进行数据表的创建,只需要写表名,同时设置相应的属性。对于队列需要设置multiple_consumers为false,如果使用发布/订阅模式需要设置为true。 begin dbms_aqadm.create_queue_table( queue_table => 'demo_queue_table', queue_payload_type => 'demo_queue_payload_type', multiple_consumers => false ); end; 执行完后可以查看oracle表中自动生成了demo_queue_table表,可以查看影响子段(含义比较清晰)。 3. 创建队列并启动 创建队列并启动队列: begin dbms_aqadm.create_queue ( queue_name => 'demo_queue', queue_table => 'demo_queue_table' ); dbms_aqadm.start_queue( queue_name => 'demo_queue' ); end; 至此,我们已经创建了队列有效负荷,队列表和队列。可以查看以下系统创建了哪些相关的对象: SELECT object_name, object_type FROM user_objects WHERE object_name != 'DEMO_QUEUE_PAYLOAD_TYPE'; OBJECT_NAME OBJECT_TYPE ------------------------------ --------------- DEMO_QUEUE_TABLE TABLE SYS_C009392 INDEX SYS_LOB0000060502C00030$$ LOB AQ$_DEMO_QUEUE_TABLE_T INDEX AQ$_DEMO_QUEUE_TABLE_I INDEX AQ$_DEMO_QUEUE_TABLE_E QUEUE AQ$DEMO_QUEUE_TABLE VIEW DEMO_QUEUE QUEUE 我们看到一个队列带出了一系列自动生成对象,有些是被后面直接用到的。不过有趣的是,创建了第二个队列。这就是所谓的异常队列(exception queue)。如果AQ无法从我们的队列接收消息,将记录在该异常队列中。 消息多次处理出错等情况会自动转移到异常的队列,对于异常队列如何处理目前笔者还没有找到相应的写法,因为我使用的场景并不要求消息必须一对一的被处理,只要起到通知的作用即可。所以如果消息转移到异常队列,可以执行清空队列表中的数据 delete from demo_queue_table; 4. 队列的停止和删除 如果需要删除或重建可以使用下面的方法进行操作: BEGIN DBMS_AQADM.STOP_QUEUE( queue_name => 'demo_queue' ); DBMS_AQADM.DROP_QUEUE( queue_name => 'demo_queue' ); DBMS_AQADM.DROP_QUEUE_TABLE( queue_table => 'demo_queue_table' ); END; 5. 入队消息 入列操作是一个基本的事务操作(就像往队列表Insert),因此我们需要提交。 declare r_enqueue_options DBMS_AQ.ENQUEUE_OPTIONS_T; r_message_properties DBMS_AQ.MESSAGE_PROPERTIES_T; v_message_handle RAW(16); o_payload demo_queue_payload_type; begin o_payload := demo_queue_payload_type('what is you name ?'); dbms_aq.enqueue( queue_name => 'demo_queue', enqueue_options => r_enqueue_options, message_properties => r_message_properties, payload => o_payload, msgid => v_message_handle ); commit; end; 通过SQL语句查看消息是否正常入队: select from aq$demo_queue_table; select user_data from aq$demo_queue_table; 6. 出队消息 使用Oracle进行出队操作,我没有实验成功(不确定是否和DBMS_OUTPUT的执行权限有关),代码如下,读者可以进行调试: declare r_dequeue_options DBMS_AQ.DEQUEUE_OPTIONS_T; r_message_properties DBMS_AQ.MESSAGE_PROPERTIES_T; v_message_handle RAW(16); o_payload demo_queue_payload_type; begin DBMS_AQ.DEQUEUE( queue_name => 'demo_queue', dequeue_options => r_dequeue_options, message_properties => r_message_properties, payload => o_payload, msgid => v_message_handle ); DBMS_OUTPUT.PUT_LINE( ' Browse message is [' || o_payload.message || ']' ); end; 二、Java使用JMS监听并处理Oracle AQ队列 Java使用JMS进行相应的处理,需要使用Oracle提供的jar,在Oracle安装目录可以找到:在linux中可以使用find命令进行查找,例如 find pwd -name 'jmscommon.jar' 需要的jar为: app/oracle/product/12.1.0/dbhome_1/rdbms/jlib/jmscommon.jar app/oracle/product/12.1.0/dbhome_1/jdbc/lib/ojdbc7.jar app/oracle/product/12.1.0/dbhome_1/jlib/orai18n.jar app/oracle/product/12.1.0/dbhome_1/jlib/jta.jar app/oracle/product/12.1.0/dbhome_1/rdbms/jlib/aqapi_g.jar 1. 创建连接参数类 实际使用时可以把参数信息配置在properties文件中,使用Spring进行注入。 package org.kevin.jms; / @author 李文锴 连接参数信息 / public class JmsConfig { public String username = "ckevin"; public String password = "a111111111"; public String jdbcUrl = "jdbc:oracle:thin:@127.0.0.1:1521:orcl"; public String queueName = "demo_queue"; } 2. 创建消息转换类 因为消息载荷是Oracle数据类型,需要提供一个转换工厂类将Oracle类型转换为Java类型。 package org.kevin.jms; import java.sql.SQLException; import oracle.jdbc.driver.OracleConnection; import oracle.jdbc.internal.OracleTypes; import oracle.jpub.runtime.MutableStruct; import oracle.sql.CustomDatum; import oracle.sql.CustomDatumFactory; import oracle.sql.Datum; import oracle.sql.STRUCT; / @author 李文锴 数据类型转换类 / @SuppressWarnings("deprecation") public class QUEUE_MESSAGE_TYPE implements CustomDatum, CustomDatumFactory { public static final String _SQL_NAME = "QUEUE_MESSAGE_TYPE"; public static final int _SQL_TYPECODE = OracleTypes.STRUCT; MutableStruct _struct; // 12表示字符串 static int[] _sqlType = { 12 }; static CustomDatumFactory[] _factory = new CustomDatumFactory[1]; static final QUEUE_MESSAGE_TYPE _MessageFactory = new QUEUE_MESSAGE_TYPE(); public static CustomDatumFactory getFactory() { return _MessageFactory; } public QUEUE_MESSAGE_TYPE() { _struct = new MutableStruct(new Object[1], _sqlType, _factory); } public Datum toDatum(OracleConnection c) throws SQLException { return _struct.toDatum(c, _SQL_NAME); } public CustomDatum create(Datum d, int sqlType) throws SQLException { if (d == null) return null; QUEUE_MESSAGE_TYPE o = new QUEUE_MESSAGE_TYPE(); o._struct = new MutableStruct((STRUCT) d, _sqlType, _factory); return o; } public String getContent() throws SQLException { return (String) _struct.getAttribute(0); } } 3. 主类进行消息处理 package org.kevin.jms; import java.util.Properties; import javax.jms.Message; import javax.jms.MessageConsumer; import javax.jms.MessageListener; import javax.jms.Queue; import javax.jms.QueueConnection; import javax.jms.QueueConnectionFactory; import javax.jms.Session; import oracle.jms.AQjmsAdtMessage; import oracle.jms.AQjmsDestination; import oracle.jms.AQjmsFactory; import oracle.jms.AQjmsSession; / @author 李文锴 消息处理类 / public class Main { public static void main(String[] args) throws Exception { JmsConfig config = new JmsConfig(); QueueConnectionFactory queueConnectionFactory = AQjmsFactory.getQueueConnectionFactory(config.jdbcUrl, new Properties()); QueueConnection conn = queueConnectionFactory.createQueueConnection(config.username, config.password); AQjmsSession session = (AQjmsSession) conn.createQueueSession(false, Session.AUTO_ACKNOWLEDGE); conn.start(); Queue queue = (AQjmsDestination) session.getQueue(config.username, config.queueName); MessageConsumer consumer = session.createConsumer(queue, null, QUEUE_MESSAGE_TYPE.getFactory(), null, false); consumer.setMessageListener(new MessageListener() { @Override public void onMessage(Message message) { System.out.println("ok"); AQjmsAdtMessage adtMessage = (AQjmsAdtMessage) message; try { QUEUE_MESSAGE_TYPE payload = (QUEUE_MESSAGE_TYPE) adtMessage.getAdtPayload(); System.out.println(payload.getContent()); } catch (Exception e) { e.printStackTrace(); } } }); Thread.sleep(1000000); } } 使用Oracle程序块进行入队操作,在没有启动Java时看到队列表中存在数据。启动Java后,控制台正确的输出的消息;通过Oracle程序块再次写入消息,发现控制台正确处理消息。Java的JMS监听不是立刻进行处理,可能存在几秒中的时间差,时间不等。 三、监控表记录变化通知Java 下面的例子创建一个数据表,然后在表中添加触发器,当数据变化后触发器调用存储过程给Oracle AQ发送消息,然后使用Java JMS对消息进行处理。 1. 创建表 创建student表,包含username和age两个子段,其中username时varchar2类型,age时number类型。 2. 创建存储过程 创建send_aq_msg存储过程,因为存储过程中调用dbms数据包,系统包在存储过程中执行需要进行授权(使用sys用户进行授权): grant execute on dbms_aq to ckevin; 注意存储过程中包含commit语句。 create or replace PROCEDURE send_aq_msg (info IN VARCHAR2) as r_enqueue_options DBMS_AQ.ENQUEUE_OPTIONS_T; r_message_properties DBMS_AQ.MESSAGE_PROPERTIES_T; v_message_handle RAW(16); o_payload demo_queue_payload_type; begin o_payload := demo_queue_payload_type(info); dbms_aq.enqueue( queue_name => 'demo_queue', enqueue_options => r_enqueue_options, message_properties => r_message_properties, payload => o_payload, msgid => v_message_handle ); commit; end send_aq_msg; 3. 创建触发器 在student表中创建触发器,当数据写入或更新时,如果age=18,则进行入队操作。需要调用存储过程发送消息,但触发器中不能包含事物提交语句,因此需要使用pragma autonomous_transaction;声明自由事物: CREATE OR REPLACE TRIGGER STUDENT_TR AFTER INSERT OR UPDATE OF AGE ON STUDENT FOR EACH ROW DECLARE pragma autonomous_transaction; BEGIN if :new.age = 18 then send_aq_msg(:new.username); end if; END; 创建完触发器后向执行插入或更新操作: insert into student (username,age) values ('jack.lee.3k', 18); update student set age=18 where username='jack003'; Java JMS可以正确的处理消息。 本篇文章为转载内容。原文链接:https://blog.csdn.net/weixin_42309178/article/details/115241521。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-12-17 14:22:22
140
转载
转载文章
...t 分别对应的是函数类型 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
680
转载
转载文章
...R负责查找并加载操作系统引导程序,如GRUB或Windows的引导管理器。在本文中,MBR还分为GRUB.MBR和DOS.MBR,其中GRUB.MBR用于引导Linux等操作系统,而DOS.MBR则主要用于传统的DOS和早期版本Windows系统。由于结构限制,MBR只能支持最多4个主分区或扩展分区。 GHO镜像 , GHO镜像是由Ghost软件创建的一种硬盘分区备份文件格式,通常用于快速备份和恢复整个硬盘分区的数据。在Windows安装方法部分提到,GHO镜像是一种常见的系统安装方式,用户可以先将Windows ISO镜像解压获取GHO文件,然后通过PE(预安装环境)启动U盘中的Ghost工具将GHO镜像还原到目标硬盘上以完成系统的快速部署。 VLAN (Virtual Local Area Network) , VLAN是在物理网络的基础上通过软件定义逻辑划分出来的虚拟局域网。在Windows系统配置章节中,使用PowerShell命令对VLAN ID进行设置,说明了VLAN技术如何实现同一物理网络内的多逻辑子网隔离,从而更灵活地管理和控制不同组群间的通信,提高网络安全性及资源利用率。每个VLAN具有独立的广播域,可以基于端口、MAC地址或协议类型等标准进行划分,并通过802.1Q标签协议在交换机间传输数据时标识所属VLAN。
2023-09-10 16:27:10
271
转载
转载文章
...igned 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
542
转载
转载文章
...号安全基本措施 1.系统账号清理 二.密码安全控制: 1.设置密码有效期: 2.要求用户下次登录时改密码 三.命令历史限制与自动注销 1.命令历史限制: 2.终端自动注销: 四.PAM安全认证 1.su的命令的安全隐患 2.什么是PAM 3.PAM认证原理 4.PAM安全认证流程 五.限制使用su命令的用户(pam-wheel认证模块) 1.su命令概述: 2. su命令的用途以及用法: 3.配置su的授权(加入wheel组)(pam_wheel认证模块:): 4.配置/etc/sudoers文件(授权用户较多的时候使用): 六.开关机安全控制 1.调整BIOS引导设置 2.GRUB限制 七.终端以及登录控制 1.限制root只在安全终端登录 2..禁止普通用户登录 八.系统弱口令检测 1.JOHN the Ripper,简称为JR 2.安装弱口令账号 3.密码文件的暴力破解 九.网络端口扫描 1.NMAP 2.格式 总结: 一.账号安全基本措施 1.系统账号清理 1.将非登录用户的Shell设为/sbin/nologin (ps:在我们使用Linux系统时,除了用户创建的账号之外,还会产生系统或程序安装过程中产生的许多其他账号,除了超级用户root外,其他账号都是用来维护系统运作的,一般不允许登录,常见的非登录用户有bin、adm、mail、lp、nobody、ftp等。) 格式:usermod -s /sbin/nologin 用户名 2锁定长期不使用的账号: [root@hehe ~] usermod -L test2 锁定用户账号方法一[root@hehe ~] passwd -l test3 锁定用户账号方法二[root@hehe ~] usermod -U test2 解锁用户账号方法一[root@hehe~] passwd -u test3 解锁用户账号方法二查看账户有没有被锁:passwd -S [用户名] 3.删除无用的账号 [root@hehe ~] userdel test1[root@hehe~] userdel -r test2 4.锁定账号文件passwd,shadow [root@hehe ~] chattr +i /etc/passwd /etc/shadow 锁定文件,包括root也无法修改[root@hehe ~] chattr -i /etc/passwd /etc/shadow 解锁文件[root@hehe ~] lsattr /etc/passwd /etc/shadow查看文件状态属性 举个例子: 二.密码安全控制: 1.设置密码有效期: 1.[root@localhost ~] chage -M 60 test3 这种方法适合修改已经存在的用户12.[root@localhost ~] vim /etc/login.defs 这种适合以后添加新用户PASS_MAX_DAYS 30 1.这个方法适用于早就已经存在的用户: 2.这个方法适用于新用户 2.要求用户下次登录时改密码: [root@hehe ~] chage -d 0 [用户名] 强制要求用户下次登陆时修改密码 三.命令历史限制与自动注销 1.命令历史限制: 1.减少记录的命令条数 减少记录命令的条数:1.[root@hehe ~] vim /etc/profile 进入配置文件修改限制命令条数。适合新用户HISTSIZE=200 修改限制命令为200条,系统默认是1000条profile [root@lhehe ~] source /etc/ 刷新配置文件,使文件立即生效2.[root@hehe~] export HISTSIZE=200 适用于当前(之后)用户[root@hehe~] source /etc/profile [root@hehe ~] source /etc/profile 刷新配置文件,使文件立即生效 1.减少记录命令的条数(适用之前的用户): 2.注销时自动清空命令历史 3. 注销时自动清空命令:[root@hehe ~] vim ~/.bash_logout(临时清除,重启缓存的话还在)echo "" > ~/.bash_history(永久删除)history是查你使用过的命令 2.终端自动注销: 1.闲置600秒后自动注销 闲置600秒后自动注销:[root@hehe ~]vim .bash_profile 进入配置文件export TMOUT=600 全局声明超过60秒闲置后自动注销终端[root@hehe ~] source .bash_profile [root@hehe ~] echo $TMOUT[root@hehe ~] export TMOUT=600 如果不在配置文件输入这条命令,那么是对当前用户生效[root@hehe ~]vim .bash_profile export TMOUT=600 注释掉这条命令,就不会自动注销了 四.PAM安全认证 1.su的命令的安全隐患 1.,默认情况下,任何用户都允许使用su命令,有机会反复尝试其他用户(如root) 的登录密码,带来安全风险; 2.为了加强su命令的使用控制,可借助于PAM认证模块,只允许极个别用户使用su命令进行切换。 2.什么是PAM 1.PAM(Pluggable Authentication Modules)可插拔式认证模块 2.是一种高效而且灵活便利的用户级别的认证方式; 3.也是当前Linux服务器普遍使用的认证方式。 4.PAM提供了对所有服务进行认证的中央机制,适用于login,远程登陆,su等应用 5.系统管理员通过PAM配置文件来制定不同的应用程序的不同认证策略 3.PAM认证原理 1.PAM认证一般遵循的顺序: Service (服务) --> PAM (配置文件) --> pam_.so;, 2.PAM认证首先要确定哪一项应用服务,然后加载相应的PAM的配置文件(位于/etc/pam.d下),最后调用认 模块(位于/lib64/security/下)进行安全认证。 3.用户访问服务器的时候,服务器的某一个服务程序把用户的请求发送到PAM模块进行认证。不同的应用程序所对应的PAM模块也是不同的。 4.如果想查看某个程序是否支持PAM认证,可以用ls命令进行查看/etc/pam.d/。 ls /etc/pam.d/ | grep su 5.PAM的配置文件中的每一行都是一个独立的认证过程,它们按从上往下的顺序依次由PAM模块调用。 4.PAM安全认证流程 控制类型也称做Control Flags,用于PAM验证类型的返回结果 用户1 用户2 用户3 用户4 auth required 模块1 pass fail pass pass auth sufficient 模块2 pass pass fail pass auth required 模块3 pass pass pass fail 结果 pass fail pass pass 4 五.限制使用su命令的用户(pam-wheel认证模块) 1.su命令概述: 通过su命令可以非常方便切换到另一个用户,但前提条件是必须知道用户登录密码。对于生产环境中的Linux服务器,每多一个人知道特权密码,安全风险就多一分。于是就多了一种折中的办法,使用sudo命令提升执行权限,不过需要由管理员预先进行授权, 指定用户使用某些命令: 2. su命令的用途以及用法: 用途:以其他用户身份(如root)执行授权命令用法:sudo 授权命令 3.配置su的授权(加入wheel组)(pam_wheel认证模块:): 进入授权命令:1.visudo 或者 vim /etc/sudoers语法格式:1.用户 主机名=命令程序列表2.用户 主机名=(用户)命令程序列表-l:列出用户在主机上可用的和被禁止的命令;一般配置好/etc/sudoers后,要用这个命令来查看和测试是不是配置正确的;-v:验证用户的时间戳;如果用户运行sudo后,输入用户的密码后,在短时间内可以不用输入口令来直接进行sudo操作;用-v可以跟踪最新的时间戳;-u:指定以以某个用户执行特定操作;-k:删除时间戳,下一个sudo命令要求用求提供密码; 1.首先创建3个组 2.vim /etc/pam.d/su把第六行注释去掉保存退出 1. 以上两行是默认状态(即开启第一行,注释第二行),这种状态下是允许所有用户间使用su命令进行切换的 2.两行都注释也是运行所有用户都能使用su命令,但root下使用su切换到其他普通用户需要输入密码: 3.如果第–行不注释,则root 使用su切换普通用户就不需要输入密码( pam_ rootok. so模块的主要作用是使uid为0的用户,即root用户能够直接通过认证而不用输入密码。) 4.如果开启第二行,表示只有root用户和wheel1组内的用户才可以使用su命令。 5.如果注释第一行,开启第二行,表示只有whee1组内的用户才能使用su命令,root用户也被禁用su命令。 3.将liunan加入到wheel之后,hehe就有了使用su命令的权限 4.使用pam_wheel认证后,没有在wheel里的用户都不能再用su 5.whoami命令确定当前用户是谁 4.配置/etc/sudoers文件(授权用户较多的时候使用): visudo单个授权visudo 或者 vim /etc/sudoers记录格式:user MACHINE=COMMANDS可以使用通配符“ ”号任意值和“ !”号进行取反操作。%组名代表一整个组权限生效后,输入密码后5分钟可以不用重新输入密码。例如:visudo命令下user kiro=(root)NOPASSWD:/usr/sbin/useradd,PASSWD:/usr/sbin/usermod代表 kiro主机里的user用户,可以无密码使用useradd命令,有密码使用usermod/etc/sudoers多个授权Host_Alias MYHOST= localhost 主机别名:主机名、IP、网络地址、其他主机别名!取反Host_Alias MAILSVRS=smtp,pop(主机名)User_Alias MYUSER = kiro,user1,lisi 用户别名:包含用户、用户组(%组名(使用引导))、还可以包含其他其他已经用户的别名User_Alias OPERATORS=zhangsan,tom,lisi(需要授权的用户)Cmnd_Alias MYCMD = /sbin/,/usr/bin/passwd 命令路劲、目录(此目录内的所有命令)、其他事先定义过的命令别名Cmnd_Alias PKGTOOLS=/bin/rpm,/usr/bin/yum(授权)MYUSER MYHOST = NOPASSWD : MYCMDDS 授权格式sudo -l 查询目前sudo操作查看sudo操作记录需启用Defaults logfile配置默认日志文件: /var/log/sudosudo -l 查看当前用户获得哪些sudo授权(启动日志文件后,sudo操作过程才会被记录) 1.首先用visudo 或者 vim /etc/sudoers进入,输入需要授权的命令 2.切换到taojian用户,因为设置了它不能使用创建用户的命令所以无法创建 六.开关机安全控制 1.调整BIOS引导设置 1.将第一引导设备设为当前系统所在硬盘2.禁止从其他设备(光盘、U盘、网络)引导系统3.将安全级别设为setup,并设置管理员密码 2.GRUB限制 1.使用grub2-mkpasswd-pbkdf2生成密钥2.修改/etclgrub.d/00_header文件中,添加密码记录3.生成新的grub.cfg配置文件 方法一: 通常情况下在系统开机进入GRUB菜单时,按e键可以查看并修改GRUB引导参数,这对服务器是一个极大的威胁。可以为GRUB菜单设置一个密码,只有提供正确的密码才被允许修改引导参数。grub2-mkpasswd-pbkdf2 根据提示设置GRUB菜单的密码PBKDF2 hash of your password is grub.pbkd..... 省略部分内容为经过加密生成的密码字符串cp /boot/grub2/grub.cfg /boot/grub2/grub.cfg.bak 8cp /etc/grub.d/00_header /etc/grub.d/00_header.bak 9vim /etc/grub.d/00_headercat << EOFset superusers="root" 设置用户名为rootpassword_pbkdf2 root grub.pbkd2..... 设置密码,省略部分内容为经过加密生成的密码字符串EOF16grub2-mkconfig -o /boot/grub2/grub.cfg 生成新的grub.cfg文件重启系统进入GRUB菜单时,按e键将需要输入账号密码才能修改引导参数。 方法二: 1.一步到位2.grub2-setpassword 七.终端以及登录控制 1.限制root只在安全终端登录 安全终端配置文件在 /etc/securetty 2..禁止普通用户登录 1.建立/etc/nologin文件 2.删除nologin文件或重启后即恢复正常 vim /etc/securetty在端口前加号拒绝访问touch /etc/nologin 禁止普通用户登录rm -rf /etc/nologin 取消禁止 八.系统弱口令检测 1.JOHN the Ripper,简称为JR 1.一款密码分析工具,支持字典式的暴力破解2.通过对shadow文件的口令分析,可以检测密码强度3.官网网站:http://www.openwall.com/john/ 2.安装弱口令账号 1.获得Linux/Unix服务器的shadow文件2.执行john程序,讲shadow文件作为参数 3.密码文件的暴力破解 1.准备好密码字典文件,默认为password.lst2.执行john程序,结合--wordlist=字典文件 九.网络端口扫描 1.NMAP 1.—款强大的网络扫描、安全检测工具,支持ping扫描,多端口检测等多种技术。2.官方网站: http://nmap.orgl3.CentOS 7.3光盘中安装包,nmap-6.40-7.el7.x86_64.rpm 2.格式 NMAP [扫描类型] [选项] <扫描目标....> 安装NMAP软件包rpm -qa | grep nmapyum install -y nmapnmap命令常用的选项和扫描类型-p:指定扫描的端口。-n:禁用反向DNS 解析 (以加快扫描速度)。-sS:TCP的SYN扫描(半开扫描),只向目标发出SYN数据包,如果收到SYN/ACK响应包就认为目标端口正在监听,并立即断开连接;否则认为目标端口并未开放。-sT:TCP连接扫描,这是完整的TCP扫描方式(默认扫描类型),用来建立一个TCP连接,如果成功则认为目标端口正在监听服务,否则认为目标端口并未开放。-sF:TCP的FIN扫描,开放的端口会忽略这种数据包,关闭的端口会回应RST数据包。许多防火墙只对SYN数据包进行简单过滤,而忽略了其他形式的TCP attack 包。这种类型的扫描可间接检测防火墙的健壮性。-sU:UDP扫描,探测目标主机提供哪些UDP服务,UDP扫描的速度会比较慢。-sP:ICMP扫描,类似于ping检测,快速判断目标主机是否存活,不做其他扫描。-P0:跳过ping检测,这种方式认为所有的目标主机是存活的,当对方不响应ICMP请求时,使用这种方式可以避免因无法 ping通而放弃扫描。 总结: 1.账号基本安全措施:系统账号处理、密码安全控制、命令历史清理、自动注销 2.用户切换与提权(su、sudo) 3.开关机安全控制(BIOS引导设置、禁止Ctrl+Alt+Del快捷键、GRUB菜单设置密码) 4.终端控制 5.弱口令检测——John the Ripper 6.端口扫描——namp 本篇文章为转载内容。原文链接:https://blog.csdn.net/weixin_67474417/article/details/123982900。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-05-07 23:37:44
100
转载
转载文章
...没有运行过磁盘整理。系统完好.任何时候的文件都可以找回来。 win7清除任务栏无意义图标:www.shanpow.com_删除Download和DataStore文件夹中的所有文件。 1、输入“regedit”打开注册表编辑器,然后打开如下键值: HKEY_CLASSES_ROOT\Local Settings\Software\Microsoft\Windows\CurrentVersion\TrayNotify 在右边你可以看到两个键值IconStreams和PastIconsStream,将它们的值删除。 2、然后调出任务管理器将进程“explorer.exe”终止,再在任务管理器中点击“文件——新建任务”, 输入“explorer”——确定 Win7安全中心服务启用不了时: 开始----运行-----输入“services.msc "确定-----找到(windows)security center 启动类型设置为自动并启动它 或者 右键单击计算机---管理----服务和应用程序----服务---找到(windows)security centerwww.shanpow.com_删除Download和DataStore文件夹中的所有文件。 ----双击-----启动类型设置为“自动”。 1.在服务管理中,关闭Windows Update服务 2.打开C:\Windows\SoftwareDistribution文件夹 3.删除DataStore和Download文件夹下的所有文件 4.启动Windows Update服务 5.进入Windows Update查看一下,Windows更新记录已经清除了。 如何用B电脑远程登录A电脑 注意:AB电脑都连接上了互联网 A电脑: 1添加一个用户名,设置登录密码。2我的电脑→属性→远程→允许用户远程连接到此计算机前 打√确定3网上邻居→属性→本地连接状态→支持→记下IP 地址XXX.XXX.XXX.XXX。 B电脑登录过程4 开始→所有程序→附件→通讯→远程桌面连→在弹出的窗口里输入A电脑的IP 地址 →连接。连接成功后会变成一个黑屏幕的画面,在屏幕的最上方有一个指示条,指示着机器是在远程登 录状态。当A电脑响应了B电脑的远程登录请求后,会给你返回一个画面,要求你输入用户名,密码。 5输入用户名和密码→确定。验证的用户名和密码是对的,他就会把其A桌面画面全传送到B电脑的屏 幕上来,稳定后就成功了! 有一事你不能作:关机。因为B电脑左下角的开始,是指挥自己用的,没 法指挥A电脑。 想使用B电脑控制A电脑关机,得在A电脑上设置:附件→windows 资源管理器→ WINDOWS 的文件夹→SYSTEM32文件夹→taskmgr.exe文件,右击把他发送到桌面上建一“桌面快捷方式”。 你在要关掉A电脑时,只要双击这个快捷方式,就会弹出来一个“WINDWOS任务管理器”窗口,上面有 “关机”命令,点“关机”就行了,当A电脑电源关闭以后,连接自然就断开了。 但这样的远程连接, 是有条件的:A电脑须有独立的 IP ,就是说,A电脑不能是局域网的内部保留 IP,所谓保留IP是指 如 10.XXX.XXX.XXX 或 192.168.XXX.XXX 等地址。如A电脑用的是ADSL,一般来说都是独立的IP,但 如果A用户是几户人家共用一个 ADSL宽带连接,通过一个ADSL共同上网的,那或许就不行了。须在路 由器上作一个“端口映射”设置。注意:A电脑防火墙的影响,有可能连不通。防火墙的缺省设置,一 般是禁止 INTERNET 上的电脑访问它的资源的。因而须开启防火墙的这个设置:允许 INTERNET上的机 器访问本机(A电脑)资源。[shutdown –s –t 0]此命令强制关机,一般不要用, WIN7远程连接前几步设置与WinXP一样。 开始→搜索框中输入MSTSC回车→在弹出的对话框中输入需要连接的计算机的IP→连接→账户密码 →确定不久显示器上出现了另一计算机的桌面,远程桌面连接成功。 教你怎样解除电脑开机密码。此方法仅供交流,严禁作为非法手段使用 方法1在开机时按下F8进入带命令提示符的安全模式输入NET USER+用户名+123456/ADD 可把某用户的密码强行设置为123456 方法2如用户忘记登录密码可 按下方法解决 此法不适用于忘记安装时所设定〔administrator〕的密码 1.在计算机启动时按F8及选Safe Mode With Command Prompt 2.选Administrator后便会跳出Command Prompt的窗口 3.用Net的命令增加一个用户,例:增加一个用户名为alanhkg888,命令语法如下: net user alanhkg888/add 4.将新增用户提升至Administrator的权力,例:提升刚才增 加用户alanhkg888的权力,命令语法如下 net localgroup administrators alanhkg888/add 5.完成上列步骤后重新启动计算机,在 启动画面上便增加了一个用户alanhkg888了,选alanhkg888进入www.shanpow.com_删除Download和DataStore文件夹中的所有文件。 6.登入后在控制台→使用者账户→选忘记密码的用户,然后选移除密码 7.在登入画面中选原来的用户便可不需密码情况下等入(因已移除了) 8.删除刚才新增的用户:在控制台→使用者账户→选alanhkg888,然后选移除账户便可 方法3 1、重新启动Windows XP,在启动画面出现后的瞬间按F8,选择带命令行的安全模 式运行。 2、运行过程停止时,系统列出了超级用户administrator和本地用户owner的选择菜单, 点击administrator,进入命令行模式。 3、键入命令:net user owner 123456/add,强制性将owner用户的口令更改为123456。 若想在此添加某一用户:用户名为abcdef,口令为123456的话,请输入net user abcdef 123456/add,添加后可用net localgroup administrators abcdef/add命令将用户提升为 系统管理组administrators用户,具有超级权限。 4.DOS下删windows\system32\config里面的SAM档就可以了 5.开机后按键盘的Delete键进入BIOS界面。找到User Password选项,其默认为关闭状 态。启动并输入用户密码(1~8位英文或数字)。计算机提示请再输入一遍以确认密码无误, 保存退出后重新启动机器,这时就会在开机时出现密码菜单 方法4我们知道在安装Windows XP过程中,首先是以administrator默认登录,然后会要 求创建一个新账户,以便进入Windows XP时使用此新建账户登录,而且在Windows XP的 登录接口中也只会出现创建的这个用户账号,不会出现administrator,但实际上该 administrator账号还是存在的,且密码为空。 【二】:Windows 7实战经验 Windows 7实战经验:完美解决Windows 7更新失败(Windows Update 错误 80070003) 很多用户反映,为什么Windows 7的自动更新会出显未知错误,导致很多更新都不能正确安装?针对这个问题,在我对自己的Windows 7进行更新的时候,有时也会发生类似的问题,经过研究,已经完美解决,下面给大家解决方案! 如果在检查更新时收到Windows Update错误80070003,则需要删除Windows用于标识计算机更新的临时文件。若要删除临时文件,请停止Windows Update服务,删除临时更新文件,重新启动Windows Update服务,然后再次尝试检查Windows更新。 以下步骤为解决Windows 7更新错误方法,本博客亲测有效。 必须以管理员身份进行登录,才能执行这些步骤。 1.单击打开“管理工具(通过单击“开始”按钮,再依次单击“控制面板”,然后单击“管理工具”。 2.双击“服务”。如果系统提示您输入管理员密码或进行确认,请键入该密码或提供确认。 3.单击“名称”列标题以逆序排列名称。找到“Windows Update”服务,右键单击该服务,然后单击“停止”。 1.打开“计算机”。 2.双击安装Windows的本地硬盘(通常是驱动器C)。 3.双击Windows文件夹,然后双击SoftwareDistribution文件夹。 4.双击打开DataStore文件夹,然后删除该文件夹中的所有文件。如果系统提示您输入管理员密码或进行确认,请键入该密码或提供确认。 5.单击“后退”按钮。在SoftwareDistribution文件夹中,双击打开Download文件夹,删除该文件夹中的所有文件,然后关闭窗口。如果系统提示您输入管理员密码或进行确认,请键入该密码或提供确认。 必须以管理员身份进行登录,才能执行这些步骤。 1.单击打开“管理工具(方法同上)”。 2.双击“服务”。如果系统提示您输入管理员密码或进行确认,请键入该密码或提供确认。 3.单击“名称”列标题以逆序排列名称。找到“Windows Update”服务,右键单击该服务,然后单击“启动”。 4.关闭“服务”窗口和“管理工具”窗口。 完成上面操作,你需要重新更新看看可以成功更新了吗,一般因为我们删除了自动更新的一些文件,如果你仔细观察的话,那些文件大小并不是很小,所以我们再更新的时候等待的时间可能会长一些! 【三】:Win10系统提示“无法完成更新正在撤销更改” 更新win10系统补丁之后,系统会提示“window10无法更新,正在撤销”,需要重启好几次,这该怎么办呢?下面小编就向大家介绍一下windows10系统无法完成更新正在撤销更改的解决方法,欢迎大家参考和学习。 系统更新失败,反复重启还是不行,那是不是下载下来的补丁没用了呢??所以我们先要删除Windows更新的缓存文件!在做以下操作之前,首先我们要确认系统内的windows update & BITS服务设置是否开启。 检查方法: 1、按“Win+R”组合键打开运行,输入“services.msc”,点击确定(如果弹出用户账户控制窗口,我们点击“继续”)。 2、双击打开“Background Intelligent Transfer Services”服务。 3、在选项卡点击“常规”,要保证“启动类型”是“自动”或者“手动”。然后点击“服务状态”“启用”按钮。 4. 重复步骤3分别对“Windows Installer”,“Cryptographic Services”, “software licensing service” 以及“Windows Update”这四项服务进行检查。 解决办法: 1、按“Windows+X”打开“命令提示符(管理员)”。 2、输入“net stop wuauserv”回车(我们先把更新服务停止)。 3、输入”%windir%\SoftwareDistribution“回车(删除Download和DataStore文件夹中的所有文件)。 4、最后输入“net start wuauserv”回车(重新开启系统更新服务)。 完成以上的步骤之后,我们就可以在“Windows Update”中再次尝试检查更新即可。 以上就是windows10系统无法完成更新正在撤销更改的解决方法介绍了。遇到同样问题的用户,可以尝试一下这个方法,如果不行,可以留言,小编会继续寻找其他的解决办法。 【四】:Windows更新失败提示错误码80070003怎么办 Windows7,Windows8.1,Windows10在更新过程中,所更新的程序无法安装,导致更新失败,提示错误码80070003。遇到这种情况,无论再试一次,或重启电脑,更新程序仍无法安装,出现错误码80070003提示。关于这个故障,下面小编就为大家介绍一下具体的解决方法吧,欢迎大家参考和学习。 具体解决方法步骤: 1、在电脑更新过程中,更新失败,程序无法安装,出现错误码80070003的提示。如图1 2、打开控制面板,点击“系统和安全”,打开对话框。如图2 3、在打开的对话框中,点击“管理工具”-双击“服务”,在打开的对话框的下方找到“Windows Update"。(如图3),选择Windows Update,点击界面左上角的”停止“按键,或是单击右键选择”停止“。(如图4),以管理员身份进入,如果提示需要输入秘码,则输入秘码。 4、在C盘,打开”Windows"文件夹,-双击打开“SoftwareDistribution"文件夹,找到下面的2个文件夹。打开”DataStore"文件夹,删除里面所有的文件。反回上一步。如图5.1,再打开"Download"文件夹,删除里面所有的文件。(如图5.2) 5、返回第三步的操作,选择Windows Update,右键单击,选择“启动”。 6、做完上面操作后,安装更新文件就会顺利了。 本篇文章为转载内容。原文链接:https://blog.csdn.net/weixin_42620202/article/details/119158423。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-02-16 16:18:33
137
转载
站内搜索
用于搜索本网站内部文章,支持栏目切换。
知识学习
实践的时候请根据实际情况谨慎操作。
随机学习一条linux命令:
sort -nr 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
历史内容
快速导航到对应月份的历史文章列表。
随便看看
拉到页底了吧,随便看看还有哪些文章你可能感兴趣。
时光飞逝
"流光容易把人抛,红了樱桃,绿了芭蕉。"