前端技术
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
[工厂模式下的对象创建策略与维护优势]的搜索结果
这里是文章列表。热门标签的颜色随机变换,标签颜色没有特殊含义。
点击某个标签可搜索标签相关的文章。
点击某个标签可搜索标签相关的文章。
NodeJS
...近期宣布在其全球多个工厂部署基于 Node.js 和 WebSocket 的实时监控平台,该平台不仅能够实时采集生产设备的运行数据,还能通过智能算法预测潜在故障,从而大幅降低维护成本并提高生产稳定性。 此外,在医疗健康行业,类似的实时监控解决方案也开始崭露头角。一家专注于远程医疗的初创公司最近推出了一款基于 Node.js 的健康管理应用,用户可以通过佩戴智能手环等设备,将心率、血压等生理指标实时上传至云端,医生则可随时随地查看患者的健康状况并提供个性化建议。这一创新模式不仅改善了医疗服务的可及性,也为慢性病管理带来了新的可能性。 值得注意的是,随着《个人信息保护法》等相关法律法规的出台,企业在开发此类实时监控系统时必须格外注意数据安全与隐私保护。一方面,企业需要严格遵守数据收集、存储和传输的相关规定;另一方面,还需加强技术手段,如加密通信、匿名化处理等,以防止敏感信息泄露。正如某网络安全专家所言:“技术本身没有善恶之分,关键在于如何正确使用。”因此,在追求技术创新的同时,企业应当始终将合规性和安全性放在首位,确保技术进步真正造福于社会。 总之,Node.js 和 WebSocket 技术的应用前景十分广阔,但同时也面临着诸多挑战。只有不断探索新技术、新方法,同时坚守法律底线和社会责任,才能让这一技术更好地服务于各行各业的发展需求。
2025-05-06 16:24:48
82
清风徐来
Mahout
...出各种有价值的信息和模式。这玩意儿一出手,数据处理界的难题就被它玩转得飞起,简直是个大数据时代的超级英雄呢!而Apache Spark Streaming,则是为实时数据流提供高性能处理的框架。哎呀,兄弟!把这两样技术给整到一块儿用,那效果简直不要太棒!不仅能快速消化那些源源不断的数据洪流,还能帮咱们做出超明智的决定,简直就是开挂的存在嘛!本文旨在探索Mahout与Spark Streaming如何协同工作,为实时流数据分析提供强大的解决方案。 2. Mahout概述 Mahout是一个基于Hadoop的机器学习库,旨在利用分布式计算资源来加速大规模数据集上的算法执行。哎呀,这个家伙可真厉害!它能用上各种各样的机器学习魔法,比如说分门别类的技巧(就是咱们说的分类)、把相似的东西归到一块儿的本事(聚类)还有能给咱们推荐超棒东西的神奇技能(推荐系统)。而且,它最擅长的就是对付那些海量的数据,就像大鱼吃小鱼一样,毫不费力就能搞定!通过Mahout,我们可以构建复杂的模型来挖掘数据中的模式和关系,从而驱动业务决策。 3. Spark Streaming简介 Apache Spark Streaming是Spark生态系统的一部分,专为实时数据流处理设计。哎呀,这个玩意儿简直就是程序员们的超级神器!它能让咱这些码农兄弟们轻松搞定那些超快速、高效率的实时应用,你懂的,就是那种分秒必争、数据飞速流转的那种。想象一下,一秒钟能处理几千条数据,那感觉简直不要太爽啊!就像是在玩转数据的魔法世界,每一次点击都是对速度与精准的极致追求。这不就是我们程序员的梦想吗?在数据的海洋里自由翱翔,每一刻都在创造奇迹!Spark Streaming的精髓就像个魔术师,能把连续不断的水流(数据流)变换成小段的小溪(微批次)。这小溪再通过Spark这个强大的分布式计算平台,就像是在魔法森林里跑的水车,一边转一边把水(数据)处理得干干净净。这样一来,咱们就能在实时中捕捉到信息的脉动,做出快速反应,既高效又灵活! 4. Mahout与Spark Streaming的集成 为了将Mahout的机器学习能力与Spark Streaming的实时处理能力结合起来,我们需要创建一个流水线,使得Mahout可以在实时数据流上执行分析任务。这可以通过以下步骤实现: - 数据接入:首先,我们需要将实时数据流接入Spark Streaming。这可以通过定义一个DStream(Data Stream)对象来完成,该对象代表了数据流的抽象表示。 scala import org.apache.spark.streaming._ import org.apache.spark.streaming.dstream._ val sparkConf = new SparkConf().setAppName("RealtimeMahoutAnalysis").setMaster("local[2]") val sc = new SparkContext(sparkConf) valssc = new StreamingContext(sc, Seconds(1)) // 创建StreamingContext,时间间隔为1秒 val inputStream = TextFileStream("/path/to/your/data") // 假设数据来自文件系统 val dstream = inputStream foreachRDD { rdd => rdd.map { line => val fields = line.split(",") (fields(0), fields.slice(1, fields.length)) } } - Mahout模型训练:然后,我们可以使用Mahout中的算法对数据进行预处理和建模。例如,假设我们想要进行用户行为的聚类分析,可以使用Mahout的KMeans算法。 scala import org.apache.mahout.cf.taste.hadoop.recommender.KNNRecommender import org.apache.mahout.cf.taste.impl.model.file.FileDataModel import org.apache.mahout.cf.taste.impl.neighborhood.ThresholdUserNeighborhood import org.apache.mahout.cf.taste.impl.recommender.GenericUserBasedRecommender import org.apache.mahout.cf.taste.impl.similarity.PearsonCorrelationSimilarity import org.apache.mahout.math.RandomAccessSparseVector import org.apache.hadoop.conf.Configuration val dataModel = new FileDataModel(new File("/path/to/your/data.csv")) val neighborhood = new ThresholdUserNeighborhood(0.5, dataModel, new Configuration()) val similarity = new PearsonCorrelationSimilarity(dataModel) val recommender = new GenericUserBasedRecommender(dataModel, neighborhood, similarity) val recommendations = dstream.map { (user, ratings) => val userVector = new RandomAccessSparseVector(ratings.size()) for ((itemId, rating) <- ratings) { userVector.setField(itemId.toInt, rating.toDouble) } val recommendation = recommender.recommend(user, userVector) (user, recommendation.map { (itemId, score) => (itemId, score) }) } - 结果输出:最后,我们可以将生成的推荐结果输出到合适的目标位置,如日志文件或数据库,以便后续分析和应用。 scala recommendations.foreachRDD { rdd => rdd.saveAsTextFile("/path/to/output") } 5. 总结与展望 通过将Mahout与Spark Streaming集成,我们能够构建一个强大的实时流数据分析平台,不仅能够实时处理大量数据,还能利用Mahout的高级机器学习功能进行深入分析。哎呀,这个融合啊,就像是给数据分析插上了翅膀,能即刻飞到你眼前,又准确得不得了!这样一来,咱们做决定的时候,心里那根弦就更紧了,因为有它在身后撑腰,决策那可是又稳又准,妥妥的!哎呀,随着科技车轮滚滚向前,咱们的Mahout和Spark Streaming这对好搭档,未来肯定会越来越默契,联手为我们做决策时,用上实时数据这个大宝贝,提供更牛逼哄哄的武器和方法!想象一下,就像你用一把锋利的剑,能更快更准地砍下胜利的果实,这俩家伙在数据战场上,就是那把超级厉害的宝剑,让你的决策快人一步,精准无比! --- 以上内容是基于实际的编程实践和理论知识的融合,旨在提供一个从概念到实现的全面指南。哎呀,当真要将这个系统或者项目实际铺展开来的时候,咱们得根据手头的实际情况,比如数据的个性、业务的流程和咱们的技术底子,来灵活地调整策略,让一切都能无缝对接,发挥出最大的效用。就像是做菜,得看食材的新鲜度,再搭配合适的调料,才能做出让人满意的美味佳肴一样。所以,别死板地照搬方案,得因地制宜,因材施教,这样才能确保我们的工作既高效又有效。
2024-09-06 16:26:39
60
月影清风
Docker
... 比如,如果你想快速创建一个新的MySQL容器,只需要打开Portainer的Web界面,点击“Add Container”,然后填写几个基本信息即可: yaml image: mysql:5.7 name: my-mysql ports: - "3306:3306" volumes: - /data/mysql:/var/lib/mysql environment: MYSQL_ROOT_PASSWORD: rootpassword 这段YAML配置文件描述了一个MySQL容器的基本参数。Portainer会自动帮你解析并生成对应的Docker命令。是不是超方便? 另外,Portainer还有一个特别棒的功能——实时监控。你打开页面就能看到每个“小房子”(就是容器)里用掉的CPU和内存情况,而且还能像穿越空间一样,去访问别的机器上跑着的那些“小房子”(Docker实例)。这种功能对于运维人员来说简直是福音! --- 4. Rancher 企业级的容器编排利器 如果你是一个团队协作的开发者,或者正在运营一个大规模的服务集群,那么Rancher可能是你的最佳选择。它不仅仅是一个Docker管理工具,更是一个完整的容器编排平台。 Rancher的核心优势在于它的“多集群管理”能力。想象一下,你的公司有好几台服务器,分别放在地球上的不同角落,有的在美国,有的在欧洲,还有的在中国。每台服务器上都跑着各种各样的服务,比如网站、数据库啥的。这时候,Rancher就派上用场了!它就像一个超级贴心的小管家,让你不用到处切换界面,在一个地方就能轻松搞定所有服务器和服务的管理工作,省时又省力! 举个例子,如果你想在Rancher中添加一个新的节点,只需要几步操作即可完成: 1. 登录Rancher控制台。 2. 点击“Add Cluster”按钮。 3. 输入目标节点的信息(IP地址、SSH密钥等)。 4. 等待几分钟,Rancher会自动为你安装必要的组件。 一旦节点加入成功,你就可以直接在这个界面上部署应用了。比如,用Kubernetes部署一个Redis集群: bash kubectl create deployment redis --image=redis:alpine kubectl expose deployment redis --type=LoadBalancer --port=6379 虽然这条命令看起来很简单,但它背后实际上涉及到了复杂的调度逻辑和网络配置。而Rancher把这些复杂的事情封装得很好,让我们可以专注于业务本身。 --- 5. Traefik 反向代理与负载均衡的最佳拍档 最后要介绍的是Traefik,这是一个轻量级的反向代理工具,专门用来处理HTTP请求的转发和负载均衡。它最厉害的地方啊,就是能跟Docker完美地融为一体,还能根据容器上的标签,自动调整路由规则呢! 比如说,你有两个服务分别监听在8080和8081端口,现在想通过一个域名访问它们。只需要给这两个容器加上相应的标签: yaml labels: - "traefik.enable=true" - "traefik.http.routers.service1.rule=Host(service1.example.com)" - "traefik.http.services.service1.loadbalancer.server.port=8080" - "traefik.http.routers.service2.rule=Host(service2.example.com)" - "traefik.http.services.service2.loadbalancer.server.port=8081" 这样一来,当用户访问service1.example.com时,Traefik会自动将请求转发到监听8080端口的容器;而访问service2.example.com则会指向8081端口。这种方式不仅高效,还极大地减少了配置的工作量。 --- 6. 总结 找到最适合自己的工具 好了,到这里咱们已经聊了不少关于服务器管理工具的话题。从Docker到Portainer,再到Rancher和Traefik,每一种工具都有其独特的优势和适用场景。 我的建议是,先根据自己的需求确定重点。要是你只想弄个小玩意儿,图个省事儿快点搞起来,那用Docker配个Portainer就完全够用了。但要是你们团队一起干活儿,或者要做大范围的部署,那Rancher这种专业的“老司机工具”就得安排上啦! 当然啦,技术的世界永远没有绝对的答案。其实啊,很多时候你会发现,最适合你的工具不一定是最火的那个,而是那个最合你心意、用起来最顺手的。就像穿鞋一样,别人觉得好看的根本不合脚,而那双不起眼的小众款却让你走得又稳又舒服!所以啊,在用这些工具的时候,别光顾着看,得多动手试试,边用边记下自己的感受和想法,这样你才能真的搞懂它们到底有啥门道! 好了,今天的分享就到这里啦!如果你还有什么问题或者想法,欢迎随时留言交流哦~咱们下次再见啦!
2025-04-16 16:05:13
98
月影清风_
ZooKeeper
...的各种操作请求(比如创建节点、删除节点等)。嘿嘿,想象一下,这就好比一个超挤的电梯,已经装满了人,再有人想挤进去肯定会被拒之门外啦!ZooKeeper也一样,当它的小“队伍”排满了的时候,新来的请求就别想加塞儿了,直接就被它无情地“拒绝”了,然后还甩给你一个“异常”的小牌子,意思是说:“兄弟,这儿真的装不下了!”这种情况通常发生在高并发场景下,或者是网络延迟导致请求堆积。 为了更好地理解这个问题,我们可以看看下面这段代码: java import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.CreateMode; public class ZookeeperExample { public static void main(String[] args) throws Exception { // 创建ZooKeeper实例 ZooKeeper zk = new ZooKeeper("localhost:2181", 5000, event -> { System.out.println("ZooKeeper event: " + event); }); // 创建一个节点 String nodePath = zk.create("/testNode", "data".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); System.out.println("Node created at path: " + nodePath); // 关闭连接 zk.close(); } } 在这个简单的例子中,我们尝试创建一个ZooKeeper实例并创建一个节点。如果这个时候ZooKeeper的队列满了,就会抛出CommitQueueFullException。所以,接下来我们要做的就是想办法避免这种情况的发生。 --- 二、为什么会出现CommitQueueFullException? 在深入讨论解决方案之前,我觉得有必要先搞清楚为什么会发生这种异常。其实,这背后涉及到了ZooKeeper的一些设计细节。 首先,ZooKeeper的队列大小是由配置文件中的zookeeper.commitlog.capacity参数决定的。默认情况下,这个值是比较小的,可能只有几兆字节。想象一下,你的应用像一个忙碌的快递站,接到了无数订单(也就是那些请求)。但要是快递小哥忙得顾不上送货,订单就会越堆越多,很快整个站点就塞满了,连下一份订单都没地方放了! 其次,网络环境也是一个重要因素。有时候,客户端和服务端之间的网络延迟会导致请求堆积。就算客户端那边请求没那么频繁,但要是服务端反应慢了,照样会出问题啊。 最后,还有一个容易被忽视的原因就是客户端的连接数过多。每个连接都会占用一定的资源,包括内存和CPU。要是连上的用户太多了,但服务器的“体力”又不够强(比如内存、CPU之类的资源有限),那它就很容易“忙不过来”,导致请求都排着队等着,根本处理不完。 说到这里,我忍不住想吐槽一下自己曾经犯过的错误。嘿,有次我在测试环境里弄了个能扛大流量的程序,结果发现ZooKeeper老是蹦出个叫“CommitQueueFullException”的错误,烦得不行!我当时就纳闷了:“我明明设了个挺合理的线程池大小啊,怎么还出问题了呢?”后来一查才发现,坏事了,是客户端的连接数配少了,结果请求都堵在那儿了,就像高速公路堵车一样。真是教训深刻啊! --- 三、如何优雅地处理CommitQueueFullException? 既然知道了问题的根源,那接下来就要谈谈具体的解决办法了。我觉得可以从以下几个方面入手: 1. 调整队列大小 最直接的办法当然是增大队列的容量。通过修改zookeeper.commitlog.capacity参数,可以让ZooKeeper拥有更大的缓冲空间。其实嘛,这个方法也不是啥灵丹妙药,毕竟咱们手头的硬件资源就那么多,要是傻乎乎地把队列弄得太长,说不定反而会惹出别的麻烦,比如让系统跑得更卡之类的。 代码示例: properties zookeeper.commitlog.capacity=10485760 上面这段配置文件的内容表示将队列大小调整为10MB。你可以根据实际情况进行调整。 2. 优化客户端逻辑 很多时候,CommitQueueFullException并不是因为服务器的问题,而是客户端的请求模式不合理造成的。比如说,你是否可以合并多个小请求为一个大请求?或者是否可以采用批量操作的方式减少请求次数? 举个例子,假设你在做一个日志采集系统,每天需要向ZooKeeper写入成千上万个临时节点。与其每次都往一个节点里写东西,不如一口气往多个节点里写,这样能大大减少你发出的请求次数,省事儿又高效! 代码示例: java List nodesToCreate = Arrays.asList("/node1", "/node2", "/node3"); List createdNodes = zk.create("/batch/", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL, nodesToCreate.size()); System.out.println("Created nodes: " + createdNodes); 在这段代码中,我们一次性创建了三个临时节点,而不是分别调用三次create()方法。这样的做法不仅减少了请求次数,还提高了效率。 3. 增加服务器资源 如果以上两种方法都不能解决问题,那么可能就需要考虑升级服务器硬件了。比如增加内存、提升CPU性能,甚至更换更快的磁盘。当然,这通常是最后的选择,因为它涉及到成本和技术难度。 4. 使用异步API ZooKeeper提供了同步和异步两种API,其中异步API可以在一定程度上缓解CommitQueueFullException的问题。异步API可酷了!你提交个请求,它立马给你返回结果,根本不用傻等那个响应回来。这样一来啊,就相当于给任务队列放了个假,压力小了很多呢! 代码示例: java import org.apache.zookeeper.AsyncCallback.StringCallback; public class AsyncExample implements StringCallback { @Override public void processResult(int rc, String path, Object ctx, String name) { if (rc == 0) { System.out.println("Node created successfully at path: " + name); } else { System.err.println("Failed to create node with error code: " + rc); } } public static void main(String[] args) throws Exception { ZooKeeper zk = new ZooKeeper("localhost:2181", 5000, null); zk.createAsync("/asyncTest", "data".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT, new AsyncExample(), null); } } 在这段代码中,我们使用了createAsync()方法来异步创建节点。相比于同步版本,这种方式不会阻塞主线程,从而降低了队列满的风险。 --- 四、总结与展望 通过今天的探讨,我相信大家都对CommitQueueFullException有了更深刻的理解。嘿,别被这个错误吓到!其实啊,它也没那么可怕。只要你找到对的方法,保证分分钟搞定,就跟玩儿似的! 回顾整个过程,我觉得最重要的是要保持冷静和耐心。遇到技术难题的时候啊,别慌!先搞清楚它到底是个啥问题,就像剥洋葱一样,一层层搞明白本质。接着呢,就一步一步地去找解决的办法,慢慢来,总能找到出路的!就像攀登一座高山一样,每一步都需要脚踏实地。 最后,我想鼓励大家多动手实践。理论固然重要,但真正的成长来自于不断的尝试和失败。希望大家能够在实际项目中运用今天学到的知识,创造出更加优秀的应用! 好了,今天的分享就到这里啦!如果你还有什么疑问或者想法,欢迎随时交流哦~
2025-03-16 15:37:44
11
林中小径
转载文章
...atabases; 创建新数据库,设置编码方式utf8 create database demo2 default charset utf8; 显示创建数据的语句 show create database demo2; 删除数据库 drop database demo2; 选择使用指定的数据库 use demo1; 查看库中所有表 show tables; 创建表 create table book(bid int(4) primary key comment '书id', bname varchar(50) comment '书名',pub varchar(50) comment '出版社',author varchar(50) comment '作者' )engine=myisam charset=utf8; 所有字段名,使用,所有的字符串使用''或者"" 查看建表语句 show create table book; 查看表结构 desc book; 修改表名 rename table book to book1; 修改表属性 ,引擎和字符集 alter table book1 engine=innodb charset=utf8; 添加字段 first after alter table book1 add(type varchar(20) 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
转载
转载文章
...le高级消息队列AQ创建消息负荷payload 创建队列表 创建队列并启动 队列的停止和删除 入队消息 出队消息 二Java使用JMS监听并处理Oracle AQ队列创建连接参数类 创建消息转换类 主类进行消息处理 三监控表记录变化通知Java创建表 创建存储过程 创建触发器 环境说明 本实验环境基于Oracle 12C和JDK1.8,其中Oracle 12C支持多租户特性,相较于之前的Oracle版本,使用‘C用户名‘表示用户,例如如果数据库用户叫kevin,则登陆时使用Ckevin进行登陆。 一、Oracle高级消息队列AQ Oracle AQ是Oracle中的消息队列,是Oracle中的一种高级应用,每个版本都在不断的加强,使用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
转载
转载文章
...、响应式 1. 监听对象的操作 2. Proxy基本使用 2.1 Proxy 的 set 和 get 捕获器 2.2 Proxy 所有捕获器 (13个) 2.3 Proxy 的 construct 和 apply 3. Reflect 3.1 Reflect 的作用 3.2 Reflect 的常见方法 3.3 Reflect 的使用 3.4 Receiver的作用 3.5 Reflect 的 construct 4. 响应式 4.1 什么是响应式? 4.2 响应式函数设计 4.3 响应式依赖的收集 4.4 监听对象的变化 4.5 对象的依赖管理 4.6 对 Depend 重构 4.7 创建响应式对象 4.8 Vue2 响应式原理 Proxy 、Relect、响应式 1. 监听对象的操作 需求:有一个对象,我们希望监听这个对象中的属性被设置或获取的过程 可以通过属性描述符中的存储属性描述符来做到 这段代码就利用了 Object.defineProperty 的存储属性描述符来对属性的操作进行监听 const obj = {name: 'why',age: 18}Object.keys(obj).forEach((key) => {let value = obj[key]Object.defineProperty(obj, key, {get: function () {console.log(监听到obj对象的${key}属性被访问了)return value},set: function (newValue) {console.log(监听到obj对象的${key}属性被设置值)value = newValue} })})obj.name = 'kobe'obj.age = 30console.log(obj.name)console.log(obj.age)/ 监听到obj对象的name属性被设置值监听到obj对象的age属性被设置值监听到obj对象的name属性被访问了kobe监听到obj对象的age属性被访问了30/ 属性描述符监听对象的缺点: 首先,Object.defineProperty 设计的初衷,不是为了去监听截止一个对象中所有的属性的 我们在定义某些属性的时候,初衷其实是定义普通的属性,但是后面我们强行将它变成了数据属性描述符 其次,如果我们想监听更加丰富的操作,比如新增属性、删除属性,那么 Object.defineProperty 是无能为力的 所以我们要知道,存储数据描述符设计的初衷并不是为了去监听一个完整的对象 Ps: 原来的对象是 数据属性描述符,通过 Object.defineProperty 变成了 访问属性描述符 2. Proxy基本使用 在ES6中,新增了一个Proxy类,这个类从名字就可以看出来,是用于帮助我们创建一个代理的: 也就是说,如果我们希望监听一个对象的相关操作,那么我们可以先创建一个代理对象(Proxy对象) 之后对该对象的所有操作,都通过代理对象来完成,代理对象可以监听我们想要对原对象进行哪些操作 将上面的案例用 Proxy 来实现一次: 首先,我们需要 new Proxy 对象,并且传入需要侦听的对象以及一个处理对象,可以称之为 handler; const p = new Proxy(target, handler) 其次,我们之后的操作都是直接对 Proxy 的操作,而不是原有的对象,因为我们需要在 handler 里面进行侦听 const obj = {name: 'why',age: 18}const objProxy = new Proxy(obj, {// 获取值时的捕获器get: function (target, key) {console.log(监听到obj对象的${key}属性被访问了)return target[key]},// 设置值时的捕获器set: function (target, key, newValue) {console.log(监听到obj对象的${key}属性被设置值)target[key] = newValue} })console.log(objProxy.name)console.log(objProxy.age)objProxy.name = 'kobe'objProxy.age = 30console.log(obj.name)console.log(obj.age)/ 监听到obj对象的name属性被访问了why监听到obj对象的age属性被访问了18监听到obj对象的name属性被设置值监听到obj对象的age属性被设置值kobe30/ 2.1 Proxy 的 set 和 get 捕获器 如果我们想要侦听某些具体的操作,那么就可以在 handler 中添加对应的捕捉器(Trap) set 和 get 分别对应的是函数类型 set 函数有四个参数: target:目标对象(侦听的对象) property:将被设置的属性 key value:新属性值 receiver:调用的代理对象 get 函数有三个参数 target:目标对象(侦听的对象) property:被获取的属性 key receiver:调用的代理对象 2.2 Proxy 所有捕获器 (13个) handler.getPrototypeOf() Object.getPrototypeOf 方法的捕捉器 handler.setPrototypeOf() Object.setPrototypeOf 方法的捕捉器 handler.isExtensible() Object.isExtensible 方法的捕捉器 handler.preventExtensions() Object.preventExtensions 方法的捕捉器 handler.getOwnPropertyDescriptor() Object.getOwnPropertyDescriptor 方法的捕捉器 handler.defineProperty() Object.defineProperty 方法的捕捉器 handler.ownKeys() Object.getOwnPropertyNames 方法和 Object.getOwnPropertySymbols 方法的捕捉器 handler.has() in 操作符的捕捉器 handler.get() 属性读取操作的捕捉器 handler.set() 属性设置操作的捕捉器 handler.deleteProperty() delete 操作符的捕捉器 handler.apply() 函数调用操作的捕捉器 handler.construct() new 操作符的捕捉器 const obj = {name: 'why',age: 18}const objProxy = new Proxy(obj, {// 获取值时的捕获器get: function (target, key) {console.log(监听到obj对象的${key}属性被访问了)return target[key]},// 设置值时的捕获器set: function (target, key, newValue) {console.log(监听到obj对象的${key}属性被设置值)target[key] = newValue},// 监听 in 的捕获器has: function (target, key) {console.log(监听到obj对象的${key}属性的in操作)return key in target},// 监听 delete 的捕获器deleteProperty: function (target, key) {console.log(监听到obj对象的${key}属性的delete操作)delete target[key]} })// in 操作符console.log('name' in objProxy)// delete 操作delete objProxy.name/ 监听到obj对象的name属性的in操作true监听到obj对象的name属性的delete操作/ 2.3 Proxy 的 construct 和 apply 到捕捉器中还有 construct 和 apply,它们是应用于函数对象的 function foo() {console.log('调用了 foo')}const fooProxy = new Proxy(foo, {apply: function (target, thisArg, argArray) {console.log(对 foo 函数进行了 apply 调用)target.apply(thisArg, argArray)},construct: function (target, argArray, newTarget) {console.log(对 foo 函数进行了 new 调用)return new target(...argArray)} })fooProxy.apply({}, ['abc', 'cba'])new fooProxy('abc', 'cba')/ 对 foo 函数进行了 apply 调用调用了 foo对 foo 函数进行了 new 调用调用了 foo/ 3. Reflect 3.1 Reflect 的作用 Reflect 也是 ES6 新增的一个 API,它是一个对象,字面的意思是反射 Reflect 的作用: 它主要提供了很多操作 JavaScript 对象的方法,有点像 Object 中操作对象的方法 比如 Reflect.getPrototypeOf(target) 类似于 Object.getPrototypeOf() 比如 Reflect.defineProperty(target, propertyKey, attributes) 类似于 Object.defineProperty() 如果我们有 Object 可以做这些操作,那么为什么还需要有Reflect这样的新增对象呢? 这是因为在早期的 ECMA 规范中没有考虑到这种对 对象本身 的操作如何设计会更加规范,所以将这些 API 放到了 Object上面 但是 Object 作为一个构造函数,这些操作实际上放到它身上并不合适 另外还包含一些类似于 in、delete 操作符,让 JS 看起来是会有一些奇怪的 所以在 ES6 中新增了 Reflect,让我们这些操作都集中到了 Reflect 对象上 那么 Object 和 Reflect 对象之间的 API 关系,可以参考 MDN 文档: 比较 Reflect 和 Object 方法 3.2 Reflect 的常见方法 Reflect中有哪些常见的方法呢?它和Proxy是一一对应的,也是13个 Reflect.getPrototypeOf(target) 类似于 Object.getPrototypeOf() Reflect.setPrototypeOf(target, prototype) 设置对象原型的函数. 返回一个 Boolean, 如果更新成功,则返回 true Reflect.isExtensible(target) 类似于 Object.isExtensible() Reflect.preventExtensions(target) 类似于 Object.preventExtensions() , 返回一个 Boolean Reflect.getOwnPropertyDescriptor(target, propertyKey) 类似于 Object.getOwnPropertyDescriptor() , 如果对象中存在该属性,则返回对应的属性描述符, 否则返回 undefined Reflect.defineProperty(target, propertyKey, attributes) 和 Object.defineProperty() 类似, 如果设置成功就会返回 true Reflect.ownKeys(target) 返回一个包含所有自身属性(不包含继承属性)的数组 (类似于 Object.keys(), 但不会受 enumerable 影响) Reflect.has(target, propertyKey) 判断一个对象是否存在某个属性,和 in 运算符 的功能完全相同 Reflect.get(target, propertyKey[, receiver]) 获取对象身上某个属性的值,类似于 target[name] Reflect.set(target, propertyKey, value[, receiver]) 将值分配给属性的函数,返回一个 Boolean,如果更新成功,则返回 true Reflect.deleteProperty(target, propertyKey) 作为函数的 delete 操作符,相当于执行 delete target[name] Reflect.apply(target, thisArgument, argumentsList) 对一个函数进行调用操作,同时可以传入一个数组作为调用参数。和 Function.prototype.apply() 功能类似 Reflect.construct(target, argumentsList[, newTarget]) 对构造函数进行 new 操作,相当于执行 new target(...args) 3.3 Reflect 的使用 那么我们可以将之前Proxy案例中对原对象的操作,都修改为Reflect来操作 const obj = {name: 'why',age: 18}const objProxy = new Proxy(obj, {get: function (target, key) {console.log(监听到obj对象的${key}属性被访问了)return Reflect.get(target, key)// return target[key] // 对原来对象进行了直接操作},set: function (target, key, newValue) {console.log(监听到obj对象的${key}属性被设置值)Reflect.set(target, key, newValue)// target[key] = newValue // 对原来对象进行了直接操作} })objProxy.name = 'kobe'console.log(objProxy.name)/ 监听到obj对象的name属性被设置值监听到obj对象的name属性被访问了kobe/ 3.4 Receiver的作用 我们发现在使用getter、setter的时候有一个receiver的参数,它的作用是什么呢? 如果我们的源对象(obj)有 setter 、getter 的访问器属性,那么可以通过 receiver 来改变里面的 this const obj = {_name: 'why',get name() {return this._name // 不使用receiver, _name属性的操作不会被objProxy代理,因为this指向obj},set name(newValue) {this._name = newValue} }const objProxy = new Proxy(obj, {get: function (target, key, receiver) {// receiver 是创建出来的代理对象console.log('get 方法被访问-------', key, receiver)console.log(objProxy === receiver) // truereturn Reflect.get(target, key, receiver)},set: function (target, key, newValue, receiver) {Reflect.set(target, key, newValue, receiver)} })objProxy.name = 'kobe'console.log(objProxy.name) // kobe/ get 方法被访问------- name { _name: 'kobe', name: [Getter/Setter] }trueget 方法被访问------- _name { _name: 'kobe', name: [Getter/Setter] }truekobe/ 3.5 Reflect 的 construct function Student(name, age) {this.name = namethis.age = age}function Teacher() {}const stu = new Student('why', 18)console.log(stu)console.log(stu.__proto__ === Student.prototype)/ Student { name: 'why', age: 18 }true/// 执行 Student 函数中的内容,但是创建出来的对象是 Teacher 对象const teacher = Reflect.construct(Student, ['why', 18], Teacher)console.log(teacher)console.log(teacher.__proto__ === Teacher.prototype)/ Teacher { name: 'why', age: 18 }true/ 4. 响应式 4.1 什么是响应式? 先来看一下响应式意味着什么?我们来看一段代码: m 有一个初始化的值,有一段代码使用了这个值; 那么在 m 有一个新的值时,这段代码可以自动重新执行 let m = 0// 一段代码console.log(m)console.log(m 2)console.log(m 2)m = 200 上面的这样一种可以自动响应数据变量的代码机制,我们就称之为是响应式的 对象的响应式 4.2 响应式函数设计 首先,执行的代码中可能不止一行代码,所以我们可以将这些代码放到一个函数中: 那么问题就变成了,当数据发生变化时,自动去执行某一个函数; 但是有一个问题:在开发中是有很多的函数的,如何区分一个函数需要响应式,还是不需要响应式呢? 很明显,下面的函数中 foo 需要在 obj 的 name 发生变化时,重新执行,做出相应; bar 函数是一个完全独立于 obj 的函数,它不需要执行任何响应式的操作; // 对象的响应式const obj = {name: 'why',age: 18}function foo() {const newName = obj.nameconsole.log('你好啊,李银河')console.log('Hello World')console.log(obj.name)}function bar() {console.log('普通的其他函数')console.log('这个函数不需要有任何的响应式')}obj.name = 'kobe' // name 发生改变时候 foo 函数执行 响应式函数的实现 watchFn 如何区分响应式函数? 这个时候我们封装一个新的函数 watchFn 凡是传入到 watchFn 的函数,就是需要响应式的 其他默认定义的函数都是不需要响应式的 / 封装一个响应式的函数 /let reactiveFns = []function watchFn(fn) {reactiveFns.push(fn)}// 对象的响应式const obj = {name: 'why',age: 18}watchFn(function foo() {const newName = obj.nameconsole.log('你好啊,李银河')console.log('Hello World')console.log(obj.name)})watchFn(function demo() {console.log(obj.name, 'demo function ---------')})function bar() {console.log('普通的其他函数')console.log('这个函数不需要有任何的响应式')}obj.name = 'kobe' // name 发生改变时候 foo 函数执行reactiveFns.forEach((fn) => {fn()}) 4.3 响应式依赖的收集 目前收集的依赖是放到一个数组中来保存的,但是这里会存在数据管理的问题: 在实际开发中需要监听很多对象的响应式 这些对象需要监听的不只是一个属性,它们很多属性的变化,都会有对应的响应式函数 不可能在全局维护一大堆的数组来保存这些响应函数 所以要设计一个类,这个类用于管理某一个对象的某一个属性的所有响应式函数: 相当于替代了原来的简单 reactiveFns 的数组; class Depend {constructor() {this.reactiveFns = []}addDepend(reactiveFn) {this.reactiveFns.push(reactiveFn)}notify() {this.reactiveFns.forEach((fn) => {fn()})} }const depend = new Depend()function watchFn(fn) {depend.addDepend(fn)}// 对象的响应式const obj = {name: 'why', // depend 对象age: 18 // depend 对象}watchFn(function foo() {const newName = obj.nameconsole.log('你好啊,李银河')console.log('Hello World')console.log(obj.name)})watchFn(function demo() {console.log(obj.name, 'demo function ---------')})function bar() {console.log('普通的其他函数')console.log('这个函数不需要有任何的响应式')}obj.name = 'kobe'depend.notify() 4.4 监听对象的变化 那么接下来就可以通过之前的方式来监听对象的变化: 方式一:通过 Object.defineProperty 的方式(vue2采用的方式); 方式二:通过 new Proxy 的方式(vue3采用的方式); 我们这里先以Proxy的方式来监听 class Depend {constructor() {this.reactiveFns = []}addDepend(reactiveFn) {this.reactiveFns.push(reactiveFn)}notify() {this.reactiveFns.forEach((fn) => {fn()})} }const depend = new Depend()function watchFn(fn) {depend.addDepend(fn)}// 对象的响应式const obj = {name: 'why', // depend 对象age: 18 // depend 对象}// 监听对象的属性变化:Proxy(vue3)/Object.defineProperty(vue2)const objProxy = new Proxy(obj, {get: function (target, key, receiver) {return Reflect.get(target, key, receiver)},set: function (target, key, newValue, receiver) {Reflect.set(target, key, newValue, receiver)depend.notify()} })watchFn(function foo() {const newName = objProxy.nameconsole.log('你好啊,李银河')console.log('Hello World')console.log(objProxy.name)})watchFn(function demo() {console.log(objProxy.name, 'demo function ---------')})objProxy.name = 'kobe'objProxy.name = 'james'/ 你好啊,李银河Hello Worldkobekobe demo function ---------你好啊,李银河Hello Worldjamesjames demo function ---------/ 4.5 对象的依赖管理 目前是创建了一个 Depend 对象,用来管理对于 name 变化需要监听的响应函数: 但是实际开发中我们会有不同的对象,另外会有不同的属性需要管理; 如何可以使用一种数据结构来管理不同对象的不同依赖关系呢? 在前面我们刚刚学习过 WeakMap,并且在学习 WeakMap 的时候我讲到了后面通过 WeakMap 如何管理这种响应式的数据依赖: 实现 可以写一个 getDepend 函数专门来管理这种依赖关系 / 封装一个获取depend的函数 /const taregtMap = new WeakMap()function getDepend(target, key) {// 根据target对象获取mapconst map = taregtMap.get(target)if (!map) {map = new Map()taregtMap.set(target, map)}// 根据key获取depend对象const depend = map.get(key)if (!depend) {depend = new Depend()map.set(key, depend)}return depend}// 监听对象的属性变化:Proxy(vue3)/Object.defineProperty(vue2)const objProxy = new Proxy(obj, {get: function (target, key, receiver) {return Reflect.get(target, key, receiver)},set: function (target, key, newValue, receiver) {Reflect.set(target, key, newValue, receiver)const depend = getDepend(target, key)depend.notify()} }) 正确的依赖收集 我们之前收集依赖的地方是在 watchFn 中: 但是这种收集依赖的方式我们根本不知道是哪一个 key 的哪一个 depend 需要收集依赖; 只能针对一个单独的 depend 对象来添加你的依赖对象; 那么正确的应该是在哪里收集呢?应该在我们调用了 Proxy 的 get 捕获器时 因为如果一个函数中使用了某个对象的 key,那么它应该被收集依赖 / 封装一个响应式函数 /let activeReactviceFn = nullfunction watchFn(fn) {activeReactviceFn = fnfn()activeReactviceFn = null}/ 封装一个获取depend的函数 /const taregtMap = new WeakMap()function getDepend(target, key) {// 根据target对象获取maplet map = taregtMap.get(target)if (!map) {map = new Map()taregtMap.set(target, map)}// 根据key获取depend对象let depend = map.get(key)if (!depend) {depend = new Depend()map.set(key, depend)}return depend}// 监听对象的属性变化:Proxy(vue3)/Object.defineProperty(vue2)const objProxy = new Proxy(obj, {get: function (target, key, receiver) {// 根据 target key 获取对应的 depnedconst depend = getDepend(target, key)// 给 depend 对象中添加响应式函数activeReactviceFn && depend.addDepend(activeReactviceFn)return Reflect.get(target, key, receiver)},set: function (target, key, newValue, receiver) {Reflect.set(target, key, newValue, receiver)const depend = getDepend(target, key)depend.notify()} }) 4.6 对 Depend 重构 两个问题: 问题一:如果函数中有用到两次 key,比如 name,那么这个函数会被收集两次 问题二:我们并不希望将添加 reactiveFn 放到 get 中,因为它是属于 Depend 的行为 所以我们需要对 Depend 类进行重构: 解决问题一的方法:不使用数组,而是使用 Set 解决问题二的方法:添加一个新的方法,用于收集依赖 // 保存当前需要收集的响应式函数let activeReactviceFn = nullclass Depend {constructor() {this.reactiveFns = new Set()}depend() {if (activeReactviceFn) {this.reactiveFns.add(activeReactviceFn)} }addDepend(reactiveFn) {this.reactiveFns.add(reactiveFn)}notify() {this.reactiveFns.forEach((fn) => {fn()})} }// 对象的响应式const obj = {name: 'why', // depend 对象age: 18 // depend 对象}/ 封装一个响应式函数 /function watchFn(fn) {activeReactviceFn = fnfn()activeReactviceFn = null}/ 封装一个获取depend的函数 /const taregtMap = new WeakMap()function getDepend(target, key) {// 根据target对象获取maplet map = taregtMap.get(target)if (!map) {map = new Map()taregtMap.set(target, map)}// 根据key获取depend对象let depend = map.get(key)if (!depend) {depend = new Depend()map.set(key, depend)}return depend}// 监听对象的属性变化:Proxy(vue3)/Object.defineProperty(vue2)const objProxy = new Proxy(obj, {get: function (target, key, receiver) {// 根据 target key 获取对应的 depnedconst depend = getDepend(target, key)// 给 depend 对象中添加响应式函数depend.depend()return Reflect.get(target, key, receiver)},set: function (target, key, newValue, receiver) {Reflect.set(target, key, newValue, receiver)const depend = getDepend(target, key)depend.notify()} })watchFn(function () {console.log(objProxy.name, '--------------')console.log(objProxy.name, '++++++++++++++')})objProxy.name = 'kobe'/ why --------------why ++++++++++++++kobe --------------kobe ++++++++++++++/ 4.7 创建响应式对象 目前的响应式是针对于obj一个对象的,我们可以创建出来一个函数,针对所有的对象都可以变成响应式对象 / 保存当前需要收集的响应式函数 /let activeReactviceFn = null/ 依赖收集类 /class Depend {constructor() {this.reactiveFns = new Set()}depend() {if (activeReactviceFn) {this.reactiveFns.add(activeReactviceFn)} }addDepend(reactiveFn) {this.reactiveFns.add(reactiveFn)}notify() {this.reactiveFns.forEach((fn) => {fn()})} }/ 封装一个响应式函数 /function watchFn(fn) {activeReactviceFn = fnfn()activeReactviceFn = null}/ 封装一个获取depend的函数 /const taregtMap = new WeakMap()function getDepend(target, key) {// 根据target对象获取maplet map = taregtMap.get(target)if (!map) {map = new Map()taregtMap.set(target, map)}// 根据key获取depend对象let depend = map.get(key)if (!depend) {depend = new Depend()map.set(key, depend)}return depend}/ 创建响应式对象函数 /function reactive(obj) {// 监听对象的属性变化:Proxy(vue3)/Object.defineProperty(vue2)return new Proxy(obj, {get: function (target, key, receiver) {// 根据 target key 获取对应的 depnedconst depend = getDepend(target, key)// 给 depend 对象中添加响应式函数depend.depend()return Reflect.get(target, key, receiver)},set: function (target, key, newValue, receiver) {Reflect.set(target, key, newValue, receiver)const depend = getDepend(target, key)depend.notify()} })}const info = reactive({address: '广州市',height: 1.88})watchFn(() => {console.log(info.address, '---')})info.address = '北京市' 4.8 Vue2 响应式原理 前面所实现的响应式的代码,其实就是 Vue3 中的响应式原理: Vue3 主要是通过 Proxy 来监听数据的变化以及收集相关的依赖的 Vue2 中通过 Object.defineProerty的方式来实现对象属性的监听 可以将 reactive 函数进行如下的重构: 在传入对象时,我们可以遍历所有的 key,并且通过属性存储描述符来监听属性的获取和修改 在 setter 和 getter 方法中的逻辑和前面的 Proxy 是一致的 / 保存当前需要收集的响应式函数 /let activeReactviceFn = null/ 依赖收集类 /class Depend {constructor() {this.reactiveFns = new Set()}depend() {if (activeReactviceFn) {this.reactiveFns.add(activeReactviceFn)} }addDepend(reactiveFn) {this.reactiveFns.add(reactiveFn)}notify() {this.reactiveFns.forEach((fn) => {fn()})} }/ 封装一个响应式函数 /function watchFn(fn) {activeReactviceFn = fnfn()activeReactviceFn = null}/ 封装一个获取depend的函数 /const taregtMap = new WeakMap()function getDepend(target, key) {// 根据target对象获取maplet map = taregtMap.get(target)if (!map) {map = new Map()taregtMap.set(target, map)}// 根据key获取depend对象let depend = map.get(key)if (!depend) {depend = new Depend()map.set(key, depend)}return depend}/ 创建响应式对象函数 /function reactive(obj) {Object.keys(obj).forEach((key) => {let value = obj[key]Object.defineProperty(obj, key, {get: function () {const dep = getDepend(obj, key)dep.depend()return value},set: function (newValue) {value = newValueconst dep = getDepend(obj, key)dep.notify()} })})return obj}const info = reactive({address: '广州市',height: 1.88})watchFn(() => {console.log(info.address, '---')})info.address = '北京市' 本篇文章为转载内容。原文链接:https://blog.csdn.net/wanghuan1020/article/details/126774033。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-01-11 12:37:47
680
转载
Spark
...列化 , 序列化是将对象转换为字节流的过程,以便在网络上传输或存储到磁盘中。在Spark中,序列化用于将数据对象转换为紧凑的二进制格式,以减少内存占用并加快数据传输速度。文章提到两种常见的序列化方式。 缓存时机 , 缓存时机是指决定何时将数据加载到内存中的策略。文章指出,缓存时机的选择直接影响内存利用率和任务执行效率。如果在任务启动初期盲目缓存数据,可能导致内存资源浪费或后期真正需要缓存的数据无法获得足够空间。合理的缓存时机应该根据任务需求动态调整,例如在某阶段即将开始前再调用.cache()方法,确保数据能及时加载到内存中。正确把握缓存时机能够最大化分布式缓存的优势,同时避免不必要的性能损失。
2025-05-02 15:46:14
82
素颜如水
转载文章
...入带命令提示符的安全模式输入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
转载
转载文章
...是什么 云计算是一种模式云计算必须通过网络来使用弹性计算、按需付费、快速扩展 (VPS就无法做到)不需要关心太多基础设施,都有云计算提供商提供 二、云计算分类 私有云 解释:私有云就是自己构建一个云计算平台公有云 解释:公有云提供商来进行提供云计算服务混合云 解释:既有私有云又包含公有云 三、云计算分层 三种不同的场景 1.IDC环境 需要考虑网络、服务器、机房位置、带宽等,都需要考虑 2.基础设施环境 平台级别,类似于阿里云的ces 提供一个平台 服务是我们自己搭建的 3.平台环境 软件级别类似于腾讯企业邮箱,只需要买用户就可以安全措施腾讯有提供服务 云计算是一种资源通过网络交互的一种模式,同时这个资源要具有弹性扩展、按需付费等特性. 四、什么是KVM KVM是内核级虚拟化技术 KVM全称Kernel-based Virtual Machine 最上面是我们的PC的形式; 在实际的服务器上一个物理机会有多个虚拟操作系统公用这些物理资源; 然后组合成群后,就是最下面的形式; 五、虚拟化分类 1.硬件虚拟化 硬件虚拟化代表:KVM 2.软件虚拟化 软件虚拟化代表:Qemu 硬件虚拟化是需要CPU支持,如果CPU不支持将无法创建KVM虚拟机 六、虚拟化技术 全虚拟化:全虚拟化代表有:KVM 半虚拟化:半虚拟化代表有Hypervisor 针对IO层面半虚拟化要比全虚拟化要好,因为磁盘IO多一层必定会慢。一般说IO就是网络IO和磁盘IO 因为这两个相对而言是比较慢的 ; 提示: Qemu和KVM的最大区别就是,如果一台物理机内存直接4G,创建一个vm虚拟机分配内存分4G,在创建一个还可以分4G。支持超配,但是Qemu不支持; 七、虚拟化使用场景分类 服务器虚拟化:解决资源利用率低的问题 桌面虚拟化:有一些弊端,图形显示层面会有问题 应用虚拟化:没接触过,公司比较穷买不起,基本上只有银行等国企才会用Xenapp ICA 八、虚拟化工具KVM介绍 KVM 全称:Kernel-based Virtual Machine(内核级虚拟化机器) 原本由以色列人创建,现在被红帽收购 ESXI 虚拟套件,现在是免费使用 VMware vSphere Hypervisor – 安装和配置 提示:一台服务器首选ESXI 九、KVM安装 调整虚拟机 虚拟化Intel使用的是Intel VT-X ; 虚拟化AMD使用的是AMD-V 创建虚拟机步骤 1.准备虚拟机硬盘 2.需要系统iso镜像3.需要安装一个vnc的客户端来连接 查看系统环境 [root@linux-node1 ~] cat /etc/redhat-release CentOS Linux release 7.2.1511 (Core) [root@linux-node1 ~] uname -r 3.10.0-327.36.2.el7.x86_64 检查是否有vmx或者svm [root@linux-node1 ~] grep -E '(vmx|svm)' /proc/cpuinfo 安装kvm用户态模块 [root@linux-node1 ~] yum list|grep kvm libvirt-daemon-kvm.x86_64 1.2.17-13.el7_2.5 updates pcp-pmda-kvm.x86_64 3.10.6-2.el7 base qemu-kvm.x86_64 10:1.5.3-105.el7_2.7 updates qemu-kvm-common.x86_64 10:1.5.3-105.el7_2.7 updates qemu-kvm-tools.x86_64 10:1.5.3-105.el7_2.7 updates [root@linux-node1 ~] yum install qemu-kvm qemu-kvm-tools libvirt -y libvirt 用来管理kvm kvm属于内核态,不需要安装。但是需要一些类似于依赖的 kvm属于内核态,不需要安装。但是需要安装一些类似于依赖的东西 启动 [root@linux-node1 ~] systemctl start libvirtd.service [root@linux-node1 ~] systemctl enable libvirtd.service 启动之后我们可以使用ifconfig进行查看,libvirtd已经为我们安装了一个桥接网卡 libvirtd为我们启动了一个dnsmasqp,这个主要是用来dhcp连接的,这个工具会给我们的虚拟机分配IP地址 [root@linux-node1 ~] ps -ef|grep dns nobody 5233 1 0 14:27 ? 00:00:00 /sbin/dnsmasq --conf-file=/var/lib/libvirt/dnsmasq/default.conf --leasefile-ro --dhcp-script=/usr/libexec/libvirt_leaseshelper root 5234 5233 0 14:27 ? 00:00:00 /sbin/dnsmasq --conf-file=/var/lib/libvirt/dnsmasq/default.conf --leasefile-ro --dhcp-script=/usr/libexec/libvirt_leaseshelperoot 5310 2783 0 14:31 pts/0 00:00:00 grep --color=auto dns 查看磁盘空间大小 最好是20G以上 [root@linux-node1 tmp] df -h 上传镜像 提示:如果使用rz上传镜像可能会出现错误,所以我们使用dd命令,复制系统的镜像。只需要挂载上光盘即可 [root@linux-node1 opt] dd if=/dev/cdrom of=/opt/CentOS-7.2.iso [root@linux-node1 opt] ll total 33792 -rw-r--r-- 1 root root 34603008 Jun 12 18:18 CentOS-7.2-x86_64-DVD-1511.iso 下载VNC 下载地址:http://www.tightvnc.com/download/2.8.5/tightvnc-2.8.5-gpl-setup-64bit.msi 安装完VNC如下图 创建磁盘 提示: qemu-img软件包是我们安装qemu-kvm-tools 依赖给安装上的 [root@linux-node1 opt] qemu-img create -f raw /opt/CentOS-7.2-x86_64.raw 10GFormatting '/opt/Centos-7-x86_64.raw', fmt=raw size=10737418240 [root@linux-node1 opt] [root@linux-node1 opt] ll /opt/Centos-7-x86_64.raw -rw-r--r-- 1 root root 10737418240 Oct 26 14:53 /opt/Centos-7-x86_64.raw-f 制定虚拟机格式,raw是裸磁盘/opt/Centos 存放路径 10G 代表镜像大小 安装启动虚拟机的包 [root@linux-node1 tmp] yum install -y virt-install 安装虚拟机 [root@linux-node1 tmp] virt-install --help 我们可以指定虚拟机的CPU、磁盘、内存等 [root@linux-node1 opt] virt-install --name CentOS-7.2-x86_64 --virt-type kvm --ram 1024 --cdrom=/opt/CentOS-7.2.iso --disk path=/opt/CentOS-7.2-x86_64.raw --network network=default --graphics vnc,listen=0.0.0.0 --noautoconsole --name = 给虚拟机起个名字 --ram = 内存大小 --cdrom = 镜像位置,就是我们上传iso镜像的位置,我放在/tmp下了 --disk path = 指定磁盘--network network= 网络配置 default 就会用我们刚刚ifconfig里面桥接的网卡--graphics vnc,listen= 监听vnc, 分区说明 提示:我们不分交换分区,因为公有云上的云主机都是没有交换分区的 十、Libvirt介绍 libvirt是一个开源免费管理工具,可以管理KVM、VMware等 他需要起一个后台的进程,它提供了API。像openstack就是通过libvirt API来管理虚拟机 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(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
122
转载
Netty
...示了如何用Netty创建一个简单的TCP服务器。话说回来,Netty这家伙简直太贴心了,它的API设计得特别直观,想设置啥处理器或者监听事件都超简单,用起来完全没压力,感觉开发效率直接拉满! 2. 大数据流处理平台中的挑战 接下来,我们聊聊大数据流处理平台面临的挑战。在这个领域,我们通常会遇到以下几个问题: - 高吞吐量:我们需要处理每秒数百万条甚至更多的数据记录。 - 低延迟:对于某些实时应用场景(如股票交易),毫秒级的延迟都是不可接受的。 - 可靠性:数据不能丢失,必须保证至少一次投递。 - 扩展性:随着业务增长,系统需要能够无缝扩容。 这些问题听起来是不是很让人头大?但别担心,Netty正是为此而生的! 让我分享一个小故事吧。嘿,有次我正忙着弄个日志收集系统,结果一测试才发现,这传统的阻塞式I/O模型简直是“人形瓶颈”啊!流量一大就直接崩溃,完全hold不住那个高峰时刻,简直让人头大!于是,我开始研究Netty,并将其引入到项目中。哈哈,结果怎么样?系统的性能直接翻了三倍!这下我可真服了,选对工具真的太重要了,感觉像是找到了开挂的装备一样爽。 为了更好地理解这些挑战,我们可以看看下面这段代码,这是Netty中用来实现高性能读写的示例: java public class HighThroughputHandler extends ChannelInboundHandlerAdapter { private final ByteBuf buffer; public HighThroughputHandler() { buffer = Unpooled.buffer(1024); } @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { for (int i = 0; i < 1024; i++) { buffer.writeByte((byte) i); } ctx.writeAndFlush(buffer.retain()); } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { ctx.write(msg); } @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { ctx.flush(); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); } } 在这段代码中,我们创建了一个自定义的处理器HighThroughputHandler,它能够在每次接收到数据后立即转发出去,从而实现高吞吐量的传输。 3. Netty如何优化大数据流处理平台? 现在,让我们进入正题——Netty是如何具体优化大数据流处理平台的呢? 3.1 异步非阻塞I/O Netty的核心优势在于其异步非阻塞I/O模型。这就相当于,当有请求进来的时候,Netty可不会给每个连接都专门安排一个“服务员”,而是让这些连接共用一个“服务团队”。这样既能节省人手,又能高效处理各种任务,多划算啊!这样做的好处是显著减少了内存占用和上下文切换开销。 假设你的大数据流处理平台每天要处理数十亿条数据记录,采用传统的阻塞式I/O模型,很可能早就崩溃了。而Netty则可以通过单线程处理数千个连接,极大地提高了资源利用率。 3.2 零拷贝技术 另一个让Netty脱颖而出的特点是零拷贝技术。嘿,咱们就拿快递打个比方吧!想象一下,你在家里等着收快递,但这个快递特别麻烦——它得先从仓库(相当于内核空间)送到快递员手里(用户空间),然后快递员再把东西送回到你家(又回到内核空间)。这就像是数据在网络通信里来回折腾了好几趟,一会儿在系统深处待着,一会儿又被搬出来给应用用,真是费劲啊!这种操作不仅耗时,还会消耗大量CPU资源。 Netty通过ZeroCopy机制,直接将数据从文件系统传递到网络套接字,避免了不必要的内存拷贝。这种做法不仅加快了数据传输速度,还降低了系统的整体负载。 这里有一个实际的例子: java FileRegion region = new DefaultFileRegion(fileChannel, 0, fileSize); ctx.write(region); 上述代码展示了如何利用Netty的零拷贝功能发送大文件,无需手动加载整个文件到内存中。 3.3 灵活的消息编解码 在大数据流处理平台中,数据格式多种多样,可能包括JSON、Protobuf、Avro等。Netty提供了一套强大的消息编解码框架,允许开发者根据需求自由定制解码逻辑。 例如,如果你的数据是以Protobuf格式传输的,可以这样做: java public class ProtobufDecoder extends MessageToMessageDecoder { @Override protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { byte[] data = new byte[in.readableBytes()]; in.readBytes(data); MyProtoMessage message = MyProtoMessage.parseFrom(data); out.add(message); } } 通过这种方式,我们可以轻松解析复杂的数据结构,同时保持代码的整洁性和可维护性。 3.4 容错与重试机制 最后但同样重要的是,Netty内置了强大的容错与重试机制。在网上聊天或者传输文件的时候,有时候会出现消息没发出去、对方迟迟收不到的情况,就像快递丢了或者送慢了。Netty这个小助手可机灵了,它会赶紧发现这些问题,然后试着帮咱们把没送到的消息重新发一遍,就像是给快递员多派一个人手,保证咱们的信息能安全顺利地到达目的地。 java RetryHandler retryHandler = new RetryHandler(maxRetries); ctx.pipeline().addFirst(retryHandler); 上面这段代码展示了如何添加一个重试处理器到Netty的管道中,让它在遇到错误时自动重试。 4. 总结与展望 经过这一番探讨,相信大家已经对Netty及其在大数据流处理平台中的应用有了更深入的理解。Netty可不只是个工具库啊,它更像是个靠谱的小伙伴,陪着咱们一起在高性能网络编程的大海里劈波斩浪、寻宝探险! 当然,Netty也有它的局限性。比如说啊,遇到那种超级复杂的业务场景,你可能就得绞尽脑汁写一堆专门定制的代码,不然根本搞不定。还有呢,这门技术的学习难度有点大,刚上手的小白很容易觉得晕头转向,不知道该怎么下手。但我相信,只要坚持实践,总有一天你会爱上它。 未来,随着5G、物联网等新技术的发展,大数据流处理的需求将会更加旺盛。而Netty凭借其卓越的性能和灵活性,必将在这一领域继续发光发热。所以,不妨大胆拥抱Netty吧,它会让你的开发之旅变得更加精彩! 好了,今天的分享就到这里啦!如果你有任何疑问或者想法,欢迎随时交流。记住,编程之路没有终点,只有不断前进的脚步。加油,朋友们!
2025-04-26 15:51:26
46
青山绿水
转载文章
...现代化、高性能且易于维护的UI体验。JavaFX提供了丰富的图形效果、CSS样式支持以及与Web技术更好的集成能力。 2. 现代Java GUI框架介绍:除了官方推荐的JavaFX外,还有诸如TornadoFX(基于JavaFX的声明式MVVM框架)、 Griffon(Groovy为基础的桌面应用框架)等新兴框架,它们为开发者提供了更简洁直观的API和强大的功能集。 3. 跨平台UI工具包Qt Jambi:尽管Java Swing是纯Java环境下的GUI解决方案,但也有其他跨平台工具包值得关注,例如Qt Jambi,它允许开发者使用Java编写原生速度和外观的应用程序,并兼容多种操作系统。 4. 无障碍性设计原则在Swing中的应用:针对日益增长的无障碍需求,开发者应了解并遵循WCAG标准,在Swing应用程序中实施无障碍设计,如提供键盘导航支持、可调整的文字大小及高对比度模式等。 5. Swing组件最佳实践分享:查阅最新的开发者博客和论坛讨论,可以发现众多关于如何优化Swing组件性能、处理并发问题以及改善用户体验的实际案例和建议,这些都能帮助你更好地运用Swing进行复杂GUI的设计与实现。 综上所述,不断跟进最新的GUI开发趋势和技术发展,结合实际项目需求,灵活运用和扩展Swing或其他相关框架,将有助于打造更为出色和易用的桌面应用程序。
2023-01-18 08:36:23
526
转载
转载文章
...种方法的一个通用工作模式就是:主进程执行 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
261
转载
转载文章
...gt; 选择演示显示模式 win + K -> 打开“连接”快速操作 win + L -> 锁定电脑或切换帐户 win + Tab -> 打开“任务视图” win + R -> 打开运行窗口 四、其他快捷键 End -> 显示活动窗口的底端 Home -> 显示活动窗口的顶端 F11 -> 最大化或最小化活动窗口 五、运行窗口快捷命令 先输入win+ R 本小结转载地址:https://blog.csdn.net/qq_42402854/article/details/93162387 1.calc:启动计算器 2.appwiz.cpl:程序和功能 3.certmgr.msc:证书管理实用程序 4.charmap:启动字符映射表 5.chkdsk.exe:Chkdsk磁盘检查(管理员身份运行命令提示符) 6.cleanmgr: 打开磁盘清理工具 7.cliconfg:SQL SERVER 客户端网络实用工具 8.cmstp:连接管理器配置文件安装程序 9.cmd:CMD命令提示符 10.自动关机命令 Shutdown -s -t 600:表示600秒后自动关机 shutdown -a :可取消定时关机 Shutdown -r -t 600:表示600秒后自动重启 rundll32 user32.dll,LockWorkStation:表示锁定计算机 11.colorcpl:颜色管理,配置显示器和打印机等中的色彩 12.CompMgmtLauncher:计算机管理 13.compmgmt.msc:计算机管理 14.credwiz:备份或还原储存的用户名和密码 15.comexp.msc:打开系统组件服务 16.control:控制面版 17.dcomcnfg:打开系统组件服务 18.Dccw:显示颜色校准 19.devmgmt.msc:设备管理器 20.desk.cpl:屏幕分辨率 21.dfrgui:优化驱动器 Windows 7→dfrg.msc:磁盘碎片整理程序 22.dialer:电话拨号程序 23.diskmgmt.msc:磁盘管理 24.dvdplay:DVD播放器 25.dxdiag:检查DirectX信息 26.eudcedit:造字程序 27.eventvwr:事件查看器 28.explorer:打开资源管理器 29.Firewall.cpl:Windows防火墙 30.FXSCOVER:传真封面编辑器 31.fsmgmt.msc:共享文件夹管理器 32.gpedit.msc:组策略 33.hdwwiz.cpl:设备管理器 34.inetcpl.cpl:Internet属性 35.intl.cpl:区域 36.iexpress:木马捆绑工具,系统自带 37.joy.cpl:游戏控制器 38.logoff:注销命令 39.lusrmgr.msc:本地用户和组 40.lpksetup:语言包安装/删除向导,安装向导会提示下载语言包 41.lusrmgr.msc:本机用户和组 42.main.cpl:鼠标属性 43.mmsys.cpl:声音 44.magnify:放大镜实用程序 45.mem.exe:显示内存使用情况(如果直接运行无效,可以先管理员身份运行命令提示符,在命令提示符里输入mem.exe>d:a.txt 即可打开d盘查看a.txt,里面的就是内存使用情况了。当然什么盘什么文件名可自己决定。) 46.MdSched:Windows内存诊断程序 47.mmc:打开控制台 48.mobsync:同步命令 49.mplayer2:简易widnows media player 50.Msconfig.exe:系统配置实用程序 51.msdt:微软支持诊断工具 52.msinfo32:系统信息 53.mspaint:画图 54.Msra:Windows远程协助 55.mstsc:远程桌面连接 56.NAPCLCFG.MSC:客户端配置 57.ncpa.cpl:网络连接 58.narrator:屏幕“讲述人” 59.Netplwiz:高级用户帐户控制面板,设置登陆安全相关的选项 60.netstat : an(TC)命令检查接口 61.notepad:打开记事本 62.Nslookup:IP地址侦测器 63.odbcad32:ODBC数据源管理器 64.OptionalFeatures:打开“打开或关闭Windows功能”对话框 65.osk:打开屏幕键盘 66.perfmon.msc:计算机性能监测器 67.perfmon:计算机性能监测器 68.PowerShell:提供强大远程处理能力 69.printmanagement.msc:打印管理 70.powercfg.cpl:电源选项 71.psr:问题步骤记录器 72.Rasphone:网络连接 73.Recdisc:创建系统修复光盘 74.Resmon:资源监视器 75.Rstrui:系统还原 76.regedit.exe:注册表 77.regedt32:注册表编辑器 78.rsop.msc:组策略结果集 79.sdclt:备份状态与配置,就是查看系统是否已备份 80.secpol.msc:本地安全策略 81.services.msc:本地服务设置 82.sfc /scannow:扫描错误并复原/windows文件保护 83.sfc.exe:系统文件检查器 84.shrpubw:创建共享文件夹 85.sigverif:文件签名验证程序 86.slui:Windows激活,查看系统激活信息 87.slmgr.vbs -dlv :显示详细的许可证信息 88.snippingtool:截图工具,支持无规则截图 89.soundrecorder:录音机,没有录音时间的限制 90.StikyNot:便笺 91.sysdm.cpl:系统属性 92.sysedit:系统配置编辑器 93.syskey:系统加密,一旦加密就不能解开,保护系统的双重密码 94.taskmgr:任务管理器(旧版) 95.TM任务管理器(新版) 96.taskschd.msc:任务计划程序 97.timedate.cpl:日期和时间 98.UserAccountControlSettings用户账户控制设置 99.utilman:辅助工具管理器 100.wf.msc:高级安全Windows防火墙 101.WFS:Windows传真和扫描 102.wiaacmgr:扫描仪和照相机向导 103.winver:关于Windows 104.wmimgmt.msc:打开windows管理体系结构(WMI) 105.write:写字板 106.wscui.cpl:操作中心 107.wuapp:Windows更新 108.wscript:windows脚本宿主设置 六、小结 键盘快捷键会大大提高使用效率,让你在外行面前显得更酷。持续更新中…感谢点赞,评论与转发,谢谢! 本篇文章为转载内容。原文链接:https://blog.csdn.net/weixin_44168588/article/details/121208530。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-02-01 13:38:26
92
转载
转载文章
...touch 文件名:创建文件 rm -r 目录/文件:删除文件/目录及它包含的所有内容 rm -f 文件:直接删除,无需确认 rm -i 文件:删除文件,会逐一询问是否删除 rmdir 目录:专门删除目录 mv :可以用来移动文件/目录,也可以用来重命名 – ifconfig:显示网络配置信息(lo:本地回环测试) ifconfig -a:显示所有网卡(上面只显示工作的,本条显示所有工作和未工作的) ifconfig eth0 up:打开eth0这个网卡 ifconfig eth0 down:关闭eth0这个网卡(0一般要sudo来执行) ifconfig eth0 你想设置的地址:重设eth0的ip地址 – 命令 --help:看看这个命令的帮助信息 reboot:重启 – sync:数据同步写入磁盘命令(一般来说,用户写的内容先保存在一个缓冲区,系统是隔一定时间像磁盘写入缓冲区内写入磁盘),用sync立刻写入 grep ”“ -i :搜索时忽略大小写 grep 默认是匹配字符, -w 选项默认匹配一个单词 例如我想匹配 “like”, 不加 -w 就会匹配到 “liker”, 加 -w 就不会匹配到 du 目录/文件 -sh : 查看某一文件/目录的大小,也可以到一个目录下du -sh,查看这个目录的大小 目录下使用du -sh 查看目录总的大小 du 文件名 -sh 查看指定文件的大小 df:检查linux服务器的文件系统磁盘空间占用情况,默认以kb为单位 gedit 文件:使用gedit软件打开一个文件(类似于windows下面的记事本) ps:查看您当前系统有哪些进程,ubuntu(多用户)下是ps -aux,嵌入式linux(单用户)下面是ps top:进程实时运行状态查询 file 文件名:查看文件类型 ubuntu的fs cd / :根目录,一切都是从根目录发散开来的 /bin:存放二进制可执行文件,比如一些命令 /boot:ubuntu的内核与启动文件 /cdrom:有光盘是存放光盘文件 /dev:存放设备驱动文件 /etc:存放配置文件,如账号和密码文件(加密后的) /home:系统默认的用户主文件夹 /lib:存放库文件 /lib64:存放库文件,. so时linux下面的动态库文件 /media:存放可插拔设备,如sd,u盘就是挂载到这个文件下面 /mnt:用户可使用的挂载点,和media类似,可以手动让可插拔设备挂载到/mnt /opt:可选的文件和程序存放目录,给第三方软件放置的目录 /proc:存放系统的运行信息,实在内存上的不是在flash上,如cat /proc/cpuinfo /root:系统管理员目录,root用户才能访问的文件 /sbin:和bin类似,存放一些二进制可执行文件,sbin下面一般是系统开机过程中所需要的命令 /srv:服务相关的目录,如网络服务 /sys:记录内核信息,是虚拟文件系统 /tmp:临时目录 /usr:不是user的缩写,而是UNIX Software Resource的缩写,存放系统用户有关的文件,占很大空间 /var:存放变化的文件,如日志文件 – 移植就是移植上面这些文件 磁盘管理 linux开发一定要选用FAT32格式的U盘或者SD卡 u盘在/dev中的名字是sd,要确定是哪个,拔了看少了哪个。就是哪个 /dev/sdb表示U盘,/dev/sdb1表示U盘的第一个分区,一般U盘 sd卡只有一个分区 df:显示linux系统的磁盘占用情况 在一个目录里使用du -sh:查看这个目录里面所有内容所占用的资源 du 文件名 -sh:一般用来看单个文件/目录的大小 du -h --max-depth=n:显示n级目录的大小 – 磁盘的挂载与取消挂载: mount 和 umount sudo mount /dev/sdb1 /media/jl/udisk sudo umount /media/jl/u盘名 (-f 强制取消挂载),如果u盘正在使用,如被另一个终端打开,那么该指令无效 mount挂载后中文显示乱码的解决方法 sudo mount -o iocharset=utf8 /dev/sdb1 udisk – 磁盘的分区和格式化 sudo fdisk -l /dev/sdb 查看所有分区信息(–help查看别的用法) sudo fdisk /dev/sdb1 ----> m ( 进入帮助 ) ----> d 删除该分区 ----> wq 保存并退出 mkfs -t vfat /dev/sdb1 mkfs -t vfat /dev/sdb2 mkfs -t vfat /dev/sdb3 给分区1,2,3分别格式化,完成后能在图形界面看见三个u盘图标 格式化u盘之前一定要先卸载u盘已经挂载的系统。 – 压缩和解压缩 linux下常用的压缩扩展名: .tar .tar.bz2 .tar.gz 后两个linux常用 windows下面用7zip软件 右键选中文件,选择7zip,添加到压缩包,压缩格式选择tar,仅存储 生成tar文件,这里只是打包,没有压缩 右键上面的tar文件,选择7zip,添加到压缩包,压缩格式选择bzip2,确定 生成.tar.bz2文件,把它放到ubuntu解压 ubuntu也支持解压.tar和.zip,但后面两个常用 – ubuntu下面的压缩工具时gzip 压缩文件 gzip 文件名:压缩文件,变成 原文件名.gz,原来的文件就不见了 解压缩文件 gzip -d .gz:还原 文件 gzip -r 目录:递归,将该目录里的各个文件压缩,不提供打包服务 – bzip2工具负责压缩和解压缩.bz2格式的压缩包 bzip2 -z 文件名,压缩成 文件名.bz2 bzip2 -d 文件名.bz2,解压缩成 文件名 bzip2不能压缩/解压缩 目录 – 打包工具 tar 常用参数 -f:使用归档文件(必须要在所有选项后面) -c:创建一个新归档 -x:从归档中解出文件 -j:使用bzip2压缩格式 -z:使用gzip压缩格式 -v:打印出命令执行过程 如以bzip2格式压缩,打包 tar -vcjf 目录名.tar.bz2 目录名 如将上面的压缩包解包 tar -vxjf 目录名.tar.bz2 – 其他压缩工具 rar工具 sudo apt-get install rar(用dhcp连不上阿里云的镜像) rar a test.rar test 把test压缩成test.rar rar x test.rar 把test.rar解压缩成test – zip工具 压缩 zip -rv test.zip test 解压缩 unzip test.zip – ubuntu的用户和用户组 linux是多用户的os,不同的用户有不同的权限,可以查看和操作不同的文件 有三种用户 1、初次用户 2、root用户 3、普通用户 root用户可以创建普通用户 linux用户记录在/etc/passwd这个文件内 linux用户密码记录在/etc/shadow这个文件内,不是以明文记录的 每个用户都有一个id,叫做UID – linux用户组 为了方便管理,将用户进行分组,每个用户可以属于多个组 可以设置非本组人员不能访问一些文件 用户和用户组的存在就是为了控制文件的访问权限的 每个用户组都有一个ID,叫做GID 用户组信息存储在/etc/group中 passwd 用户名:修改该用户的密码 – ubuntu文件权限 ls -al 文件名 如以b开头: -brwx - rwx - rwx -:b表示 块文件,设备文件里面可供存储的周边设备 以d开头是目录 以b是块设备文件 以-开头是普通文件 以 l 开头表示软连接文件 以c开头是设备文件里的串行端口设备 -rwx - rwx - rwx -:用户权限,用户组内其他成员,其它组用户 数字 1 表示链接数,包括软链接和硬链接 第三列 jl 表示文件的拥有者 第四列 jl 表示文件的用户组 第五列 3517 表示这个文件的大小,单位是字节 ls -l 显示的文件大小单位是字节 ls -lh 现实的文件大小单位是 M / G 第六七八列是最近修改时间 最后一列是文件名 – 修改文件权限命令 chmod 777 文件名 修改文件所属用户 sudo chown root 文件 修改文件用户组 sudo chown .root 文件 同时修改文件用户和用户组 sudo chown jl.jl 文件 修改目录的用户/用户组 sudo chown -r jl.jl 目录( root.root ) – linux连接文件 1、硬连接 2、符号连接(软连接) linux有两种连接文件,软连接/符号连接,硬连接 符号连接类似于windows下面的快捷方式 硬连接通过文件系统的inode连接来产生新文件名,而不是产生新文件 inode:记录文件属性,一个文件对应一个inode, inode相当于文件ID 查找文件要先找到inode,然后才能读到文件内容 – ln 命令用于创建连接文件 ln 【选项】源文件 目标文件 不加选项就是默认创建硬连接 -s 创建软连接 -f 强制创建连接文件,如果目标存在,就先删掉目标文件,再创建连接文件 – 硬连接:多个文件都指向同一个inode 具有向inode的多个文件互为硬连接文件,创建硬连接相当于文件实体多了入口 只有删除了源文件、和它所有的硬连接文件,晚间实体才会被删除 可以给文件创建硬连接来防止文件误删除 改了源文件还是硬连接文件,另一个文件的数据都会被改变 硬连接不能跨文件系统(另一个格式的u盘中的文件) 硬连接不能连接到目录 出于以上原因,硬连接不常用 ls -li:此时第一列显示的就是每个文件的inode – 软连接/符号连接 类似windows下面的快捷方式 使用较多 软连接相当于串联里一个独立的文件,该文件会让数据读取指向它连接的文件 ln -s 源文件 目标文件 特点: 可以连接到目录 可以跨文件系统 删除源文件,软连接文件也打不开了 软连接文件通过 “ -> ” 来指示具体的连接文件(ls -l) 创建软连接的时候,源文件一定要使用绝对路径给出,(硬连接无此要求) 软连接文件直接用cp复制到别的目录下,软连接文件就会变成实体文件,就算你把源文件删掉,该文件还是有效 正确的复制、移动软连接的用法是:cp -d 如果不用绝对路径,cp -d 软连接文件到别的目录,该软连接文件就会变红,失效 如果用了绝对路径,cp -d 软连接文件到别的目录,该软连接文件还是有效的,还是软连接文件 不用绝对路径,一拷贝就会出问题 – 软连接一个目录,也是可以用cp -d复制到其他位置的 – gedit 是基于图形界面的 vim有三种模式: 1、一般模式:默认模式,用vim打开一个文件就自动进入这个模式 2、编辑模式:按 i,a等进入,按esc回到一般模式 3、命令行/底行模式:在一般模式下输入:/ ?可进入命令行模式 ,按esc回到一般模式 一般模式下,dd删除光标所在的一整行; ndd,删除掉光标所在行和下面的一共n行 点 . 重复上一个操作 yy复制光标所在行 小p复制到光标下一行 大p复制到光标上一行n nyy复制光标所在往下n行 设置vim里的tab是四个空格:在/etc/vim/vimrc里面添加:set ts=4 设置vim中显示行号:在上面那个文件里添加:set nu – vscode是编辑器 gcc能编译汇编,c,cpp 电脑上的ubuntu自带的gcc用来编译x86架构的程序,而嵌入式设备的code要用针对于该芯片架构如arm的gcc编译器,又叫做交叉编译器(在一种架构的电脑上编译成另一种架构的代码) gcc -c 源文件:只编译不链接,编译成.o文件 -o 输出文件名( 默认名是 .out ) -O 对程序进行优化编译,这样产生的可执行文件执行效率更高 -O2:比-O幅度更大的优化,但编译速度会很慢 -v:显示编译的过程 gcc main.c 输出main.out的可执行文件 预处理 --> 编译 --> 汇编 --> 链接 – makefile里第一个目标默认是终极目标 其他目标的顺序可以变 makefile中的变量都是字符串 变量的引用方法 : $ ( 变量名 ) – Makefile中执行shell命令默认会把命令本身打印出来 如果在shell命令前加 @ ,那么shell’命令本身就不会被打印 – 赋值符:= 变量的有效值取决于他最后一次被赋值的值 : = 赋值时右边的值只是用前面已经定义好的,不会使用后面的 ?= 如果左边的前面没有被赋值,那么在这里赋值,佛则就用前面的赋值 + = 左边前面已经复制了一些字串,在这里添加右边的内容,用空格隔开 – 模式规则 % . o : % . c %在这里意思是通配符,只能用于模式规则 依赖中 % 的内容取决于目标 % 的内容 – CFLAGS:指定头文件的位置 LDFLAGS:用于优化参数,指定库文件的位置 LIBS:告诉链接器要链接哪些库文件 VPATH:特殊变量,指定源文件的位置,冒号隔开,按序查找源文件 vpath:关键字,三种模式,指定、清除 – 自动化变量 $ @ 规则中的目标集合 $ % 当目标是函数库的时候,表示规则中的目标成员名 $ < 依赖文件集合中的第一个文件,如果依赖文件是以 % 定义的,那么 $ < 就是符合模式的一系列文件的集合 $ ? 所有比目标新的依赖文件的集合,以空格分开 $ ^ 所有依赖文件的集合,用空格分开,如果有重复的依赖文件,只保留一次 $ + 和 $ ^ 类似,但有多少重复文件都会保留 $ 表明目标模式中 % 及其以前的部分 如果目标是 test/a.test.c,目标模式是 a.%.c,那么 $ 就表示 test/a.test – 常用的是 $@ , $< , $^ – Makefile的伪目标 不生成目标文件,只是执行它下面的命令 如果被错认为是文件,由于伪目标一般没有依赖,那么目标就被认为是最新的,那么它下面的命令就不会执行 。 如果目录下有同名文件,伪目标错认为是该文件,由于没有依赖,伪目标下面的指令不会被执行 伪目标声明方法 .PHONY : clean 那么就算目录下有伪目标同名文件,伪目标也同样会执行 – 条件判断 ifeq ifneq ifdef ifndef – makefile函数使用 shell脚本 类似于windoes的批处理文件 将连续执行的命令写成一个文件 shell脚本可以提供数组,循环,条件判断等功能 开头必须是:!/bin/bash 表示使用bash 脚本的扩展名:.sh – 交互式shell 有输入有输出 输入:read 第三行 name在这里作为变量,read输入这个变量 下一行使用这个变量直接是 $name,不用像 Makefile 里面那样子加括号 read -p “读取前你想打印的内容” 变量1 变量2 变量3… – 数值计算 第五行等于号两边不能有空格 右边计算的时候是 $( ( ) ),注意要两个括号 – test 测试命令 文件状态查询,字符、数字比较 && cmd1 && cmd2 当cmd1执行完并且正确,那么cmd2也执行 当cmd2执行完并且错误,那么cmd2不执行 || cmd1 || cmd2 当cmd1执行完并且正确,那么cmd2不执行 当cmd2执行完并且错误,那么cmd2也执行 查看一个文件是否存在 – 测试两个字符串是否相等 ==两边必须要有空格,如果不加空格,test这句就一直是对的。 – 中括号判断符 [ ] 作用和test类似 里面只能输入 == 或者 != 四个箭头所指必须用空格隔开 而且如果变量是字符串的话,一定要加双引号 – 默认变量 $0——shell脚本本身的命令 $——最后一个参数的标号(1,2,3,4…) $@——表示 $1 , $2 , $3 … $1 $2 $3 – shell 脚本的条件判断 if [ 条件判断 ];then //do something fi 红点处都要加空格 exit 0——表示退出 – if 条件判断;then //do something elif 条件判断;them //do something else //do something fi 红线处要加空格 – case 语句 case $var in “第一个变量的内容”) //do something ;; “第二个变量的内容”) // do something ;; . . . “第n个变量的内容”) //do something ;; esac 不能用 “”,否则就不是通配符的意思,而是表示字符 – shell 脚本函数 function fname(){ //函数代码段 } 其中function可以写也可以不写 调用函数的时候不要加括号 shell 脚本函数传参方式 – shell 循环 while[条件] //括号内的状态是判断式 do //循环代码段 done – until [条件] do //循环代码段 done – for循环,使用该循环可以知道有循环次数 for var con1 con2 con3 … … do //循环代码段 done – for 循环数值处理 for((初始值;限制值;执行步长)) do //循环代码段 done – 红点处必须要加空格!! loop 环 – – 注意变量有的地方用了 $ ,有的地方不需要 $ 这里的赋值号两边都不用加 空格 $(())数值运算 本篇文章为转载内容。原文链接:https://blog.csdn.net/engineer0/article/details/107965908。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-11-23 17:18:30
81
转载
转载文章
...深入学习和理解了文档对象模型(DOM)的基础知识之后,我们可以进一步关注DOM技术的最新发展动态与应用场景。近年来,随着Web前端技术的飞速进步,DOM操作在现代JavaScript框架如React、Vue.js和Angular中扮演着至关重要的角色。例如,React通过虚拟DOM实现高效的UI更新策略,大大提升了网页渲染性能。 同时,在Web组件化开发领域,Custom Elements V1规范已得到广泛支持,开发者可以通过自定义HTML元素并扩展其行为,这背后离不开DOM API的强力支撑。例如,借助MutationObserver接口可以监听DOM树的变化,实现实时响应式布局。 此外,无障碍性(Accessibility)也是当前Web开发的重要考量因素之一,正确且高效的DOM操作有助于提升网站对屏幕阅读器等辅助技术的支持,确保信息能够无障碍地传达给所有用户。 近期,W3C还在持续推动DOM标准的发展,如Shadow DOM v1规范让组件样式和DOM结构更加独立和可控,对于构建复杂Web应用具有重要意义。了解和掌握这些前沿技术和标准,将有助于开发者更好地利用DOM API创建高性能、可维护且符合现代Web标准的页面和应用。
2023-08-04 13:36:05
248
转载
转载文章
...ertView以减少对象创建、减少findViewById调用次数、分页加载数据以及图片缓存策略等,这些措施都是为了提升ListView的滚动流畅度和整体性能。 图片三级缓存机制 , 在移动应用尤其是Android应用开发中,图片资源的高效管理和加载对于用户体验至关重要。三级缓存机制是一种常见的优化手段,主要包括内存缓存、本地缓存(文件缓存)和网络缓存三层结构。当需要加载图片时,首先检查内存缓存中是否存在该图片,若存在则直接使用;若不存在,则查询本地缓存,如果找到则从本地读取并加载至内存缓存;若本地也未存储,则发起网络请求下载图片,并将下载后的图片同时保存至内存缓存和本地缓存,以便后续快速访问。这样做的目的是减少对网络带宽和内存资源的消耗,防止因频繁加载图片导致的卡顿、延迟甚至OOM问题,提升应用性能和用户体验。在文中,面试者详细介绍了自己如何利用这一机制来优化项目中的图片处理部分。
2023-06-19 17:42:52
338
转载
转载文章
...搭建稳定、高效且易于维护的后端服务,例如定义Service和Controller层接口并实现相关业务逻辑。 Timestamp , Timestamp是一种数据库中的时间戳类型,表示从1970年1月1日(UTC/GMT的午夜)开始所经过的秒数,精确到微秒级别。在文中提到的SeckillGoods实体类中,startDate和endDate字段采用了Timestamp类型,以便精确记录秒杀活动的开始和结束时间,并使用DateTimeFormat注解进行格式化处理,确保与前端展示的时间格式一致。 VO(Value Object) , VO是值对象(Value Object)的简称,在面向对象编程领域中,VO通常用来封装从数据库查询或由用户输入的数据,仅包含属性以及它们的getter和setter方法,不包含行为。在本文中,创建了SeckillGoodsVo这个实体类VO,用于连表查询时接收和展示商品名字等多张表的关联数据,便于前后端之间的数据传输和展示。 前后端分离架构 , 前后端分离架构是一种常见的Web应用程序设计模式,其中前端专注于用户界面的设计和交互逻辑,而后端则关注业务逻辑处理、数据存储和API接口提供。在本篇文章中,前端通过Ajax请求调用后端提供的RESTful API获取数据并渲染页面,实现了前后端职责清晰、开发并行且可独立部署升级的现代Web应用架构。
2023-02-25 23:20:34
122
转载
转载文章
... 特点 支持四种分词模式: 精确模式,试图将句子最精确地切开,适合文本分析; 全模式,把句子中所有的可以成词的词语都扫描出来, 速度非常快,但是不能解决歧义; 搜索引擎模式,在精确模式的基础上,对长词再次切分,提高召回率,适合用于搜索引擎分词。 paddle模式,利用PaddlePaddle深度学习框架,训练序列标注(双向GRU)网络模型实现分词。同时支持词性标注。paddle模式使用需安装paddlepaddle-tiny,pip install paddlepaddle-tiny==1.6.1。目前paddle模式支持jieba v0.40及以上版本。jieba v0.40以下版本,请升级jieba,pip install jieba --upgrade 。PaddlePaddle官网 支持繁体分词 支持自定义词典 MIT 授权协议 安装说明 代码对 Python 2/3 均兼容 全自动安装:easy_install jieba 或者 pip install jieba / pip3 install jieba 半自动安装:先下载 http://pypi.python.org/pypi/jieba/ ,解压后运行 python setup.py install 手动安装:将 jieba 目录放置于当前目录或者 site-packages 目录 通过 import jieba 来引用 如果需要使用paddle模式下的分词和词性标注功能,请先安装paddlepaddle-tiny,pip install paddlepaddle-tiny==1.6.1。 算法 基于前缀词典实现高效的词图扫描,生成句子中汉字所有可能成词情况所构成的有向无环图 (DAG) 采用了动态规划查找最大概率路径, 找出基于词频的最大切分组合 对于未登录词,采用了基于汉字成词能力的 HMM 模型,使用了 Viterbi 算法 主要功能 分词 jieba.cut 方法接受四个输入参数: 需要分词的字符串;cut_all 参数用来控制是否采用全模式;HMM 参数用来控制是否使用 HMM 模型;use_paddle 参数用来控制是否使用paddle模式下的分词模式,paddle模式采用延迟加载方式,通过enable_paddle接口安装paddlepaddle-tiny,并且import相关代码; jieba.cut_for_search 方法接受两个参数:需要分词的字符串;是否使用 HMM 模型。该方法适合用于搜索引擎构建倒排索引的分词,粒度比较细 待分词的字符串可以是 unicode 或 UTF-8 字符串、GBK 字符串。注意:不建议直接输入 GBK 字符串,可能无法预料地错误解码成 UTF-8 jieba.cut 以及 jieba.cut_for_search 返回的结构都是一个可迭代的 generator,可以使用 for 循环来获得分词后得到的每一个词语(unicode),或者用 jieba.lcut 以及 jieba.lcut_for_search 直接返回 list jieba.Tokenizer(dictionary=DEFAULT_DICT) 新建自定义分词器,可用于同时使用不同词典。jieba.dt 为默认分词器,所有全局分词相关函数都是该分词器的映射。 代码示例 encoding=utf-8import jiebajieba.enable_paddle() 启动paddle模式。 0.40版之后开始支持,早期版本不支持strs=["我来到北京清华大学","乒乓球拍卖完了","中国科学技术大学"]for str in strs:seg_list = jieba.cut(str,use_paddle=True) 使用paddle模式print("Paddle Mode: " + '/'.join(list(seg_list)))seg_list = jieba.cut("我来到北京清华大学", cut_all=True)print("Full Mode: " + "/ ".join(seg_list)) 全模式seg_list = jieba.cut("我来到北京清华大学", cut_all=False)print("Default Mode: " + "/ ".join(seg_list)) 精确模式seg_list = jieba.cut("他来到了网易杭研大厦") 默认是精确模式print(", ".join(seg_list))seg_list = jieba.cut_for_search("小明硕士毕业于中国科学院计算所,后在日本京都大学深造") 搜索引擎模式print(", ".join(seg_list)) 输出: 【全模式】: 我/ 来到/ 北京/ 清华/ 清华大学/ 华大/ 大学【精确模式】: 我/ 来到/ 北京/ 清华大学【新词识别】:他, 来到, 了, 网易, 杭研, 大厦 (此处,“杭研”并没有在词典中,但是也被Viterbi算法识别出来了)【搜索引擎模式】: 小明, 硕士, 毕业, 于, 中国, 科学, 学院, 科学院, 中国科学院, 计算, 计算所, 后, 在, 日本, 京都, 大学, 日本京都大学, 深造 添加自定义词典 载入词典 开发者可以指定自己自定义的词典,以便包含 jieba 词库里没有的词。虽然 jieba 有新词识别能力,但是自行添加新词可以保证更高的正确率 用法: jieba.load_userdict(file_name) file_name 为文件类对象或自定义词典的路径 词典格式和 dict.txt 一样,一个词占一行;每一行分三部分:词语、词频(可省略)、词性(可省略),用空格隔开,顺序不可颠倒。file_name 若为路径或二进制方式打开的文件,则文件必须为 UTF-8 编码。 词频省略时使用自动计算的能保证分出该词的词频。 例如: 创新办 3 i云计算 5凱特琳 nz台中 更改分词器(默认为 jieba.dt)的 tmp_dir 和 cache_file 属性,可分别指定缓存文件所在的文件夹及其文件名,用于受限的文件系统。 范例: 自定义词典:https://github.com/fxsjy/jieba/blob/master/test/userdict.txt 用法示例:https://github.com/fxsjy/jieba/blob/master/test/test_userdict.py 之前: 李小福 / 是 / 创新 / 办 / 主任 / 也 / 是 / 云 / 计算 / 方面 / 的 / 专家 / 加载自定义词库后: 李小福 / 是 / 创新办 / 主任 / 也 / 是 / 云计算 / 方面 / 的 / 专家 / 调整词典 使用 add_word(word, freq=None, tag=None) 和 del_word(word) 可在程序中动态修改词典。 使用 suggest_freq(segment, tune=True) 可调节单个词语的词频,使其能(或不能)被分出来。 注意:自动计算的词频在使用 HMM 新词发现功能时可能无效。 代码示例: >>> print('/'.join(jieba.cut('如果放到post中将出错。', HMM=False)))如果/放到/post/中将/出错/。>>> jieba.suggest_freq(('中', '将'), True)494>>> print('/'.join(jieba.cut('如果放到post中将出错。', HMM=False)))如果/放到/post/中/将/出错/。>>> print('/'.join(jieba.cut('「台中」正确应该不会被切开', HMM=False)))「/台/中/」/正确/应该/不会/被/切开>>> jieba.suggest_freq('台中', True)69>>> print('/'.join(jieba.cut('「台中」正确应该不会被切开', HMM=False)))「/台中/」/正确/应该/不会/被/切开 “通过用户自定义词典来增强歧义纠错能力” — https://github.com/fxsjy/jieba/issues/14 关键词提取 基于 TF-IDF 算法的关键词抽取 import jieba.analyse jieba.analyse.extract_tags(sentence, topK=20, withWeight=False, allowPOS=()) sentence 为待提取的文本 topK 为返回几个 TF/IDF 权重最大的关键词,默认值为 20 withWeight 为是否一并返回关键词权重值,默认值为 False allowPOS 仅包括指定词性的词,默认值为空,即不筛选 jieba.analyse.TFIDF(idf_path=None) 新建 TFIDF 实例,idf_path 为 IDF 频率文件 代码示例 (关键词提取) https://github.com/fxsjy/jieba/blob/master/test/extract_tags.py 关键词提取所使用逆向文件频率(IDF)文本语料库可以切换成自定义语料库的路径 用法: jieba.analyse.set_idf_path(file_name) file_name为自定义语料库的路径 自定义语料库示例:https://github.com/fxsjy/jieba/blob/master/extra_dict/idf.txt.big 用法示例:https://github.com/fxsjy/jieba/blob/master/test/extract_tags_idfpath.py 关键词提取所使用停止词(Stop Words)文本语料库可以切换成自定义语料库的路径 用法: jieba.analyse.set_stop_words(file_name) file_name为自定义语料库的路径 自定义语料库示例:https://github.com/fxsjy/jieba/blob/master/extra_dict/stop_words.txt 用法示例:https://github.com/fxsjy/jieba/blob/master/test/extract_tags_stop_words.py 关键词一并返回关键词权重值示例 用法示例:https://github.com/fxsjy/jieba/blob/master/test/extract_tags_with_weight.py 基于 TextRank 算法的关键词抽取 jieba.analyse.textrank(sentence, topK=20, withWeight=False, allowPOS=(‘ns’, ‘n’, ‘vn’, ‘v’)) 直接使用,接口相同,注意默认过滤词性。 jieba.analyse.TextRank() 新建自定义 TextRank 实例 算法论文: TextRank: Bringing Order into Texts 基本思想: 将待抽取关键词的文本进行分词 以固定窗口大小(默认为5,通过span属性调整),词之间的共现关系,构建图 计算图中节点的PageRank,注意是无向带权图 使用示例: 见 test/demo.py 词性标注 jieba.posseg.POSTokenizer(tokenizer=None) 新建自定义分词器,tokenizer 参数可指定内部使用的 jieba.Tokenizer 分词器。jieba.posseg.dt 为默认词性标注分词器。 标注句子分词后每个词的词性,采用和 ictclas 兼容的标记法。 除了jieba默认分词模式,提供paddle模式下的词性标注功能。paddle模式采用延迟加载方式,通过enable_paddle()安装paddlepaddle-tiny,并且import相关代码; 用法示例 >>> import jieba>>> import jieba.posseg as pseg>>> words = pseg.cut("我爱北京天安门") jieba默认模式>>> jieba.enable_paddle() 启动paddle模式。 0.40版之后开始支持,早期版本不支持>>> words = pseg.cut("我爱北京天安门",use_paddle=True) paddle模式>>> for word, flag in words:... print('%s %s' % (word, flag))...我 r爱 v北京 ns天安门 ns paddle模式词性标注对应表如下: paddle模式词性和专名类别标签集合如下表,其中词性标签 24 个(小写字母),专名类别标签 4 个(大写字母)。 标签 含义 标签 含义 标签 含义 标签 含义 n 普通名词 f 方位名词 s 处所名词 t 时间 nr 人名 ns 地名 nt 机构名 nw 作品名 nz 其他专名 v 普通动词 vd 动副词 vn 名动词 a 形容词 ad 副形词 an 名形词 d 副词 m 数量词 q 量词 r 代词 p 介词 c 连词 u 助词 xc 其他虚词 w 标点符号 PER 人名 LOC 地名 ORG 机构名 TIME 时间 并行分词 原理:将目标文本按行分隔后,把各行文本分配到多个 Python 进程并行分词,然后归并结果,从而获得分词速度的可观提升 基于 python 自带的 multiprocessing 模块,目前暂不支持 Windows 用法: jieba.enable_parallel(4) 开启并行分词模式,参数为并行进程数 jieba.disable_parallel() 关闭并行分词模式 例子:https://github.com/fxsjy/jieba/blob/master/test/parallel/test_file.py 实验结果:在 4 核 3.4GHz Linux 机器上,对金庸全集进行精确分词,获得了 1MB/s 的速度,是单进程版的 3.3 倍。 注意:并行分词仅支持默认分词器 jieba.dt 和 jieba.posseg.dt。 Tokenize:返回词语在原文的起止位置 注意,输入参数只接受 unicode 默认模式 result = jieba.tokenize(u'永和服装饰品有限公司')for tk in result:print("word %s\t\t start: %d \t\t end:%d" % (tk[0],tk[1],tk[2])) word 永和 start: 0 end:2word 服装 start: 2 end:4word 饰品 start: 4 end:6word 有限公司 start: 6 end:10 搜索模式 result = jieba.tokenize(u'永和服装饰品有限公司', mode='search')for tk in result:print("word %s\t\t start: %d \t\t end:%d" % (tk[0],tk[1],tk[2])) word 永和 start: 0 end:2word 服装 start: 2 end:4word 饰品 start: 4 end:6word 有限 start: 6 end:8word 公司 start: 8 end:10word 有限公司 start: 6 end:10 ChineseAnalyzer for Whoosh 搜索引擎 引用: from jieba.analyse import ChineseAnalyzer 用法示例:https://github.com/fxsjy/jieba/blob/master/test/test_whoosh.py 命令行分词 使用示例:python -m jieba news.txt > cut_result.txt 命令行选项(翻译): 使用: python -m jieba [options] filename结巴命令行界面。固定参数:filename 输入文件可选参数:-h, --help 显示此帮助信息并退出-d [DELIM], --delimiter [DELIM]使用 DELIM 分隔词语,而不是用默认的' / '。若不指定 DELIM,则使用一个空格分隔。-p [DELIM], --pos [DELIM]启用词性标注;如果指定 DELIM,词语和词性之间用它分隔,否则用 _ 分隔-D DICT, --dict DICT 使用 DICT 代替默认词典-u USER_DICT, --user-dict USER_DICT使用 USER_DICT 作为附加词典,与默认词典或自定义词典配合使用-a, --cut-all 全模式分词(不支持词性标注)-n, --no-hmm 不使用隐含马尔可夫模型-q, --quiet 不输出载入信息到 STDERR-V, --version 显示版本信息并退出如果没有指定文件名,则使用标准输入。 --help 选项输出: $> python -m jieba --helpJieba command line interface.positional arguments:filename input fileoptional arguments:-h, --help show this help message and exit-d [DELIM], --delimiter [DELIM]use DELIM instead of ' / ' for word delimiter; or aspace if it is used without DELIM-p [DELIM], --pos [DELIM]enable POS tagging; if DELIM is specified, use DELIMinstead of '_' for POS delimiter-D DICT, --dict DICT use DICT as dictionary-u USER_DICT, --user-dict USER_DICTuse USER_DICT together with the default dictionary orDICT (if specified)-a, --cut-all full pattern cutting (ignored with POS tagging)-n, --no-hmm don't use the Hidden Markov Model-q, --quiet don't print loading messages to stderr-V, --version show program's version number and exitIf no filename specified, use STDIN instead. 延迟加载机制 jieba 采用延迟加载,import jieba 和 jieba.Tokenizer() 不会立即触发词典的加载,一旦有必要才开始加载词典构建前缀字典。如果你想手工初始 jieba,也可以手动初始化。 import jiebajieba.initialize() 手动初始化(可选) 在 0.28 之前的版本是不能指定主词典的路径的,有了延迟加载机制后,你可以改变主词典的路径: jieba.set_dictionary('data/dict.txt.big') 例子: https://github.com/fxsjy/jieba/blob/master/test/test_change_dictpath.py 其他词典 占用内存较小的词典文件 https://github.com/fxsjy/jieba/raw/master/extra_dict/dict.txt.small 支持繁体分词更好的词典文件 https://github.com/fxsjy/jieba/raw/master/extra_dict/dict.txt.big 下载你所需要的词典,然后覆盖 jieba/dict.txt 即可;或者用 jieba.set_dictionary('data/dict.txt.big') 其他语言实现 结巴分词 Java 版本 作者:piaolingxue 地址:https://github.com/huaban/jieba-analysis 结巴分词 C++ 版本 作者:yanyiwu 地址:https://github.com/yanyiwu/cppjieba 结巴分词 Rust 版本 作者:messense, MnO2 地址:https://github.com/messense/jieba-rs 结巴分词 Node.js 版本 作者:yanyiwu 地址:https://github.com/yanyiwu/nodejieba 结巴分词 Erlang 版本 作者:falood 地址:https://github.com/falood/exjieba 结巴分词 R 版本 作者:qinwf 地址:https://github.com/qinwf/jiebaR 结巴分词 iOS 版本 作者:yanyiwu 地址:https://github.com/yanyiwu/iosjieba 结巴分词 PHP 版本 作者:fukuball 地址:https://github.com/fukuball/jieba-php 结巴分词 .NET(C) 版本 作者:anderscui 地址:https://github.com/anderscui/jieba.NET/ 结巴分词 Go 版本 作者: wangbin 地址: https://github.com/wangbin/jiebago 作者: yanyiwu 地址: https://github.com/yanyiwu/gojieba 结巴分词Android版本 作者 Dongliang.W 地址:https://github.com/452896915/jieba-android 友情链接 https://github.com/baidu/lac 百度中文词法分析(分词+词性+专名)系统 https://github.com/baidu/AnyQ 百度FAQ自动问答系统 https://github.com/baidu/Senta 百度情感识别系统 系统集成 Solr: https://github.com/sing1ee/jieba-solr 分词速度 1.5 MB / Second in Full Mode 400 KB / Second in Default Mode 测试环境: Intel® Core™ i7-2600 CPU @ 3.4GHz;《围城》.txt 常见问题 1. 模型的数据是如何生成的? 详见: https://github.com/fxsjy/jieba/issues/7 2. “台中”总是被切成“台 中”?(以及类似情况) P(台中) < P(台)×P(中),“台中”词频不够导致其成词概率较低 解决方法:强制调高词频 jieba.add_word('台中') 或者 jieba.suggest_freq('台中', True) 3. “今天天气 不错”应该被切成“今天 天气 不错”?(以及类似情况) 解决方法:强制调低词频 jieba.suggest_freq(('今天', '天气'), True) 或者直接删除该词 jieba.del_word('今天天气') 4. 切出了词典中没有的词语,效果不理想? 解决方法:关闭新词发现 jieba.cut('丰田太省了', HMM=False) jieba.cut('我们中出了一个叛徒', HMM=False) 更多问题请点击:https://github.com/fxsjy/jieba/issues?sort=updated&state=closed 修订历史 https://github.com/fxsjy/jieba/blob/master/Changelog jieba “Jieba” (Chinese for “to stutter”) Chinese text segmentation: built to be the best Python Chinese word segmentation module. Features Support three types of segmentation mode: Accurate Mode attempts to cut the sentence into the most accurate segmentations, which is suitable for text analysis. Full Mode gets all the possible words from the sentence. Fast but not accurate. Search Engine Mode, based on the Accurate Mode, attempts to cut long words into several short words, which can raise the recall rate. Suitable for search engines. Supports Traditional Chinese Supports customized dictionaries MIT License Online demo http://jiebademo.ap01.aws.af.cm/ (Powered by Appfog) Usage Fully automatic installation: easy_install jieba or pip install jieba Semi-automatic installation: Download http://pypi.python.org/pypi/jieba/ , run python setup.py install after extracting. Manual installation: place the jieba directory in the current directory or python site-packages directory. import jieba. Algorithm Based on a prefix dictionary structure to achieve efficient word graph scanning. Build a directed acyclic graph (DAG) for all possible word combinations. Use dynamic programming to find the most probable combination based on the word frequency. For unknown words, a HMM-based model is used with the Viterbi algorithm. Main Functions Cut The jieba.cut function accepts three input parameters: the first parameter is the string to be cut; the second parameter is cut_all, controlling the cut mode; the third parameter is to control whether to use the Hidden Markov Model. jieba.cut_for_search accepts two parameter: the string to be cut; whether to use the Hidden Markov Model. This will cut the sentence into short words suitable for search engines. The input string can be an unicode/str object, or a str/bytes object which is encoded in UTF-8 or GBK. Note that using GBK encoding is not recommended because it may be unexpectly decoded as UTF-8. jieba.cut and jieba.cut_for_search returns an generator, from which you can use a for loop to get the segmentation result (in unicode). jieba.lcut and jieba.lcut_for_search returns a list. jieba.Tokenizer(dictionary=DEFAULT_DICT) creates a new customized Tokenizer, which enables you to use different dictionaries at the same time. jieba.dt is the default Tokenizer, to which almost all global functions are mapped. Code example: segmentation encoding=utf-8import jiebaseg_list = jieba.cut("我来到北京清华大学", cut_all=True)print("Full Mode: " + "/ ".join(seg_list)) 全模式seg_list = jieba.cut("我来到北京清华大学", cut_all=False)print("Default Mode: " + "/ ".join(seg_list)) 默认模式seg_list = jieba.cut("他来到了网易杭研大厦")print(", ".join(seg_list))seg_list = jieba.cut_for_search("小明硕士毕业于中国科学院计算所,后在日本京都大学深造") 搜索引擎模式print(", ".join(seg_list)) Output: [Full Mode]: 我/ 来到/ 北京/ 清华/ 清华大学/ 华大/ 大学[Accurate Mode]: 我/ 来到/ 北京/ 清华大学[Unknown Words Recognize] 他, 来到, 了, 网易, 杭研, 大厦 (In this case, "杭研" is not in the dictionary, but is identified by the Viterbi algorithm)[Search Engine Mode]: 小明, 硕士, 毕业, 于, 中国, 科学, 学院, 科学院, 中国科学院, 计算, 计算所, 后, 在, 日本, 京都, 大学, 日本京都大学, 深造 Add a custom dictionary Load dictionary Developers can specify their own custom dictionary to be included in the jieba default dictionary. Jieba is able to identify new words, but you can add your own new words can ensure a higher accuracy. Usage: jieba.load_userdict(file_name) file_name is a file-like object or the path of the custom dictionary The dictionary format is the same as that of dict.txt: one word per line; each line is divided into three parts separated by a space: word, word frequency, POS tag. If file_name is a path or a file opened in binary mode, the dictionary must be UTF-8 encoded. The word frequency and POS tag can be omitted respectively. The word frequency will be filled with a suitable value if omitted. For example: 创新办 3 i云计算 5凱特琳 nz台中 Change a Tokenizer’s tmp_dir and cache_file to specify the path of the cache file, for using on a restricted file system. Example: 云计算 5李小福 2创新办 3[Before]: 李小福 / 是 / 创新 / 办 / 主任 / 也 / 是 / 云 / 计算 / 方面 / 的 / 专家 /[After]: 李小福 / 是 / 创新办 / 主任 / 也 / 是 / 云计算 / 方面 / 的 / 专家 / Modify dictionary Use add_word(word, freq=None, tag=None) and del_word(word) to modify the dictionary dynamically in programs. Use suggest_freq(segment, tune=True) to adjust the frequency of a single word so that it can (or cannot) be segmented. Note that HMM may affect the final result. Example: >>> print('/'.join(jieba.cut('如果放到post中将出错。', HMM=False)))如果/放到/post/中将/出错/。>>> jieba.suggest_freq(('中', '将'), True)494>>> print('/'.join(jieba.cut('如果放到post中将出错。', HMM=False)))如果/放到/post/中/将/出错/。>>> print('/'.join(jieba.cut('「台中」正确应该不会被切开', HMM=False)))「/台/中/」/正确/应该/不会/被/切开>>> jieba.suggest_freq('台中', True)69>>> print('/'.join(jieba.cut('「台中」正确应该不会被切开', HMM=False)))「/台中/」/正确/应该/不会/被/切开 Keyword Extraction import jieba.analyse jieba.analyse.extract_tags(sentence, topK=20, withWeight=False, allowPOS=()) sentence: the text to be extracted topK: return how many keywords with the highest TF/IDF weights. The default value is 20 withWeight: whether return TF/IDF weights with the keywords. The default value is False allowPOS: filter words with which POSs are included. Empty for no filtering. jieba.analyse.TFIDF(idf_path=None) creates a new TFIDF instance, idf_path specifies IDF file path. Example (keyword extraction) https://github.com/fxsjy/jieba/blob/master/test/extract_tags.py Developers can specify their own custom IDF corpus in jieba keyword extraction Usage: jieba.analyse.set_idf_path(file_name) file_name is the path for the custom corpus Custom Corpus Sample:https://github.com/fxsjy/jieba/blob/master/extra_dict/idf.txt.big Sample Code:https://github.com/fxsjy/jieba/blob/master/test/extract_tags_idfpath.py Developers can specify their own custom stop words corpus in jieba keyword extraction Usage: jieba.analyse.set_stop_words(file_name) file_name is the path for the custom corpus Custom Corpus Sample:https://github.com/fxsjy/jieba/blob/master/extra_dict/stop_words.txt Sample Code:https://github.com/fxsjy/jieba/blob/master/test/extract_tags_stop_words.py There’s also a TextRank implementation available. Use: jieba.analyse.textrank(sentence, topK=20, withWeight=False, allowPOS=('ns', 'n', 'vn', 'v')) Note that it filters POS by default. jieba.analyse.TextRank() creates a new TextRank instance. Part of Speech Tagging jieba.posseg.POSTokenizer(tokenizer=None) creates a new customized Tokenizer. tokenizer specifies the jieba.Tokenizer to internally use. jieba.posseg.dt is the default POSTokenizer. Tags the POS of each word after segmentation, using labels compatible with ictclas. Example: >>> import jieba.posseg as pseg>>> words = pseg.cut("我爱北京天安门")>>> for w in words:... print('%s %s' % (w.word, w.flag))...我 r爱 v北京 ns天安门 ns Parallel Processing Principle: Split target text by line, assign the lines into multiple Python processes, and then merge the results, which is considerably faster. Based on the multiprocessing module of Python. Usage: jieba.enable_parallel(4) Enable parallel processing. The parameter is the number of processes. jieba.disable_parallel() Disable parallel processing. Example: https://github.com/fxsjy/jieba/blob/master/test/parallel/test_file.py Result: On a four-core 3.4GHz Linux machine, do accurate word segmentation on Complete Works of Jin Yong, and the speed reaches 1MB/s, which is 3.3 times faster than the single-process version. Note that parallel processing supports only default tokenizers, jieba.dt and jieba.posseg.dt. Tokenize: return words with position The input must be unicode Default mode result = jieba.tokenize(u'永和服装饰品有限公司')for tk in result:print("word %s\t\t start: %d \t\t end:%d" % (tk[0],tk[1],tk[2])) word 永和 start: 0 end:2word 服装 start: 2 end:4word 饰品 start: 4 end:6word 有限公司 start: 6 end:10 Search mode result = jieba.tokenize(u'永和服装饰品有限公司',mode='search')for tk in result:print("word %s\t\t start: %d \t\t end:%d" % (tk[0],tk[1],tk[2])) word 永和 start: 0 end:2word 服装 start: 2 end:4word 饰品 start: 4 end:6word 有限 start: 6 end:8word 公司 start: 8 end:10word 有限公司 start: 6 end:10 ChineseAnalyzer for Whoosh from jieba.analyse import ChineseAnalyzer Example: https://github.com/fxsjy/jieba/blob/master/test/test_whoosh.py Command Line Interface $> python -m jieba --helpJieba command line interface.positional arguments:filename input fileoptional arguments:-h, --help show this help message and exit-d [DELIM], --delimiter [DELIM]use DELIM instead of ' / ' for word delimiter; or aspace if it is used without DELIM-p [DELIM], --pos [DELIM]enable POS tagging; if DELIM is specified, use DELIMinstead of '_' for POS delimiter-D DICT, --dict DICT use DICT as dictionary-u USER_DICT, --user-dict USER_DICTuse USER_DICT together with the default dictionary orDICT (if specified)-a, --cut-all full pattern cutting (ignored with POS tagging)-n, --no-hmm don't use the Hidden Markov Model-q, --quiet don't print loading messages to stderr-V, --version show program's version number and exitIf no filename specified, use STDIN instead. Initialization By default, Jieba don’t build the prefix dictionary unless it’s necessary. This takes 1-3 seconds, after which it is not initialized again. If you want to initialize Jieba manually, you can call: import jiebajieba.initialize() (optional) You can also specify the dictionary (not supported before version 0.28) : jieba.set_dictionary('data/dict.txt.big') Using Other Dictionaries It is possible to use your own dictionary with Jieba, and there are also two dictionaries ready for download: A smaller dictionary for a smaller memory footprint: https://github.com/fxsjy/jieba/raw/master/extra_dict/dict.txt.small There is also a bigger dictionary that has better support for traditional Chinese (繁體): https://github.com/fxsjy/jieba/raw/master/extra_dict/dict.txt.big By default, an in-between dictionary is used, called dict.txt and included in the distribution. In either case, download the file you want, and then call jieba.set_dictionary('data/dict.txt.big') or just replace the existing dict.txt. Segmentation speed 1.5 MB / Second in Full Mode 400 KB / Second in Default Mode Test Env: Intel® Core™ i7-2600 CPU @ 3.4GHz;《围城》.txt 本篇文章为转载内容。原文链接:https://blog.csdn.net/yegeli/article/details/107246661。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-12-02 10:38:37
501
转载
转载文章
...模并行处理系统)架构模式 支持OLTP应用,读写性能可扩展 集群级别的ACID特性 多租户安全 也可被用作分布式Key-Value存储 事务处理与数据分析处理混合型数据库 支持丰富的SQL语句类型,比如:关联子查询 支持绝大部分PostgreSQL的SQL语句 分布式多版本并发控制(MVCC:Multi-version Concurrency Control) 支持JSON和XML格式 Postgres-XL缺少的功能 内建的高可用机制 使用外部机制实现高可能,如:Corosync/Pacemaker 有未来功能提升的空间 增加节点/重新分片数据(re-shard)的简便性 数据重分布(redistribution)期间会锁表 可采用预分片(pre-shard)方式解决,在同台物理服务器上建立多个数据节点,每个节点存储一个数据分片。数据重分布时,将一些数据节点迁出即可 某些外键、唯一性约束功能 Postgres-XL架构 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-M9lFuEIP-1640133702200)(./assets/postgre-xl.jpg)] 基于开源项目Postgres-XC XL增加了MPP,允许数据节点间直接通讯,交换复杂跨节点关联查询相关数据信息,减少协调器负载。 多个协调器(Coordinator) 应用程序的数据库连入点 分析查询语句,生成执行计划 多个数据节点(DataNode) 实际的数据存储 数据自动打散分布到集群中各数据节点 本地执行查询 一个查询在所有相关节点上并行查询 全局事务管理器(GTM:Global Transaction Manager) 提供事务间一致性视图 部署GTM Proxy实例,以提高性能 Postgre-XL主要组件 GTM (Global Transaction Manager) - 全局事务管理器 GTM是Postgres-XL的一个关键组件,用于提供一致的事务管理和元组可见性控制。 GTM Standby GTM的备节点,在pgxc,pgxl中,GTM控制所有的全局事务分配,如果出现问题,就会导致整个集群不可用,为了增加可用性,增加该备用节点。当GTM出现问题时,GTM Standby可以升级为GTM,保证集群正常工作。 GTM-Proxy GTM需要与所有的Coordinators通信,为了降低压力,可以在每个Coordinator机器上部署一个GTM-Proxy。 Coordinator --协调器 协调器是应用程序到数据库的接口。它的作用类似于传统的PostgreSQL后台进程,但是协调器不存储任何实际数据。实际数据由数据节点存储。协调器接收SQL语句,根据需要获取全局事务Id和全局快照,确定涉及哪些数据节点,并要求它们执行(部分)语句。当向数据节点发出语句时,它与GXID和全局快照相关联,以便多版本并发控制(MVCC)属性扩展到集群范围。 Datanode --数据节点 用于实际存储数据。表可以分布在各个数据节点之间,也可以复制到所有数据节点。数据节点没有整个数据库的全局视图,它只负责本地存储的数据。接下来,协调器将检查传入语句,并制定子计划。然后,根据需要将这些数据连同GXID和全局快照一起传输到涉及的每个数据节点。数据节点可以在不同的会话中接收来自各个协调器的请求。但是,由于每个事务都是惟一标识的,并且与一致的(全局)快照相关联,所以每个数据节点都可以在其事务和快照上下文中正确执行。 Postgres-XL继承了PostgreSQL Postgres-XL是PostgreSQL的扩展并继承了其很多特性: 复杂查询 外键 触发器 视图 事务 MVCC(多版本控制) 此外,类似于PostgreSQL,用户可以通过多种方式扩展Postgres-XL,例如添加新的 数据类型 函数 操作 聚合函数 索引类型 过程语言 安装 环境说明 由于资源有限,gtm一台、另外两台身兼数职。 主机名 IP 角色 端口 nodename 数据目录 gtm 192.168.20.132 GTM 6666 gtm /nodes/gtm 协调器 5432 coord1 /nodes/coordinator xl1 192.168.20.133 数据节点 5433 node1 /nodes/pgdata gtm代理 6666 gtmpoxy01 /nodes/gtm_pxy1 协调器 5432 coord2 /nodes/coordinator xl2 192.168.20.134 数据节点 5433 node2 /nodes/pgdata gtm代理 6666 gtmpoxy02 /nodes/gtm_pxy2 要求 GNU make版本 3.8及以上版本 [root@pg ~] make --versionGNU Make 3.82Built for x86_64-redhat-linux-gnuCopyright (C) 2010 Free Software Foundation, Inc.License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>This is free software: you are free to change and redistribute it.There is NO WARRANTY, to the extent permitted by law. 需安装GCC包 需安装tar包 用于解压缩文件 默认需要GNU Readline library 其作用是可以让psql命令行记住执行过的命令,并且可以通过键盘上下键切换命令。但是可以通过--without-readline禁用这个特性,或者可以指定--withlibedit-preferred选项来使用libedit 默认使用zlib压缩库 可通过--without-zlib选项来禁用 配置hosts 所有主机上都配置 [root@xl2 11] cat /etc/hosts127.0.0.1 localhost192.168.20.132 gtm192.168.20.133 xl1192.168.20.134 xl2 关闭防火墙、Selinux 所有主机都执行 关闭防火墙: [root@gtm ~] systemctl stop firewalld.service[root@gtm ~] systemctl disable firewalld.service selinux设置: [root@gtm ~]vim /etc/selinux/config 设置SELINUX=disabled,保存退出。 This file controls the state of SELinux on the system. SELINUX= can take one of these three values: enforcing - SELinux security policy is enforced. permissive - SELinux prints warnings instead of enforcing. disabled - No SELinux policy is loaded.SELINUX=disabled SELINUXTYPE= can take one of three two values: targeted - Targeted processes are protected, minimum - Modification of targeted policy. Only selected processes are protected. mls - Multi Level Security protection. 安装依赖包 所有主机上都执行 yum install -y flex bison readline-devel zlib-devel openjade docbook-style-dsssl gcc 创建用户 所有主机上都执行 [root@gtm ~] useradd postgres[root@gtm ~] passwd postgres[root@gtm ~] su - postgres[root@gtm ~] mkdir ~/.ssh[root@gtm ~] chmod 700 ~/.ssh 配置SSH免密登录 仅仅在gtm节点配置如下操作: [root@gtm ~] su - postgres[postgres@gtm ~] ssh-keygen -t rsa[postgres@gtm ~] cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys[postgres@gtm ~] chmod 600 ~/.ssh/authorized_keys 将刚生成的认证文件拷贝到xl1到xl2中,使得gtm节点可以免密码登录xl1~xl2的任意一个节点: [postgres@gtm ~] scp ~/.ssh/authorized_keys postgres@xl1:~/.ssh/[postgres@gtm ~] scp ~/.ssh/authorized_keys postgres@xl2:~/.ssh/ 对所有提示都不要输入,直接enter下一步。直到最后,因为第一次要求输入目标机器的用户密码,输入即可。 下载源码 下载地址:https://www.postgres-xl.org/download/ [root@slave ~] ll postgres-xl-10r1.1.tar.gz-rw-r--r-- 1 root root 28121666 May 30 05:21 postgres-xl-10r1.1.tar.gz 编译、安装Postgres-XL 所有节点都安装,编译需要一点时间,最好同时进行编译。 [root@slave ~] tar xvf postgres-xl-10r1.1.tar.gz[root@slave ~] ./configure --prefix=/home/postgres/pgxl/[root@slave ~] make[root@slave ~] make install[root@slave ~] cd contrib/ --安装必要的工具,在gtm节点上安装即可[root@slave ~] make[root@slave ~] make install 配置环境变量 所有节点都要配置 进入postgres用户,修改其环境变量,开始编辑 [root@gtm ~]su - postgres[postgres@gtm ~]vi .bashrc --不是.bash_profile 在打开的文件末尾,新增如下变量配置: export PGHOME=/home/postgres/pgxlexport LD_LIBRARY_PATH=$PGHOME/lib:$LD_LIBRARY_PATHexport PATH=$PGHOME/bin:$PATH 按住esc,然后输入:wq!保存退出。输入以下命令对更改重启生效。 [postgres@gtm ~] source .bashrc --不是.bash_profile 输入以下语句,如果输出变量结果,代表生效 [postgres@gtm ~] echo $PGHOME 应该输出/home/postgres/pgxl代表生效 配置集群 生成pgxc_ctl.conf配置文件 [postgres@gtm ~] pgxc_ctl prepare/bin/bashInstalling pgxc_ctl_bash script as /home/postgres/pgxl/pgxc_ctl/pgxc_ctl_bash.ERROR: File "/home/postgres/pgxl/pgxc_ctl/pgxc_ctl.conf" not found or not a regular file. No such file or directoryInstalling pgxc_ctl_bash script as /home/postgres/pgxl/pgxc_ctl/pgxc_ctl_bash.Reading configuration using /home/postgres/pgxl/pgxc_ctl/pgxc_ctl_bash --home /home/postgres/pgxl/pgxc_ctl --configuration /home/postgres/pgxl/pgxc_ctl/pgxc_ctl.confFinished reading configuration. PGXC_CTL START Current directory: /home/postgres/pgxl/pgxc_ctl 配置pgxc_ctl.conf 新建/home/postgres/pgxc_ctl/pgxc_ctl.conf文件,编辑如下: 对着模板文件一个一个修改,否则会造成初始化过程出现各种神奇问题。 pgxcInstallDir=$PGHOMEpgxlDATA=$PGHOME/data pgxcOwner=postgres---- GTM Master -----------------------------------------gtmName=gtmgtmMasterServer=gtmgtmMasterPort=6666gtmMasterDir=$pgxlDATA/nodes/gtmgtmSlave=y Specify y if you configure GTM Slave. Otherwise, GTM slave will not be configured and all the following variables will be reset.gtmSlaveName=gtmSlavegtmSlaveServer=gtm value none means GTM slave is not available. Give none if you don't configure GTM Slave.gtmSlavePort=20001 Not used if you don't configure GTM slave.gtmSlaveDir=$pgxlDATA/nodes/gtmSlave Not used if you don't configure GTM slave.---- GTM-Proxy Master -------gtmProxyDir=$pgxlDATA/nodes/gtm_proxygtmProxy=y gtmProxyNames=(gtm_pxy1 gtm_pxy2) gtmProxyServers=(xl1 xl2) gtmProxyPorts=(6666 6666) gtmProxyDirs=($gtmProxyDir $gtmProxyDir) ---- Coordinators ---------coordMasterDir=$pgxlDATA/nodes/coordcoordNames=(coord1 coord2) coordPorts=(5432 5432) poolerPorts=(6667 6667) coordPgHbaEntries=(0.0.0.0/0)coordMasterServers=(xl1 xl2) coordMasterDirs=($coordMasterDir $coordMasterDir)coordMaxWALsernder=0 没设置备份节点,设置为0coordMaxWALSenders=($coordMaxWALsernder $coordMaxWALsernder) 数量保持和coordMasterServers一致coordSlave=n---- Datanodes ----------datanodeMasterDir=$pgxlDATA/nodes/dn_masterprimaryDatanode=xl1 主数据节点datanodeNames=(node1 node2)datanodePorts=(5433 5433) datanodePoolerPorts=(6668 6668) datanodePgHbaEntries=(0.0.0.0/0)datanodeMasterServers=(xl1 xl2)datanodeMasterDirs=($datanodeMasterDir $datanodeMasterDir)datanodeMaxWalSender=4datanodeMaxWALSenders=($datanodeMaxWalSender $datanodeMaxWalSender) 集群初始化,启动,停止 初始化 pgxc_ctl -c /home/postgres/pgxc_ctl/pgxc_ctl.conf init all 输出结果: /bin/bashInstalling pgxc_ctl_bash script as /home/postgres/pgxc_ctl/pgxc_ctl_bash.Installing pgxc_ctl_bash script as /home/postgres/pgxc_ctl/pgxc_ctl_bash.Reading configuration using /home/postgres/pgxc_ctl/pgxc_ctl_bash --home /home/postgres/pgxc_ctl --configuration /home/postgres/pgxc_ctl/pgxc_ctl.conf/home/postgres/pgxc_ctl/pgxc_ctl.conf: line 189: $coordExtraConfig: ambiguous redirectFinished reading configuration. PGXC_CTL START Current directory: /home/postgres/pgxc_ctlStopping all the coordinator masters.Stopping coordinator master coord1.Stopping coordinator master coord2.pg_ctl: directory "/home/postgres/pgxc/nodes/coord/coord1" does not existpg_ctl: directory "/home/postgres/pgxc/nodes/coord/coord2" does not existDone.Stopping all the datanode masters.Stopping datanode master datanode1.Stopping datanode master datanode2.pg_ctl: PID file "/home/postgres/pgxc/nodes/datanode/datanode1/postmaster.pid" does not existIs server running?Done.Stop GTM masterwaiting for server to shut down.... doneserver stopped[postgres@gtm ~]$ echo $PGHOME/home/postgres/pgxl[postgres@gtm ~]$ ll /home/postgres/pgxl/pgxc/nodes/gtm/gtm.^C[postgres@gtm ~]$ pgxc_ctl -c /home/postgres/pgxc_ctl/pgxc_ctl.conf init all/bin/bashInstalling pgxc_ctl_bash script as /home/postgres/pgxc_ctl/pgxc_ctl_bash.Installing pgxc_ctl_bash script as /home/postgres/pgxc_ctl/pgxc_ctl_bash.Reading configuration using /home/postgres/pgxc_ctl/pgxc_ctl_bash --home /home/postgres/pgxc_ctl --configuration /home/postgres/pgxc_ctl/pgxc_ctl.conf/home/postgres/pgxc_ctl/pgxc_ctl.conf: line 189: $coordExtraConfig: ambiguous redirectFinished reading configuration. PGXC_CTL START Current directory: /home/postgres/pgxc_ctlInitialize GTM masterERROR: target directory (/home/postgres/pgxc/nodes/gtm) exists and not empty. Skip GTM initilializationDone.Start GTM masterserver startingInitialize all the coordinator masters.Initialize coordinator master coord1.ERROR: target coordinator master coord1 is running now. Skip initilialization.Initialize coordinator master coord2.The files belonging to this database system will be owned by user "postgres".This user must also own the server process.The database cluster will be initialized with locale "en_US.UTF-8".The default database encoding has accordingly been set to "UTF8".The default text search configuration will be set to "english".Data page checksums are disabled.fixing permissions on existing directory /home/postgres/pgxc/nodes/coord/coord2 ... okcreating subdirectories ... okselecting default max_connections ... 100selecting default shared_buffers ... 128MBselecting dynamic shared memory implementation ... posixcreating configuration files ... okrunning bootstrap script ... okperforming post-bootstrap initialization ... creating cluster information ... oksyncing data to disk ... okfreezing database template0 ... okfreezing database template1 ... okfreezing database postgres ... okWARNING: enabling "trust" authentication for local connectionsYou can change this by editing pg_hba.conf or using the option -A, or--auth-local and --auth-host, the next time you run initdb.Success.Done.Starting coordinator master.Starting coordinator master coord1ERROR: target coordinator master coord1 is already running now. Skip initialization.Starting coordinator master coord22019-05-30 21:09:25.562 EDT [2148] LOG: listening on IPv4 address "0.0.0.0", port 54322019-05-30 21:09:25.562 EDT [2148] LOG: listening on IPv6 address "::", port 54322019-05-30 21:09:25.563 EDT [2148] LOG: listening on Unix socket "/tmp/.s.PGSQL.5432"2019-05-30 21:09:25.601 EDT [2149] LOG: database system was shut down at 2019-05-30 21:09:22 EDT2019-05-30 21:09:25.605 EDT [2148] LOG: database system is ready to accept connections2019-05-30 21:09:25.612 EDT [2156] LOG: cluster monitor startedDone.Initialize all the datanode masters.Initialize the datanode master datanode1.Initialize the datanode master datanode2.The files belonging to this database system will be owned by user "postgres".This user must also own the server process.The database cluster will be initialized with locale "en_US.UTF-8".The default database encoding has accordingly been set to "UTF8".The default text search configuration will be set to "english".Data page checksums are disabled.fixing permissions on existing directory /home/postgres/pgxc/nodes/datanode/datanode1 ... okcreating subdirectories ... okselecting default max_connections ... 100selecting default shared_buffers ... 128MBselecting dynamic shared memory implementation ... posixcreating configuration files ... okrunning bootstrap script ... okperforming post-bootstrap initialization ... creating cluster information ... oksyncing data to disk ... okfreezing database template0 ... okfreezing database template1 ... okfreezing database postgres ... okWARNING: enabling "trust" authentication for local connectionsYou can change this by editing pg_hba.conf or using the option -A, or--auth-local and --auth-host, the next time you run initdb.Success.The files belonging to this database system will be owned by user "postgres".This user must also own the server process.The database cluster will be initialized with locale "en_US.UTF-8".The default database encoding has accordingly been set to "UTF8".The default text search configuration will be set to "english".Data page checksums are disabled.fixing permissions on existing directory /home/postgres/pgxc/nodes/datanode/datanode2 ... okcreating subdirectories ... okselecting default max_connections ... 100selecting default shared_buffers ... 128MBselecting dynamic shared memory implementation ... posixcreating configuration files ... okrunning bootstrap script ... okperforming post-bootstrap initialization ... creating cluster information ... oksyncing data to disk ... okfreezing database template0 ... okfreezing database template1 ... okfreezing database postgres ... okWARNING: enabling "trust" authentication for local connectionsYou can change this by editing pg_hba.conf or using the option -A, or--auth-local and --auth-host, the next time you run initdb.Success.Done.Starting all the datanode masters.Starting datanode master datanode1.WARNING: datanode master datanode1 is running now. Skipping.Starting datanode master datanode2.2019-05-30 21:09:33.352 EDT [2404] LOG: listening on IPv4 address "0.0.0.0", port 154322019-05-30 21:09:33.352 EDT [2404] LOG: listening on IPv6 address "::", port 154322019-05-30 21:09:33.355 EDT [2404] LOG: listening on Unix socket "/tmp/.s.PGSQL.15432"2019-05-30 21:09:33.392 EDT [2404] LOG: redirecting log output to logging collector process2019-05-30 21:09:33.392 EDT [2404] HINT: Future log output will appear in directory "pg_log".Done.psql: FATAL: no pg_hba.conf entry for host "192.168.20.132", user "postgres", database "postgres"psql: FATAL: no pg_hba.conf entry for host "192.168.20.132", user "postgres", database "postgres"Done.psql: FATAL: no pg_hba.conf entry for host "192.168.20.132", user "postgres", database "postgres"psql: FATAL: no pg_hba.conf entry for host "192.168.20.132", user "postgres", database "postgres"Done.[postgres@gtm ~]$ pgxc_ctl -c /home/postgres/pgxc_ctl/pgxc_ctl.conf stop all/bin/bashInstalling pgxc_ctl_bash script as /home/postgres/pgxc_ctl/pgxc_ctl_bash.Installing pgxc_ctl_bash script as /home/postgres/pgxc_ctl/pgxc_ctl_bash.Reading configuration using /home/postgres/pgxc_ctl/pgxc_ctl_bash --home /home/postgres/pgxc_ctl --configuration /home/postgres/pgxc_ctl/pgxc_ctl.conf/home/postgres/pgxc_ctl/pgxc_ctl.conf: line 189: $coordExtraConfig: ambiguous redirectFinished reading configuration. PGXC_CTL START Current directory: /home/postgres/pgxc_ctlStopping all the coordinator masters.Stopping coordinator master coord1.Stopping coordinator master coord2.pg_ctl: directory "/home/postgres/pgxc/nodes/coord/coord1" does not existDone.Stopping all the datanode masters.Stopping datanode master datanode1.Stopping datanode master datanode2.pg_ctl: PID file "/home/postgres/pgxc/nodes/datanode/datanode1/postmaster.pid" does not existIs server running?Done.Stop GTM masterwaiting for server to shut down.... doneserver stopped[postgres@gtm ~]$ pgxc_ctl/bin/bashInstalling pgxc_ctl_bash script as /home/postgres/pgxc_ctl/pgxc_ctl_bash.Installing pgxc_ctl_bash script as /home/postgres/pgxc_ctl/pgxc_ctl_bash.Reading configuration using /home/postgres/pgxc_ctl/pgxc_ctl_bash --home /home/postgres/pgxc_ctl --configuration /home/postgres/pgxc_ctl/pgxc_ctl.conf/home/postgres/pgxc_ctl/pgxc_ctl.conf: line 189: $coordExtraConfig: ambiguous redirectFinished reading configuration. PGXC_CTL START Current directory: /home/postgres/pgxc_ctlPGXC monitor allNot running: gtm masterRunning: coordinator master coord1Not running: coordinator master coord2Running: datanode master datanode1Not running: datanode master datanode2PGXC stop coordinator master coord1Stopping coordinator master coord1.pg_ctl: directory "/home/postgres/pgxc/nodes/coord/coord1" does not existDone.PGXC stop datanode master datanode1Stopping datanode master datanode1.pg_ctl: PID file "/home/postgres/pgxc/nodes/datanode/datanode1/postmaster.pid" does not existIs server running?Done.PGXC monitor allNot running: gtm masterRunning: coordinator master coord1Not running: coordinator master coord2Running: datanode master datanode1Not running: datanode master datanode2PGXC monitor allNot running: gtm masterNot running: coordinator master coord1Not running: coordinator master coord2Not running: datanode master datanode1Not running: datanode master datanode2PGXC exit[postgres@gtm ~]$ pgxc_ctl -c /home/postgres/pgxc_ctl/pgxc_ctl.conf init all/bin/bashInstalling pgxc_ctl_bash script as /home/postgres/pgxc_ctl/pgxc_ctl_bash.Installing pgxc_ctl_bash script as /home/postgres/pgxc_ctl/pgxc_ctl_bash.Reading configuration using /home/postgres/pgxc_ctl/pgxc_ctl_bash --home /home/postgres/pgxc_ctl --configuration /home/postgres/pgxc_ctl/pgxc_ctl.conf/home/postgres/pgxc_ctl/pgxc_ctl.conf: line 189: $coordExtraConfig: ambiguous redirectFinished reading configuration. PGXC_CTL START Current directory: /home/postgres/pgxc_ctlInitialize GTM masterERROR: target directory (/home/postgres/pgxc/nodes/gtm) exists and not empty. Skip GTM initilializationDone.Start GTM masterserver startingInitialize all the coordinator masters.Initialize coordinator master coord1.Initialize coordinator master coord2.The files belonging to this database system will be owned by user "postgres".This user must also own the server process.The database cluster will be initialized with locale "en_US.UTF-8".The default database encoding has accordingly been set to "UTF8".The default text search configuration will be set to "english".Data page checksums are disabled.fixing permissions on existing directory /home/postgres/pgxc/nodes/coord/coord1 ... okcreating subdirectories ... okselecting default max_connections ... 100selecting default shared_buffers ... 128MBselecting dynamic shared memory implementation ... posixcreating configuration files ... okrunning bootstrap script ... okperforming post-bootstrap initialization ... creating cluster information ... oksyncing data to disk ... okfreezing database template0 ... okfreezing database template1 ... okfreezing database postgres ... okWARNING: enabling "trust" authentication for local connectionsYou can change this by editing pg_hba.conf or using the option -A, or--auth-local and --auth-host, the next time you run initdb.Success.The files belonging to this database system will be owned by user "postgres".This user must also own the server process.The database cluster will be initialized with locale "en_US.UTF-8".The default database encoding has accordingly been set to "UTF8".The default text search configuration will be set to "english".Data page checksums are disabled.fixing permissions on existing directory /home/postgres/pgxc/nodes/coord/coord2 ... okcreating subdirectories ... okselecting default max_connections ... 100selecting default shared_buffers ... 128MBselecting dynamic shared memory implementation ... posixcreating configuration files ... okrunning bootstrap script ... okperforming post-bootstrap initialization ... creating cluster information ... oksyncing data to disk ... okfreezing database template0 ... okfreezing database template1 ... okfreezing database postgres ... okWARNING: enabling "trust" authentication for local connectionsYou can change this by editing pg_hba.conf or using the option -A, or--auth-local and --auth-host, the next time you run initdb.Success.Done.Starting coordinator master.Starting coordinator master coord1Starting coordinator master coord22019-05-30 21:13:03.998 EDT [25137] LOG: listening on IPv4 address "0.0.0.0", port 54322019-05-30 21:13:03.998 EDT [25137] LOG: listening on IPv6 address "::", port 54322019-05-30 21:13:04.000 EDT [25137] LOG: listening on Unix socket "/tmp/.s.PGSQL.5432"2019-05-30 21:13:04.038 EDT [25138] LOG: database system was shut down at 2019-05-30 21:13:00 EDT2019-05-30 21:13:04.042 EDT [25137] LOG: database system is ready to accept connections2019-05-30 21:13:04.049 EDT [25145] LOG: cluster monitor started2019-05-30 21:13:04.020 EDT [2730] LOG: listening on IPv4 address "0.0.0.0", port 54322019-05-30 21:13:04.020 EDT [2730] LOG: listening on IPv6 address "::", port 54322019-05-30 21:13:04.021 EDT [2730] LOG: listening on Unix socket "/tmp/.s.PGSQL.5432"2019-05-30 21:13:04.057 EDT [2731] LOG: database system was shut down at 2019-05-30 21:13:00 EDT2019-05-30 21:13:04.061 EDT [2730] LOG: database system is ready to accept connections2019-05-30 21:13:04.062 EDT [2738] LOG: cluster monitor startedDone.Initialize all the datanode masters.Initialize the datanode master datanode1.Initialize the datanode master datanode2.The files belonging to this database system will be owned by user "postgres".This user must also own the server process.The database cluster will be initialized with locale "en_US.UTF-8".The default database encoding has accordingly been set to "UTF8".The default text search configuration will be set to "english".Data page checksums are disabled.fixing permissions on existing directory /home/postgres/pgxc/nodes/datanode/datanode1 ... okcreating subdirectories ... okselecting default max_connections ... 100selecting default shared_buffers ... 128MBselecting dynamic shared memory implementation ... posixcreating configuration files ... okrunning bootstrap script ... okperforming post-bootstrap initialization ... creating cluster information ... oksyncing data to disk ... okfreezing database template0 ... okfreezing database template1 ... okfreezing database postgres ... okWARNING: enabling "trust" authentication for local connectionsYou can change this by editing pg_hba.conf or using the option -A, or--auth-local and --auth-host, the next time you run initdb.Success.The files belonging to this database system will be owned by user "postgres".This user must also own the server process.The database cluster will be initialized with locale "en_US.UTF-8".The default database encoding has accordingly been set to "UTF8".The default text search configuration will be set to "english".Data page checksums are disabled.fixing permissions on existing directory /home/postgres/pgxc/nodes/datanode/datanode2 ... okcreating subdirectories ... okselecting default max_connections ... 100selecting default shared_buffers ... 128MBselecting dynamic shared memory implementation ... posixcreating configuration files ... okrunning bootstrap script ... okperforming post-bootstrap initialization ... creating cluster information ... oksyncing data to disk ... okfreezing database template0 ... okfreezing database template1 ... okfreezing database postgres ... okWARNING: enabling "trust" authentication for local connectionsYou can change this by editing pg_hba.conf or using the option -A, or--auth-local and --auth-host, the next time you run initdb.Success.Done.Starting all the datanode masters.Starting datanode master datanode1.Starting datanode master datanode2.2019-05-30 21:13:12.077 EDT [25392] LOG: listening on IPv4 address "0.0.0.0", port 154322019-05-30 21:13:12.077 EDT [25392] LOG: listening on IPv6 address "::", port 154322019-05-30 21:13:12.079 EDT [25392] LOG: listening on Unix socket "/tmp/.s.PGSQL.15432"2019-05-30 21:13:12.114 EDT [25392] LOG: redirecting log output to logging collector process2019-05-30 21:13:12.114 EDT [25392] HINT: Future log output will appear in directory "pg_log".2019-05-30 21:13:12.079 EDT [2985] LOG: listening on IPv4 address "0.0.0.0", port 154322019-05-30 21:13:12.079 EDT [2985] LOG: listening on IPv6 address "::", port 154322019-05-30 21:13:12.081 EDT [2985] LOG: listening on Unix socket "/tmp/.s.PGSQL.15432"2019-05-30 21:13:12.117 EDT [2985] LOG: redirecting log output to logging collector process2019-05-30 21:13:12.117 EDT [2985] HINT: Future log output will appear in directory "pg_log".Done.psql: FATAL: no pg_hba.conf entry for host "192.168.20.132", user "postgres", database "postgres"psql: FATAL: no pg_hba.conf entry for host "192.168.20.132", user "postgres", database "postgres"Done.psql: FATAL: no pg_hba.conf entry for host "192.168.20.132", user "postgres", database "postgres"psql: FATAL: no pg_hba.conf entry for host "192.168.20.132", user "postgres", database "postgres"Done. 启动 pgxc_ctl -c /home/postgres/pgxc_ctl/pgxc_ctl.conf start all 关闭 pgxc_ctl -c /home/postgres/pgxc_ctl/pgxc_ctl.conf stop all 查看集群状态 [postgres@gtm ~]$ pgxc_ctl monitor all/bin/bashInstalling pgxc_ctl_bash script as /home/postgres/pgxc_ctl/pgxc_ctl_bash.Installing pgxc_ctl_bash script as /home/postgres/pgxc_ctl/pgxc_ctl_bash.Reading configuration using /home/postgres/pgxc_ctl/pgxc_ctl_bash --home /home/postgres/pgxc_ctl --configuration /home/postgres/pgxc_ctl/pgxc_ctl.conf/home/postgres/pgxc_ctl/pgxc_ctl.conf: line 189: $coordExtraConfig: ambiguous redirectFinished reading configuration. PGXC_CTL START Current directory: /home/postgres/pgxc_ctlRunning: gtm masterRunning: coordinator master coord1Running: coordinator master coord2Running: datanode master datanode1Running: datanode master datanode2 配置集群信息 分别在数据节点、协调器节点上分别执行以下命令: 注:本节点只执行修改操作即可(alert node),其他节点执行创建命令(create node)。因为本节点已经包含本节点的信息。 create node coord1 with (type=coordinator,host=xl1, port=5432);create node coord2 with (type=coordinator,host=xl2, port=5432);alter node coord1 with (type=coordinator,host=xl1, port=5432);alter node coord2 with (type=coordinator,host=xl2, port=5432);create node datanode1 with (type=datanode, host=xl1,port=15432,primary=true,PREFERRED);create node datanode2 with (type=datanode, host=xl2,port=15432);alter node datanode1 with (type=datanode, host=xl1,port=15432,primary=true,PREFERRED);alter node datanode2 with (type=datanode, host=xl2,port=15432);select pgxc_pool_reload(); 分别登陆数据节点、协调器节点验证 postgres= select from pgxc_node;node_name | node_type | node_port | node_host | nodeis_primary | nodeis_preferred | node_id-----------+-----------+-----------+-----------+----------------+------------------+-------------coord1 | C | 5432 | xl1 | f | f | 1885696643coord2 | C | 5432 | xl2 | f | f | -1197102633datanode2 | D | 15432 | xl2 | f | f | -905831925datanode1 | D | 15432 | xl1 | t | f | 888802358(4 rows) 测试 插入数据 在数据节点1,执行相关操作。 通过协调器端口登录PG [postgres@xl1 ~]$ psql -p 5432psql (PGXL 10r1.1, based on PG 10.6 (Postgres-XL 10r1.1))Type "help" for help.postgres= create database lei;CREATE DATABASEpostgres= \c lei;You are now connected to database "lei" as user "postgres".lei= create table test1(id int,name text);CREATE TABLElei= insert into test1(id,name) select generate_series(1,8),'测试';INSERT 0 8lei= select from test1;id | name----+------1 | 测试2 | 测试5 | 测试6 | 测试8 | 测试3 | 测试4 | 测试7 | 测试(8 rows) 注:默认创建的表为分布式表,也就是每个数据节点值存储表的部分数据。关于表类型具体说明,下面有说明。 通过15432端口登录数据节点,查看数据 有5条数据 [postgres@xl1 ~]$ psql -p 15432psql (PGXL 10r1.1, based on PG 10.6 (Postgres-XL 10r1.1))Type "help" for help.postgres= \c lei;You are now connected to database "lei" as user "postgres".lei= select from test1;id | name----+------1 | 测试2 | 测试5 | 测试6 | 测试8 | 测试(5 rows) 登录到节点2,查看数据 有3条数据 [postgres@xl2 ~]$ psql -p15432psql (PGXL 10r1.1, based on PG 10.6 (Postgres-XL 10r1.1))Type "help" for help.postgres= \c lei;You are now connected to database "lei" as user "postgres".lei= select from test1;id | name----+------3 | 测试4 | 测试7 | 测试(3 rows) 两个节点的数据加起来整个8条,没有问题。 至此Postgre-XL集群搭建完成。 创建数据库、表时可能会出现以下错误: ERROR: Failed to get pooled connections 是因为pg_hba.conf配置不对,所有节点加上host all all 192.168.20.0/0 trust并重启集群即可。 ERROR: No Datanode defined in cluster 首先确认是否创建了数据节点,也就是create node相关的命令。如果创建了则执行select pgxc_pool_reload();使其生效即可。 集群管理与应用 表类型说明 REPLICATION表:各个datanode节点中,表的数据完全相同,也就是说,插入数据时,会分别在每个datanode节点插入相同数据。读数据时,只需要读任意一个datanode节点上的数据。 建表语法: CREATE TABLE repltab (col1 int, col2 int) DISTRIBUTE BY REPLICATION; DISTRIBUTE :会将插入的数据,按照拆分规则,分配到不同的datanode节点中存储,也就是sharding技术。每个datanode节点只保存了部分数据,通过coordinate节点可以查询完整的数据视图。 CREATE TABLE disttab(col1 int, col2 int, col3 text) DISTRIBUTE BY HASH(col1); 模拟数据插入 任意登录一个coordinate节点进行建表操作 [postgres@gtm ~]$ psql -h xl1 -p 5432 -U postgrespostgres= INSERT INTO disttab SELECT generate_series(1,100), generate_series(101, 200), 'foo';INSERT 0 100postgres= INSERT INTO repltab SELECT generate_series(1,100), generate_series(101, 200);INSERT 0 100 查看数据分布结果: DISTRIBUTE表分布结果 postgres= SELECT xc_node_id, count() FROM disttab GROUP BY xc_node_id;xc_node_id | count ------------+-------1148549230 | 42-927910690 | 58(2 rows) REPLICATION表分布结果 postgres= SELECT count() FROM repltab;count -------100(1 row) 查看另一个datanode2中repltab表结果 [postgres@datanode2 pgxl9.5]$ psql -p 15432psql (PGXL 10r1.1, based on PG 10.6 (Postgres-XL 10r1.1))Type "help" for help.postgres= SELECT count() FROM repltab;count -------100(1 row) 结论:REPLICATION表中,datanode1,datanode2中表是全部数据,一模一样。而DISTRIBUTE表,数据散落近乎平均分配到了datanode1,datanode2节点中。 新增数据节点与数据重分布 在线新增节点、并重新分布数据。 新增datanode节点 在gtm集群管理节点上执行pgxc_ctl命令 [postgres@gtm ~]$ pgxc_ctl/bin/bashInstalling pgxc_ctl_bash script as /home/postgres/pgxc_ctl/pgxc_ctl_bash.Installing pgxc_ctl_bash script as /home/postgres/pgxc_ctl/pgxc_ctl_bash.Reading configuration using /home/postgres/pgxc_ctl/pgxc_ctl_bash --home /home/postgres/pgxc_ctl --configuration /home/postgres/pgxc_ctl/pgxc_ctl.confFinished reading configuration. PGXC_CTL START Current directory: /home/postgres/pgxc_ctlPGXC 在服务器xl3上,新增一个master角色的datanode节点,名称是datanode3 端口号暂定5430,pool master暂定6669 ,指定好数据目录位置,从两个节点升级到3个节点,之后要写3个none none应该是datanodeSpecificExtraConfig或者datanodeSpecificExtraPgHba配置PGXC add datanode master datanode3 xl3 15432 6671 /home/postgres/pgxc/nodes/datanode/datanode3 none none none 等待新增完成后,查询集群节点状态: postgres= select from pgxc_node;node_name | node_type | node_port | node_host | nodeis_primary | nodeis_preferred | node_id-----------+-----------+-----------+-----------+----------------+------------------+-------------datanode1 | D | 15432 | xl1 | t | f | 888802358datanode2 | D | 15432 | xl2 | f | f | -905831925datanode3 | D | 15432 | xl3 | f | f | -705831925coord1 | C | 5432 | xl1 | f | f | 1885696643coord2 | C | 5432 | xl2 | f | f | -1197102633(4 rows) 节点新增完毕 数据重新分布 由于新增节点后无法自动完成数据重新分布,需要手动操作。 DISTRIBUTE表分布在了node1,node2节点上,如下: postgres= SELECT xc_node_id, count() FROM disttab GROUP BY xc_node_id;xc_node_id | count ------------+-------1148549230 | 42-927910690 | 58(2 rows) 新增一个节点后,将sharding表数据重新分配到三个节点上,将repl表复制到新节点 重分布sharding表postgres= ALTER TABLE disttab ADD NODE (datanode3);ALTER TABLE 复制数据到新节点postgres= ALTER TABLE repltab ADD NODE (datanode3);ALTER TABLE 查看新的数据分布: postgres= SELECT xc_node_id, count() FROM disttab GROUP BY xc_node_id;xc_node_id | count ------------+--------700122826 | 36-927910690 | 321148549230 | 32(3 rows) 登录datanode3(新增的时候,放在了xl3服务器上,端口15432)节点查看数据: [postgres@gtm ~]$ psql -h xl3 -p 15432 -U postgrespsql (PGXL 10r1.1, based on PG 10.6 (Postgres-XL 10r1.1))Type "help" for help.postgres= select count() from repltab;count -------100(1 row) 很明显,通过 ALTER TABLE tt ADD NODE (dn)命令,可以将DISTRIBUTE表数据重新分布到新节点,重分布过程中会中断所有事务。可以将REPLICATION表数据复制到新节点。 从datanode节点中回收数据 postgres= ALTER TABLE disttab DELETE NODE (datanode3);ALTER TABLEpostgres= ALTER TABLE repltab DELETE NODE (datanode3);ALTER TABLE 删除数据节点 Postgresql-XL并没有检查将被删除的datanode节点是否有replicated/distributed表的数据,为了数据安全,在删除之前需要检查下被删除节点上的数据,有数据的话,要回收掉分配到其他节点,然后才能安全删除。删除数据节点分为四步骤: 1.查询要删除节点dn3的oid postgres= SELECT oid, FROM pgxc_node;oid | node_name | node_type | node_port | node_host | nodeis_primary | nodeis_preferred | node_id -------+-----------+-----------+-----------+-----------+----------------+------------------+-------------11819 | coord1 | C | 5432 | datanode1 | f | f | 188569664316384 | coord2 | C | 5432 | datanode2 | f | f | -119710263316385 | node1 | D | 5433 | datanode1 | f | t | 114854923016386 | node2 | D | 5433 | datanode2 | f | f | -92791069016397 | dn3 | D | 5430 | datanode1 | f | f | -700122826(5 rows) 2.查询dn3对应的oid中是否有数据 testdb= SELECT FROM pgxc_class WHERE nodeoids::integer[] @> ARRAY[16397];pcrelid | pclocatortype | pcattnum | pchashalgorithm | pchashbuckets | nodeoids ---------+---------------+----------+-----------------+---------------+-------------------16388 | H | 1 | 1 | 4096 | 16397 16385 1638616394 | R | 0 | 0 | 0 | 16397 16385 16386(2 rows) 3.有数据的先回收数据 postgres= ALTER TABLE disttab DELETE NODE (dn3);ALTER TABLEpostgres= ALTER TABLE repltab DELETE NODE (dn3);ALTER TABLEpostgres= SELECT FROM pgxc_class WHERE nodeoids::integer[] @> ARRAY[16397];pcrelid | pclocatortype | pcattnum | pchashalgorithm | pchashbuckets | nodeoids ---------+---------------+----------+-----------------+---------------+----------(0 rows) 4.安全删除dn3 PGXC$ remove datanode master dn3 clean 故障节点FAILOVER 1.查看当前集群状态 [postgres@gtm ~]$ psql -h xl1 -p 5432psql (PGXL 10r1.1, based on PG 10.6 (Postgres-XL 10r1.1))Type "help" for help.postgres= SELECT oid, FROM pgxc_node;oid | node_name | node_type | node_port | node_host | nodeis_primary | nodeis_preferred | node_id-------+-----------+-----------+-----------+-----------+----------------+------------------+-------------11739 | coord1 | C | 5432 | xl1 | f | f | 188569664316384 | coord2 | C | 5432 | xl2 | f | f | -119710263316387 | datanode2 | D | 15432 | xl2 | f | f | -90583192516388 | datanode1 | D | 15432 | xl1 | t | t | 888802358(4 rows) 2.模拟datanode1节点故障 直接关闭即可 PGXC stop -m immediate datanode master datanode1Stopping datanode master datanode1.Done. 3.测试查询 只要查询涉及到datanode1上的数据,那么该查询就会报错 postgres= SELECT xc_node_id, count() FROM disttab GROUP BY xc_node_id;WARNING: failed to receive file descriptors for connectionsERROR: Failed to get pooled connectionsHINT: This may happen because one or more nodes are currently unreachable, either because of node or network failure.Its also possible that the target node may have hit the connection limit or the pooler is configured with low connections.Please check if all nodes are running fine and also review max_connections and max_pool_size configuration parameterspostgres= SELECT xc_node_id, FROM disttab WHERE col1 = 3;xc_node_id | col1 | col2 | col3------------+------+------+-------905831925 | 3 | 103 | foo(1 row) 测试发现,查询范围如果涉及到故障的node1节点,会报错,而查询的数据范围不在node1上的话,仍然可以查询。 4.手动切换 要想切换,必须要提前配置slave节点。 PGXC$ failover datanode node1 切换完成后,查询集群 postgres= SELECT oid, FROM pgxc_node;oid | node_name | node_type | node_port | node_host | nodeis_primary | nodeis_preferred | node_id -------+-----------+-----------+-----------+-----------+----------------+------------------+-------------11819 | coord1 | C | 5432 | datanode1 | f | f | 188569664316384 | coord2 | C | 5432 | datanode2 | f | f | -119710263316386 | node2 | D | 15432 | datanode2 | f | f | -92791069016385 | node1 | D | 15433 | datanode2 | f | t | 1148549230(4 rows) 发现datanode1节点的ip和端口都已经替换为配置的slave了。 本篇文章为转载内容。原文链接:https://blog.csdn.net/qianglei6077/article/details/94379331。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-01-30 11:09:03
97
转载
转载文章
...ckpoint 2、工厂final SparkConf conf = new SparkConf().setAppName("SparkStreamingOnKafkaDirect").setMaster("hdfs://Master:7077/");final String checkpointDirectory = "hdfs://Master:9000/library/SparkStreaming/CheckPoint_Data";JavaStreamingContextFactory factory = new JavaStreamingContextFactory() {public JavaStreamingContext create() {// TODO Auto-generated method stubreturn createContext(checkpointDirectory, conf);} };/ 可以从失败中恢复Driver,不过还需要指定Driver这个进程运行在Cluster,并且在提交应用程序的时候制定--supervise;/JavaStreamingContext javassc = JavaStreamingContext.getOrCreate(checkpointDirectory, factory);/ 第三步:创建Spark Streaming输入数据来源input Stream: 1、数据输入来源可以基于File、HDFS、Flume、Kafka、Socket等 2、在这里我们指定数据来源于网络Socket端口,Spark Streaming连接上该端口并在运行的时候一直监听该端口的数据 (当然该端口服务首先必须存在),并且在后续会根据业务需要不断有数据产生(当然对于Spark Streaming 应用程序的运行而言,有无数据其处理流程都是一样的) 3、如果经常在每间隔5秒钟没有数据的话不断启动空的Job其实会造成调度资源的浪费,因为并没有数据需要发生计算;所以 实际的企业级生成环境的代码在具体提交Job前会判断是否有数据,如果没有的话就不再提交Job;///创建Kafka元数据来让Spark Streaming这个Kafka Consumer利用Map<String, String> kafkaParameters = new HashMap<String, String>();kafkaParameters.put("metadata.broker.list", "Master:9092,Worker1:9092,Worker2:9092");Set<String> topics = new HashSet<String>();topics.add("SparkStreamingDirected");JavaPairInputDStream<String, String> adClickedStreaming = KafkaUtils.createDirectStream(javassc, String.class, String.class, StringDecoder.class, StringDecoder.class,kafkaParameters, topics);/因为要对黑名单进行过滤,而数据是在RDD中的,所以必然使用transform这个函数; 但是在这里我们必须使用transformToPair,原因是读取进来的Kafka的数据是Pair<String,String>类型, 另一个原因是过滤后的数据要进行进一步处理,所以必须是读进的Kafka数据的原始类型 在此再次说明,每个Batch Duration中实际上讲输入的数据就是被一个且仅被一个RDD封装的,你可以有多个 InputDStream,但其实在产生job的时候,这些不同的InputDStream在Batch Duration中就相当于Spark基于HDFS 数据操作的不同文件来源而已罢了。/JavaPairDStream<String, String> filteredadClickedStreaming = adClickedStreaming.transformToPair(new Function<JavaPairRDD<String,String>, JavaPairRDD<String,String>>() {public JavaPairRDD<String, String> call(JavaPairRDD<String, String> rdd) throws Exception {/ 在线黑名单过滤思路步骤: 1、从数据库中获取黑名单转换成RDD,即新的RDD实例封装黑名单数据; 2、然后把代表黑名单的RDD的实例和Batch Duration产生的RDD进行Join操作, 准确的说是进行leftOuterJoin操作,也就是说使用Batch Duration产生的RDD和代表黑名单的RDD实例进行 leftOuterJoin操作,如果两者都有内容的话,就会是true,否则的话就是false 我们要留下的是leftOuterJoin结果为false; /final List<String> blackListNames = new ArrayList<String>();JDBCWrapper jdbcWrapper = JDBCWrapper.getJDBCInstance();jdbcWrapper.doQuery("SELECT FROM blacklisttable", null, new ExecuteCallBack() {public void resultCallBack(ResultSet result) throws Exception {while(result.next()){blackListNames.add(result.getString(1));} }});List<Tuple2<String, Boolean>> blackListTuple = new ArrayList<Tuple2<String,Boolean>>();for(String name : blackListNames) {blackListTuple.add(new Tuple2<String, Boolean>(name, true));}List<Tuple2<String, Boolean>> blacklistFromListDB = blackListTuple; //数据来自于查询的黑名单表并且映射成为<String, Boolean>JavaSparkContext jsc = new JavaSparkContext(rdd.context());/ 黑名单的表中只有userID,但是如果要进行join操作的话就必须是Key-Value,所以在这里我们需要 基于数据表中的数据产生Key-Value类型的数据集合/JavaPairRDD<String, Boolean> blackListRDD = jsc.parallelizePairs(blacklistFromListDB);/ 进行操作的时候肯定是基于userID进行join,所以必须把传入的rdd进行mapToPair操作转化成为符合格式的RDD/JavaPairRDD<String, Tuple2<String, String>> rdd2Pair = rdd.mapToPair(new PairFunction<Tuple2<String,String>, String, Tuple2<String, String>>() {public Tuple2<String, Tuple2<String, String>> call(Tuple2<String, String> t) throws Exception {// TODO Auto-generated method stubString userID = t._2.split("\t")[2];return new Tuple2<String, Tuple2<String,String>>(userID, t);} });JavaPairRDD<String, Tuple2<Tuple2<String, String>, Optional<Boolean>>> joined = rdd2Pair.leftOuterJoin(blackListRDD);JavaPairRDD<String, String> result = joined.filter(new Function<Tuple2<String,Tuple2<Tuple2<String,String>,Optional<Boolean>>>, Boolean>() {public Boolean call(Tuple2<String, Tuple2<Tuple2<String, String>, Optional<Boolean>>> tuple)throws Exception {// TODO Auto-generated method stubOptional<Boolean> optional = tuple._2._2;if(optional.isPresent() && optional.get()){return false;} else {return true;} }}).mapToPair(new PairFunction<Tuple2<String,Tuple2<Tuple2<String,String>,Optional<Boolean>>>, String, String>() {public Tuple2<String, String> call(Tuple2<String, Tuple2<Tuple2<String, String>, Optional<Boolean>>> t)throws Exception {// TODO Auto-generated method stubreturn t._2._1;} });return result;} });//广告点击的基本数据格式:timestamp、ip、userID、adID、province、cityJavaPairDStream<String, Long> pairs = filteredadClickedStreaming.mapToPair(new PairFunction<Tuple2<String,String>, String, Long>() {public Tuple2<String, Long> call(Tuple2<String, String> t) throws Exception {String[] splited=t._2.split("\t");String timestamp = splited[0]; //YYYY-MM-DDString ip = splited[1];String userID = splited[2];String adID = splited[3];String province = splited[4];String city = splited[5]; String clickedRecord = timestamp + "_" +ip + "_"+userID+"_"+adID+"_"+province +"_"+city;return new Tuple2<String, Long>(clickedRecord, 1L);} });/ 第4.3步:在单词实例计数为1基础上,统计每个单词在文件中出现的总次数/JavaPairDStream<String, Long> adClickedUsers= pairs.reduceByKey(new Function2<Long, Long, Long>() {public Long call(Long i1, Long i2) throws Exception{return i1 + i2;} });/判断有效的点击,复杂化的采用机器学习训练模型进行在线过滤 简单的根据ip判断1天不超过100次;也可以通过一个batch duration的点击次数判断是否非法广告点击,通过一个batch来判断是不完整的,还需要一天的数据也可以每一个小时来判断。/JavaPairDStream<String, Long> filterClickedBatch = adClickedUsers.filter(new Function<Tuple2<String,Long>, Boolean>() {public Boolean call(Tuple2<String, Long> v1) throws Exception {if (1 < v1._2){//更新一些黑名单的数据库表return false;} else { return true;} }});//filterClickedBatch.print();//写入数据库filterClickedBatch.foreachRDD(new Function<JavaPairRDD<String,Long>, Void>() {public Void call(JavaPairRDD<String, Long> rdd) throws Exception {rdd.foreachPartition(new VoidFunction<Iterator<Tuple2<String,Long>>>() {public void call(Iterator<Tuple2<String, Long>> partition) throws Exception {//使用数据库连接池的高效读写数据库的方式将数据写入数据库mysql//例如一次插入 1000条 records,使用insertBatch 或 updateBatch//插入的用户数据信息:userID,adID,clickedCount,time//这里面有一个问题,可能出现两条记录的key是一样的,此时需要更新累加操作List<UserAdClicked> userAdClickedList = new ArrayList<UserAdClicked>();while(partition.hasNext()) {Tuple2<String, Long> record = partition.next();String[] splited = record._1.split("\t");UserAdClicked userClicked = new UserAdClicked();userClicked.setTimestamp(splited[0]);userClicked.setIp(splited[1]);userClicked.setUserID(splited[2]);userClicked.setAdID(splited[3]);userClicked.setProvince(splited[4]);userClicked.setCity(splited[5]);userAdClickedList.add(userClicked);}final List<UserAdClicked> inserting = new ArrayList<UserAdClicked>();final List<UserAdClicked> updating = new ArrayList<UserAdClicked>();JDBCWrapper jdbcWrapper = JDBCWrapper.getJDBCInstance();//表的字段timestamp、ip、userID、adID、province、city、clickedCountfor(final UserAdClicked clicked : userAdClickedList) {jdbcWrapper.doQuery("SELECT clickedCount FROM adclicked WHERE"+ " timestamp =? AND userID = ? AND adID = ?",new Object[]{clicked.getTimestamp(), clicked.getUserID(),clicked.getAdID()}, new ExecuteCallBack() {public void resultCallBack(ResultSet result) throws Exception {// TODO Auto-generated method stubif(result.next()) {long count = result.getLong(1);clicked.setClickedCount(count);updating.add(clicked);} else {inserting.add(clicked);clicked.setClickedCount(1L);} }});}//表的字段timestamp、ip、userID、adID、province、city、clickedCountList<Object[]> insertParametersList = new ArrayList<Object[]>();for(UserAdClicked insertRecord : inserting) {insertParametersList.add(new Object[] {insertRecord.getTimestamp(),insertRecord.getIp(),insertRecord.getUserID(),insertRecord.getAdID(),insertRecord.getProvince(),insertRecord.getCity(),insertRecord.getClickedCount()});}jdbcWrapper.doBatch("INSERT INTO adclicked VALUES(?, ?, ?, ?, ?, ?, ?)", insertParametersList);//表的字段timestamp、ip、userID、adID、province、city、clickedCountList<Object[]> updateParametersList = new ArrayList<Object[]>();for(UserAdClicked updateRecord : updating) {updateParametersList.add(new Object[] {updateRecord.getTimestamp(),updateRecord.getIp(),updateRecord.getUserID(),updateRecord.getAdID(),updateRecord.getProvince(),updateRecord.getCity(),updateRecord.getClickedCount() + 1});}jdbcWrapper.doBatch("UPDATE adclicked SET clickedCount = ? WHERE"+ " timestamp =? AND ip = ? AND userID = ? AND adID = ? "+ "AND province = ? AND city = ?", updateParametersList);} });return null;} });//再次过滤,从数据库中读取数据过滤黑名单JavaPairDStream<String, Long> blackListBasedOnHistory = filterClickedBatch.filter(new Function<Tuple2<String,Long>, Boolean>() {public Boolean call(Tuple2<String, Long> v1) throws Exception {//广告点击的基本数据格式:timestamp,ip,userID,adID,province,cityString[] splited = v1._1.split("\t"); //提取key值String date =splited[0];String userID =splited[2];String adID =splited[3];//查询一下数据库同一个用户同一个广告id点击量超过50次列入黑名单//接下来 根据date、userID、adID条件去查询用户点击广告的数据表,获得总的点击次数//这个时候基于点击次数判断是否属于黑名单点击int clickedCountTotalToday = 81 ;if (clickedCountTotalToday > 50) {return true;}else {return false ;} }});//map操作,找出用户的idJavaDStream<String> blackListuserIDBasedInBatchOnhistroy =blackListBasedOnHistory.map(new Function<Tuple2<String,Long>, String>() {public String call(Tuple2<String, Long> v1) throws Exception {// TODO Auto-generated method stubreturn v1._1.split("\t")[2];} });//有一个问题,数据可能重复,在一个partition里面重复,这个好办;//但多个partition不能保证一个用户重复,需要对黑名单的整个rdd进行去重操作。//rdd去重了,partition也就去重了,一石二鸟,一箭双雕// 找出了黑名单,下一步就写入黑名单数据库表中JavaDStream<String> blackListUniqueuserBasedInBatchOnhistroy = blackListuserIDBasedInBatchOnhistroy.transform(new Function<JavaRDD<String>, JavaRDD<String>>() {public JavaRDD<String> call(JavaRDD<String> rdd) throws Exception {// TODO Auto-generated method stubreturn rdd.distinct();} });// 下一步写入到数据表中blackListUniqueuserBasedInBatchOnhistroy.foreachRDD(new Function<JavaRDD<String>, Void>() {public Void call(JavaRDD<String> rdd) throws Exception {rdd.foreachPartition(new VoidFunction<Iterator<String>>() {public void call(Iterator<String> t) throws Exception {// TODO Auto-generated method stub//插入的用户信息可以只包含:useID//此时直接插入黑名单数据表即可。//写入数据库List<Object[]> blackList = new ArrayList<Object[]>();while(t.hasNext()) {blackList.add(new Object[]{t.next()});}JDBCWrapper jdbcWrapper = JDBCWrapper.getJDBCInstance();jdbcWrapper.doBatch("INSERT INTO blacklisttable values (?)", blackList);} });return null;} });/广告点击累计动态更新,每个updateStateByKey都会在Batch Duration的时间间隔的基础上进行广告点击次数的更新, 更新之后我们一般都会持久化到外部存储设备上,在这里我们存储到MySQL数据库中/JavaPairDStream<String, Long> updateStateByKeyDSteam = filteredadClickedStreaming.mapToPair(new PairFunction<Tuple2<String,String>, String, Long>() {public Tuple2<String, Long> call(Tuple2<String, String> t)throws Exception {String[] splited=t._2.split("\t");String timestamp = splited[0]; //YYYY-MM-DDString ip = splited[1];String userID = splited[2];String adID = splited[3];String province = splited[4];String city = splited[5]; String clickedRecord = timestamp + "_" +ip + "_"+userID+"_"+adID+"_"+province +"_"+city;return new Tuple2<String, Long>(clickedRecord, 1L);} }).updateStateByKey(new Function2<List<Long>, Optional<Long>, Optional<Long>>() {public Optional<Long> call(List<Long> v1, Optional<Long> v2)throws Exception {// v1:当前的Key在当前的Batch Duration中出现的次数的集合,例如{1,1,1,。。。,1}// v2:当前的Key在以前的Batch Duration中积累下来的结果;Long clickedTotalHistory = 0L; if(v2.isPresent()){clickedTotalHistory = v2.get();}for(Long one : v1) {clickedTotalHistory += one;}return Optional.of(clickedTotalHistory);} });updateStateByKeyDSteam.foreachRDD(new Function<JavaPairRDD<String,Long>, Void>() {public Void call(JavaPairRDD<String, Long> rdd) throws Exception {rdd.foreachPartition(new VoidFunction<Iterator<Tuple2<String,Long>>>() {public void call(Iterator<Tuple2<String, Long>> partition) throws Exception {//使用数据库连接池的高效读写数据库的方式将数据写入数据库mysql//例如一次插入 1000条 records,使用insertBatch 或 updateBatch//插入的用户数据信息:timestamp、adID、province、city//这里面有一个问题,可能出现两条记录的key是一样的,此时需要更新累加操作List<AdClicked> AdClickedList = new ArrayList<AdClicked>();while(partition.hasNext()) {Tuple2<String, Long> record = partition.next();String[] splited = record._1.split("\t");AdClicked adClicked = new AdClicked();adClicked.setTimestamp(splited[0]);adClicked.setAdID(splited[1]);adClicked.setProvince(splited[2]);adClicked.setCity(splited[3]);adClicked.setClickedCount(record._2);AdClickedList.add(adClicked);}final List<AdClicked> inserting = new ArrayList<AdClicked>();final List<AdClicked> updating = new ArrayList<AdClicked>();JDBCWrapper jdbcWrapper = JDBCWrapper.getJDBCInstance();//表的字段timestamp、ip、userID、adID、province、city、clickedCountfor(final AdClicked clicked : AdClickedList) {jdbcWrapper.doQuery("SELECT clickedCount FROM adclickedcount WHERE"+ " timestamp = ? AND adID = ? AND province = ? AND city = ?",new Object[]{clicked.getTimestamp(), clicked.getAdID(),clicked.getProvince(), clicked.getCity()}, new ExecuteCallBack() {public void resultCallBack(ResultSet result) throws Exception {// TODO Auto-generated method stubif(result.next()) {long count = result.getLong(1);clicked.setClickedCount(count);updating.add(clicked);} else {inserting.add(clicked);clicked.setClickedCount(1L);} }});}//表的字段timestamp、ip、userID、adID、province、city、clickedCountList<Object[]> insertParametersList = new ArrayList<Object[]>();for(AdClicked insertRecord : inserting) {insertParametersList.add(new Object[] {insertRecord.getTimestamp(),insertRecord.getAdID(),insertRecord.getProvince(),insertRecord.getCity(),insertRecord.getClickedCount()});}jdbcWrapper.doBatch("INSERT INTO adclickedcount VALUES(?, ?, ?, ?, ?)", insertParametersList);//表的字段timestamp、ip、userID、adID、province、city、clickedCountList<Object[]> updateParametersList = new ArrayList<Object[]>();for(AdClicked updateRecord : updating) {updateParametersList.add(new Object[] {updateRecord.getClickedCount(),updateRecord.getTimestamp(),updateRecord.getAdID(),updateRecord.getProvince(),updateRecord.getCity()});}jdbcWrapper.doBatch("UPDATE adclickedcount SET clickedCount = ? WHERE"+ " timestamp =? AND adID = ? AND province = ? AND city = ?", updateParametersList);} });return null;} });/ 对广告点击进行TopN计算,计算出每天每个省份Top5排名的广告 因为我们直接对RDD进行操作,所以使用了transfomr算子;/updateStateByKeyDSteam.transform(new Function<JavaPairRDD<String,Long>, JavaRDD<Row>>() {public JavaRDD<Row> call(JavaPairRDD<String, Long> rdd) throws Exception {JavaRDD<Row> rowRDD = rdd.mapToPair(new PairFunction<Tuple2<String,Long>, String, Long>() {public Tuple2<String, Long> call(Tuple2<String, Long> t)throws Exception {// TODO Auto-generated method stubString[] splited=t._1.split("_");String timestamp = splited[0]; //YYYY-MM-DDString adID = splited[3];String province = splited[4];String clickedRecord = timestamp + "_" + adID + "_" + province;return new Tuple2<String, Long>(clickedRecord, t._2);} }).reduceByKey(new Function2<Long, Long, Long>() {public Long call(Long v1, Long v2) throws Exception {// TODO Auto-generated method stubreturn v1 + v2;} }).map(new Function<Tuple2<String,Long>, Row>() {public Row call(Tuple2<String, Long> v1) throws Exception {// TODO Auto-generated method stubString[] splited=v1._1.split("_");String timestamp = splited[0]; //YYYY-MM-DDString adID = splited[3];String province = splited[4];return RowFactory.create(timestamp, adID, province, v1._2);} });StructType structType = DataTypes.createStructType(Arrays.asList(DataTypes.createStructField("timestamp", DataTypes.StringType, true),DataTypes.createStructField("adID", DataTypes.StringType, true),DataTypes.createStructField("province", DataTypes.StringType, true),DataTypes.createStructField("clickedCount", DataTypes.LongType, true)));HiveContext hiveContext = new HiveContext(rdd.context());DataFrame df = hiveContext.createDataFrame(rowRDD, structType);df.registerTempTable("topNTableSource");DataFrame result = hiveContext.sql("SELECT timestamp, adID, province, clickedCount, FROM"+ " (SELECT timestamp, adID, province,clickedCount, "+ "ROW_NUMBER() OVER(PARTITION BY province ORDER BY clickeCount DESC) rank "+ "FROM topNTableSource) subquery "+ "WHERE rank <= 5");return result.toJavaRDD();} }).foreachRDD(new Function<JavaRDD<Row>, Void>() {public Void call(JavaRDD<Row> rdd) throws Exception {// TODO Auto-generated method stubrdd.foreachPartition(new VoidFunction<Iterator<Row>>() {public void call(Iterator<Row> t) throws Exception {// TODO Auto-generated method stubList<AdProvinceTopN> adProvinceTopN = new ArrayList<AdProvinceTopN>();while(t.hasNext()) {Row row = t.next();AdProvinceTopN item = new AdProvinceTopN();item.setTimestamp(row.getString(0));item.setAdID(row.getString(1));item.setProvince(row.getString(2));item.setClickedCount(row.getLong(3));adProvinceTopN.add(item);}// final List<AdProvinceTopN> inserting = new ArrayList<AdProvinceTopN>();// final List<AdProvinceTopN> updating = new ArrayList<AdProvinceTopN>();JDBCWrapper jdbcWrapper = JDBCWrapper.getJDBCInstance();Set<String> set = new HashSet<String>();for(AdProvinceTopN item: adProvinceTopN){set.add(item.getTimestamp() + "_" + item.getProvince());}//表的字段timestamp、adID、province、clickedCountArrayList<Object[]> deleteParametersList = new ArrayList<Object[]>();for(String deleteRecord : set) {String[] splited = deleteRecord.split("_");deleteParametersList.add(new Object[]{splited[0],splited[1]});}jdbcWrapper.doBatch("DELETE FROM adprovincetopn WHERE timestamp = ? AND province = ?", deleteParametersList);//表的字段timestamp、ip、userID、adID、province、city、clickedCountList<Object[]> insertParametersList = new ArrayList<Object[]>();for(AdProvinceTopN insertRecord : adProvinceTopN) {insertParametersList.add(new Object[] {insertRecord.getClickedCount(),insertRecord.getTimestamp(),insertRecord.getAdID(),insertRecord.getProvince()});}jdbcWrapper.doBatch("INSERT INTO adprovincetopn VALUES (?, ?, ?, ?)", insertParametersList);} });return null;} });/ 计算过去半个小时内广告点击的趋势 广告点击的基本数据格式:timestamp、ip、userID、adID、province、city/filteredadClickedStreaming.mapToPair(new PairFunction<Tuple2<String,String>, String, Long>() {public Tuple2<String, Long> call(Tuple2<String, String> t)throws Exception {String splited[] = t._2.split("\t");String adID = splited[3];String time = splited[0]; //Todo:后续需要重构代码实现时间戳和分钟的转换提取。此处需要提取出该广告的点击分钟单位return new Tuple2<String, Long>(time + "_" + adID, 1L);} }).reduceByKeyAndWindow(new Function2<Long, Long, Long>() {public Long call(Long v1, Long v2) throws Exception {// TODO Auto-generated method stubreturn v1 + v2;} }, new Function2<Long, Long, Long>() {public Long call(Long v1, Long v2) throws Exception {// TODO Auto-generated method stubreturn v1 - v2;} }, Durations.minutes(30), Durations.milliseconds(5)).foreachRDD(new Function<JavaPairRDD<String,Long>, Void>() {public Void call(JavaPairRDD<String, Long> rdd) throws Exception {// TODO Auto-generated method stubrdd.foreachPartition(new VoidFunction<Iterator<Tuple2<String,Long>>>() {public void call(Iterator<Tuple2<String, Long>> partition)throws Exception {List<AdTrendStat> adTrend = new ArrayList<AdTrendStat>();// TODO Auto-generated method stubwhile(partition.hasNext()) {Tuple2<String, Long> record = partition.next();String[] splited = record._1.split("_");String time = splited[0];String adID = splited[1];Long clickedCount = record._2;/ 在插入数据到数据库的时候具体需要哪些字段?time、adID、clickedCount; 而我们通过J2EE技术进行趋势绘图的时候肯定是需要年、月、日、时、分这个维度的,所以我们在这里需要 年月日、小时、分钟这些时间维度;/AdTrendStat adTrendStat = new AdTrendStat();adTrendStat.setAdID(adID);adTrendStat.setClickedCount(clickedCount);adTrendStat.set_date(time); //Todo:获取年月日adTrendStat.set_hour(time); //Todo:获取小时adTrendStat.set_minute(time);//Todo:获取分钟adTrend.add(adTrendStat);}final List<AdTrendStat> inserting = new ArrayList<AdTrendStat>();final List<AdTrendStat> updating = new ArrayList<AdTrendStat>();JDBCWrapper jdbcWrapper = JDBCWrapper.getJDBCInstance();//表的字段timestamp、ip、userID、adID、province、city、clickedCountfor(final AdTrendStat trend : adTrend) {final AdTrendCountHistory adTrendhistory = new AdTrendCountHistory();jdbcWrapper.doQuery("SELECT clickedCount FROM adclickedtrend WHERE"+ " date =? AND hour = ? AND minute = ? AND AdID = ?",new Object[]{trend.get_date(), trend.get_hour(), trend.get_minute(),trend.getAdID()}, new ExecuteCallBack() {public void resultCallBack(ResultSet result) throws Exception {// TODO Auto-generated method stubif(result.next()) {long count = result.getLong(1);adTrendhistory.setClickedCountHistoryLong(count);updating.add(trend);} else { inserting.add(trend);} }});}//表的字段date、hour、minute、adID、clickedCountList<Object[]> insertParametersList = new ArrayList<Object[]>();for(AdTrendStat insertRecord : inserting) {insertParametersList.add(new Object[] {insertRecord.get_date(),insertRecord.get_hour(),insertRecord.get_minute(),insertRecord.getAdID(),insertRecord.getClickedCount()});}jdbcWrapper.doBatch("INSERT INTO adclickedtrend VALUES(?, ?, ?, ?, ?)", insertParametersList);//表的字段date、hour、minute、adID、clickedCountList<Object[]> updateParametersList = new ArrayList<Object[]>();for(AdTrendStat updateRecord : updating) {updateParametersList.add(new Object[] {updateRecord.getClickedCount(),updateRecord.get_date(),updateRecord.get_hour(),updateRecord.get_minute(),updateRecord.getAdID()});}jdbcWrapper.doBatch("UPDATE adclickedtrend SET clickedCount = ? WHERE"+ " date =? AND hour = ? AND minute = ? AND AdID = ?", updateParametersList);} });return null;} });;/ Spark Streaming 执行引擎也就是Driver开始运行,Driver启动的时候是位于一条新的线程中的,当然其内部有消息循环体,用于 接收应用程序本身或者Executor中的消息,/javassc.start();javassc.awaitTermination();javassc.close();}private static JavaStreamingContext createContext(String checkpointDirectory, SparkConf conf) {// If you do not see this printed, that means the StreamingContext has been loaded// from the new checkpointSystem.out.println("Creating new context");// Create the context with a 5 second batch sizeJavaStreamingContext ssc = new JavaStreamingContext(conf, Durations.seconds(10));ssc.checkpoint(checkpointDirectory);return ssc;} }class JDBCWrapper {private static JDBCWrapper jdbcInstance = null;private static LinkedBlockingQueue<Connection> dbConnectionPool = new LinkedBlockingQueue<Connection>();static {try {Class.forName("com.mysql.jdbc.Driver");} catch (ClassNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();} }public static JDBCWrapper getJDBCInstance() {if(jdbcInstance == null) {synchronized (JDBCWrapper.class) {if(jdbcInstance == null) {jdbcInstance = new JDBCWrapper();} }}return jdbcInstance; }private JDBCWrapper() {for(int i = 0; i < 10; i++){try {Connection conn = DriverManager.getConnection("jdbc:mysql://Master:3306/sparkstreaming","root", "root");dbConnectionPool.put(conn);} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();} } }public synchronized Connection getConnection() {while(0 == dbConnectionPool.size()){try {Thread.sleep(20);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();} }return dbConnectionPool.poll();}public int[] doBatch(String sqlText, List<Object[]> paramsList){Connection conn = getConnection();PreparedStatement preparedStatement = null;int[] result = null;try {conn.setAutoCommit(false);preparedStatement = conn.prepareStatement(sqlText);for(Object[] parameters: paramsList) {for(int i = 0; i < parameters.length; i++){preparedStatement.setObject(i + 1, parameters[i]);} preparedStatement.addBatch();}result = preparedStatement.executeBatch();conn.commit();} catch (SQLException e) {// TODO Auto-generated catch blocke.printStackTrace();} finally {if(preparedStatement != null) {try {preparedStatement.close();} catch (SQLException e) {// TODO Auto-generated catch blocke.printStackTrace();} }if(conn != null) {try {dbConnectionPool.put(conn);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();} }}return result; }public void doQuery(String sqlText, Object[] paramsList, ExecuteCallBack callback){Connection conn = getConnection();PreparedStatement preparedStatement = null;ResultSet result = null;try {preparedStatement = conn.prepareStatement(sqlText);for(int i = 0; i < paramsList.length; i++){preparedStatement.setObject(i + 1, paramsList[i]);} result = preparedStatement.executeQuery();try {callback.resultCallBack(result);} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();} } catch (SQLException e) {// TODO Auto-generated catch blocke.printStackTrace();} finally {if(preparedStatement != null) {try {preparedStatement.close();} catch (SQLException e) {// TODO Auto-generated catch blocke.printStackTrace();} }if(conn != null) {try {dbConnectionPool.put(conn);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();} }} }}interface ExecuteCallBack {void resultCallBack(ResultSet result) throws Exception;}class UserAdClicked {private String timestamp;private String ip;private String userID;private String adID;private String province;private String city;private Long clickedCount;public String getTimestamp() {return timestamp;}public void setTimestamp(String timestamp) {this.timestamp = timestamp;}public String getIp() {return ip;}public void setIp(String ip) {this.ip = ip;}public String getUserID() {return userID;}public void setUserID(String userID) {this.userID = userID;}public String getAdID() {return adID;}public void setAdID(String adID) {this.adID = adID;}public String getProvince() {return province;}public void setProvince(String province) {this.province = province;}public String getCity() {return city;}public void setCity(String city) {this.city = city;}public Long getClickedCount() {return clickedCount;}public void setClickedCount(Long clickedCount) {this.clickedCount = clickedCount;} }class AdClicked {private String timestamp;private String adID;private String province;private String city;private Long clickedCount;public String getTimestamp() {return timestamp;}public void setTimestamp(String timestamp) {this.timestamp = timestamp;}public String getAdID() {return adID;}public void setAdID(String adID) {this.adID = adID;}public String getProvince() {return province;}public void setProvince(String province) {this.province = province;}public String getCity() {return city;}public void setCity(String city) {this.city = city;}public Long getClickedCount() {return clickedCount;}public void setClickedCount(Long clickedCount) {this.clickedCount = clickedCount;} }class AdProvinceTopN {private String timestamp;private String adID;private String province;private Long clickedCount;public String getTimestamp() {return timestamp;}public void setTimestamp(String timestamp) {this.timestamp = timestamp;}public String getAdID() {return adID;}public void setAdID(String adID) {this.adID = adID;}public String getProvince() {return province;}public void setProvince(String province) {this.province = province;}public Long getClickedCount() {return clickedCount;}public void setClickedCount(Long clickedCount) {this.clickedCount = clickedCount;} }class AdTrendStat {private String _date;private String _hour;private String _minute;private String adID;private Long clickedCount;public String get_date() {return _date;}public void set_date(String _date) {this._date = _date;}public String get_hour() {return _hour;}public void set_hour(String _hour) {this._hour = _hour;}public String get_minute() {return _minute;}public void set_minute(String _minute) {this._minute = _minute;}public String getAdID() {return adID;}public void setAdID(String adID) {this.adID = adID;}public Long getClickedCount() {return clickedCount;}public void setClickedCount(Long clickedCount) {this.clickedCount = clickedCount;} }class AdTrendCountHistory{private Long clickedCountHistoryLong;public Long getClickedCountHistoryLong() {return clickedCountHistoryLong;}public void setClickedCountHistoryLong(Long clickedCountHistoryLong) {this.clickedCountHistoryLong = clickedCountHistoryLong;} } 本篇文章为转载内容。原文链接:https://blog.csdn.net/tom_8899_li/article/details/71194434。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-02-14 19:16:35
300
转载
转载文章
...它打印的是由dp模块维护的简单的流。 如果你想查看OpenFlow条目,请使用ovs-ofctl dump-flows。dpif/del-fow dp 删除指定dp上所有流表。同上所述,这些不是OpenFlow流表。 ovs-appctl dpif/dump-flows br-int 创建网络 openstack network create --share --external --provider-physical-network provider --provider-network-type flat provider $ openstack subnet create --network provider \ --allocation-pool start=192.168.56.100,end=192.168.56.200 \ --dns-nameserver 8.8.8.8 --gateway 192.168.56.1 \ --subnet-range 192.168.56.0/24 provider openstack network create selfservice $ openstack subnet create --network selfservice \ --dns-nameserver 8.8.8.8 --gateway 192.168.1.1 \ --subnet-range 192.168.1.0/24 selfservice openstack router create router openstack router add subnet router selfservice openstack router set router --external-gateway provider openstack port list --router router +--------------------------------------+------+-------------------+-------------------------------------------------------------------------------+--------+ | ID | Name | MAC Address | Fixed IP Addresses | Status | +--------------------------------------+------+-------------------+-------------------------------------------------------------------------------+--------+ | bff6605d-824c-41f9-b744-21d128fc86e1 | | fa:16:3e:2f:34:9b | ip_address='172.16.1.1', subnet_id='3482f524-8bff-4871-80d4-5774c2730728' | ACTIVE | | d6fe98db-ae01-42b0-a860-37b1661f5950 | | fa:16:3e:e8:c1:41 | ip_address='203.0.113.102', subnet_id='5cc70da8-4ee7-4565-be53-b9c011fca011' | ACTIVE | +--------------------------------------+------+-------------------+-------------------------------------------------------------------------------+--------+ $ ping -c 4 203.0.113.102 创建虚机 openstack keypair list $ ssh-keygen -q -N "" $ openstack keypair create --public-key ~/.ssh/id_rsa.pub mykey openstack flavor list openstack image list openstack network list openstack server create --flavor tiny --image cirros --nic net-id=27616098-0374-4ab4-95a8-b5bf4839dcf8 --key-name mykey provider-instance 网络配置 python /usr/lib/python2.7/site-packages/networking_odl/cmd/set_ovs_hostconfigs.py --ovs_hostconfigs='{ "ODL L2": { "allowed_network_types": [ "flat", "vlan", "vxlan" ], "bridge_mappings": { "provider": "br-int" }, "supported_vnic_types": [ { "vnic_type": "normal", "vif_type": "ovs", "vif_details": {} } ] }, "ODL L3": {} }' ovs-vsctl list open . [2019/1/16 19:09] 高正伟: ovs-vsctl set Open_vSwitch . other_config:local_ip=hostip ovs-vsctl set Open_vSwitch . other_config:local_ip=192.168.56.122 ovs-vsctl set Open_vSwitch . other_config:remote_ip=192.168.56.122 ovs-vsctl remove interface tunca7b782f232 options remote_ip ovs-vsctl set Open_vSwitch . other_config:provider_mappings=provider:br-ex ovs-vsctl set Open_vSwitch . external_ids:provider_mappings="{\"provider\": \"br-ex\"}" 清空 ovs-vsctl clear Open_vSwitch . external_ids ovs-vsctl set-manager tcp:10.13.80.34:6640 ovs-vsctl set-controller br-ex tcp:10.13.80.34:6640 ovs-vsctl del-controller br-ex sudo neutron-odl-ovs-hostconfig ovs-vsctl show ovs-vsctl add-port <bridge name> <port name> ovs-vsctl add-port br-ex enp0s10 ovs-vsctl del-port br-ex phy-br-ex ovs-vsctl del-port br-ex tun2ad7e9e91e4 重启odl后 systemctl restart openvswitch.service systemctl restart neutron-server.service systemctl stop neutron-server.service 创建虚机 openstack network create --share --external --provider-physical-network provider --provider-network-type flat provider openstack subnet create --network provider --allocation-pool start=192.168.56.2,end=192.168.56.100 --dns-nameserver 8.8.8.8 --gateway 192.168.56.1 --subnet-range 192.168.56.0/24 provider nova boot --image cirros --flavor tiny --nic net-id= --availability-zone nova:rcontroller01 vm-01 openstack server create --flavor tiny --image cirros --nic net-id= --key-name mykey test nova boot --image cirros --flavor tiny --nic net-id=0fe983c2-8178-403b-a00e-e8561580b210 --availability-zone nova:rcontroller01 vm-01 虚机可以学习到mac但是ping不通 抓包,先在虚机网卡上抓包, 然后在br-int上抓包 发现虚拟网卡上是发送了icmp请求报文的,但是br-int上没有 查看报文情况 [root@rcontroller01 ~] ovs-appctl dpif/dump-flows br-int recirc_id(0),tunnel(tun_id=0x0,src=192.168.56.102,dst=192.168.56.122,flags(-df-csum+key)),in_port(4),eth(),eth_type(0x0800),ipv4(proto=17,frag=no),udp(dst=3784), packets:266436, bytes:17584776, used:0.591s, actions:userspace(pid=4294962063,slow_path(bfd)) recirc_id(0xa0),in_port(5),ct_state(+new-est-rel-inv+trk),ct_mark(0/0x1),eth(),eth_type(0x0800),ipv4(frag=no), packets:148165, bytes:14520170, used:0.566s, actions:drop recirc_id(0),in_port(3),eth(),eth_type(0x0806), packets:1, bytes:60, used:5.228s, actions:drop recirc_id(0),tunnel(tun_id=0xb,src=192.168.56.102,dst=192.168.56.122,flags(-df-csum+key)),in_port(4),eth(dst=fa:16:3e:ab:ba:7e),eth_type(0x0806), packets:0, bytes:0, used:never, actions:5 recirc_id(0),in_port(5),eth(src=fa:16:3e:ab:ba:7e),eth_type(0x0800),ipv4(src=192.168.0.16,proto=1,frag=no), packets:148165, bytes:14520170, used:0.566s, actions:ct(zone=5004),recirc(0xa0) recirc_id(0),in_port(3),eth(),eth_type(0x0800),ipv4(frag=no), packets:886646, bytes:316947183, used:0.210s, flags:SFPR., actions:drop recirc_id(0),in_port(5),eth(src=fa:16:3e:ab:ba:7e,dst=fa:16:3e:7d:95:75),eth_type(0x0806),arp(sip=192.168.0.16,tip=192.168.0.5,op=1/0xff,sha=fa:16:3e:ab:ba:7e), packets:0, bytes:0, used:never, actions:userspace(pid=4294961925,controller(reason=4,dont_send=0,continuation=0,recirc_id=4618,rule_cookie=0x822002d,controller_id=0,max_len=65535)),set(tunnel(tun_id=0xb,src=192.168.56.122,dst=192.168.56.102,ttl=64,tp_dst=4789,flags(df|key))),4 安全组设置 openstack security group rule create --proto tcp 2e19a748-9086-49f8-9498-01abc1a964fe openstack security group rule create --proto tcp 6095293d-c2cd-433d-8a8f-e77ecb03609e openstack security group rule create --proto udp 2e19a748-9086-49f8-9498-01abc1a964fe openstack security group rule create --proto udp 6095293d-c2cd-433d-8a8f-e77ecb03609e ovs-vsctl add-port br-ex "ex-patch-int" ovs-vsctl set interface "ex-patch-int" type=patch ovs-vsctl set interface "ex-patch-int" options:peer=int-patch-ex ovs-vsctl add-port br-int "int-patch-ex" ovs-vsctl set interface "int-patch-ex" type=patch ovs-vsctl set interface "int-patch-ex" options:peer=ex-patch-int ovs-vsctl del-port br-ex "ex-patch-int" ovs-vsctl del-port br-int "int-patch-ex" ovs-vsctl del-port br-ex enp0s9 ovs-vsctl add-port br-int enp0s9 ovs-appctl ofproto/trace 重要命令 sudo ovs-ofctl -O OpenFlow13 show br-int sudo ovs-appctl ofproto/trace br-int "in_port=5,ip,nw_src=192.168.0.16,nw_dst=192.168.0.5" ovs-appctl dpctl/dump-conntrack 11.查看接口id等 ovs-appctl dpif/show 12.查看接口统计 ovs-ofctl dump-ports br-int 查看接口 sudo ovs-ofctl show br-int -O OpenFlow13 ovs常用命令 控制管理类 1.查看网桥和端口 ovs-vsctl show 1 2.创建一个网桥 ovs-vsctl add-br br0 ovs-vsctl set bridge br0 datapath_type=netdev 1 2 3.添加/删除一个端口 for system interfaces ovs-vsctl add-port br0 eth1 ovs-vsctl del-port br0 eth1 for DPDK ovs-vsctl add-port br0 dpdk1 -- set interface dpdk1 type=dpdk options:dpdk-devargs=0000:01:00.0 for DPDK bonds ovs-vsctl add-bond br0 dpdkbond0 dpdk1 dpdk2 \ -- set interface dpdk1 type=dpdk options:dpdk-devargs=0000:01:00.0 \ -- set interface dpdk2 type=dpdk options:dpdk-devargs=0000:02:00.0 1 2 3 4 5 6 7 8 9 4.设置/清除网桥的openflow协议版本 ovs-vsctl set bridge br0 protocols=OpenFlow13 ovs-vsctl clear bridge br0 protocols 1 2 5.查看某网桥当前流表 ovs-ofctl dump-flows br0 ovs-ofctl -O OpenFlow13 dump-flows br0 ovs-appctl bridge/dump-flows br0 1 2 3 6.设置/删除控制器 ovs-vsctl set-controller br0 tcp:1.2.3.4:6633 ovs-vsctl del-controller br0 1 2 7.查看控制器列表 ovs-vsctl list controller 1 8.设置/删除被动连接控制器 ovs-vsctl set-manager tcp:1.2.3.4:6640 ovs-vsctl get-manager ovs-vsctl del-manager 1 2 3 9.设置/移除可选选项 ovs-vsctl set Interface eth0 options:link_speed=1G ovs-vsctl remove Interface eth0 options link_speed 1 2 10.设置fail模式,支持standalone或者secure standalone(default):清除所有控制器下发的流表,ovs自己接管 secure:按照原来流表继续转发 ovs-vsctl del-fail-mode br0 ovs-vsctl set-fail-mode br0 secure ovs-vsctl get-fail-mode br0 1 2 3 11.查看接口id等 ovs-appctl dpif/show 1 12.查看接口统计 ovs-ofctl dump-ports br0 1 流表类 流表操作 1.添加普通流表 ovs-ofctl add-flow br0 in_port=1,actions=output:2 1 2.删除所有流表 ovs-ofctl del-flows br0 1 3.按匹配项来删除流表 ovs-ofctl del-flows br0 "in_port=1" 1 匹配项 1.匹配vlan tag,范围为0-4095 ovs-ofctl add-flow br0 priority=401,in_port=1,dl_vlan=777,actions=output:2 1 2.匹配vlan pcp,范围为0-7 ovs-ofctl add-flow br0 priority=401,in_port=1,dl_vlan_pcp=7,actions=output:2 1 3.匹配源/目的MAC ovs-ofctl add-flow br0 in_port=1,dl_src=00:00:00:00:00:01/00:00:00:00:00:01,actions=output:2 ovs-ofctl add-flow br0 in_port=1,dl_dst=00:00:00:00:00:01/00:00:00:00:00:01,actions=output:2 1 2 4.匹配以太网类型,范围为0-65535 ovs-ofctl add-flow br0 in_port=1,dl_type=0x0806,actions=output:2 1 5.匹配源/目的IP 条件:指定dl_type=0x0800,或者ip/tcp ovs-ofctl add-flow br0 ip,in_port=1,nw_src=10.10.0.0/16,actions=output:2 ovs-ofctl add-flow br0 ip,in_port=1,nw_dst=10.20.0.0/16,actions=output:2 1 2 6.匹配协议号,范围为0-255 条件:指定dl_type=0x0800或者ip ICMP ovs-ofctl add-flow br0 ip,in_port=1,nw_proto=1,actions=output:2 7.匹配IP ToS/DSCP,tos范围为0-255,DSCP范围为0-63 条件:指定dl_type=0x0800/0x86dd,并且ToS低2位会被忽略(DSCP值为ToS的高6位,并且低2位为预留位) ovs-ofctl add-flow br0 ip,in_port=1,nw_tos=68,actions=output:2 ovs-ofctl add-flow br0 ip,in_port=1,ip_dscp=62,actions=output:2 8.匹配IP ecn位,范围为0-3 条件:指定dl_type=0x0800/0x86dd ovs-ofctl add-flow br0 ip,in_port=1,ip_ecn=2,actions=output:2 9.匹配IP TTL,范围为0-255 ovs-ofctl add-flow br0 ip,in_port=1,nw_ttl=128,actions=output:2 10.匹配tcp/udp,源/目的端口,范围为0-65535 匹配源tcp端口179 ovs-ofctl add-flow br0 tcp,tcp_src=179/0xfff0,actions=output:2 匹配目的tcp端口179 ovs-ofctl add-flow br0 tcp,tcp_dst=179/0xfff0,actions=output:2 匹配源udp端口1234 ovs-ofctl add-flow br0 udp,udp_src=1234/0xfff0,actions=output:2 匹配目的udp端口1234 ovs-ofctl add-flow br0 udp,udp_dst=1234/0xfff0,actions=output:2 11.匹配tcp flags tcp flags=fin,syn,rst,psh,ack,urg,ece,cwr,ns ovs-ofctl add-flow br0 tcp,tcp_flags=ack,actions=output:2 12.匹配icmp code,范围为0-255 条件:指定icmp ovs-ofctl add-flow br0 icmp,icmp_code=2,actions=output:2 13.匹配vlan TCI TCI低12位为vlan id,高3位为priority,例如tci=0xf123则vlan_id为0x123和vlan_pcp=7 ovs-ofctl add-flow br0 in_port=1,vlan_tci=0xf123,actions=output:2 14.匹配mpls label 条件:指定dl_type=0x8847/0x8848 ovs-ofctl add-flow br0 mpls,in_port=1,mpls_label=7,actions=output:2 15.匹配mpls tc,范围为0-7 条件:指定dl_type=0x8847/0x8848 ovs-ofctl add-flow br0 mpls,in_port=1,mpls_tc=7,actions=output:2 1 16.匹配tunnel id,源/目的IP 匹配tunnel id ovs-ofctl add-flow br0 in_port=1,tun_id=0x7/0xf,actions=output:2 匹配tunnel源IP ovs-ofctl add-flow br0 in_port=1,tun_src=192.168.1.0/255.255.255.0,actions=output:2 匹配tunnel目的IP ovs-ofctl add-flow br0 in_port=1,tun_dst=192.168.1.0/255.255.255.0,actions=output:2 一些匹配项的速记符 速记符 匹配项 ip dl_type=0x800 ipv6 dl_type=0x86dd icmp dl_type=0x0800,nw_proto=1 icmp6 dl_type=0x86dd,nw_proto=58 tcp dl_type=0x0800,nw_proto=6 tcp6 dl_type=0x86dd,nw_proto=6 udp dl_type=0x0800,nw_proto=17 udp6 dl_type=0x86dd,nw_proto=17 sctp dl_type=0x0800,nw_proto=132 sctp6 dl_type=0x86dd,nw_proto=132 arp dl_type=0x0806 rarp dl_type=0x8035 mpls dl_type=0x8847 mplsm dl_type=0x8848 指令动作 1.动作为出接口 从指定接口转发出去 ovs-ofctl add-flow br0 in_port=1,actions=output:2 1 2.动作为指定group group id为已创建的group table ovs-ofctl add-flow br0 in_port=1,actions=group:666 1 3.动作为normal 转为L2/L3处理流程 ovs-ofctl add-flow br0 in_port=1,actions=normal 1 4.动作为flood 从所有物理接口转发出去,除了入接口和已关闭flooding的接口 ovs-ofctl add-flow br0 in_port=1,actions=flood 1 5.动作为all 从所有物理接口转发出去,除了入接口 ovs-ofctl add-flow br0 in_port=1,actions=all 1 6.动作为local 一般是转发给本地网桥 ovs-ofctl add-flow br0 in_port=1,actions=local 1 7.动作为in_port 从入接口转发回去 ovs-ofctl add-flow br0 in_port=1,actions=in_port 1 8.动作为controller 以packet-in消息上送给控制器 ovs-ofctl add-flow br0 in_port=1,actions=controller 1 9.动作为drop 丢弃数据包操作 ovs-ofctl add-flow br0 in_port=1,actions=drop 1 10.动作为mod_vlan_vid 修改报文的vlan id,该选项会使vlan_pcp置为0 ovs-ofctl add-flow br0 in_port=1,actions=mod_vlan_vid:8,output:2 1 11.动作为mod_vlan_pcp 修改报文的vlan优先级,该选项会使vlan_id置为0 ovs-ofctl add-flow br0 in_port=1,actions=mod_vlan_pcp:7,output:2 1 12.动作为strip_vlan 剥掉报文内外层vlan tag ovs-ofctl add-flow br0 in_port=1,actions=strip_vlan,output:2 1 13.动作为push_vlan 在报文外层压入一层vlan tag,需要使用openflow1.1以上版本兼容 ovs-ofctl add-flow -O OpenFlow13 br0 in_port=1,actions=push_vlan:0x8100,set_field:4097-\>vlan_vid,output:2 1 ps: set field值为4096+vlan_id,并且vlan优先级为0,即4096-8191,对应的vlan_id为0-4095 14.动作为push_mpls 修改报文的ethertype,并且压入一个MPLS LSE ovs-ofctl add-flow br0 in_port=1,actions=push_mpls:0x8847,set_field:10-\>mpls_label,output:2 1 15.动作为pop_mpls 剥掉最外层mpls标签,并且修改ethertype为非mpls类型 ovs-ofctl add-flow br0 mpls,in_port=1,mpls_label=20,actions=pop_mpls:0x0800,output:2 1 16.动作为修改源/目的MAC,修改源/目的IP 修改源MAC ovs-ofctl add-flow br0 in_port=1,actions=mod_dl_src:00:00:00:00:00:01,output:2 修改目的MAC ovs-ofctl add-flow br0 in_port=1,actions=mod_dl_dst:00:00:00:00:00:01,output:2 修改源IP ovs-ofctl add-flow br0 in_port=1,actions=mod_nw_src:192.168.1.1,output:2 修改目的IP ovs-ofctl add-flow br0 in_port=1,actions=mod_nw_dst:192.168.1.1,output:2 17.动作为修改TCP/UDP/SCTP源目的端口 修改TCP源端口 ovs-ofctl add-flow br0 tcp,in_port=1,actions=mod_tp_src:67,output:2 修改TCP目的端口 ovs-ofctl add-flow br0 tcp,in_port=1,actions=mod_tp_dst:68,output:2 修改UDP源端口 ovs-ofctl add-flow br0 udp,in_port=1,actions=mod_tp_src:67,output:2 修改UDP目的端口 ovs-ofctl add-flow br0 udp,in_port=1,actions=mod_tp_dst:68,output:2 18.动作为mod_nw_tos 条件:指定dl_type=0x0800 修改ToS字段的高6位,范围为0-255,值必须为4的倍数,并且不会去修改ToS低2位ecn值 ovs-ofctl add-flow br0 ip,in_port=1,actions=mod_nw_tos:68,output:2 1 19.动作为mod_nw_ecn 条件:指定dl_type=0x0800,需要使用openflow1.1以上版本兼容 修改ToS字段的低2位,范围为0-3,并且不会去修改ToS高6位的DSCP值 ovs-ofctl add-flow br0 ip,in_port=1,actions=mod_nw_ecn:2,output:2 1 20.动作为mod_nw_ttl 修改IP报文ttl值,需要使用openflow1.1以上版本兼容 ovs-ofctl add-flow -O OpenFlow13 br0 in_port=1,actions=mod_nw_ttl:6,output:2 1 21.动作为dec_ttl 对IP报文进行ttl自减操作 ovs-ofctl add-flow br0 in_port=1,actions=dec_ttl,output:2 1 22.动作为set_mpls_label 对报文最外层mpls标签进行修改,范围为20bit值 ovs-ofctl add-flow br0 in_port=1,actions=set_mpls_label:666,output:2 1 23.动作为set_mpls_tc 对报文最外层mpls tc进行修改,范围为0-7 ovs-ofctl add-flow br0 in_port=1,actions=set_mpls_tc:7,output:2 1 24.动作为set_mpls_ttl 对报文最外层mpls ttl进行修改,范围为0-255 ovs-ofctl add-flow br0 in_port=1,actions=set_mpls_ttl:255,output:2 1 25.动作为dec_mpls_ttl 对报文最外层mpls ttl进行自减操作 ovs-ofctl add-flow br0 in_port=1,actions=dec_mpls_ttl,output:2 1 26.动作为move NXM字段 使用move参数对NXM字段进行操作 将报文源MAC复制到目的MAC字段,并且将源MAC改为00:00:00:00:00:01 ovs-ofctl add-flow br0 in_port=1,actions=move:NXM_OF_ETH_SRC[]-\>NXM_OF_ETH_DST[],mod_dl_src:00:00:00:00:00:01,output:2 1 2 ps: 常用NXM字段参照表 NXM字段 报文字段 NXM_OF_ETH_SRC 源MAC NXM_OF_ETH_DST 目的MAC NXM_OF_ETH_TYPE 以太网类型 NXM_OF_VLAN_TCI vid NXM_OF_IP_PROTO IP协议号 NXM_OF_IP_TOS IP ToS值 NXM_NX_IP_ECN IP ToS ECN NXM_OF_IP_SRC 源IP NXM_OF_IP_DST 目的IP NXM_OF_TCP_SRC TCP源端口 NXM_OF_TCP_DST TCP目的端口 NXM_OF_UDP_SRC UDP源端口 NXM_OF_UDP_DST UDP目的端口 NXM_OF_SCTP_SRC SCTP源端口 NXM_OF_SCTP_DST SCTP目的端口 27.动作为load NXM字段 使用load参数对NXM字段进行赋值操作 push mpls label,并且把10(0xa)赋值给mpls label ovs-ofctl add-flow br0 in_port=1,actions=push_mpls:0x8847,load:0xa-\>OXM_OF_MPLS_LABEL[],output:2 对目的MAC进行赋值 ovs-ofctl add-flow br0 in_port=1,actions=load:0x001122334455-\>OXM_OF_ETH_DST[],output:2 1 2 3 4 28.动作为pop_vlan 弹出报文最外层vlan tag ovs-ofctl add-flow br0 in_port=1,dl_type=0x8100,dl_vlan=777,actions=pop_vlan,output:2 1 meter表 常用操作 由于meter表是openflow1.3版本以后才支持,所以所有命令需要指定OpenFlow1.3版本以上 ps: 在openvswitch-v2.8之前的版本中,还不支持meter 在v2.8版本之后已经实现,要正常使用的话,需要注意的是datapath类型要指定为netdev,band type暂时只支持drop,还不支持DSCP REMARK 1.查看当前设备对meter的支持 ovs-ofctl -O OpenFlow13 meter-features br0 2.查看meter表 ovs-ofctl -O OpenFlow13 dump-meters br0 3.查看meter统计 ovs-ofctl -O OpenFlow13 meter-stats br0 4.创建meter表 限速类型以kbps(kilobits per second)计算,超过20kb/s则丢弃 ovs-ofctl -O OpenFlow13 add-meter br0 meter=1,kbps,band=type=drop,rate=20 同上,增加burst size参数 ovs-ofctl -O OpenFlow13 add-meter br0 meter=2,kbps,band=type=drop,rate=20,burst_size=256 同上,增加stats参数,对meter进行计数统计 ovs-ofctl -O OpenFlow13 add-meter br0 meter=3,kbps,stats,band=type=drop,rate=20,burst_size=256 限速类型以pktps(packets per second)计算,超过1000pkt/s则丢弃 ovs-ofctl -O OpenFlow13 add-meter br0 meter=4,pktps,band=type=drop,rate=1000 5.删除meter表 删除全部meter表 ovs-ofctl -O OpenFlow13 del-meters br0 删除meter id=1 ovs-ofctl -O OpenFlow13 del-meter br0 meter=1 6.创建流表 ovs-ofctl -O OpenFlow13 add-flow br0 in_port=1,actions=meter:1,output:2 group表 由于group表是openflow1.1版本以后才支持,所以所有命令需要指定OpenFlow1.1版本以上 常用操作 group table支持4种类型 all:所有buckets都执行一遍 select: 每次选择其中一个bucket执行,常用于负载均衡应用 ff(FAST FAILOVER):快速故障修复,用于检测解决接口等故障 indirect:间接执行,类似于一个函数方法,被另一个group来调用 1.查看当前设备对group的支持 ovs-ofctl -O OpenFlow13 dump-group-features br0 2.查看group表 ovs-ofctl -O OpenFlow13 dump-groups br0 3.创建group表 类型为all ovs-ofctl -O OpenFlow13 add-group br0 group_id=1,type=all,bucket=output:1,bucket=output:2,bucket=output:3 类型为select ovs-ofctl -O OpenFlow13 add-group br0 group_id=2,type=select,bucket=output:1,bucket=output:2,bucket=output:3 类型为select,指定hash方法(5元组,OpenFlow1.5+) ovs-ofctl -O OpenFlow15 add-group br0 group_id=3,type=select,selection_method=hash,fields=ip_src,bucket=output:2,bucket=output:3 4.删除group表 ovs-ofctl -O OpenFlow13 del-groups br0 group_id=2 5.创建流表 ovs-ofctl -O OpenFlow13 add-flow br0 in_port=1,actions=group:2 goto table配置 数据流先从table0开始匹配,如actions有goto_table,再进行后续table的匹配,实现多级流水线,如需使用goto table,则创建流表时,指定table id,范围为0-255,不指定则默认为table0 1.在table0中添加一条流表条目 ovs-ofctl add-flow br0 table=0,in_port=1,actions=goto_table=1 2.在table1中添加一条流表条目 ovs-ofctl add-flow br0 table=1,ip,nw_dst=10.10.0.0/16,actions=output:2 tunnel配置 如需配置tunnel,必需确保当前系统对各tunnel的remote ip网络可达 gre 1.创建一个gre接口,并且指定端口id=1001 ovs-vsctl add-port br0 gre1 -- set Interface gre1 type=gre options:remote_ip=1.1.1.1 ofport_request=1001 2.可选选项 将tos或者ttl在隧道上继承,并将tunnel id设置成123 ovs-vsctl set Interface gre1 options:tos=inherit options:ttl=inherit options:key=123 3.创建关于gre流表 封装gre转发 ovs-ofctl add-flow br0 ip,in_port=1,nw_dst=10.10.0.0/16,actions=output:1001 解封gre转发 ovs-ofctl add-flow br0 in_port=1001,actions=output:1 vxlan 1.创建一个vxlan接口,并且指定端口id=2001 ovs-vsctl add-port br0 vxlan1 -- set Interface vxlan1 type=vxlan options:remote_ip=1.1.1.1 ofport_request=2001 2.可选选项 将tos或者ttl在隧道上继承,将vni设置成123,UDP目的端为设置成8472(默认为4789) ovs-vsctl set Interface vxlan1 options:tos=inherit options:ttl=inherit options:key=123 options:dst_port=8472 3.创建关于vxlan流表 封装vxlan转发 ovs-ofctl add-flow br0 ip,in_port=1,nw_dst=10.10.0.0/16,actions=output:2001 解封vxlan转发 ovs-ofctl add-flow br0 in_port=2001,actions=output:1 sflow配置 1.对网桥br0进行sflow监控 agent: 与collector通信所在的网口名,通常为管理口 target: collector监听的IP地址和端口,端口默认为6343 header: sFlow在采样时截取报文头的长度 polling: 采样时间间隔,单位为秒 ovs-vsctl -- --id=@sflow create sflow agent=eth0 target=\"10.0.0.1:6343\" header=128 sampling=64 polling=10 -- set bridge br0 sflow=@sflow 2.查看创建的sflow ovs-vsctl list sflow 3.删除对应的网桥sflow配置,参数为sFlow UUID ovs-vsctl remove bridge br0 sflow 7b9b962e-fe09-407c-b224-5d37d9c1f2b3 4.删除网桥下所有sflow配置 ovs-vsctl -- clear bridge br0 sflow 1 QoS配置 ingress policing 1.配置ingress policing,对接口eth0入流限速10Mbps ovs-vsctl set interface eth0 ingress_policing_rate=10000 ovs-vsctl set interface eth0 ingress_policing_burst=8000 2.清除相应接口的ingress policer配置 ovs-vsctl set interface eth0 ingress_policing_rate=0 ovs-vsctl set interface eth0 ingress_policing_burst=0 3.查看接口ingress policer配置 ovs-vsctl list interface eth0 4.查看网桥支持的Qos类型 ovs-appctl qos/show-types br0 端口镜像配置 1.配置eth0收到/发送的数据包镜像到eth1 ovs-vsctl -- set bridge br0 mirrors=@m \ -- --id=@eth0 get port eth0 \ -- --id=@eth1 get port eth1 \ -- --id=@m create mirror name=mymirror select-dst-port=@eth0 select-src-port=@eth0 output-port=@eth1 2.删除端口镜像配置 ovs-vsctl -- --id=@m get mirror mymirror -- remove bridge br0 mirrors @m 3.清除网桥下所有端口镜像配置 ovs-vsctl clear bridge br0 mirrors 4.查看端口镜像配置 ovs-vsctl get bridge br0 mirrors Open vSwitch中有多个命令,分别有不同的作用,大致如下: ovs-vsctl用于控制ovs db ovs-ofctl用于管理OpenFlow switch 的 flow ovs-dpctl用于管理ovs的datapath ovs-appctl用于查询和管理ovs daemon 转载于:https://www.cnblogs.com/liuhongru/p/10336849.html 本篇文章为转载内容。原文链接:https://blog.csdn.net/weixin_30876945/article/details/99916308。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-06-08 17:13:19
295
转载
站内搜索
用于搜索本网站内部文章,支持栏目切换。
知识学习
实践的时候请根据实际情况谨慎操作。
随机学习一条linux命令:
unset VAR
- 删除环境变量。
推荐内容
推荐本栏目内的其它文章,看看还有哪些文章让你感兴趣。
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
历史内容
快速导航到对应月份的历史文章列表。
随便看看
拉到页底了吧,随便看看还有哪些文章你可能感兴趣。
时光飞逝
"流光容易把人抛,红了樱桃,绿了芭蕉。"