前端技术
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
[数据存储限制解决方案 ]的搜索结果
这里是文章列表。热门标签的颜色随机变换,标签颜色没有特殊含义。
点击某个标签可搜索标签相关的文章。
点击某个标签可搜索标签相关的文章。
转载文章
...特别慢,该如何排查和解决? 项目内存或者CPU占用率过高如何排查? ConcurrentHashmap原理 数据库分库分表 MQ相关,为什么kafka这么快,什么是零拷贝? 小算法题 http和https协议区别,具体原理 四面(Leader) 手画自己项目的架构图,并且针对架构和中间件提问 印象最深的一本技术书籍是什么? 五面(HR) 没什么过多的问题,主要就是聊了一下自己今后的职业规划,告知了薪资组成体系等等。 插播一条福利!!!最近整理了一套1000道面试题的文档(详细内容见文首推荐文章),以及大厂面试真题,和最近看的几本书。 需要刷题和跳槽的朋友,这些可以免费赠送给大家,帮忙转发文章,宣传一下,后台私信【面试】免费领取! 小天:好像问了两次看书的情况诶?现在面试还问这个? 程序员H:是啊,幸亏之前为了弄懂JVM还看了两本书,不然真不知道说啥了! 小天:看来,我也要找几本书去看了,感情没看过两本书都不敢跳槽了! 程序员H:对了,还有简历,告诉你一个捷径 简历尽量写好一些,项目经验突出: 1、自己的知识广度和深度 2、自身的优势 3、项目的复杂性和难度以及指标 4、自己对于项目做的贡献或者优化 程序员H:唉~这还不能走可怎么办呀!你说,我把主管打一顿,是不是马上就可以走了? 小天:... 查看全文 http://www.taodudu.cc/news/show-3387369.html 相关文章: 阿里菜鸟面经 Java后端开发 社招三年 已拿offer 阿里 菜鸟网络(一面) 2021年阿里菜鸟网络春招实习岗面试分享,简历+面试+面经全套资料! 阿里菜鸟国际Java研发面经(三面+总结):JVM+架构+MySQL+Redis等 2021年3月29日 阿里菜鸟实习面试(一面)(含部分总结) mongodb 子文档排序_猫鼬101:基础知识,子文档和人口简介 特征工程 计算方法Gauss-Jordan消去法求线性方程组的解 使用(VAE)生成建模,理解可变自动编码器背后的数学原理 视觉SLAM入门 -- 学习笔记 - Part2 带你入门nodejs第一天——node基础语法及使用 python3数据结构_Python3-数据结构 debezium-connect-oracle使用 相关数值分析多种算法代码 android iphone treeview,Android之IphoneTreeView带组指示器的ExpandableListView效果 nginx rewrite功能使用 3-3 OneHot编码 JavaWeb:shiro入门小案例 MySQL的定义、操作、控制、查询语言的用法 MongoDB入门学习(三):MongoDB的增删查改 赋值、浅复制和深复制解析 以及get/set应用 他是吴恩达导师,被马云聘为「达摩院」首座 Jordan 标准型定理 列主元的Gauss-Jordan消元法-python实现 Jordan 块的几何 若尔当型(The Jordan form) 第七章 其他神经网络类型 解决迁移系统后无法配置启用WindowsRE环境的问题 宝塔面板迁移系统盘/www到数据盘/home 使用vmware vconverter从物理机迁移系统到虚拟机P2V 本篇文章为转载内容。原文链接:https://blog.csdn.net/m0_62695120/article/details/124510157。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-03-08 20:01:49
69
转载
转载文章
...02 复习 显示所有数据 show databases; 创建新数据库,设置编码方式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
转载
转载文章
...,专注于让Excel数据处理变得简单、快速且占用内存低。通过使用EasyExcel,开发者可以轻松实现Excel文件的读写操作,支持大文件流式读写、自定义样式和模板填充等功能,并提供了丰富的API及回调接口以满足复杂场景下的表格数据处理需求。 MybatisPlus , MybatisPlus是在Mybatis的基础上进行扩展的一套持久层框架,它提供了丰富的增强功能,例如单表基本的CRUD操作、分页查询、性能分析插件以及动态表名、自动填充字段等特性。MybatisPlus简化了开发人员对数据库的操作,降低了SQL编写的工作量,尤其在处理简单的单表操作时,极大地提升了开发效率和代码可读性。 JSON , JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,易于人阅读和编写,同时也易于机器解析和生成。在文中提到的Fastjson是一个Java语言编写的高性能功能完备的JSON库,它可以将Java对象转换成JSON字符串,也可以将JSON字符串反序列化成Java对象,广泛应用于Web服务与前后端数据交互、配置文件存储、日志记录等多种场景。 IPage , IPage是MybatisPlus中封装的分页对象,用来进行数据分页查询。它包含了当前页码、每页显示条数以及总记录数等信息。在执行SQL查询时,MybatisPlus会根据IPage对象的内容自动拼接SQL分页语句,从而实现了数据的高效分页加载,减轻了数据库压力并优化了应用程序性能。
2023-05-26 23:30:52
269
转载
转载文章
...架构计算机系统中用于存储段描述符的数据结构,它包含了内存管理的关键信息。在文中,操作系统内核通过全局描述符表来定义和管理每个进程的代码段、数据段以及其他系统段的权限和地址范围,以确保不同进程间的隔离性和安全性。当cmd_execute_program函数加载并执行用户程序时,会根据当前进程的全局描述符表项设置相应的代码段和数据段,而文中提到的安全漏洞正是由于恶意程序crack.c利用了这个机制,使得两个进程间的数据段得以共享,从而进行非法篡改。 中断处理 , 中断处理是操作系统内核实现的一种重要机制,用于响应来自硬件或软件的各种事件。当CPU检测到如输入输出完成、计时器溢出、错误条件等中断事件时,会暂时停止当前正在执行的程序,并转而去执行预先设定好的中断服务例程(ISR)。在文章中,通过中断处理机制,操作系统能够在应用程序试图执行特权指令或侵犯内核空间时,及时切断其执行,并重新获得对CPU的控制权,从而保障系统的安全稳定运行。 特权指令 , 特权指令是在计算机体系结构中只能由操作系统内核或者处于特定特权模式下的程序执行的一类特殊指令。这类指令通常涉及到诸如访问和修改关键系统资源(如内存管理、中断控制器、任务调度等)的操作。在本文的上下文中,如果用户态的应用程序尝试执行特权指令,系统将触发中断,防止非授权访问内核资源,确保操作系统底层的安全性不受侵害。
2023-03-14 19:08:07
256
转载
转载文章
...近日,随着云计算、大数据以及5G网络的普及,大文件传输与高效下载的需求愈发显著。例如,某知名云存储服务提供商近期宣布升级其文件下载服务,采用先进的分段传输技术以应对用户对超大文件快速稳定下载的需求,这正是基于类似上述文章所介绍的HTTP Range请求头原理。通过服务器端的智能分片处理和客户端的断点续传支持,极大提升了用户在各种网络环境下的下载体验。 此外,前端技术社区也在不断优化大文件下载的用户体验。有开发者分享了一篇关于如何利用Vue.js配合WebSocket实现实时下载进度展示的文章,其中详细解读了在进行文件分片下载时,如何从前端角度实时获取并更新下载进度信息,从而提升用户界面的互动性和友好性。 对于深入理解文件切片下载机制,推荐阅读《HTTP协议权威指南》一书,书中详尽剖析了HTTP协议中的范围请求(Range Request)及其实现方式,这对于掌握和优化文件下载功能具有极高的参考价值。同时,关注Spring Boot官方文档和社区讨论,可以及时获取到针对大文件处理的最新最佳实践和技术动态。 综上所述,在当前高速发展的互联网环境下,结合SpringBoot后端框架与Vue前端技术实现文件切片下载功能,并关注该领域的最新发展与应用案例,无疑将有助于我们更好地服务于用户的实际需求,提升产品竞争力。
2023-01-19 08:12:45
547
转载
转载文章
...太低。 ·借助额外的数据结构描述这种引用关系,例如使用类似位图(bitmap)的方法,记录A和B的内存块之间的引用关系,用一个位来描述一个字,假设在32位机器上(一个字为32位),需要32KB(32KB×32=1M)的空间来描述一个分区。那么我们就可以在这个对象ObjA所在分区A里面添加一个额外的指针,这个指针指向另外一个分区B的位图,如果我们可以把对象ObjA和指针关系进行映射,那么当访问ObjA的时候,顺便访问这个额外的指针,从这个指针指向的位图就能找到被ObjA引用的分区B对应的内存块。通常我们只需要判定位图里面对应的位是否有1,有的话则认为发生了引用。 class CardTable: public CHeapObj<mtGC> {friend class VMStructs;public:typedef uint8_t CardValue;// All code generators assume that the size of a card table entry is one byte.// They need to be updated to reflect any change to this.// This code can typically be found by searching for the byte_map_base() method.STATIC_ASSERT(sizeof(CardValue) == 1);protected:// The declaration order of these const fields is important; see the// constructor before changing.const MemRegion _whole_heap; // the region covered by the card tableconst size_t _page_size; // page size used when mapping _byte_mapsize_t _byte_map_size; // in bytesCardValue _byte_map; // the card marking arrayCardValue _byte_map_base;// Some barrier sets create tables whose elements correspond to parts of// the heap; the CardTableBarrierSet is an example. Such barrier sets will// normally reserve space for such tables, and commit parts of the table// "covering" parts of the heap that are committed. At most one covered// region per generation is needed.static constexpr int max_covered_regions = 2;// The covered regions should be in address order.MemRegion _covered[max_covered_regions];// The last card is a guard card; never committed.MemRegion _guard_region;inline size_t compute_byte_map_size(size_t num_bytes);enum CardValues {clean_card = (CardValue)-1,dirty_card = 0,CT_MR_BS_last_reserved = 1};// a word's worth (row) of clean card valuesstatic const intptr_t clean_card_row = (intptr_t)(-1);// CardTable entry sizestatic uint _card_shift;static uint _card_size;static uint _card_size_in_words;size_t last_valid_index() const {return cards_required(_whole_heap.word_size()) - 1;}private:void initialize_covered_region(void region0_start, void region1_start);MemRegion committed_for(const MemRegion mr) const;public:CardTable(MemRegion whole_heap);virtual ~CardTable() = default;void initialize(void region0_start, void region1_start);// Barrier set functions.// Initialization utilities; covered_words is the size of the covered region// in, um, words.inline size_t cards_required(size_t covered_words) const {assert(is_aligned(covered_words, _card_size_in_words), "precondition");return covered_words / _card_size_in_words;}// Dirty the bytes corresponding to "mr" (not all of which must be// covered.)void dirty_MemRegion(MemRegion mr);// Clear (to clean_card) the bytes entirely contained within "mr" (not// all of which must be covered.)void clear_MemRegion(MemRegion mr);// Return true if "p" is at the start of a card.bool is_card_aligned(HeapWord p) {CardValue pcard = byte_for(p);return (addr_for(pcard) == p);}// Mapping from address to card marking array entryCardValue byte_for(const void p) const {assert(_whole_heap.contains(p),"Attempt to access p = " PTR_FORMAT " out of bounds of "" card marking array's _whole_heap = [" PTR_FORMAT "," PTR_FORMAT ")",p2i(p), p2i(_whole_heap.start()), p2i(_whole_heap.end()));CardValue result = &_byte_map_base[uintptr_t(p) >> _card_shift];assert(result >= _byte_map && result < _byte_map + _byte_map_size,"out of bounds accessor for card marking array");return result;}// The card table byte one after the card marking array// entry for argument address. Typically used for higher bounds// for loops iterating through the card table.CardValue byte_after(const void p) const {return byte_for(p) + 1;}void invalidate(MemRegion mr);// Provide read-only access to the card table array.const CardValue byte_for_const(const void p) const {return byte_for(p);}const CardValue byte_after_const(const void p) const {return byte_after(p);}// Mapping from card marking array entry to address of first wordHeapWord addr_for(const CardValue p) const {assert(p >= _byte_map && p < _byte_map + _byte_map_size,"out of bounds access to card marking array. p: " PTR_FORMAT" _byte_map: " PTR_FORMAT " _byte_map + _byte_map_size: " PTR_FORMAT,p2i(p), p2i(_byte_map), p2i(_byte_map + _byte_map_size));// As _byte_map_base may be "negative" (the card table has been allocated before// the heap in memory), do not use pointer_delta() to avoid the assertion failure.size_t delta = p - _byte_map_base;HeapWord result = (HeapWord) (delta << _card_shift);assert(_whole_heap.contains(result),"Returning result = " PTR_FORMAT " out of bounds of "" card marking array's _whole_heap = [" PTR_FORMAT "," PTR_FORMAT ")",p2i(result), p2i(_whole_heap.start()), p2i(_whole_heap.end()));return result;}// Mapping from address to card marking array index.size_t index_for(void p) {assert(_whole_heap.contains(p),"Attempt to access p = " PTR_FORMAT " out of bounds of "" card marking array's _whole_heap = [" PTR_FORMAT "," PTR_FORMAT ")",p2i(p), p2i(_whole_heap.start()), p2i(_whole_heap.end()));return byte_for(p) - _byte_map;}CardValue byte_for_index(const size_t card_index) const {return _byte_map + card_index;}// Resize one of the regions covered by the remembered set.void resize_covered_region(MemRegion new_region);// Card-table-RemSet-specific things.static uintx ct_max_alignment_constraint();static uint card_shift() {return _card_shift;}static uint card_size() {return _card_size;}static uint card_size_in_words() {return _card_size_in_words;}static constexpr CardValue clean_card_val() { return clean_card; }static constexpr CardValue dirty_card_val() { return dirty_card; }static intptr_t clean_card_row_val() { return clean_card_row; }// Initialize card sizestatic void initialize_card_size();// Card marking array base (adjusted for heap low boundary)// This would be the 0th element of _byte_map, if the heap started at 0x0.// But since the heap starts at some higher address, this points to somewhere// before the beginning of the actual _byte_map.CardValue byte_map_base() const { return _byte_map_base; }virtual bool is_in_young(const void p) const = 0;}; class G1CardTable : public CardTable {friend class VMStructs;friend class G1CardTableChangedListener;G1CardTableChangedListener _listener;public:enum G1CardValues {g1_young_gen = CT_MR_BS_last_reserved << 1,// During evacuation we use the card table to consolidate the cards we need to// scan for roots onto the card table from the various sources. Further it is// used to record already completely scanned cards to avoid re-scanning them// when incrementally evacuating the old gen regions of a collection set.// This means that already scanned cards should be preserved.//// The merge at the start of each evacuation round simply sets cards to dirty// that are clean; scanned cards are set to 0x1.//// This means that the LSB determines what to do with the card during evacuation// given the following possible values://// 11111111 - clean, do not scan// 00000001 - already scanned, do not scan// 00000000 - dirty, needs to be scanned.//g1_card_already_scanned = 0x1};static const size_t WordAllClean = SIZE_MAX;static const size_t WordAllDirty = 0;STATIC_ASSERT(BitsPerByte == 8);static const size_t WordAlreadyScanned = (SIZE_MAX / 255) g1_card_already_scanned;G1CardTable(MemRegion whole_heap): CardTable(whole_heap), _listener() {_listener.set_card_table(this);}static CardValue g1_young_card_val() { return g1_young_gen; }static CardValue g1_scanned_card_val() { return g1_card_already_scanned; }void verify_g1_young_region(MemRegion mr) PRODUCT_RETURN;void g1_mark_as_young(const MemRegion& mr);size_t index_for_cardvalue(CardValue const p) const {return pointer_delta(p, _byte_map, sizeof(CardValue));}// Mark the given card as Dirty if it is Clean. Returns whether the card was// Clean before this operation. This result may be inaccurate as it does not// perform the dirtying atomically.inline bool mark_clean_as_dirty(CardValue card);// Change Clean cards in a (large) area on the card table as Dirty, preserving// already scanned cards. Assumes that most cards in that area are Clean.inline void mark_range_dirty(size_t start_card_index, size_t num_cards);// Change the given range of dirty cards to "which". All of these cards must be Dirty.inline void change_dirty_cards_to(CardValue start_card, CardValue end_card, CardValue which);inline uint region_idx_for(CardValue p);static size_t compute_size(size_t mem_region_size_in_words) {size_t number_of_slots = (mem_region_size_in_words / _card_size_in_words);return ReservedSpace::allocation_align_size_up(number_of_slots);}// Returns how many bytes of the heap a single byte of the Card Table corresponds to.static size_t heap_map_factor() { return _card_size; }void initialize(G1RegionToSpaceMapper mapper);bool is_in_young(const void p) const override;}; 以位为粒度的位图能准确描述每一个字的引用关系,但是一个位通常包含的信息太少,只能描述2个状态:引用还是未引用。实际应用中JVM在垃圾回收的时候需要更多的状态,如果增加至一个字节来描述状态,则位图需要256KB的空间,这个数字太大,开销占了25%。所以一个可能的做法位图不再描述一个字,而是一个区域,JVM选择512字节为单位,即用一个字节描述512字节的引用关系。选择一个区域除了空间利用率的问题之外,实际上还有现实的意义。我们知道Java对象实际上不是一个字能描述的(有一个参数可以控制对象最小对齐的大小,默认是8字节,实际上Java在JVM中还有一些附加信息,所以对齐后最小的Java对象是16字节),很多Java对象可能是几十个字节或者几百个字节,所以用一个字节描述一个区域是有意义的。但是我没有找到512的来源,为什么512效果最好?没有相应的数据来支持这个数字,而且这个值不可以配置,不能修改,但是有理由相信512字节的区域是为了节约内存额外开销。按照这个值,1MB的内存只需要2KB的额外空间就能描述引用关系。这又带来另一个问题,就是512字节里面的内存可能被引用多次,所以这是一个粗略的关系描述,那么在使用的时候需要遍历这512字节。 再举一个例子,假设有两个对象B、C都在这512字节的区域内。为了方便处理,记录对象引用关系的时候,都使用对象的起始位置,然后用这个地址和512对齐,因此B和C对象的卡表指针都指向这一个卡表的位置。那么对于引用处理也有可有两种处理方法:·处理的时候会以堆分区为处理单位,遍历整个堆分区,在遍历的时候,每次都会以对象大小为步长,结合卡表,如果该卡表中对应的位置被设置,则说明对象和其他分区的对象发生了引用。具体内容在后文中介绍Refine的时候还会详细介绍。·处理的时候借助于额外的数据结构,找到真正对象的位置,而不需要从头开始遍历。在后文的并发标记处理时就使用了这种方法,用于找到第一个对象的起始位置。在G1除了512字节粒度的卡表之外,还有bitMap,例如使用bitMap可以描述一个分区对另外一个分区的引用情况。在JVM中bitMap使用非常多,例如还可以描述内存的分配情况。 在G1除了512字节粒度的卡表之外,还有bitMap,例如使用bitMap可以描述一个分区对另外一个分区的引用情况。在JVM中bitMap使用非常多,例如还可以描述内存的分配情况。G1在混合收集算法中用到了并发标记。在并发标记的时候使用了bitMap来描述对象的分配情况。例如1MB的分区可以用16KB(16KB×ObjectAlignmentInBytes×8=1MB)来描述,即16KB额外的空间。其中ObjectAlignmentInBytes是8字节,指的是对象对齐,第二个8是指一个字节有8位。即每一个位可以描述64位。例如一个对象长度对齐之后为24字节,理论上它占用3个位来描述这个24字节已被使用了,实际上并不需要,在标记的时候只需要标记这3个位中的第一个位,再结合堆分区对象的大小信息就能准确找出。其最主要的目的是为了效率,标记一个位和标记3个位相比能节约不少时间,如果对象很大,则更划算。这些都是源码的实现细节,大家在阅读源码时需要细细斟酌。 本篇文章为转载内容。原文链接:https://blog.csdn.net/qq_16500963/article/details/132133125。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-12-16 20:37:50
247
转载
转载文章
...va 源代码的语法元数据。类、方法、变量、参数、包都可以被注解,可用来将信息元数据与程序元素进行关联。目前很多开源库都使用到了注解,最熟悉的ButtonKnife中的@ViewInject(R.id.x)就可以替代findViewId,不懂这一块技术的同学第一眼看上去肯定会一脸懵逼,下面会手把手带大家写出ButtonKnife的注解使用。使用注解可以简化代码,提高开发效率。本文简单介绍下注解的使用,并对几个 Android 开源库的注解使用原理进行简析。 1、作用 标记,用于告诉编译器一些信息 ; 编译时动态处理,如动态生成代码 ; 运行时动态处理,如得到注解信息。 2、分类 标准 Annotation, 包括 Override, Deprecated, SuppressWarnings。也都是Java自带的几个 Annotation,上面三个分别表示重写函数,不鼓励使用(有更好方式、使用有风险或已不在维护),忽略某项 Warning; 元 Annotation ,@Retention, @Target, @Inherited, @Documented。元 Annotation 是指用来定义 Annotation 的 Annotation,在后面 Annotation 自定义部分会详细介绍含义; 自定义 Annotation , 表示自己根据需要定义的 Annotation,定义时需要用到上面的元 Annotation 这里只是一种分类而已,也可以根据作用域分为源码时、编译时、运行时 Annotation。通过 @interface 定义,注解名即为自定义注解名。 一、自定义注解 例如,注解@MethodInfo: @Documented@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)@Inheritedpublic @interface MethodInfo {String author() default "annotation@gmail.com";String date();int version() default 1;} 使用到了元Annotation: @Documented 是否会保存到 Javadoc 文档中 ; @Retention 保留时间,可选值 SOURCE(源码时),CLASS(编译时),RUNTIME(运行时),默认为 CLASS,值为 SOURCE 大都为 Mark Annotation,这类 Annotation 大都用来校验,比如 Override, Deprecated, SuppressWarnings ; @Target 用来指定修饰的元素,如 CONSTRUCTOR:用于描述构造器、FIELD:用于描述域、LOCAL_VARIABLE:用于描述局部变量、METHOD:用于描述方法、PACKAGE:用于描述包、PARAMETER:用于描述参数、TYPE:用于描述类、接口(包括注解类型) 或enum声明。 @Inherited 是否可以被继承,默认为 false。 注解的参数名为注解类的方法名,且: 所有方法没有方法体,没有参数没有修饰符,实际只允许 public & abstract 修饰符,默认为 public ,不允许抛异常; 方法返回值只能是基本类型,String, Class, annotation, enumeration 或者是他们的一维数组; 若只有一个默认属性,可直接用 value() 函数。一个属性都没有表示该 Annotation 为 Mark Annotation。 public class App {@MethodInfo(author = “annotation.cn+android@gmail.com”,date = "2011/01/11",version = 2)public String getAppName() {return "appname";} } 调用自定义MethodInfo 的示例,这里注解的作用实际是给方法添加相关信息: author、date、version 。 二、实战注解Butter Knife 首先,先定义一个ViewInject注解。 public @interface ViewInject { int value() default -1;} 紧接着,为刚自定义注解添加元注解。 @Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)public @interface ViewInject {int value() default -1;} 再定义一个注解LayoutInject @Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)public @interface LayoutInject {int value() default -1;} 定义一个基础的Activity。 package cn.wsy.myretrofit.annotation;import android.os.Bundle;import android.support.v7.app.AppCompatActivity;import android.util.Log;import java.lang.reflect.Field;public class InjectActivity extends AppCompatActivity {private int mLayoutId = -1;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);displayInjectLayout();displayInjectView();}/ 解析注解view id/private void displayInjectView() {if (mLayoutId <=0){return ;}Class<?> clazz = this.getClass();Field[] fields = clazz.getDeclaredFields();//获得声明的成员变量for (Field field : fields) {//判断是否有注解try {if (field.getAnnotations() != null) {if (field.isAnnotationPresent(ViewInject.class)) {//如果属于这个注解//为这个控件设置属性field.setAccessible(true);//允许修改反射属性ViewInject inject = field.getAnnotation(ViewInject.class);field.set(this, this.findViewById(inject.value()));} }} catch (Exception e) {Log.e("wusy", "not found view id!");} }}/ 注解布局Layout id/private void displayInjectLayout() {Class<?> clazz = this.getClass();if (clazz.getAnnotations() != null){if (clazz.isAnnotationPresent(LayouyInject.class)){LayouyInject inject = clazz.getAnnotation(LayouyInject.class);mLayoutId = inject.value();setContentView(mLayoutId);} }} } 首先,这里是根据映射实现设置控件的注解,java中使用反射的机制效率性能并不高。这里只是举例子实现注解。ButterKnife官方申明不是通过反射机制,因此效率会高点。 package cn.wsy.myretrofit;import android.os.Bundle;import android.widget.TextView;import cn.wsy.myretrofit.annotation.InjectActivity;import cn.wsy.myretrofit.annotation.LayouyInject;import cn.wsy.myretrofit.annotation.ViewInject;@LayoutInject(R.layout.activity_main)public class MainActivity extends InjectActivity {@ViewInject(R.id.textview)private TextView textView;@ViewInject(R.id.textview1)private TextView textview1;@ViewInject(R.id.textview2)private TextView textview2;@ViewInject(R.id.textview3)private TextView textview3;@ViewInject(R.id.textview4)private TextView textview4;@ViewInject(R.id.textview5)private TextView textview5;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);//设置属性textView.setText("OK");textview1.setText("OK1");textview2.setText("OK2");textview3.setText("OK3");textview4.setText("OK4");textview5.setText("OK5");} } 上面直接继承InjectActivity即可,文章上面也有说过:LayouyInject为什么作用域是TYPE,首先在加载view的时候,肯定是优先加载布局啊,ButterKnife也不例外。因此选择作用域在描述类,并且存在运行时。 二、解析Annotation原理 1、运行时 Annotation 解析 (1) 运行时 Annotation 指 @Retention 为 RUNTIME 的 Annotation,可手动调用下面常用 API 解析 method.getAnnotation(AnnotationName.class);method.getAnnotations();method.isAnnotationPresent(AnnotationName.class); 其他 @Target 如 Field,Class 方法类似 。 getAnnotation(AnnotationName.class) 表示得到该 Target 某个 Annotation 的信息,一个 Target 可以被多个 Annotation 修饰; getAnnotations() 则表示得到该 Target 所有 Annotation ; isAnnotationPresent(AnnotationName.class) 表示该 Target 是否被某个 Annotation 修饰; (2) 解析示例如下: public static void main(String[] args) {try {Class cls = Class.forName("cn.trinea.java.test.annotation.App");for (Method method : cls.getMethods()) {MethodInfo methodInfo = method.getAnnotation(MethodInfo.class);if (methodInfo != null) {System.out.println("method name:" + method.getName());System.out.println("method author:" + methodInfo.author());System.out.println("method version:" + methodInfo.version());System.out.println("method date:" + methodInfo.date());} }} catch (ClassNotFoundException e) {e.printStackTrace();} } 以之前自定义的 MethodInfo 为例,利用 Target(这里是 Method)getAnnotation 函数得到 Annotation 信息,然后就可以调用 Annotation 的方法得到响应属性值 。 2、编译时 Annotation 解析 (1) 编译时 Annotation 指 @Retention 为 CLASS 的 Annotation,甴 apt(Annotation Processing Tool) 解析自动解析。 使用方法: 自定义类集成自 AbstractProcessor; 重写其中的 process 函数 这块很多同学不理解,实际是 apt(Annotation Processing Tool) 在编译时自动查找所有继承自 AbstractProcessor 的类,然后调用他们的 process 方法去处理。 (2) 假设之前自定义的 MethodInfo 的 @Retention 为 CLASS,解析示例如下: @SupportedAnnotationTypes({ "cn.trinea.java.test.annotation.MethodInfo" })public class MethodInfoProcessor extends AbstractProcessor {@Overridepublic boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) {HashMap<String, String> map = new HashMap<String, String>();for (TypeElement te : annotations) {for (Element element : env.getElementsAnnotatedWith(te)) {MethodInfo methodInfo = element.getAnnotation(MethodInfo.class);map.put(element.getEnclosingElement().toString(), methodInfo.author());} }return false;} } SupportedAnnotationTypes 表示这个 Processor 要处理的 Annotation 名字。 process 函数中参数 annotations 表示待处理的 Annotations,参数 env 表示当前或是之前的运行环境 process 函数返回值表示这组 annotations 是否被这个 Processor 接受,如果接受后续子的 rocessor 不会再对这个 Annotations 进行处理 三、几个 Android 开源库 Annotation 原理简析 1、Retrofit (1) 调用 @GET("/users/{username}")User getUser(@Path("username") String username); (2) 定义 @Documented@Target(METHOD)@Retention(RUNTIME)@RestMethod("GET")public @interface GET {String value();} 从定义可看出 Retrofit 的 Get Annotation 是运行时 Annotation,并且只能用于修饰 Method (3) 原理 private void parseMethodAnnotations() {for (Annotation methodAnnotation : method.getAnnotations()) {Class<? extends Annotation> annotationType = methodAnnotation.annotationType();RestMethod methodInfo = null;for (Annotation innerAnnotation : annotationType.getAnnotations()) {if (RestMethod.class == innerAnnotation.annotationType()) {methodInfo = (RestMethod) innerAnnotation;break;} }……} } RestMethodInfo.java 的 parseMethodAnnotations 方法如上,会检查每个方法的每个 Annotation, 看是否被 RestMethod 这个 Annotation 修饰的 Annotation 修饰,这个有点绕,就是是否被 GET、DELETE、POST、PUT、HEAD、PATCH 这些 Annotation 修饰,然后得到 Annotation 信息,在对接口进行动态代理时会掉用到这些 Annotation 信息从而完成调用。 因为 Retrofit 原理设计到动态代理,这里只介绍 Annotation。 2、Butter Knife (1) 调用 @InjectView(R.id.user) EditText username; (2) 定义 @Retention(CLASS) @Target(FIELD)public @interface InjectView {int value();} 可看出 Butter Knife 的 InjectView Annotation 是编译时 Annotation,并且只能用于修饰属性 (3) 原理 @Override public boolean process(Set<? extends TypeElement> elements, RoundEnvironment env) {Map<TypeElement, ViewInjector> targetClassMap = findAndParseTargets(env);for (Map.Entry<TypeElement, ViewInjector> entry : targetClassMap.entrySet()) {TypeElement typeElement = entry.getKey();ViewInjector viewInjector = entry.getValue();try {JavaFileObject jfo = filer.createSourceFile(viewInjector.getFqcn(), typeElement);Writer writer = jfo.openWriter();writer.write(viewInjector.brewJava());writer.flush();writer.close();} catch (IOException e) {error(typeElement, "Unable to write injector for type %s: %s", typeElement, e.getMessage());} }return true;} ButterKnifeProcessor.java 的 process 方法如上,编译时,在此方法中过滤 InjectView 这个 Annotation 到 targetClassMap 后,会根据 targetClassMap 中元素生成不同的 class 文件到最终的 APK 中,然后在运行时调用 ButterKnife.inject(x) 函数时会到之前编译时生成的类中去找。 3、ActiveAndroid (1) 调用 @Column(name = “Name") public String name; (2) 定义 @Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)public @interface Column {……} 可看出 ActiveAndroid 的 Column Annotation 是运行时 Annotation,并且只能用于修饰属性 (3) 原理 Field idField = getIdField(type);mColumnNames.put(idField, mIdName);List<Field> fields = new LinkedList<Field>(ReflectionUtils.getDeclaredColumnFields(type));Collections.reverse(fields);for (Field field : fields) {if (field.isAnnotationPresent(Column.class)) {final Column columnAnnotation = field.getAnnotation(Column.class);String columnName = columnAnnotation.name();if (TextUtils.isEmpty(columnName)) {columnName = field.getName();}mColumnNames.put(field, columnName);} } TableInfo.java 的构造函数如上,运行时,得到所有行信息并存储起来用来构件表信息。 ———————————————————————— 最后一个问题,看看这段代码最后运行结果: public class Person {private int id;private String name;public Person(int id, String name) {this.id = id;this.name = name;}public boolean equals(Person person) {return person.id == id;}public int hashCode() {return id;}public static void main(String[] args) {Set<Person> set = new HashSet<Person>();for (int i = 0; i < 10; i++) {set.add(new Person(i, "Jim"));}System.out.println(set.size());} } 答案:示例代码运行结果应该是 10 而不是 1,这个示例代码程序实际想说明的是标记型注解 Override 的作用,为 equals 方法加上 Override 注解就知道 equals 方法的重载是错误的,参数不对。 本篇文章为转载内容。原文链接:https://blog.csdn.net/csdn_aiyang/article/details/81564408。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-03-28 22:30:35
105
转载
Netty
Netty与大数据流处理平台的优化 1. Netty是什么?为什么它这么重要? 嗨,大家好!我是你们的老朋友,今天我们要聊聊一个超级厉害的技术——Netty。嘿,要是你对分布式系统、高能网络编程或者大数据流处理这些酷炫的东西感兴趣,那Netty可就太值得一试了!它就像是个隐藏的宝藏,能让你在这些领域玩得更溜。 首先,Netty是什么?简单来说,Netty是一个基于Java的异步事件驱动网络应用框架。它可以帮助开发者快速构建可扩展的服务器端应用程序。想象一下,你正在开发一个需要处理海量数据的大数据流处理平台,这时候Netty就显得尤为重要了。它不仅能够帮助我们高效地管理网络连接,还能让我们轻松应对高并发场景。 我第一次接触Netty的时候,真的被它的灵活性震撼到了。哎,说到程序员的烦心事,那肯定得提一提怎么让程序在被成千上万的人同时戳的时候还能稳如老狗啊!这事儿真心让人头大,尤其是看着服务器指标噌噌往上涨,心里直打鼓,生怕哪一秒就崩了。而Netty通过非阻塞I/O模型,完美解决了这个问题。这就像是一个超级能干的服务员,能够在同一时间同时服务上万个客人,而且就算有个客人纠结半天点菜(也就是某个请求拖拉),也不会耽误其他客人的服务,更不会让整个餐厅都停下来等他。 举个栗子: java EventLoopGroup bossGroup = new NioEventLoopGroup(); // 主线程组 EventLoopGroup workerGroup = new NioEventLoopGroup(); // 工作线程组 try { ServerBootstrap b = new ServerBootstrap(); // 启动辅助类 b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) // 使用NIO通道 .childHandler(new ChannelInitializer() { // 子处理器 @Override protected void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(new StringDecoder()); // 解码器 ch.pipeline().addLast(new StringEncoder()); // 编码器 ch.pipeline().addLast(new SimpleChannelInboundHandler() { @Override protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception { System.out.println("Received message: " + msg); ctx.writeAndFlush("Echo: " + msg); // 回显消息 } }); } }); ChannelFuture f = b.bind(8080).sync(); // 绑定端口并同步等待完成 f.channel().closeFuture().sync(); // 等待服务关闭 } finally { workerGroup.shutdownGracefully(); bossGroup.shutdownGracefully(); } 这段代码展示了如何用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
青山绿水
转载文章
...cad32: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
转载
转载文章
...le 表名(属性名 数据类型[约束条件],…); Paimary key 主键 auto_increment自增 foreign key 外键 references 另一表名(字段名).–>外键这个表连接着另外一个表的哪个键. 删除表: drop table 表名;–>表结构也删除了(也即是这个表没了) Truncate table 表名 --> 只删除表中数据,表结构不会删除. 2.In 与 not in 在或不在这个(1,3)里面,单个查询,只会查询(1或者3) 3.Between and 与 not … 和上面差不多,Between 1 and 3 但是这个是范围查询(1,3) 1-3 之间(包含1,3) 4.Like,模糊查询 “%” 代表任意字符,”_”代表单个字符. 5.Is Not null 与 is null 是否为空 6.And 与 or 一个是所有条件都要完成,or则是任何一个条件完成即可 7.Distinct 去重 8.Order by age asc 与 desc 排序,假如根据age排序,asc正序(升序默认),desc倒叙(降序) 9.Gruop by 分组查询,单独使用无意义,group_concat(字段),拼接,若是根据age group by 则会发现age一样的会出现在同一字段内 例如: : 最后要注意group by 后面的字段与所查字段的关系(一对一),当然还有having,having和where基本一样,只不过跟在group by后面. 10.Limit 分页查询 limit 0,5 .查询前5条数据,从0开始,5结束,但是5取不到,也即是取头不取尾. 11.聚合函数:count() 查询数据的总数据量 经常使用别名 例如:as total sum(字段)函数:求和…若字段为成绩,where条件或gruop by 为个人的id,那么查出的就是个人的成绩总分. AVG(字段),但是查的是平均分,min(字段)与max(字段) 查出最小或最大. 三者都类似sum(),当然max()与min()若是在最前面使用,就会当条件查询只会出来这一笔数据.例如: 12.Sql多表查询,内连接不只是inner join,平时写的from a表,b表 where 条件这也是内连接,意思就是两张表中数据都有才可以查询出来 13.而外连接分为左连接和右连接,意思是以左表或右表为主,假如两张表,左表数据多,右表数据少,且条件符合,则左连接的时候左表数据全部出来,右表没有的为null,反之也是一样. 14.Exist() 与 not exist() …()内的数据是否为空,若是为空则代表false,返回数据为空,若不为空,则代表true,正常查询. 15.Any 与 all 例如 age > any(age1,age2) 大于两者中的一个就可以,但是all的情况下则是全部大于.也就是相当于,any为大于最小的,all则是大于最大的就行了,当然若是小于号那就是另外一种情况了,另外分析. 16.Union,(也就是联合的意思,自带distinct,重复的去除)用法,例如两张表的id要全部查出来,则:select id from A union select id from B ,若Aid为1,2,3,Bid为1,2,4.则查出来的数据为1.2.3.4,若是union all,则不带distinct,用法一样,查出来以后为1.2.3.1.2.4. 17.给表取别名,表名 空格 别名 给字段取别名 字段名 as 别名. 18.Insert插入数据时若是使用insert into 表名 values();主键必须到写进去,当然与其他数据不相同即可,若是自增,可以写null.若是insert into 表名(字段)values(值),这时插入数据,字段不用写主键字段,写入其他数据字段名与值就可以完成数据的添加.(主键自己生成为前提,UUID,auto_increament都可以). 19.Insert into 插入多条数据时,其他与18一样,只不过由values()变成了values(),(),(); 20.索引是由数据库表中一列或多列组合而成,其作用提高对表数据的查询速度.像图书目录. 优缺点:优:提高了查询数据的效率.缺:创建和维护索引的时间增加了(内容改了,目录也要改). 21.索引分类:普通索引,唯一性索引UNIQUE(unique修饰,例如主键),全文索引FULLTEXT(创建在文本上,例如:char,varchar,varchar2等,mysql默认引擎不支持,),单列索引:单个字段建立索引,多列索引:多个字段创建一个索引,空间索引SPATIAL:不常用(mysql默认引擎不支持) 22.创建索引: index为关键字,或者key (1)可以index(字段名)–>普通索引 (2)Unique index(字段名)–>唯一索引 (3)Unique index 别名(字段名)–>取别名的唯一索引 (4)index 别名(字段名1,字段名2)–>取别名的多列索引 1.创建表的时候创建索引, 前三个为参数修饰,唯一性,全文,空间索引; 2.在已存在的表上创建索引,或者用ALTER TABLE 表名 ADD 索引,也就是用修改表的形式来创建索引 Create index 索引别名 on 表名(字段名) -->普通单列索引 Create index 索引别名 on 表名(字段名1,字段名2) -->多列索引 Create unique index 索引别名 on 表名(字段名) -->唯一单列索引 Alter table 表名 add +(1)|(2)|(3)|(4)即可. 23.删除索引: drop index 索引名 on 表名. 24.NOW(); mysql的函数,表示当前时间 25.视图:是一个虚拟的表,没有物理数据,是从其他表中导出的数据,当原表数据发生改变时,视图数据也会发生改变,反之也一样. (1)作用:操作简单化;增加数据安全性:不直接对表进行操作;提高表的逻辑性:原表修改字段对视图无影响. (2)创建视图:语法:create view 视图名 as 查询语句. 例如:create view vi as select id,name from user;–>这是把user中id,name字段的数据写入到vi视图中. 若是想自己定义字段名不用查出的字段名,可以如下面这样写. 例如:create view vi(vi_id,vi_name) as select id,name from user;–>这样的话id对应vi_id,name对应vi_name; 上面的都是单表的视图,多表的视图也是一样的,只不过后面的单表查询变成多表查询了. 建议创建视图后自己定义字段名,也即是定义别名. (3)查看视图: Describe(desc) 视图名–>查看视图基本信息 Show table status like ‘视图名’ --> 查看视图基本信息 Show create view 视图名 --> 视图详细信息,建表具体信息. 在view表中查看视图详细信息–>view 系统表 自带的. (4)修改视图:修改使徒的定义 Create or replace view 没有的话就创建,有的话就替换 例如:Create or replace view vi(id,name) as select语句. Alter view 只修改不能创建(也就是说视图必须存在的情况下才可修改) Alter view vi as select语句 (5)更新视图:视图是虚拟的,对视图进行的crud操作都会对原表的数据产生影响. 也就是说对视图的操作最后都会转换为对视图所连接那个表的操作. (6)删除视图:删除数据库中已存在的视图,视图为虚表,因此只会删除结构,不会删除数据. Drop view if exist 视图名. 26.触发器:由事件来触发某个操作,这些事件包括insert语句,update语句和delete语句.当数据库系统执行这些事件时,就会激活触发器执行相应的方法. 创建触发器:create trigger 触发器名 (before/after) 触发事件 on 表名 for each row sql语句. 这里的new是指代新插入的拿一条数据(更新的也算),若是old的话,指的是删除的那一条数据(更新之前的数据).(new和old属于过渡变量) 这条触发器的意思时:当t_book有插入数据时,就会根据新插入数据的id找到t_bookType的id,并试该条数据的bookNum加1. Begin与end写sql语句,中间可以写多条sql语句用分号;分隔开…也即是说语句要写完成,不能少分号. Delimiter | 设置分隔符,要不然好像只会执行begin与and之间的第一条sql语句. 查看触发器: 1.show triggers; 语句查看触发器信息.(查询所有的触发器) 2.在triggers表中查看触发器信息.(在数据库原始表triggers中可以查看) 删除触发器: Drop trigger 触发器名称 ; 27.函数: (1)日期函数: CURDATE()当前日期,CURTIME()当前时间,MONTH(d):返回日期d中的月份值,范围试1-12 (2)字符串函数:CHAR_LENGTH(s) 计算字段s值->字符串的长度.UPPER(s) 把该字段的值中所有英文都变成大写,LOWER(s) 和相面相反->把英文都变成小写. (3)数学函数:sum():求和,ABS(s) 求绝对值,SQRT(s):求平方根,mod(x,y),求余x/y (4)加密函数:PASSWORD(STR) 一般对密码加密 不可逆… MD5(STR) 普通加密 ,不可逆. ENCODE(str,pswd_str) 加密函数,结果是一个二进制文件,用blob类型的字段保存,pswd_str类似一个加密的钥匙,可以随便写. DECODE(被加密的值,pswd_str)–>对encode进行解密. 28.存储过程: (1)存储过程和函数:两者是在数据库中定义一些SQL语句的集合,然后直接调用这些存储过程和函数来执行已经定义好的SQL语句.存储过程和函数可以避免重复的写一些sql语句,而且存储过程是在mysql服务器中存储和执行的,减少客户端和服务器端的数据传输.(类似于java代码写的工具类.) (2)创建存储过程和函数: Create procedure 关键字 pro_book 存储过程名称, in 输入 bT 输入参数名称 int 输入参数类型 out 输出 count_num 输出参数名称 int 输入参数类型 Begin 过程开始 end过程结束 中间是sql语句, Delimiter 默认是分号,而他的作用就是若是遇见分号时就开始执行该过程(语句),但是一个存储过程可能有很多sql语句且以分号结束,若这样的情况下当第一条sql语句结束后就会开始执行该过程,产生的后果是创建过程时,执行到第一个分号就会开始创建,导致存储过程创建错误.(若是有多个参数,在多条sql中均有参数,第一条设置完执行了,而这时第二条的参数有可能还么有设置完成,导致sql执行失败.)因此,需要把默认执行过程的demiliter关键字的默认值改为其他的字符,例如上面的就是改为&&,(当然我认为上面就一条sql语句,改不改默认的demiliter的默认值都一样.) . 使用navicat的话不使用delimiter好像也是可以的. Reads sql data则是上面图片所提到的参数指定存储过程的特性.(这个是指读数据,当然还有写输入与读写数据专用的参数类型.)看下图 经常用contains sql (应该是可以读,) 这个是调用上面的存储过程,1为入参,@total相当于全局变量,为出参. 这是一个存储函数,create function 为关键字,fun_book为函数名称, 括号里面为传入的参数名(值)以及入参的类型.RETURNS 为返回的关键字,后面接返回的类型. BEGIN函数开始,END函数结束.中间是return 以及查询数据的sql语句, 这里是指把bookId 传进去,通过存储函数返回对应的书本名字, ---------存储函数的调用和调用系统函数一样 例如:select 存储函数名称(入参值) Select 为查询 func_book 为存储函数名 2为入参值. (3)变量的使用:declaer:声明变量的值 Delimiter && Create procedure user() Begin Declare a,b varchar2(20) ; — a,b有默认的值,为空 Insert into user values(a,b); End && Delimiter ; Set 可以用来赋值,例如: 可以从其他表中查询出对应的值插入到另一个表中.例如: 从t_user2中查询出username2与password2放入到变量a,b中,然后再插入到t_user表中.(当然这只是创建存储过程),创建完以后,需要用CALL 存储过程名(根据过程参数描写.)来调用存储过程.注意:这一种的写法只可以插入单笔数据,若是select查询出多笔数据,因为无循环故而会插入不进去语句,会导致倒致存储过程时出错.下面的游标也是如此. (4)游标的使用.查询语句可能查询出多条记录,在存储过程和函数中使用游标逐条读取查询结果集中的记录.游标的使用包括声明游标,打开游标,使用游标和关闭游标.游标必须声明到处理程序之前,并且声明在变量和条件之后. 声明:declare 游标名 curson for 查询sql语句. 打开:open 游标名 使用:fetch 游标名 into x, 关闭:close 游标名 ----- 游标只能保存单笔数据. 类似于这一个,意思就是先查询出来username2,与password2的值放入到cur_t_user2的游标中(声明,类似于赋值),然后开启->使用.使用的意思就是把游标中存储的值分别赋值到a,b中,然后执行sql语句插入到t_user表中.最后关闭游标. (5)流程控制的使用:mysql可以使用:IF 语句 CASE语句 LOOP语句 LEAVE语句 ITERATE 语句 REPEAT语句与WHILE语句. 这个过程的意思是,查询t_user表中是否存在id等于我们入参时所写的id,若有的情况下查出有几笔这样的数据并且把数值给到全局变量@num中,if判断是否这样的数据是否存在,若是存在执行THEN后面的语句,即使更新该id对应的username,若没有则插入一条新的数据,最后注意END IF. 相当于java中的switch case.例如: 这里想当然于,while(ture){ break; } 这里的意思是,参数一个int类型的参数,loop aaa循环,把参数当做主键id插入到t_user表中,每循环一次参入的参数值减一,直到参数值为0,跳出循环(if判断,leave实现.) 相当于java的continue. 比上面的多了一个当totalNum = 3时,结束本次循环,下面的语句不在执行,直接执行下一次循环,也即是说插入的数据没有主键为3的数据. 和上面的差不多,只不过当执行到UNTIL时满足条件时,就跳出循环.就如上面那一个意思就是当执行到totalNum = 1时,跳出循环,也就是说不会插入主键为0的那一笔数据 当while条件判断为true时,执行do后面的语句,否则就不再执行. (6)调用存储过程和函数 CALL 存储过程名字(参数值1,参数值2,…) 存储函数名称(参数值1,参数值2,…) (7)查看存储过程和函数. Show procedure status like ‘存储过程名’ --只能查看状态 Show create procedure ‘存储过程名’ – 查看定义(使用频率高). 存储函数查看也和上面的一样. 当然还可以从information_schema.Routines中(系统数据库表)查看存储过程与函数. (8)修改存储过程与函数: 修改存储过程comment属性的值 ALTER procedure 存储过程名 comment ‘新值’; (9)删除存储过程与函数: DROP PROCEDURE 存储过程名; DROP function 存储函数名; 29.数据备份与还原: (1)数据备份:数据备份可以保证数据库表的安全性,数据库管理员需要定期的进行数据库备份. 命令:使用mysqldump(下图),或者使用图形工具 Mysqldump在msql文件夹+bin+mysqldump.exe中,相当于一个小软件.执行的话是在dos命令窗操作的. 其实就是导出数据库数据,在navacat中可以如下图导出 (2)数据还原: 若是从navacat中就是把外部的.sql文件数据导入到数据库中去.如下图 本篇文章为转载内容。原文链接:https://blog.csdn.net/qq_42847571/article/details/102686087。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-04-26 19:09:16
84
转载
转载文章
...一些常见异常的原因和解决方法 AccessViolationException 快速了解 .NET 的异常机制 Exception 类 我们大多数小伙伴可能更多的使用 Exception 的类型、Message 属性、StackTrace 以及内部异常来定位问题,但其实 Exception 类型还有更多的信息可以用于辅助定位问题。 Message 用来描述异常原因的详细信息 如果你捕捉到了异常,一般使用这段描述能知道发生的大致原因。 如果你准备抛出异常,在这个信息里面记录能帮助调试问题的详细文字信息。 StackTrace 包含用来确定错误位置的堆栈跟踪(当有调试信息如 PDB 时,这里就会包含源代码文件名和源代码行号) InnerException 包含内部异常信息 Source 这个属性包含导致错误的应用程序或对象的名称 Data 这是一个字典,可以存放基于键值的任意数据,帮助在异常信息中获得更多可以用于调试的数据 HelpLink 这是一个 url,这个 url 里可以提供大量用于说明此异常原因的信息 如果你自己写一个自定义异常类,那么你可以在自定义的异常类中记录更多的信息。然而大多数情况下我们都考虑使用 .NET 中自带的异常类,因此可以充分利用 Exception 类中的已有属性在特殊情况下报告更详细的利于调试的异常信息。 捕捉异常 捕捉异常的基本语法是: try{// 可能引发异常的代码。}catch (FileNotFoundException ex){// 处理一种类型的异常。}catch (IOException ex){// 处理另一种类的异常。} 除此之外,还有 when 关键字用于筛选异常: try{// 可能引发异常的代码。}catch (FileNotFoundException ex) when (Path.GetExtension(ex.FileName) is ".png"){// 处理一种类型的异常,并且此文件扩展名为 .png。}catch (FileNotFoundException ex){// 处理一种类型的异常。} 无论是否有带 when 关键字,都是前面的 catch 块匹配的时候执行匹配的 catch 块而无视后面可能也匹配的 catch 块。 如果 when 块中抛出异常,那么此异常将被忽略,when 中的表达式值视为 false。有个但是,请看:.NET Framework 的 bug?try-catch-when 中如果 when 语句抛出异常,程序将彻底崩溃 - walterlv。 引发异常 引发异常使用 throw 关键字。只是注意如果要重新抛出异常,请使用 throw; 语句或者将原有异常作为内部异常。 创建自定义异常 如果你只是随便在业务上创建一个异常,那么写一个类继承自 Exception 即可: public class MyCustomException : Exception{public string MyCustomProperty { get; }public MyCustomException(string customProperty) => MyCustomProperty = customProperty;} 不过,如果你需要写一些比较通用抽象的异常(用于被继承),或者在底层组件代码中写自定义异常,那么就建议考虑写全异常的所有构造函数,并且加上可序列化: [Serializable]public class InvalidDepartmentException : Exception{public InvalidDepartmentException() : base() { }public InvalidDepartmentException(string message) : base(message) { }public InvalidDepartmentException(string message, Exception innerException) : base(message, innerException) { }// 如果异常需要跨应用程序域、跨进程或者跨计算机抛出,就需要能被序列化。protected InvalidDepartmentException(SerializationInfo info, StreamingContext context) : base(info, context) { } } 在创建自定义异常的时候,建议: 名称以 Exception 结尾 Message 属性的值是一个句子,用于描述异常发生的原因。 提供帮助诊断错误的属性。 尽量写全四个构造函数,前三个方便使用,最后一个用于序列化异常(新的异常类应可序列化)。 finally 异常堆栈跟踪 堆栈跟踪从引发异常的语句开始,到捕获异常的 catch 语句结束。 利用这一点,你可以迅速找到引发异常的那个方法,也能找到是哪个方法中的 catch 捕捉到的这个异常。 异常处理原则 try-catch-finally 我们第一个要了解的异常处理原则是——明确 try catch finally 的用途! try 块中,编写可能会发生异常的代码。 最好的情况是,你只将可能会发生异常的代码放到 try 块中,当然实际应用的时候可能会需要额外放入一些相关代码。但是如果你将多个可能发生异常的代码放到一个 try 块中,那么将来定位问题的时候你就会很抓狂(尤其是多个异常还是一个类别的时候)。 catch 块的作用是用来 “恢复错误” 的,是用来 “恢复错误” 的,是用来 “恢复错误” 的。 如果你在 try 块中先更改了类的状态,随后出了异常,那么最好能将状态改回来——这可以避免这个类型或者应用程序的其他状态出现不一致——这很容易造成应用程序“雪崩”。举一个例子:我们写一个程序有简洁模式和专业模式,在从简洁模式切换到专业模式的时候,我们设置 IsProfessionalMode 为 true,但随后出现了异常导致没有成功切换为专业模式;然而接下来所有的代码在执行时都判断 IsProfessionalMode 为 true 状态不正确,于是执行了一些非预期的操作,甚至可能用到了很多专业模式中才会初始化的类型实例(然而没有完成初始化),产生大量的额外异常;我们说程序雪崩了,多数功能再也无法正常使用了。 当然如果任务已全部完成,仅仅在对外通知的时候出现了异常,那么这个时候不需要恢复状态,因为实际上已经完成了任务。 你可能会有些担心如果我没有任何手段可以恢复错误怎么办?那这个时候就不要处理异常!——如果不知道如何恢复错误,请不要处理异常!让异常交给更上一层的模块处理,或者交给整个应用程序全局异常处理模块进行统一处理(这个后面会讲到)。 另外,异常不能用于在正常执行过程中更改程序的流程。异常只能用于报告和处理错误条件。 finally 块的作用是清理资源。 虽然 .NET 的垃圾回收机制可以在回收类型实例的时候帮助我们回收托管资源(例如 FileStream 类打开的文件),但那个时机不可控。因此我们需要在 finally 块中确保资源可被回收,这样当重新使用这个文件的时候能够立刻使用而不会被占用。 一段异常处理代码中可能没有 catch 块而有 finally 块,这个时候的重点是清理资源,通常也不知道如何正确处理这个错误。 一段异常处理代码中也可能 try 块留空,而只在 finally 里面写代码,这是为了“线程终止”安全考虑。在 .NET Core 中由于不支持线程终止因此可以不用这么写。详情可以参考:.NET/C 异常处理:写一个空的 try 块代码,而把重要代码写到 finally 中(Constrained Execution Regions) - walterlv。 该不该引发异常? 什么情况下该引发异常?答案是——这真的是一个异常情况! 于是,我们可能需要知道什么是“异常情况”。 一个可以参考的判断方法是——判断这件事发生的频率: 如果这件事并不常见,当它发生时确实代表发生了一个错误,那么这件事情就可以认为是异常。 如果这件事经常发生,代码中正常情况就应该处理这件事情,那么这件事情就不应该被认为是异常(而是正常流程的一部分)。 例如这些情况都应该认为是异常: 方法中某个参数不应该传入 null 时但传入了 null 这是开发者使用这个方法时没有遵循此方法的契约导致的,让开发者改变调用此方法的代码就可以完全避免这件事情发生 而下面这些情况则不应该认为是异常: 用户输入了一串字符,你需要将这串字符转换为数字 用户输入的内容本身就千奇百怪,出现非数字的输入再正常不过了,对非数字的处理本就应该成为正常流程的一部分 对于这些不应该认为是异常的情况,编写的代码就应该尽可能避免异常。 有两种方法来避免异常: 先判断再使用。 例如读取文件之前,先判断文件是否存在;例如读取文件流时先判断是否已到达文件末尾。 如果提前判断的成本过高,可采用 TryDo 模式来完成,例如字符串转数字中的 TryParse 方法,字典中的 TryGetValue 方法。 对极为常见的错误案例返回 null(或默认值),而不是引发异常。极其常见的错误案例可被视为常规控制流。通过在这些情况下返回 NULL(或默认值),可最大程度地减小对应用的性能产生的影响。(后面会专门说 null) 而当存在下列一种或多种情况时,应引发异常: 方法无法完成其定义的功能。 根据对象的状态,对某个对象进行不适当的调用。 请勿有意从自己的源代码中引发 System.Exception、System.SystemException、System.NullReferenceException 或 System.IndexOutOfRangeException。 该不该捕获异常? 在前面 try-catch-finally 小节中,我们提到了 catch 块中应该写哪些代码,那里其实已经说明了哪些情况下应该处理异常,哪些情况下不应该处理异常。一句总结性的话是——如果知道如何从错误中恢复,那么就捕获并处理异常,否则交给更上层的业务去捕获异常;如果所有层都不知道如何处理异常,就交给全局异常处理模块进行处理。 应用程序全局处理异常 对于 .NET 程序,无论是 .NET Framework 还是 .NET Core,都有下面这三个可以全局处理的异常。这三个都是事件,可以自行监听。 AppDomain.UnhandledException 应用程序域未处理的异常,任何线程中未处理掉的异常都会进入此事件中 当这里能够收到事件,意味着应用程序现在频临崩溃的边缘(从设计上讲,都到这里了,也再没有任何代码能够使得程序从错误中恢复了) 不过也可以配置 legacyUnhandledExceptionPolicy 防止后台线程抛出的异常让程序崩溃退出 建议在这个事件中记录崩溃日志,然后对应用程序进行最后的拯救恢复操作(例如保存用户的文档数据) AppDomain.FirstChanceException 应用程序域中的第一次机会异常 我们前面说过,一个异常被捕获时,其堆栈信息将包含从 throw 块到 catch 块之间的所有帧,而在第一次机会异常事件中,只是刚刚 throw 出来,还没有被任何 catch 块捕捉,因此在这个事件中堆栈信息永远只会包含一帧(不过可以稍微变通一下在第一次机会异常 FirstChanceException 中获取比较完整的异常堆栈) 注意第一次机会异常事件即便异常会被 catch 也会引发,因为它引发在 catch 之前 不要认为异常已经被 catch 就万事大吉可以无视这个事件了。前面我们说过异常仅在真的是异常的情况才应该引发,因此如果这个事件中引发了异常,通常也真的意味着发生了错误(差别只是我们能否从错误中恢复而已)。如果你经常在正常的操作中发现可以通过此事件监听到第一次机会异常,那么一定是应用程序或框架中的异常设计出了问题(可能把正常应该处理的流程当作了异常,可能内部实现代码错误,可能出现了使用错误),这种情况一定是要改代码修 Bug 的。而一些被认为是异常的情况下收到此事件则是正常的。 TaskScheduler.UnobservedTaskException 在使用 async / await 关键字编写异步代码的时候,如果一直有 await 传递,那么异常始终可以被处理到;但中间有异步任务没有 await 导致异常没有被传递的时候,就会引发此事件。 如果在此事件中监听到异常,通常意味着代码中出现了不正确的 async / await 的使用(要么应该修改实现避免异常,要么应该正确处理异常并从中恢复错误) 对于 GUI 应用程序,还可以监听 UI 线程上专属的全局异常: WPF:Application.DispatcherUnhandledException 或者 Dispatcher.UnhandledException Windows Forms:Application.ThreadException 关于这些全局异常的处理方式和示例代码,可以参阅博客: WPF UnhandledException - Iron 的博客 - CSDN博客 抛出哪些异常? 任何情况下都不应该抛出这些异常: 过于抽象,以至于无法表明其含义 Exception 这可是顶级基类,这都抛出来了,使用者再也无法正确地处理此异常了 SystemException 这是各种异常的基类,本身并没有明确的意义 ApplicationException 这是各种异常的基类,本身并没有明确的意义 由 CLR 引发的异常 NullReferenceException 试图在空引用上执行某些方法,除了告诉实现者出现了意料之外的 null 之外,没有什么其它价值了 IndexOutOfRangeException 使用索引的时候超出了边界 InvalidCastException 表示试图对某个类型进行强转但类型不匹配 StackOverflow 表示栈溢出,这通常说明实现代码的时候写了不正确的显式或隐式的递归 OutOfMemoryException 表示托管堆中已无法分出期望的内存空间,或程序已经没有更多内存可用了 AccessViolationException 这说明使用非托管内存时发生了错误 BadImageFormatException 这说明了加载的 dll 并不是期望中的托管 dll TypeLoadException 表示类型初始化的时候发生了错误 .NET 设计失误 FormatException 因为当它抛出来时无法准确描述到底什么错了 首先是你自己不应该抛出这样的异常。其次,你如果在运行中捕获到了上面这些异常,那么代码一定是写得有问题。 如果是捕获到了上面 CLR 的异常,那么有两种可能: 你的代码编写错误(例如本该判空的代码没有判空,又如索引数组超出界限) 你使用到的别人写的代码编写错误(那你就需要找到它改正,或者如果开源就去开源社区中修复吧) 而一旦捕获到了上面其他种类的异常,那就找到抛这个异常的人,然后对它一帧狂扁即可。 其他的异常则是可以抛出的,只要你可以准确地表明错误原因。 另外,尽量不要考虑抛出聚合异常 AggregateException,而是优先使用 ExceptionDispatchInfo 抛出其内部异常。详见:使用 ExceptionDispatchInfo 捕捉并重新抛出异常 - walterlv。 异常的分类 在 该不该引发异常 小节中我们说到一个异常会被引发,是因为某个方法声称的任务没有成功完成(失败),而失败的原因有四种: 方法的使用者用错了(没有按照方法的契约使用) 方法的执行代码写错了 方法执行时所在的环境不符合预期 简单说来,就是:使用错误,实现错误、环境错误。 使用错误: ArgumentException 表示参数使用错了 ArgumentNullException 表示参数不应该传入 null ArgumentOutOfRangeException 表示参数中的序号超出了范围 InvalidEnumArgumentException 表示参数中的枚举值不正确 InvalidOperationException 表示当前状态下不允许进行此操作(也就是说存在着允许进行此操作的另一种状态) ObjectDisposedException 表示对象已经 Dispose 过了,不能再使用了 NotSupportedException 表示不支持进行此操作(这是在说不要再试图对这种类型的对象调用此方法了,不支持) PlatformNotSupportedException 表示在此平台下不支持(如果程序跨平台的话) NotImplementedException 表示此功能尚在开发中,暂时请勿使用 实现错误: 前面由 CLR 抛出的异常代码主要都是实现错误 NullReferenceException 试图在空引用上执行某些方法,除了告诉实现者出现了意料之外的 null 之外,没有什么其它价值了 IndexOutOfRangeException 使用索引的时候超出了边界 InvalidCastException 表示试图对某个类型进行强转但类型不匹配 StackOverflow 表示栈溢出,这通常说明实现代码的时候写了不正确的显式或隐式的递归 OutOfMemoryException 表示托管堆中已无法分出期望的内存空间,或程序已经没有更多内存可用了 AccessViolationException 这说明使用非托管内存时发生了错误 BadImageFormatException 这说明了加载的 dll 并不是期望中的托管 dll TypeLoadException 表示类型初始化的时候发生了错误 环境错误: IOException 下的各种子类 Win32Exception 下的各种子类 …… 另外,还剩下一些不应该抛出的异常,例如过于抽象的异常和已经过时的异常,这在前面一小结中有说明。 其他 一些常见异常的原因和解决方法 在平时的开发当中,你可能会遇到这样一些异常,它不像是自己代码中抛出的那些常见的异常,但也不包含我们自己的异常堆栈。 这里介绍一些常见这些异常的原因和解决办法。 AccessViolationException 当出现此异常时,说明非托管内存中发生了错误。如果要解决问题,需要从非托管代码中着手调查。 这个异常是访问了不允许的内存时引发的。在原因上会类似于托管中的 NullReferenceException。 参考资料 Handling and throwing exceptions in .NET - Microsoft Docs Exceptions and Exception Handling - C Programming Guide - Microsoft Docs 我的博客会首发于 https://blog.walterlv.com/,而 CSDN 会从其中精选发布,但是一旦发布了就很少更新。 如果在博客看到有任何不懂的内容,欢迎交流。我搭建了 dotnet 职业技术学院 欢迎大家加入。 本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。欢迎转载、使用、重新发布,但务必保留文章署名吕毅(包含链接:https://walterlv.blog.csdn.net/),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我联系。 本篇文章为转载内容。原文链接:https://blog.csdn.net/WPwalter/article/details/94610764。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-04-13 13:38:26
60
转载
转载文章
...速度非常快,但是不能解决歧义; 搜索引擎模式,在精确模式的基础上,对长词再次切分,提高召回率,适合用于搜索引擎分词。 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
转载
转载文章
...viewport 元数据标签。 在移动设备浏览器上,通过为视口(viewport)设置 meta 属性为 user-scalable=no 可以禁用其缩放(zooming)功能。这样禁用缩放功能后,用户只能滚动屏幕,就能让你的网站看上去更像原生应用的感觉。注意,这种方式我们并不推荐所有网站使用,还是要看你自己的情况而定! 1.2 Normalize.css BootStrap内置了Normalize.css 1.3 布局容器 Bootstrap 需要为页面内容和栅格系统包裹一个 .container 容器。我们提供了两个作此用处的类。注意,由于 padding 等属性的原因,这两种 容器类不能互相嵌套。 .container 类用于固定宽度并支持响应式布局的容器。 <div class="container">...</div> .container-fluid 类用于 100% 宽度,占据全部视口(viewport)的容器。 <div class="container-fluid">...</div> 2 栅格系统 Bootstrap 提供了一套响应式、移动设备优先的流式栅格系统,随着屏幕或视口(viewport)尺寸的增加,系统会自动分为最多12列 2.1 栅格系统简介 栅格系统用于通过一系列的行(row)与列(column)的组合来创建页面布局,你的内容就可以放入这些创建好的布局中。下面就介绍一下 Bootstrap 栅格系统的工作原理: “行(row)”必须包含在 .container (固定宽度)或 .container-fluid (100% 宽度)中,以便为其赋予合适的排列(aligment)和内补(padding)。 通过“行(row)”在水平方向创建一组“列(column)”。 你的内容应当放置于“列(column)”内,并且,只有“列(column)”可以作为行(row)”的直接子元素。 类似 .row 和 .col-xs-4 这种预定义的类,可以用来快速创建栅格布局。Bootstrap 源码中定义的 mixin 也可以用来创建语义化的布局。 通过为“列(column)”设置 padding 属性,从而创建列与列之间的间隔(gutter)。通过为 .row 元素设置负值 margin 从而抵消掉为 .container 元素设置的 padding,也就间接为“行(row)”所包含的“列(column)”抵消掉了padding。 负值的 margin就是下面的示例为什么是向外突出的原因。在栅格列中的内容排成一行。 栅格系统中的列是通过指定1到12的值来表示其跨越的范围。例如,三个等宽的列可以使用三个 .col-xs-4 来创建。 如果一“行(row)”中包含了的“列(column)”大于 12,多余的“列(column)”所在的元素将被作为一个整体另起一行排列。 栅格类适用于与屏幕宽度大于或等于分界点大小的设备 , 并且针对小屏幕设备覆盖栅格类。 因此,在元素上应用任何 .col-md-栅格类适用于与屏幕宽度大于或等于分界点大小的设备 , 并且针对小屏幕设备覆盖栅格类。 因此,在元素上应用任何 .col-lg-不存在, 也影响大屏幕设备。 2.2 栅格参数 超小屏幕 手机 (<768px) 小屏幕 平板 (≥768px) 中等屏幕 桌面显示器 (≥992px) 大屏幕 大桌面显示器 (≥1200px) .container 最大宽度 None (自动) 750px 970px 1170px 类前缀 .col-xs- .col-sm- .col-md- .col-lg- 最大列(column)宽 自动 ~62px ~81px ~97px 2.3 栅格系统使用 使用单一的一组 .col-md- 栅格类,就可以创建一个基本的栅格系统,在手机和平板设备上一开始是堆叠在一起的(超小屏幕到小屏幕这一范围),在桌面(中等)屏幕设备上变为水平排列。所有“列(column)必须放在 ” .row 内。 <div class="row"><div class="col-md-1">.col-md-1</div><div class="col-md-1">.col-md-1</div><div class="col-md-1">.col-md-1</div><div class="col-md-1">.col-md-1</div><div class="col-md-1">.col-md-1</div><div class="col-md-1">.col-md-1</div><div class="col-md-1">.col-md-1</div><div class="col-md-1">.col-md-1</div><div class="col-md-1">.col-md-1</div><div class="col-md-1">.col-md-1</div><div class="col-md-1">.col-md-1</div><div class="col-md-1">.col-md-1</div></div><div class="row"><div class="col-md-8">.col-md-8</div><div class="col-md-4">.col-md-4</div></div><div class="row"><div class="col-md-4">.col-md-4</div><div class="col-md-4">.col-md-4</div><div class="col-md-4">.col-md-4</div></div><div class="row"><div class="col-md-6">.col-md-6</div><div class="col-md-6">.col-md-6</div></div> 2.4 不同屏幕设置不同宽度 <div class="row"><div class="col-xs-12 col-sm-6 col-md-8">.col-xs-12 .col-sm-6 .col-md-8</div><div class="col-xs-6 col-md-4">.col-xs-6 .col-md-4</div></div><div class="row"><div class="col-xs-6 col-sm-4">.col-xs-6 .col-sm-4</div><div class="col-xs-6 col-sm-4">.col-xs-6 .col-sm-4</div><!-- Optional: clear the XS cols if their content doesn't match in height --><div class="clearfix visible-xs-block"></div><div class="col-xs-6 col-sm-4">.col-xs-6 .col-sm-4</div></div> 2.5 列偏移 使用 .col-md-offset- 类可以将列向右侧偏移。这些类实际是通过使用 选择器为当前元素增加了左侧的边距(margin)。例如,.col-md-offset-4 类将 .col-md-4 元素向右侧偏移了4个列(column)的宽度。 <div class="row"><div class="col-md-4">.col-md-4</div><div class="col-md-4 col-md-offset-4">.col-md-4 .col-md-offset-4</div></div><div class="row"><div class="col-md-3 col-md-offset-3">.col-md-3 .col-md-offset-3</div><div class="col-md-3 col-md-offset-3">.col-md-3 .col-md-offset-3</div></div><div class="row"><div class="col-md-6 col-md-offset-3">.col-md-6 .col-md-offset-3</div></div> 2.6 列位置移动 通过使用 .col-md-push- 和 .col-md-pull- 类就可以很容易的改变列(column)的顺序。 <div class="row"><div class="col-md-9 col-md-push-3">.col-md-9 .col-md-push-3</div><div class="col-md-3 col-md-pull-9">.col-md-3 .col-md-pull-9</div></div> 3 排版 3.1 标题 HTML 中的所有标题标签,<h1> 到 <h6> 均可使用。另外,还提供了 .h1 到 .h6 类,为的是给内联(inline)属性的文本赋予标题的样式。 <h1>h1. Bootstrap heading</h1><h2>h2. Bootstrap heading</h2><h3>h3. Bootstrap heading</h3><h4>h4. Bootstrap heading</h4><h5>h5. Bootstrap heading</h5><h6>h6. Bootstrap heading</h6> 在标题内还可以包含 <small> 标签或赋予 .small 类的元素,可以用来标记副标题。 <h1>h1. Bootstrap heading <small>Secondary text</small></h1><h2>h2. Bootstrap heading <small>Secondary text</small></h2><h3>h3. Bootstrap heading <small>Secondary text</small></h3><h4>h4. Bootstrap heading <small>Secondary text</small></h4><h5>h5. Bootstrap heading <small>Secondary text</small></h5><h6>h6. Bootstrap heading <small>Secondary text</small></h6> 3.2 突出显示 通过添加 .lead 类可以让段落突出显示。 <p class="lead">...</p> 3.3 对齐 <p class="text-left">Left aligned text.</p><p class="text-center">Center aligned text.</p><p class="text-right">Right aligned text.</p><p class="text-justify">Justified text.</p><p class="text-nowrap">No wrap text.</p> 3.4 改变大小写 <p class="text-lowercase">Lowercased text.</p><p class="text-uppercase">Uppercased text.</p><p class="text-capitalize">Capitalized text.</p> 3.5 引用 <blockquote><p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer posuere erat a ante.</p></blockquote><blockquote><p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer posuere erat a ante.</p><footer>Someone famous in <cite title="Source Title">Source Title</cite></footer></blockquote><blockquote class="blockquote-reverse">...</blockquote> 3.6 列表 无样式列表 <ul class="list-unstyled"><li>...</li></ul> 内联列表 <ul class="list-inline"><li>...</li></ul> 水平排列的内联列表 <dl class="dl-horizontal"><dt>...</dt><dd>...</dd></dl> 4 代码 4.1 内联代码 通过 <code> 标签包裹内联样式的代码片段。 For example, <code><section></code> should be wrapped as inline. 4.2 用户输入 通过 <kbd> 标签标记用户通过键盘输入的内容。 To switch directories, type <kbd>cd</kbd> followed by the name of the directory.<br>To edit settings, press <kbd><kbd>ctrl</kbd> + <kbd>,</kbd></kbd> 4.3 代码块 多行代码可以使用 <pre> 标签。为了正确的展示代码,注意将尖括号做转义处理。 <pre><p>Sample text here...</p></pre> 还可以使用 .pre-scrollable 类,其作用是设置 max-height 为 350px ,并在垂直方向展示滚动条。 4.3 变量 通过 <var> 标签标记变量。 <var>y</var> = <var>m</var><var>x</var> + <var>b</var> 4.4 程序输出 通过 <samp> 标签来标记程序输出的内容。 <samp>This text is meant to be treated as sample output from a computer program.</samp> 5 表格 5.1 基本 为任意 <table> 标签添加 .table 类可以为其赋予基本的样式 <table class="table">...</table> 5.2 条纹状表格 <table class="table table-striped">...</table> 5.3 带边框的表格 <table class="table table-bordered">...</table> 5.4 鼠标悬停 <table class="table table-hover">...</table> 5.5 紧缩表格 <table class="table table-condensed">...</table> 5.6 状态类 通过这些状态类可以为行或单元格设置颜色。 Class 描述 .active 鼠标悬停在行或单元格上时所设置的颜色 .success 标识成功或积极的动作 .info 标识普通的提示信息或动作 .warning 标识警告或需要用户注意 .danger 标识危险或潜在的带来负面影响的动作 5.7 响应式表格 将任何 .table 元素包裹在 .table-responsive 元素内,即可创建响应式表格,其会在小屏幕设备上(小于768px)水平滚动。当屏幕大于 768px 宽度时,水平滚动条消失。 6 表单 6.1 基本实例 单独的表单控件会被自动赋予一些全局样式。所有设置了 .form-control 类的 <input>、<textarea> 和 <select> 元素都将被默认设置宽度属性为 width: 100%;。 将 label 元素和前面提到的控件包裹在 .form-group 中可以获得最好的排列。 <form><div class="form-group"><label for="exampleInputEmail1">Email address</label><input type="email" class="form-control" id="exampleInputEmail1" placeholder="Email"></div><div class="form-group"><label for="exampleInputPassword1">Password</label><input type="password" class="form-control" id="exampleInputPassword1" placeholder="Password"></div><div class="form-group"><label for="exampleInputFile">File input</label><input type="file" id="exampleInputFile"><p class="help-block">Example block-level help text here.</p></div><div class="checkbox"><label><input type="checkbox"> Check me out</label></div><button type="submit" class="btn btn-default">Submit</button></form> 6.2 内联表单 为 <form> 元素添加 .form-inline 类可使其内容左对齐并且表现为 inline-block 级别的控件。只适用于视口(viewport)至少在 768px 宽度时(视口宽度再小的话就会使表单折叠) 6.3 水平排列的表单 通过为表单添加 .form-horizontal 类,并联合使用 Bootstrap 预置的栅格类,可以将 label 标签和控件组水平并排布局。这样做将改变 .form-group 的行为,使其表现为栅格系统中的行(row),因此就无需再额外添加 .row 了 <form class="form-horizontal"><div class="form-group"><label for="inputEmail3" class="col-sm-2 control-label">Email</label><div class="col-sm-10"><input type="email" class="form-control" id="inputEmail3" placeholder="Email"></div></div><div class="form-group"><label for="inputPassword3" class="col-sm-2 control-label">Password</label><div class="col-sm-10"><input type="password" class="form-control" id="inputPassword3" placeholder="Password"></div></div><div class="form-group"><div class="col-sm-offset-2 col-sm-10"><div class="checkbox"><label><input type="checkbox"> Remember me</label></div></div></div><div class="form-group"><div class="col-sm-offset-2 col-sm-10"><button type="submit" class="btn btn-default">Sign in</button></div></div></form> 6.4 表单控件 输入框 包括大部分表单控件、文本输入域控件,还支持所有 HTML5 类型的输入控件: text、password、datetime、datetime-local、date、month、time、week、number、email、url、search、tel 和 color。 只有正确设置了 type 属性的输入控件才能被赋予正确的样式。 文本域 支持多行文本的表单控件。可根据需要改变 rows 属性。 多选和单选框 默认样式 <div class="checkbox"><label><input type="checkbox" value="">Option one is this and that—be sure to include why it's great</label></div><div class="checkbox disabled"><label><input type="checkbox" value="" disabled>Option two is disabled</label></div><div class="radio"><label><input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>Option one is this and that—be sure to include why it's great</label></div><div class="radio"><label><input type="radio" name="optionsRadios" id="optionsRadios2" value="option2">Option two can be something else and selecting it will deselect option one</label></div><div class="radio disabled"><label><input type="radio" name="optionsRadios" id="optionsRadios3" value="option3" disabled>Option three is disabled</label></div> 内联单选和多选框 <label class="checkbox-inline"><input type="checkbox" id="inlineCheckbox1" value="option1"> 1</label><label class="checkbox-inline"><input type="checkbox" id="inlineCheckbox2" value="option2"> 2</label><label class="checkbox-inline"><input type="checkbox" id="inlineCheckbox3" value="option3"> 3</label><label class="radio-inline"><input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1</label><label class="radio-inline"><input type="radio" name="inlineRadioOptions" id="inlineRadio2" value="option2"> 2</label><label class="radio-inline"><input type="radio" name="inlineRadioOptions" id="inlineRadio3" value="option3"> 3</label> 不带文本的Checkbox 和 radio <label><input type="checkbox" id="blankCheckbox" value="option1" aria-label="..."></label></div><div class="radio"><label><input type="radio" name="blankRadio" id="blankRadio1" value="option1" aria-label="..."></label></div> 下拉列表 <select class="form-control"><option>1</option><option>2</option><option>3</option><option>4</option><option>5</option></select> 静态内容 如果需要在表单中将一行纯文本和 label 元素放置于同一行,为 <p> 元素添加 .form-control-static 类即可 <form class="form-horizontal"><div class="form-group"><label class="col-sm-2 control-label">Email</label><div class="col-sm-10"><p class="form-control-static">email@example.com</p></div></div><div class="form-group"><label for="inputPassword" class="col-sm-2 control-label">Password</label><div class="col-sm-10"><input type="password" class="form-control" id="inputPassword" placeholder="Password"></div></div></form> 帮助文字 <label class="sr-only" for="inputHelpBlock">Input with help text</label><input type="text" id="inputHelpBlock" class="form-control" aria-describedby="helpBlock">...<span id="helpBlock" class="help-block">A block of help text that breaks onto a new line and may extend beyond one line.</span> 校验状态 Bootstrap 对表单控件的校验状态,如 error、warning 和 success 状态,都定义了样式。使用时,添加 .has-warning、.has-error或 .has-success 类到这些控件的父元素即可。任何包含在此元素之内的 .control-label、.form-control 和 .help-block 元素都将接受这些校验状态的样式。 <div class="form-group has-success"><label class="control-label" for="inputSuccess1">Input with success</label><input type="text" class="form-control" id="inputSuccess1" aria-describedby="helpBlock2"><span id="helpBlock2" class="help-block">A block of help text that breaks onto a new line and may extend beyond one line.</span></div><div class="form-group has-warning"><label class="control-label" for="inputWarning1">Input with warning</label><input type="text" class="form-control" id="inputWarning1"></div><div class="form-group has-error"><label class="control-label" for="inputError1">Input with error</label><input type="text" class="form-control" id="inputError1"></div><div class="has-success"><div class="checkbox"><label><input type="checkbox" id="checkboxSuccess" value="option1">Checkbox with success</label></div></div><div class="has-warning"><div class="checkbox"><label><input type="checkbox" id="checkboxWarning" value="option1">Checkbox with warning</label></div></div><div class="has-error"><div class="checkbox"><label><input type="checkbox" id="checkboxError" value="option1">Checkbox with error</label></div></div> 添加额外的图标 你还可以针对校验状态为输入框添加额外的图标。只需设置相应的 .has-feedback 类并添加正确的图标即可 <div class="form-group has-success has-feedback"><label class="control-label" for="inputSuccess2">Input with success</label><input type="text" class="form-control" id="inputSuccess2" aria-describedby="inputSuccess2Status"><span class="glyphicon glyphicon-ok form-control-feedback" aria-hidden="true"></span><span id="inputSuccess2Status" class="sr-only">(success)</span></div> 控件尺寸 通过 .input-lg 类似的类可以为控件设置高度,通过 .col-lg- 类似的类可以为控件设置宽度。 高度尺寸 创建大一些或小一些的表单控件以匹配按钮尺寸 <input class="form-control input-lg" type="text" placeholder=".input-lg"><input class="form-control" type="text" placeholder="Default input"><input class="form-control input-sm" type="text" placeholder=".input-sm"><select class="form-control input-lg">...</select><select class="form-control">...</select><select class="form-control input-sm">...</select> 水平排列的表单组的尺寸 通过添加 .form-group-lg 或 .form-group-sm 类,为 .form-horizontal 包裹的 label 元素和表单控件快速设置尺寸。 <form class="form-horizontal"><div class="form-group form-group-lg"><label class="col-sm-2 control-label" for="formGroupInputLarge">Large label</label><div class="col-sm-10"><input class="form-control" type="text" id="formGroupInputLarge" placeholder="Large input"></div></div><div class="form-group form-group-sm"><label class="col-sm-2 control-label" for="formGroupInputSmall">Small label</label><div class="col-sm-10"><input class="form-control" type="text" id="formGroupInputSmall" placeholder="Small input"></div></div></form> 7 按钮 7.1 可作为按钮使用的标签或元素 为 <a>、<button> 或 <input> 元素添加按钮类(button class)即可使用 Bootstrap 提供的样式 <a class="btn btn-default" href="" role="button">Link</a><button class="btn btn-default" type="submit">Button</button><input class="btn btn-default" type="button" value="Input"><input class="btn btn-default" type="submit" value="Submit"> 7.2 预定义样式 <!-- Standard button --><button type="button" class="btn btn-default">(默认样式)Default</button><!-- Provides extra visual weight and identifies the primary action in a set of buttons --><button type="button" class="btn btn-primary">(首选项)Primary</button><!-- Indicates a successful or positive action --><button type="button" class="btn btn-success">(成功)Success</button><!-- Contextual button for informational alert messages --><button type="button" class="btn btn-info">(一般信息)Info</button><!-- Indicates caution should be taken with this action --><button type="button" class="btn btn-warning">(警告)Warning</button><!-- Indicates a dangerous or potentially negative action --><button type="button" class="btn btn-danger">(危险)Danger</button><!-- Deemphasize a button by making it look like a link while maintaining button behavior --><button type="button" class="btn btn-link">(链接)Link</button> 7.3 尺寸 需要让按钮具有不同尺寸吗?使用 .btn-lg、.btn-sm 或 .btn-xs 就可以获得不同尺寸的按钮。 通过给按钮添加 .btn-block 类可以将其拉伸至父元素100%的宽度,而且按钮也变为了块级(block)元素。 7.4 激活状态 添加 .active 类 7.5 禁用状态 为 <button> 元素添加 disabled 属性,使其表现出禁用状态。 为基于 <a> 元素创建的按钮添加 .disabled 类。 8 图片 8.1 响应式图片 在 Bootstrap 版本 3 中,通过为图片添加 .img-responsive 类可以让图片支持响应式布局。其实质是为图片设置了 max-width: 100%;、 height: auto; 和 display: block; 属性,从而让图片在其父元素中更好的缩放。 如果需要让使用了 .img-responsive 类的图片水平居中,请使用 .center-block 类,不要用 .text-center <img src="..." class="img-responsive" alt="Responsive image"> 8.2 图片形状 <img src="..." alt="..." class="img-rounded"><img src="..." alt="..." class="img-circle"><img src="..." alt="..." class="img-thumbnail"> 9 辅助类 9.1 文本颜色 <p class="text-muted">...</p><p class="text-primary">...</p><p class="text-success">...</p><p class="text-info">...</p><p class="text-warning">...</p><p class="text-danger">...</p> 9.2 背景色 <p class="bg-primary">...</p><p class="bg-success">...</p><p class="bg-info">...</p><p class="bg-warning">...</p><p class="bg-danger">...</p> 9.3 三角符号 <span class="caret"></span> 9.4 浮动 <div class="pull-left">...</div><div class="pull-right">...</div> 9.5 让内容块居中 <div class="center-block">...</div> 9.6 清除浮动 通过为父元素添加 .clearfix 类可以很容易地清除浮动(float) <!-- Usage as a class --><div class="clearfix">...</div> 9.7 显示或隐藏内容 <div class="show">...</div><div class="hidden">...</div> 9.10 图片替换 使用 .text-hide 类或对应的 mixin 可以用来将元素的文本内容替换为一张背景图。 <h1 class="text-hide">Custom heading</h1> 10 响应式工具 10.1 不同视口下隐藏显示 .visible-xs- .visible-sm- .visible-md- .visible-lg- .hidden-xs .hidden-sm .hidden-md .hidden-lg.visible--block .visible--inline .visible--inline-block 10.2 打印类 .visible-print-block.visible-print-inline.visible-print-inline-block.hidden-print 打印机下隐藏 本篇文章为转载内容。原文链接:https://blog.csdn.net/m0_67155975/article/details/123351126。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-10-18 14:41:25
151
转载
转载文章
...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
300
转载
建站模板下载
资源介绍 该“简洁数据挖掘分析网站模板”是一款专为数据分析师设计的HTML网页模板,专注于提供数据爬取、挖掘及深度分析等功能展示。模板以清晰、简洁的布局呈现,适用于搭建数据分析类网站,方便用户了解更多关于数据挖掘和分析的知识与应用实例,提升数据价值洞察力。 点我下载 文件大小:764.46 KB 您将下载一个资源包,该资源包内部文件的目录结构如下: 本网站提供模板下载功能,旨在帮助广大用户在工作学习中提升效率、节约时间。 本网站的下载内容来自于互联网。如您发现任何侵犯您权益的内容,请立即告知我们,我们将迅速响应并删除相关内容。 免责声明:站内所有资源仅供个人学习研究及参考之用,严禁将这些资源应用于商业场景。 若擅自商用导致的一切后果,由使用者承担责任。
2023-11-20 10:42:07
89
本站
建站模板下载
...,提供便捷的网站搭建方案。用户可快速下载并自定义,打造独特且专业的餐饮类企业网站,提升在线影响力与用户体验。 点我下载 文件大小:1.83 MB 您将下载一个资源包,该资源包内部文件的目录结构如下: 本网站提供模板下载功能,旨在帮助广大用户在工作学习中提升效率、节约时间。 本网站的下载内容来自于互联网。如您发现任何侵犯您权益的内容,请立即告知我们,我们将迅速响应并删除相关内容。 免责声明:站内所有资源仅供个人学习研究及参考之用,严禁将这些资源应用于商业场景。 若擅自商用导致的一切后果,由使用者承担责任。
2023-06-03 10:04:08
58
本站
建站模板下载
...的宽屏响应式网页设计方案,具备高度个性化的界面设计和流畅的跨设备浏览体验。此模板适用于各类建筑与装修设计公司,提供一站式的企业网站模板下载服务,能够展现公司的专业形象与项目作品,助力提升在线品牌影响力。 点我下载 文件大小:2.43 MB 您将下载一个资源包,该资源包内部文件的目录结构如下: 本网站提供模板下载功能,旨在帮助广大用户在工作学习中提升效率、节约时间。 本网站的下载内容来自于互联网。如您发现任何侵犯您权益的内容,请立即告知我们,我们将迅速响应并删除相关内容。 免责声明:站内所有资源仅供个人学习研究及参考之用,严禁将这些资源应用于商业场景。 若擅自商用导致的一切后果,由使用者承担责任。
2023-06-10 20:24:45
110
本站
建站模板下载
...下的后台操作需求,如数据管理、用户管理等。下载后即可快速构建出专业的企业级后台管理页面,提升办公效率,实现便捷化的企业后台管理体系。 点我下载 文件大小:1.01 MB 您将下载一个资源包,该资源包内部文件的目录结构如下: 本网站提供模板下载功能,旨在帮助广大用户在工作学习中提升效率、节约时间。 本网站的下载内容来自于互联网。如您发现任何侵犯您权益的内容,请立即告知我们,我们将迅速响应并删除相关内容。 免责声明:站内所有资源仅供个人学习研究及参考之用,严禁将这些资源应用于商业场景。 若擅自商用导致的一切后果,由使用者承担责任。
2023-02-04 23:29:44
80
本站
建站模板下载
资源介绍 该“简约数据平台后台管理模板”是一款专为企业后台管理需求打造的响应式网页模板,设计风格简洁高效,适用于各类公司自动化数据后台的搭建与管理。此模板下载后易于部署和自定义,提供丰富的功能模块以实现对企业的全方位、精细化管控,助力企业实现数据后台的自动化运营与维护,提升管理效率。 点我下载 文件大小:150.27 KB 您将下载一个资源包,该资源包内部文件的目录结构如下: 本网站提供模板下载功能,旨在帮助广大用户在工作学习中提升效率、节约时间。 本网站的下载内容来自于互联网。如您发现任何侵犯您权益的内容,请立即告知我们,我们将迅速响应并删除相关内容。 免责声明:站内所有资源仅供个人学习研究及参考之用,严禁将这些资源应用于商业场景。 若擅自商用导致的一切后果,由使用者承担责任。
2023-11-09 15:17:45
88
本站
建站模板下载
...广及各类食品包装设计方案,凸显公司的专业设计能力与全面服务范围,帮助用户轻松构建出独具特色且功能完善的官方网站,吸引更多潜在客户。 点我下载 文件大小:10.34 MB 您将下载一个资源包,该资源包内部文件的目录结构如下: 本网站提供模板下载功能,旨在帮助广大用户在工作学习中提升效率、节约时间。 本网站的下载内容来自于互联网。如您发现任何侵犯您权益的内容,请立即告知我们,我们将迅速响应并删除相关内容。 免责声明:站内所有资源仅供个人学习研究及参考之用,严禁将这些资源应用于商业场景。 若擅自商用导致的一切后果,由使用者承担责任。
2023-08-28 15:36:24
41
本站
建站模板下载
...礼盒产品及个性化定制方案。模板内含更多礼盒定制类网站必备功能模块,并支持便捷下载,是打造个性定制礼盒网站或礼盒设计类网站的理想之选。 点我下载 文件大小:1.04 MB 您将下载一个资源包,该资源包内部文件的目录结构如下: 本网站提供模板下载功能,旨在帮助广大用户在工作学习中提升效率、节约时间。 本网站的下载内容来自于互联网。如您发现任何侵犯您权益的内容,请立即告知我们,我们将迅速响应并删除相关内容。 免责声明:站内所有资源仅供个人学习研究及参考之用,严禁将这些资源应用于商业场景。 若擅自商用导致的一切后果,由使用者承担责任。
2023-03-26 23:22:42
73
本站
建站模板下载
... 该“简约医院门诊大数据展示模板”是一款专为医院门诊数据可视化设计的HTML网页模板。它以直观、清晰的方式呈现医院看病人口数据和门诊数据,深度挖掘并整合医院各项业务数据,帮助用户快速了解和分析医院运营状况。此模板下载后可便捷应用,实现对医院数据的实时、动态展示,提升信息管理效率与决策支持能力。 点我下载 文件大小:1.46 MB 您将下载一个资源包,该资源包内部文件的目录结构如下: 本网站提供模板下载功能,旨在帮助广大用户在工作学习中提升效率、节约时间。 本网站的下载内容来自于互联网。如您发现任何侵犯您权益的内容,请立即告知我们,我们将迅速响应并删除相关内容。 免责声明:站内所有资源仅供个人学习研究及参考之用,严禁将这些资源应用于商业场景。 若擅自商用导致的一切后果,由使用者承担责任。
2023-07-02 09:38:57
56
本站
站内搜索
用于搜索本网站内部文章,支持栏目切换。
知识学习
实践的时候请根据实际情况谨慎操作。
随机学习一条linux命令:
set -o vi 或 set -o emacs
- 更改bash shell的命令行编辑模式为vi或emacs风格。
推荐内容
推荐本栏目内的其它文章,看看还有哪些文章让你感兴趣。
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
历史内容
快速导航到对应月份的历史文章列表。
随便看看
拉到页底了吧,随便看看还有哪些文章你可能感兴趣。
时光飞逝
"流光容易把人抛,红了樱桃,绿了芭蕉。"