前端技术
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
[Redis 数据结构一致性 ]的搜索结果
这里是文章列表。热门标签的颜色随机变换,标签颜色没有特殊含义。
点击某个标签可搜索标签相关的文章。
点击某个标签可搜索标签相关的文章。
转载文章
...到你的c 盘中的全部数据。2. 如果你在安装系统时没有像方案1所说的那样挂载上你的fat32分区,没关系,仍然能够很方便的解决这个问题。首先,用一个文本编辑器(如vi)打开 /etc/fstab,在文件的最后加入类似如下的几行 /dev/hda1 /mnt/c vfat default 0 0 你所要做的修改就是,把/dev/hda1改成你要挂载的fat32分区在linux中的设备号,把/mnt/c改成相应的挂载点即可。注意,挂载点就是一个目录,这个目录要事先建立。举一个例子,我有三个fat32分区,在windows中是c,d,e盘,在linux中的设备号分别为 /dev/hda1,/dev/hda5,/dev/hda6。那么我就要先建立3个挂载点,如/mnt/c,/mnt/d,/mnt/e,然后在 /etc/fstab中加上这么几行: /dev/hda1 /mnt/c vfat default 0 0 /dev/hda5 /mnt/d vfat default 0 0 /dev/hda6 /mnt/e vfat default 0 0 保存一下退出编辑器。这样以后你重启机器后就能直接使用c,d,e这三个fat32格式的windows分区了 十七.问:我的机器重装windows后,开机启动就直接进入了windows,原来的linux进不去了,怎么办? 答:这是由于windows的霸道。重装windows后,windows重写了你的mbr,覆盖掉了grub。解决方法很简单:用你的linux第一张安装盘引导进入linx rescue模式(如何进入?你注意一下系统的提示信息就知道了),执行下面两条命令就可以了 chroot /mnt/sysimage 改变你的根目录 grub-install /dev/hda 安装grub到mbr 十八.问:我的linux开机直接进入文本界面,怎样才能让它默认进入图形界面? 答:修改/etc/inittab文件,其中有一行id:3:initdefault,意思是说开机默认进入运行级别3(多用户的文本界面),把它改成id:5:initdefault,既开机默认进入运行级别5(多用户的图形界面)。这样就行了。 十九.如何同时启动多个x 以前的帖子,估计很多人没看过,贴出来温习一下 Linux里的X-Windows以其独特的面貌和强大的功能吸引了很多原先对linux不感兴趣的人,特别是KDE和GNOME,功能强大不说,而且自带了很多很棒的软件,界面非常友好,很适合于初学者。下面告诉大家一个同时启动6个X的小技巧: 在~/.bashrc中加入 以下几行: alias X=startx -- -bpp 32 -quiet& alias X1=startx -- :1 -bpp 32 -quiet& alias X2=startx -- :2 -bpp 32 -quiet& alias X3=startx -- :3 -bpp 32 -quiet& alias X4=startx -- :4 -bpp 32 -quiet& alias X5=startx -- :5 -bpp 32 -quiet& 其中32是显示器的色彩深度,你应该根据自己的实际情况设置。 之后运行 bash 使改变生效,以后只要依次运行X,X1,X2,X3,X4,X5就可以启动6个X-Windows了。 二十.装了rpm的postgresql之后启动 /etc/init.d/postgresql start 是不能启动postgresql的tcp/ip连接支持的,所以打开/etc/init.d/postgresql这个文件把 su -l postgres -s /bin/sh -c "/usr/bin/pg_ctl -D $PGDATA -p /usr/bin/postmaster start > /dev/null 2>&1" < /dev/null 改为: su -l postgres -s /bin/sh -c "/usr/bin/pg_ctl -o -o -F -i -w -D $PGDATA -p /usr/bin/postmaster start > /dev/null 2>&1" < /dev/null 这样就可以启动数据库的tcp/ip链接了 二十一.如何将man转存为文本文件 以ls的man为例 man ls |col -b >ls.txt 将info变成文本,以make为例 info make -o make.txt -s 二十二.如何在文本模式下发送2进制文件 首先检查系统有没有uuencode 和 uudecode如果没有从光盘上装 rpm -ivh sharutils-x.xx.x-x.rpm 假设要发送的文件是vpopmail-5.2.1.tar.gz执行 uuencode -m vpopmail-5.2.1.tar.gz vpopmail.tar.gz>encodefile 说明: uuenode是编码命令,-m是使用mime64编码,vpopmail-5.2.1.tar.gz是要编码的文件,vpopmail.tar.gz是如果解码后得到的文件名,encodefile是编码后的文件名。 执行上述命令之后就可以通过mail命令发送编码后的文件了 mail chenlf@chinalinuxpub.com<encodefile 好了,现在我来接收邮件 在控制台上输入mail命令: mail Mail version 8.1 6/6/93. Type ? for help. "/var/spool/mail/chenlf": 2 messages 2 new >N 1 chenlf@ns1.catv.net Mon Jun 10 16:44 17/363 N 2 root@ns2.catv.net Mon Jun 10 16:45 6091/371145 & 2 Message 2: From root@ns2.catv.net Mon Jun 10 16:45:28 2002 Date: Mon, 10 Jun 2002 16:44:51 +0800 From: root <root@ns2.catv.net> To: chenlf@chinalinuxpub.com begin-base64 644 vpopmai.tar.gz H4sIABr15TwAA+w9a2PbNpL7NfwVqNPbWIlFPSzbiR2n9SuxE7/OcuLNtdmU EiGLMUWqfFhWt7u//eYBgKRE2U7iTa+3VndjiQQGg5nBYDAYDC6H4XDgeH51 yW7ajdpf/h2fer1VX1lagr/1+spyq/BXff5SX2mtNBZXmovN5l/qjWZrqfEX sfRvwWbik8aJEwnxl7ifDofXlLvp/Z/0c1nk/8uN/777NuqNen251ZrB/+XF pcUG8r/ZbC0vL9ZXoPwi/O8von73qEx//sP5bwHHxanT8aUIe2IrDBIZJLFl 7QVJFFovpZOkkYxFL4yEFhVLCKhk1W2xG45E1wnEnohlIsJAiksvSlLHF24I JQORhKIjRdKXYhh5Ayca6xcAD8DQm4HT7XuB/EGcSXgbPErEyAkSrNp3LqVw grGoyaRbGzpxPHJFGssotq0Gtw6l9gTgJbixode9EOlQDMaTmEjE/AerydVc rAY4jJzIFY7vC3wL2DgJvJIxIjFwkm6fWkfw1KoAIti/EgkWc3A6YRp05ReB aeXAQH34GoXOwAvOVUnoEnwRYRqJeJAMgczRpYzEyEv6YQoUH8oACltLtjjD Rr1YOCJ2BkPgJop1IuJu5A0TYh9xIdQwfrCWTdt9pMKvaZg4j5jT3PgojC5+ sFZswM0LAJzvSyhGXQSCOmLoO9DtEOAicBCD2qUT1agAg44BSd+1niIEzVPs ................. ................. ................. & s 2 encodefile "encode" [New file] & q 然后进行解码 uudecode encodefile ls encodefile vpopmai.tar.gz tar zxvf vpopmail.tar.gz OK了 二十三.将 man page 转成 HTML 格式 使用 man2html 这个指令,就可以将 man page 转成 HTML 格式了。用法是: man2html filename > htmlfile.html 二十四.如何在gnome和kde之间切换。 如果你是以图形登录方式登录linux,那么点击登录界面上的session(任务)即可以选择gnome和kde。如果你是以文本方式登录,那执行switchdesk gnome或switchdesk kde,然后再startx就可以进入gnome或kde。 25...tar,.tar.gz,.bz2,.tar.bz2,.bz,.gz是什么文件,如何解开他们? 他们都是文件(压缩)包。 .tar:把文件打包,不压缩:tar cvf .tar dirName 解开:tar xvf .tar .tar.gz:把文件打包并压缩:tar czvf .tar.gz dirName 解开:tar xzvf .tar.gz .bz2:解开:bzip2 -d .bz2 .bz:解开:bzip -d .bz .gz:解开:gzip -d .gz 26.linux下如何解开.zip,.rar压缩文件? rh8下有一个图形界面的软件file-roller可以做这件事。令外可以用unzip .zip解开zip文件,unrar .rar解开rar文件,不过unrar一般系统不自带,要到网上下载。 27.linux下如何浏览.iso光盘镜像文件? a.建一个目录,如:mkdir a b.把iso文件挂载到该目录上:mount -o loop xxxx.iso a 现在目录a里的内容就是iso文件里的内容了。 28.linux下如何配置网络? 用netconfig。“IP address:”就是要配置的IP地址,“Netmask:”子网掩码,“Default gateway (IP):”网关,“Primary nameserver:”DNS服务器IP。 29.如何让鼠标支持滚轮? 在配置鼠标时,选择微软的鼠标,并正确选择端口如ps2,usb等 30.如何让控制台支持中文显示? 安装zhcon。zhcon需要libimm_server.so和libpth.so.13这两个库支持。一般的中文输入法应该都有libimm_server.so。libpth.so.13出自pth-1.3.x。把这两个文件放到/usr/lib下就行了。 31.如何配置grub? 修改/boot/grub/grub.conf文件。其中 “default=n”(n是个数字)是grub引导菜单默认被选中的项,n从0开始,0表示第一项,1表示第二项,依此类推。 “timeout=x”(x是一个数)是超时时间,单位是妙。也就是引导菜单显示后,如果x秒内用户不进行选择,那么grub将启动默认项。 “splashimage =xxxxxx”,这是引导菜单的背景图,先不理他。 其它常用项我用下面的例子来说明: title Red Hat 8.0 root (hd1,6) kernel /boot/vmlinuz-2.4.18-14 ro root=/dev/hdb7 initrd /boot/initrd-2.4.18-14.img 其中"Red Hat 8.0"是在启动菜单列表里显示的名字 root (hdx,y)用来指定你的boot分区位置,如果你没有分boot分区(本例就没分boot分区),那就指向根分区就行了,hdx是linux所在硬盘,hd0是第一块硬盘,hd1是第二块,依此类推。y是分区位置,从0开始,也就是等于分区号减一,比如你要指向的分区是hdx7,那么y就是6,如果是hdx1,那y就是0。注意root后面要有一个空格。 kernel /boot/vmlinuz-2.4.18-14,其中"/boot/vmlinuz-2.4.18-14"是你要用的内核路径,如果你编译了心内核,把它改成你的新内核的路径就行了。 ro就不用管,写上不会有错。 root=/dev/hdxx指定根分区,本例是hdb7,所以root=/dev/hdb7 initrd xxxxxxxxxxxxx这行不要也行,目前我还不清楚它是做什么用的。 上面是linux的,下面是windows的 title windows 98 rootnoverify (hd0,0) chainloader +1 title xxxxxxx不用解释了,上面有解释。 rootnoverify (hdx,y)用来指定windows所在分区,x,y跟上面一样,注意rootnoverify后有空格。 chainloader +1照抄就行,注意空格。 本篇文章为转载内容。原文链接:https://blog.csdn.net/gudulyn/article/details/764890。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-10-27 09:27:49
255
转载
转载文章
...100 时,会不断把数据发给监视它的对象。 Observer:监视者,它监视Subject,当 Subject 中的某件事发生的时候,会告知Observer,而Observer 则会采取相应的行动。在本范例中,Observer 有警报器和显示器,它们采取的行动分别是发出警报和显示水温。 在本例中,事情发生的顺序应该是这样的: 1. 警报器和显示器告诉热水器,它对它的温度比较感兴趣(注册)。 2. 热水器知道后保留对警报器和显示器的引用。 3. 热水器进行烧水这一动作,当水温超过 95 度时,通过对警报器和显示器的引用,自动调用警报器的MakeAlert()方法、显示器的ShowMsg()方法。 类似这样的例子是很多的,GOF 对它进行了抽象,称为 Observer 设计模式:Observer 设计模式是为了定义对象间的一种一对多的依赖关系,以便于当一个对象的状态改变时,其他依赖于它的对象会被自动告知并更新。Observer 模式是一种松耦合的设计模式。 1.4.3 实现范例的Observer 设计模式 我们之前已经对委托和事件介绍很多了,现在写代码应该很容易了,现在在这里直接给出代码,并在注释中加以说明。 namespace Delegate{public class Heater{private int temperature;public delegate void BoilHandler(int param);public event BoilHandler BoilEvent;public void BoilWater(){for (int i = 0; i <= 100; i++){temperature = i;if (temperature > 95){if (BoilEvent != null){ BoilEvent(temperature); // 调用所有注册对象的方法} }} }}public class Alarm{public void MakeAlert(int param){Console.WriteLine("Alarm:嘀嘀嘀,水已经 {0} 度了:", param);} }public class Display{public static void ShowMsg(int param) // 静态方法{ Console.WriteLine("Display:水快烧开了,当前温度:{0}度。", param);} }class Program{static void Main(){Heater heater = new Heater();Alarm alarm = new Alarm();heater.BoilEvent += alarm.MakeAlert; // 注册方法heater.BoilEvent += (new Alarm()).MakeAlert; // 给匿名对象注册方法heater.BoilEvent += Display.ShowMsg; // 注册静态方法heater.BoilWater(); // 烧水,会自动调用注册过对象的方法} }} 输出为: // Alarm:嘀嘀嘀,水已经 96 度了: Alarm:嘀嘀嘀,水已经 96 度了: Display:水快烧开了,当前温度:96 度。 // 省略... // 1.4.4 .NET 框架中的委托与事件 尽管上面的范例很好地完成了我们想要完成的工作,但是我们不仅疑惑:为什么.NET Framework 中的事件模型和上面的不同?为什么有很多的EventArgs 参数? 在回答上面的问题之前,我们先搞懂 .NET Framework 的编码规范: 1. 委托类型的名称都应该以 EventHandler 结束。 2. 委托的原型定义:有一个void 返回值,并接受两个输入参数:一个Object 类型,一个EventArgs 类型(或继承自EventArgs)。 3. 事件的命名为委托去掉 EventHandler 之后剩余的部分。 4. 继承自 EventArgs 的类型应该以EventArgs 结尾。 再做一下说明: 1. 委托声明原型中的Object 类型的参数代表了Subject,也就是监视对象,在本例中是Heater(热水器)。回调函数(比如Alarm 的MakeAlert)可以通过它访问触发事件的对象(Heater)。 2. EventArgs 对象包含了Observer 所感兴趣的数据,在本例中是temperature。 上面这些其实不仅仅是为了编码规范而已,这样也使得程序有更大的灵活性。比如说,如果我们不光想获得热水器的温度,还想在Observer 端(警报器或者显示器)方法中获得它的生产日期、型号、价格,那么委托和方法的声明都会变得很麻烦,而如果我们将热水器的引用传给警报器的方法,就可以在方法中直接访问热水器了。 现在我们改写之前的范例,让它符合.NET Framework的规范: using System;using System.Collections.Generic;using System.Text;namespace Delegate{public class Heater{private int temperature;public string type = "RealFire 001"; // 添加型号作为演示public string area = "China Xian"; // 添加产地作为演示public delegate void BoiledEventHandler(Object sender, BoiledEventArgs e);public event BoiledEventHandler Boiled; // 声明事件// 定义 BoiledEventArgs 类,传递给 Observer 所感兴趣的信息public class BoiledEventArgs : EventArgs{public readonly int temperature;public BoiledEventArgs(int temperature){this.temperature = temperature;} }// 可以供继承自 Heater 的类重写,以便继承类拒绝其他对象对它的监视protected virtual void OnBoiled(BoiledEventArgs e){if (Boiled != null){Boiled(this, e); // 调用所有注册对象的方法} }public void BoilWater(){for (int i = 0; i <= 100; i++){temperature = i;if (temperature > 95){// 建立BoiledEventArgs 对象。BoiledEventArgs e = new BoiledEventArgs(temperature);OnBoiled(e); // 调用 OnBolied 方法} }}public class Alarm{public void MakeAlert(Object sender, Heater.BoiledEventArgs e){Heater heater = (Heater)sender; // 这里是不是很熟悉呢?// 访问 sender 中的公共字段Console.WriteLine("Alarm:{0} - {1}: ", heater.area, heater.type);Console.WriteLine("Alarm: 嘀嘀嘀,水已经 {0} 度了:", e.temperature);Console.WriteLine();} }public class Display{public static void ShowMsg(Object sender, Heater.BoiledEventArgs e) // 静态方法{Heater heater = (Heater)sender;Console.WriteLine("Display:{0} - {1}: ", heater.area, heater.type);Console.WriteLine("Display:水快烧开了,当前温度:{0}度。", e.temperature);Console.WriteLine();} }class Program{static void Main(){Heater heater = new Heater();Alarm alarm = new Alarm();heater.Boiled += alarm.MakeAlert; //注册方法heater.Boiled += (new Alarm()).MakeAlert; //给匿名对象注册方法heater.Boiled += new Heater.BoiledEventHandler(alarm.MakeAlert); //也可以这么注册heater.Boiled += Display.ShowMsg; //注册静态方法heater.BoilWater(); //烧水,会自动调用注册过对象的方法} }} } 输出为: Alarm:China Xian - RealFire 001: Alarm: 嘀嘀嘀,水已经 96 度了: Alarm:China Xian - RealFire 001: Alarm: 嘀嘀嘀,水已经 96 度了: Alarm:China Xian - RealFire 001: Alarm: 嘀嘀嘀,水已经 96 度了: Display:China Xian - RealFire 001: Display:水快烧开了,当前温度:96 度。 // 省略 ... 1.5 委托进阶 1.5.1 为什么委托定义的返回值通常都为 void ? 尽管并非必需,但是我们发现很多的委托定义返回值都为 void,为什么呢?这是因为委托变量可以供多个订阅者注册,如果定义了返回值,那么多个订阅者的方法都会向发布者返回数值,结果就是后面一个返回的方法值将前面的返回值覆盖掉了,因此,实际上只能获得最后一个方法调用的返回值。可以运行下面的代码测试一下。除此以外,发布者和订阅者是松耦合的,发布者根本不关心谁订阅了它的事件、为什么要订阅,更别说订阅者的返回值了,所以返回订阅者的方法返回值大多数情况下根本没有必要。 1.5.2 如何让事件只允许一个客户订阅? 少数情况下,比如像上面,为了避免发生“值覆盖”的情况(更多是在异步调用方法时,后面会讨论),我们可能想限制只允许一个客户端注册。此时怎么做呢?我们可以向下面这样,将事件声明为private 的,然后提供两个方法来进行注册和取消注册: public class Publishser{private event GeneralEventHandler NumberChanged; // 声明一个私有事件// 注册事件public void Register(GeneralEventHandler method){NumberChanged = method;}// 取消注册public void UnRegister(GeneralEventHandler method){NumberChanged -= method;}public void DoSomething(){// 做某些其余的事情if (NumberChanged != null){ // 触发事件string rtn = NumberChanged();Console.WriteLine("Return: {0}", rtn); // 打印返回的字符串,输出为Subscriber3} }} 注意上面,在UnRegister()中,没有进行任何判断就使用了NumberChanged -= method 语句。这是因为即使method 方法没有进行过注册,此行语句也不会有任何问题,不会抛出异常,仅仅是不会产生任何效果而已。 注意在Register()方法中,我们使用了赋值操作符“=”,而非“+=”,通过这种方式就避免了多个方法注册。 1.7 委托和方法的异步调用 通常情况下,如果需要异步执行一个耗时的操作,我们会新起一个线程,然后让这个线程去执行代码。但是对于每一个异步调用都通过创建线程来进行操作显然会对性能产生一定的影响,同时操作也相对繁琐一些。.NET 中可以通过委托进行方法的异步调用,就是说客户端在异步调用方法时,本身并不会因为方法的调用而中断,而是从线程池中抓取一个线程去执行该方法,自身线程(主线程)在完成抓取线程这一过程之后,继续执行下面的代码,这样就实现了代码的并行执行。使用线程池的好处就是避免了频繁进行异步调用时创建、销毁线程的开销。当我们在委托对象上调用BeginInvoke()时,便进行了一个异步的方法调用。 事件发布者和订阅者之间往往是松耦合的,发布者通常不需要获得订阅者方法执行的情况;而当使用异步调用时,更多情况下是为了提升系统的性能,而并非专用于事件的发布和订阅这一编程模型。而在这种情况下使用异步编程时,就需要进行更多的控制,比如当异步执行方法的方法结束时通知客户端、返回异步执行方法的返回值等。本节就对 BeginInvoke() 方法、EndInvoke() 方法和其相关的 IAysncResult 做一个简单的介绍。 我们先看这样一段代码,它演示了不使用异步调用的通常情况: class Program7{static void Main(string[] args){Console.WriteLine("Client application started!\n");Thread.CurrentThread.Name = "Main Thread";Calculator cal = new Calculator();int result = cal.Add(2, 5);Console.WriteLine("Result: {0}\n", result);// 做某些其它的事情,模拟需要执行3 秒钟for (int i = 1; i <= 3; i++){Thread.Sleep(TimeSpan.FromSeconds(i));Console.WriteLine("{0}: Client executed {1} second(s).", Thread.CurrentThread.Name, i);}Console.WriteLine("\nPress any key to exit...");Console.ReadLine();} }public class Calculator{public int Add(int x, int y){if (Thread.CurrentThread.IsThreadPoolThread){Thread.CurrentThread.Name = "Pool Thread";}Console.WriteLine("Method invoked!");// 执行某些事情,模拟需要执行2 秒钟for (int i = 1; i <= 2; i++){Thread.Sleep(TimeSpan.FromSeconds(i));Console.WriteLine("{0}: Add executed {1} second(s).", Thread.CurrentThread.Name, i);}Console.WriteLine("Method complete!");return x + y;} } 上面代码有几个关于对于线程的操作,如果不了解可以看一下下面的说明,如果你已经了解可以直接跳过: 1. Thread.Sleep(),它会让执行当前代码的线程暂停一段时间(如果你对线程的概念比较陌生,可以理解为使程序的执行暂停一段时间),以毫秒为单位,比如Thread.Sleep(1000),将会使线程暂停1 秒钟。在上面我使用了它的重载方法,个人觉得使用TimeSpan.FromSeconds(1),可读性更好一些。 2. Thread.CurrentThread.Name,通过这个属性可以设置、获取执行当前代码的线程的名称,值得注意的是这个属性只可以设置一次,如果设置两次,会抛出异常。 3. Thread.IsThreadPoolThread,可以判断执行当前代码的线程是否为线程池中的线程。 通过这几个方法和属性,有助于我们更好地调试异步调用方法。上面代码中除了加入了一些对线程的操作以外再没有什么特别之处。我们建了一个Calculator 类,它只有一个Add 方法,我们模拟了这个方法需要执行2 秒钟时间,并且每隔一秒进行一次输出。而在客户端程序中,我们使用result 变量保存了方法的返回值并进行了打印。随后,我们再次模拟了客户端程序接下来的操作需要执行2 秒钟时间。运行这段程序,会产生下面的输出: // Client application started! Method invoked! Main Thread: Add executed 1 second(s). Main Thread: Add executed 2 second(s). Method complete! Result: 7 Main Thread: Client executed 1 second(s). Main Thread: Client executed 2 second(s). Main Thread: Client executed 3 second(s). Press any key to exit... // 如果你确实执行了这段代码,会看到这些输出并不是一瞬间输出的,而是执行了大概5 秒钟的时间,因为线程是串行执行的,所以在执行完 Add() 方法之后才会继续客户端剩下的代码。 接下来我们定义一个AddDelegate 委托,并使用BeginInvoke()方法来异步地调用它。在上面已经介绍过,BeginInvoke()除了最后两个参数为AsyncCallback 类型和Object 类型以外,前面的参数类型和个数与委托定义相同。另外BeginInvoke()方法返回了一个实现了IAsyncResult 接口的对象(实际上就是一个AsyncResult 类型实例,注意这里IAsyncResult 和AysncResult 是不同的,它们均包含在.NET Framework 中)。 AsyncResult 的用途有这么几个:传递参数,它包含了对调用了BeginInvoke()的委托的引用;它还包含了BeginInvoke()的最后一个Object 类型的参数;它可以鉴别出是哪个方法的哪一次调用,因为通过同一个委托变量可以对同一个方法调用多次。 EndInvoke()方法接受IAsyncResult 类型的对象(以及ref 和out 类型参数,这里不讨论了,对它们的处理和返回值类似),所以在调用BeginInvoke()之后,我们需要保留IAsyncResult,以便在调用EndInvoke()时进行传递。这里最重要的就是EndInvoke()方法的返回值,它就是方法的返回值。除此以外,当客户端调用EndInvoke()时,如果异步调用的方法没有执行完毕,则会中断当前线程而去等待该方法,只有当异步方法执行完毕后才会继续执行后面的代码。所以在调用完BeginInvoke()后立即执行EndInvoke()是没有任何意义的。我们通常在尽可能早的时候调用BeginInvoke(),然后在需要方法的返回值的时候再去调用EndInvoke(),或者是根据情况在晚些时候调用。说了这么多,我们现在看一下使用异步调用改写后上面的代码吧: using System.Threading;using System;public delegate int AddDelegate(int x, int y);class Program8{static void Main(string[] args){Console.WriteLine("Client application started!\n");Thread.CurrentThread.Name = "Main Thread";Calculator cal = new Calculator();AddDelegate del = new AddDelegate(cal.Add);IAsyncResult asyncResult = del.BeginInvoke(2, 5, null, null); // 异步调用方法// 做某些其它的事情,模拟需要执行3 秒钟for (int i = 1; i <= 3; i++){Thread.Sleep(TimeSpan.FromSeconds(i));Console.WriteLine("{0}: Client executed {1} second(s).", Thread.CurrentThread.Name, i);}int rtn = del.EndInvoke(asyncResult);Console.WriteLine("Result: {0}\n", rtn);Console.WriteLine("\nPress any key to exit...");Console.ReadLine();} }public class Calculator{public int Add(int x, int y){if (Thread.CurrentThread.IsThreadPoolThread){Thread.CurrentThread.Name = "Pool Thread";}Console.WriteLine("Method invoked!");// 执行某些事情,模拟需要执行2 秒钟for (int i = 1; i <= 2; i++){Thread.Sleep(TimeSpan.FromSeconds(i));Console.WriteLine("{0}: Add executed {1} second(s).", Thread.CurrentThread.Name, i);}Console.WriteLine("Method complete!");return x + y;} } 此时的输出为: // Client application started! Method invoked! Main Thread: Client executed 1 second(s). Pool Thread: Add executed 1 second(s). Main Thread: Client executed 2 second(s). Pool Thread: Add executed 2 second(s). Method complete! Main Thread: Client executed 3 second(s). Result: 7 Press any key to exit... // 现在执行完这段代码只需要3 秒钟时间,两个for 循环所产生的输出交替进行,这也说明了这两段代码并行执行的情况。可以看到Add() 方法是由线程池中的线程在执行, 因为Thread.CurrentThread.IsThreadPoolThread 返回了True,同时我们对该线程命名为了Pool Thread。另外我们可以看到通过EndInvoke()方法得到了返回值。有时候,我们可能会将获得返回值的操作放到另一段代码或者客户端去执行,而不是向上面那样直接写在BeginInvoke()的后面。比如说我们在Program 中新建一个方法GetReturn(),此时可以通过AsyncResult 的AsyncDelegate 获得del 委托对象,然后再在其上调用EndInvoke()方法,这也说明了AsyncResult 可以唯一的获取到与它相关的调用了的方法(或者也可以理解成委托对象)。所以上面获取返回值的代码也可以改写成这样: private static int GetReturn(IAsyncResult asyncResult){AsyncResult result = (AsyncResult)asyncResult;AddDelegate del = (AddDelegate)result.AsyncDelegate;int rtn = del.EndInvoke(asyncResult);return rtn;} 然后再将int rtn = del.EndInvoke(asyncResult);语句改为int rtn = GetReturn(asyncResult);。注意上面IAsyncResult 要转换为实际的类型AsyncResult 才能访问AsyncDelegate 属性,因为它没有包含在IAsyncResult 接口的定义中。 BeginInvoke 的另外两个参数分别是AsyncCallback 和Object 类型,其中AsyncCallback 是一个委托类型,它用于方法的回调,即是说当异步方法执行完毕时自动进行调用的方法。它的定义为: // public delegate void AsyncCallback(IAsyncResult ar); // Object 类型用于传递任何你想要的数值,它可以通过IAsyncResult 的AsyncState 属性获得。下面我们将获取方法返回值、打印返回值的操作放到了OnAddComplete()回调方法中: using System.Threading;using System;using System.Runtime.Remoting.Messaging;public delegate int AddDelegate(int x, int y);class Program9{static void Main(string[] args){Console.WriteLine("Client application started!\n");Thread.CurrentThread.Name = "Main Thread";Calculator cal = new Calculator();AddDelegate del = new AddDelegate(cal.Add);string data = "Any data you want to pass.";AsyncCallback callBack = new AsyncCallback(OnAddComplete);del.BeginInvoke(2, 5, callBack, data); // 异步调用方法// 做某些其它的事情,模拟需要执行3 秒钟for (int i = 1; i <= 3; i++){Thread.Sleep(TimeSpan.FromSeconds(i));Console.WriteLine("{0}: Client executed {1} second(s).", Thread.CurrentThread.Name, i);}Console.WriteLine("\nPress any key to exit...");Console.ReadLine();}static void OnAddComplete(IAsyncResult asyncResult){AsyncResult result = (AsyncResult)asyncResult;AddDelegate del = (AddDelegate)result.AsyncDelegate;string data = (string)asyncResult.AsyncState;int rtn = del.EndInvoke(asyncResult);Console.WriteLine("{0}: Result, {1}; Data: {2}\n", Thread.CurrentThread.Name, rtn, data);} }public class Calculator{public int Add(int x, int y){if (Thread.CurrentThread.IsThreadPoolThread){Thread.CurrentThread.Name = "Pool Thread";}Console.WriteLine("Method invoked!");// 执行某些事情,模拟需要执行2 秒钟for (int i = 1; i <= 2; i++){Thread.Sleep(TimeSpan.FromSeconds(i));Console.WriteLine("{0}: Add executed {1} second(s).", Thread.CurrentThread.Name, i);}Console.WriteLine("Method complete!");return x + y;} } 它产生的输出为: Client application started! Method invoked! Main Thread: Client executed 1 second(s). Pool Thread: Add executed 1 second(s). Main Thread: Client executed 2 second(s). Pool Thread: Add executed 2 second(s). Method complete! Pool Thread: Result, 7; Data: Any data you want to pass. Main Thread: Client executed 3 second(s). Press any key to exit... 这里有几个值得注意的地方: 1、我们在调用BeginInvoke()后不再需要保存IAysncResult 了,因为AysncCallback 委托将该对象定义在了回调方法的参数列表中; 2、我们在OnAddComplete()方法中获得了调用BeginInvoke()时最后一个参数传递的值,字符串“Any data you want to pass”; 3、执行回调方法的线程并非客户端线程Main Thread,而是来自线程池中的线程Pool Thread。另外如前面所说,在调用EndInvoke()时有可能会抛出异常,所以在应该将它放到try/catch 块中,这里就不再示范了。 1.8 总结 我们详细地讨论了C中的委托和事件,包括什么是委托、为什么要使用委托、事件的由来、.NET Framework 中的委托和事件、委托中方法异常和超时的处理、委托与异步编程、委托和事件对Observer 设计模式的意义。拥有了本章的知识,相信你以后遇到委托和事件时,将不会再有所畏惧。 本篇文章为转载内容。原文链接:https://blog.csdn.net/beyonddeg/article/details/53528482。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-10-05 16:02:19
81
转载
转载文章
...ket往后台发送日志数据,在这里我们是要做基于SparkStreaming做实时在线统计。那么数据就需要放进消息系统(Kafka)中,我们的Spark Streaming应用程序就会去Kafka中Pull数据过来进行计算和消费,并把计算后的数据放入到持久化系统中(MySQL) 广告点击系统实时分析的意义:因为可以在线实时的看见广告的投放效果,就为广告的更大规模的投入和调整打下了坚实的基础,从而为公司带来最大化的经济回报。 核心需求: 1、实时黑名单动态过滤出有效的用户广告点击行为:因为黑名单用户可能随时出现,所以需要动态更新; 2、在线计算广告点击流量; 3、Top3热门广告; 4、每个广告流量趋势; 5、广告点击用户的区域分布分析 6、最近一分钟的广告点击量; 7、整个广告点击Spark Streaming处理程序724小时运行; 数据格式: 时间、用户、广告、城市等 技术细节: 在线计算用户点击的次数分析,屏蔽IP等; 使用updateStateByKey或者mapWithState进行不同地区广告点击排名的计算; Spark Streaming+Spark SQL+Spark Core等综合分析数据; 使用Window类型的操作; 高可用和性能调优等等; 流量趋势,一般会结合DB等; Spark Core / /package com.tom.spark.SparkApps.sparkstreaming;import java.util.Date;import java.util.HashMap;import java.util.Map;import java.util.Properties;import java.util.Random;import kafka.javaapi.producer.Producer;import kafka.producer.KeyedMessage;import kafka.producer.ProducerConfig;/ 数据生成代码,Kafka Producer产生数据/public class MockAdClickedStat {/ @param args/public static void main(String[] args) {final Random random = new Random();final String[] provinces = new String[]{"Guangdong", "Zhejiang", "Jiangsu", "Fujian"};final Map<String, String[]> cities = new HashMap<String, String[]>();cities.put("Guangdong", new String[]{"Guangzhou", "Shenzhen", "Dongguan"});cities.put("Zhejiang", new String[]{"Hangzhou", "Wenzhou", "Ningbo"});cities.put("Jiangsu", new String[]{"Nanjing", "Suzhou", "Wuxi"});cities.put("Fujian", new String[]{"Fuzhou", "Xiamen", "Sanming"});final String[] ips = new String[] {"192.168.112.240","192.168.112.239","192.168.112.245","192.168.112.246","192.168.112.247","192.168.112.248","192.168.112.249","192.168.112.250","192.168.112.251","192.168.112.252","192.168.112.253","192.168.112.254",};/ Kafka相关的基本配置信息/Properties kafkaConf = new Properties();kafkaConf.put("serializer.class", "kafka.serializer.StringEncoder");kafkaConf.put("metadeta.broker.list", "Master:9092,Worker1:9092,Worker2:9092");ProducerConfig producerConfig = new ProducerConfig(kafkaConf);final Producer<Integer, String> producer = new Producer<Integer, String>(producerConfig);new Thread(new Runnable() {public void run() {while(true) {//在线处理广告点击流的基本数据格式:timestamp、ip、userID、adID、province、cityLong timestamp = new Date().getTime();String ip = ips[random.nextInt(12)]; //可以采用网络上免费提供的ip库int userID = random.nextInt(10000);int adID = random.nextInt(100);String province = provinces[random.nextInt(4)];String city = cities.get(province)[random.nextInt(3)];String clickedAd = timestamp + "\t" + ip + "\t" + userID + "\t" + adID + "\t" + province + "\t" + city;producer.send(new KeyedMessage<Integer, String>("AdClicked", clickedAd));try {Thread.sleep(50);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();} }} }).start();} } package com.tom.spark.SparkApps.sparkstreaming;import java.sql.Connection;import java.sql.DriverManager;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.sql.SQLException;import java.util.ArrayList;import java.util.Arrays;import java.util.HashMap;import java.util.HashSet;import java.util.Iterator;import java.util.List;import java.util.Map;import java.util.Set;import java.util.concurrent.LinkedBlockingQueue;import kafka.serializer.StringDecoder;import org.apache.spark.SparkConf;import org.apache.spark.api.java.JavaPairRDD;import org.apache.spark.api.java.JavaRDD;import org.apache.spark.api.java.JavaSparkContext;import org.apache.spark.api.java.function.Function;import org.apache.spark.api.java.function.Function2;import org.apache.spark.api.java.function.PairFunction;import org.apache.spark.api.java.function.VoidFunction;import org.apache.spark.sql.DataFrame;import org.apache.spark.sql.Row;import org.apache.spark.sql.RowFactory;import org.apache.spark.sql.hive.HiveContext;import org.apache.spark.sql.types.DataTypes;import org.apache.spark.sql.types.StructType;import org.apache.spark.streaming.Durations;import org.apache.spark.streaming.api.java.JavaDStream;import org.apache.spark.streaming.api.java.JavaPairDStream;import org.apache.spark.streaming.api.java.JavaPairInputDStream;import org.apache.spark.streaming.api.java.JavaStreamingContext;import org.apache.spark.streaming.api.java.JavaStreamingContextFactory;import org.apache.spark.streaming.kafka.KafkaUtils;import com.google.common.base.Optional;import scala.Tuple2;/ 数据处理,Kafka消费者/public class AdClickedStreamingStats {/ @param args/public static void main(String[] args) {// TODO Auto-generated method stub//好处:1、checkpoint 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
297
转载
转载文章
...动作为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
294
转载
转载文章
...具有服务器身份验证和数据传输加密功能 在爬虫时可能会遇到这样的报错(SSLError)这说明我们要爬取的网站没有SSL证书 处理:res = requests.get(url,verify=False) 二、cookie 通过记录用户信息来确定身份 1 模拟登陆 人人网保持登陆状态import requestsurl = 'http://www.renren.com/976686556/profile' 个人主界面headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.128 Safari/537.36','Cookie':'anonymid=knvqe21amc6ghy; depovince=ZGQT; _r01_=1; taihe_bi\_sdk_uid=c2bd353cea6830a73eb74760fbc9fd5c; taihe_bi_sdk_session=9a91c\62f18e74ee26c3145bb49b4eb9e; ick_login=286c45d0-e571-4fb7-918a-46a9706\18110; first_login_flag=1; ln_uact=17315371375; ln_hurl=http://head.xiao\nei.com/photos/0/0/men_main.gif; wp_fold=0; jebecookies=ee811760-7bc0-43a9-\883c-0d041cb1baf0|||||; _de=A4C6B1A20CD5F525F9DA27654C2D2FDA; p=f5239823cd0af743a5f015652568b6036; t=42783075a815b6cef9f651ca18ff5c166; societyguester=42783075a815b6cef9f651ca18ff5c166; id=976686556; xnsid=f72459d7; ver=7.0; loginfrom=null'}res = requests.get(url,headers=headers) res 响应对象 html = res.textwith open('rr.html','w',encoding='utf-8') as file_obj:file_obj.write(res.text) 2 反反爬机制 12306查票import requests import json json.loads -- json类型的str -> python类型的字典def query():headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.128 Safari/537.36','Cookie':'_uab_collina=159490169403897938828076; JSESSIONID=090F384AC50BE0F1AFA3892BE3F6DBE9; _jc_save_wfdc_flag=dc; _jc_save_fromStation=%u957F%u6C99%2CCSQ; _jc_save_toStation=%u5317%u4EAC%2CBJP; RAIL_DEVICEID=bbXqzYOPTc-SPgujxnGkCBr9t3sq0JQoMSYUdg-FxjyQ5IkfcPCNoreXmBAIh2HSrM9Z9awDR5onIQwy4EZ8pAhaGXWYBAH6etIlFc4dyxLudz525GAcRgVX5HLIxOE1orODUNSb9wvTBAJptPms1z5Pz5K6FXES; RAIL_EXPIRATION=1619479086609; _jc_save_toDate=2021-04-23; BIGipServerpool_passport=182714890.50215.0000; route=6f50b51faa11b987e576cdb301e545c4; _jc_save_fromDate=2021-04-26; BIGipServerportal=3067347210.16671.0000; BIGipServerotn=1725497610.50210.0000'}response = requests.get('https://kyfw.12306.cn/otn/leftTicket/query?leftTicketDTO.train_date=2021-\04-26&leftTicketDTO.from_station=CSQ&leftTicketDTO.to_station=BJP&purpose_codes=ADULT',headers=headers) print(response.content.decode('utf-8'))return response.json()['data']['result']for i in query(): print(i)tem_list = i.split('|') 定义一个标记 给每个数据做个标记 j = 0 技术特别 for n in tem_list: print(j,n) j += 1 通过以上的测试我们知道了 列出是下标索引为3的数据 软卧是下标索引为23的数据if tem_list[23] != '无' and tem_list[23] != '':print(tem_list[3],'有票',tem_list[23])else:print(tem_list[3],'无票') 三、session Session与cookie功能效果相同。Session与Cookie的区别在于Session是记录在服务端的,而Cookie是记录在客户端的。 由于cookie 是存在用户端,而且它本身存储的尺寸大小也有限,最关键是用户可以是可见的,并可以随意的修改,很不安全。那如何又要安全,又可以方便的全局读取信息呢?于是,这个时候,一种新的存储会话机制:session 诞生了 突破12306验证码import requestsreq = requests.session() 保持会话def login(): 笔记本 win7 python3.6 获取验证码图片pic_response = req.get('https://kyfw.12306.cn/passport/captcha/captcha-image?login_site=E&module=login&rand=sjrand')codeImage = pic_response.contentfn = open('code2.png','wb')fn.write(codeImage)fn.close() 从验证码图片的左上角 (0,0)codeStr = input('请输入验证码坐标:')headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.128 Safari/537.36'}data = {'answer': codeStr,'rand': 'sjrand','login_site': 'E'}response = req.post('https://kyfw.12306.cn/passport/captcha/captcha-check',data=data,headers=headers)print(response.text)login() base64伪加密 根本不算是一种加密算法 只不过它的数据看上去更像密文而已 64个字符来表示任意的二进制数据的方法 使用 A-Z A-Z 0 - 9 + / 这64个字符进行加密 import base64url = '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'img_data = base64.b64decode(url) 返回的是二进制数据print(type(img_data))fn = open('code.png','wb')fn.write(img_data)fn.close()'''我们打开了一个有base64加密的图片数据''' 本篇文章为转载内容。原文链接:https://blog.csdn.net/httpsssss/article/details/116136614。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-03-01 12:40:55
564
转载
站内搜索
用于搜索本网站内部文章,支持栏目切换。
知识学习
实践的时候请根据实际情况谨慎操作。
随机学习一条linux命令:
mkdir -p dir1/dir2
- 创建多级目录。
推荐内容
推荐本栏目内的其它文章,看看还有哪些文章让你感兴趣。
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
历史内容
快速导航到对应月份的历史文章列表。
随便看看
拉到页底了吧,随便看看还有哪些文章你可能感兴趣。
时光飞逝
"流光容易把人抛,红了樱桃,绿了芭蕉。"