由于七牛突然取消了我的测试域名使用权限,所以决定更新博客。
**
新的个人博客地址是: https://bigjun2017.github.io/
**
另外,抵制七牛从我做起。
最近闲来无事,就用Vue2.x模仿着自己的github博客手撸了一个博客,主要用到的技术有:VueX、Vue2.x、axios、iView、Router。最终的博客成果如下图:
博客架构图如下:
源码:https://gitee.com/Super_Jun/BlogDevByVue
开源说明:https://gitee.com/Super_Jun/BlogDevByVue/blob/master/README.md
我的朋友都去上海了,讨厌公司的用人套路,MMP,不开心,我也想去了。。。。。。
首先介绍下,什么是单设备登录?
单设备登录可以理解为同一个应用某一时刻只允许单一用户使用处于登录状态。单设备登录可以类比QQ的踢出第二者登录模式,可以在一定的程度上保障账号的安全。
单设备登录的难点在哪?
单设备登录的难点在于如何主动、准确的推动消息到客户端。但是幸运的是我们可以通过WebSocket实现消息的准确推送,并且React中也有对应的WebSocket的Js依赖包可以使用。
下面将介绍详细的实现流程:
A、前端开发准备流程:
1、使用npm 安装正确的js依赖包:
|
|
参考文章:阮一峰的WebSocket 教程
2、websocket前端实现代码:
|
|
主要步骤就是在登录校验通过后就建立和WebSocket服务终端的链接,并启动消息监听,在多用户登录的时候,前端弹出提示消息并跳转到登录页面,在用户执行退出操作的时候终断websocket链接并跳转到登录页面。
具体情境如下图所示:
B、websocket后端开发流程
1、在用户成功登录后创建session并存储用户的登录信息;
2、websocket客户端和websocket服务端握手成功之后,将第一步中的session中的数据放入WebSocketSession中,便于在MyWebSocketHandler中创建在线用户表,最终将根据在线用户表中的userId实现一对一的消息推送。
3、ws请求进入MyWebSocketHandler【ws处理】中,在ws链接建立的生命周期中进行合理的数据处理。
WebSocketInterceptor.java
|
|
MyWebSocketHandler.java
|
|
当需要统计应用的试试在线人数的时候,统计成功登录后创建的session的数量是最为准确的数据。SpringBoot通过SessionListener可以很方便的监听session的生命周期。在SpringBoot中监听session的步骤如下:
1、创建session监听器.
|
|
2、创建session处理工具类,里面需要一个静态的HashMap存储应用中登录后创建的有效的session.
|
|
Logger.java
|
|
3、添加SessionListener到系统配置中:
|
|
原理:通过SessionListener监听session的创建和销毁,并在对应的session生命周期中从静态的HashMap【自定义的session容器】中添加或者删除对应的session,从而实现session的动态管理,提供准确的统计数据。
注意:在本例中提供了根据sessionId获取session的方法。
最近因工作需要,我需要爬取国家统计局的最新统计数据。因此参照网上的例子使用JSOUP爬取了国家统计局的省、市、县、镇、村的数据。因为要爬取的数据较多,因此在里面使用了多线程的相关技术。下面首先讲解下多线程相关的东西。
首先理解下什么是线程池?
因为创建和销毁线程是一件非常耗费时间的工作,因此,如果线程可以再一定程度上复用,那么肯定可以再节省不少的时间。线程池的作用可以类比MYSQL中的连接池理解。
参考文章:Java常用四大线程池用法以及ThreadPoolExecutor详解
其次是多线程中的Future模式。
因为爬取的省份较多,因此需要多个线程并发执行,但是必须保证在IO操作的写操作执行前,所有的数据都已经爬取完毕。因此,必须使用多线程的Future模式,Future模式可以保证多个线程并发执行,并且可以通过future.get()方法在适当的地方进行线程堵塞,保证在结果输出前各个子线程已经执行结束。因为在调用到某个子线程的future.get()方法之前,各个子线程会继续并发执行,因此,调用future.get()方法阻塞主线程消耗的时间会非常的少,因此可以再一定的程度上节省时间开销。
|
|
Future模式的参考文章:Java多线程 - Future模式
最后讲述下说说Runnable与Callable的区别:
二者的实际异同为以下几点:
相同点:
1、 两者都是接口;(废话)
2、两者都可用来编写多线程程序;
3、两者都需要调用Thread.start()启动线程;
不同点:
1、两者最大的不同点是:实现Callable接口的任务线程能返回执行结果;而实现Runnable接口的任务线程不能返回结果;
2、Callable接口的call()方法允许抛出异常;而Runnable接口的run()方法的异常只能在内部消化,不能继续上抛;
注意点:
Callable接口支持返回执行结果,此时需要调用FutureTask.get()方法实现,此方法会阻塞主线程直到获取‘将来’结果;当不调用此方法时,主线程不会阻塞!
参考文章:说说Runnable与Callable
下面是使用Jsoup爬取国家统计局数据的具体代码:
最近在公司的代码里面看到RabbitMQ相关的代码,于是带着好奇心研究了下RabbitMQ.
RabbitMQ的核心是交换机和队列。
交换机的功能主要是接收消息并且转发到绑定的队列,交换机不存 储消息,在启用ack模式后,交换机找不到队列会返回错误。队列用于临时存储消息和转发消息。
交换机有四种类型:Direct, topic, Headers and Fanout。
Direct:direct 类型的行为是”先匹配, 再投送”. 即在绑定时设定一个 routing_key, 消息的routing_key 匹配时, 才会被交换器投送到绑定的队列中去.[精确匹配类型]
Topic:按规则转发消息(最灵活)[模式匹配]
Headers:设置header attribute参数类型的交换机
RabbitMQ的队列类型有两种,即时队列和延时队列【DelayQueue】。
即时队列:队列中的消息会被立即消费;
延时队列:队列中的消息会在指定的时间延时之后被消费。
下面将从即时队列和延时队列的具体实现来详解RabbitMQ.
即时队列详解:
DirectExchange:
DirectExchangeTest.java
|
|
HelloSender.java
|
|
HelloReceiver.java
|
|
RabbitConfig.java
|
|
FanoutExchange:
FanoutExchangeTest.java
|
|
FanoutSender .java
FanoutReceiverA .java……FanoutReceiverC .java
FanoutRabbitConfig.java
|
|
TopicExchangeTest.java
|
|
TopicSender .java
TopicReceiver .java
TopicReceiver2 .java
TopicRabbitConfig .java
topic 和 direct 类似, 只是匹配上支持了”模式”, 在”点分”的 routing_key 形式中, 可以使用两个通配符:
“*”表示一个词.
“#”表示零个或多个词.
在上面的TopicExchange中,当调用send1()方法,执行this.rabbitTemplate.convertAndSend("topicExchange","topic.message", context);
时,实际上只有两个队列,即采用with(“topic.#”);和with(“topic.message”);绑定的两个队列会接受到消息;
以上就是非常简单的即时队列的详解,线面是延时队列的详解。
延时队列有哪些用处呢?打个比方,加入用户下了单,但是在30分钟内没有支付我们就返回支付失败提示这个情景就可以采用延时队列来处理。
延时队列的原理图如下:
客户端:指具体往MQ发生消息端, 客户端将消息内容进行自定义包装, 将消息中附带目标队列名称。如:客户端向队列Q1发送字符串“hello” , 延时时间为60秒, 包装后修改为{“queueName”:”Q1”,”body”: “hello”},此时,将消息发送到DLX死信队列,而非Q1队列,并将消息设置为60秒超时。
DLX:死信队列,用来存储有超时时间信息的消息, 并且可以设置当消息超时时,转发到另一个指定队列(此处设置转发到router), 死信队列无消费者,当接收到客户端消息之后,等待消息超时,将消息转发到指定的Router队列
Router: 转发队列,用来接收死信队列超时消息, 如上示例消息,在接收到之后,消费者将消息解析,获取queueName,body,再向所获取的queueName队列中发送一条消息,内容为body.
延时队列的具体实现代码如下:
DelaySendTest.java
|
|
QueueMessage.java
MessageException.java
|
|
DeafaultMessageServiceImpl.java
|
|
QueueConfiguration.java
|
|
TradeProcessor.java
|
|
HelloProcessor.java
|
|
延时队列在可视化界面的表现形式为:
其实最终的原理非常简单:
归纳起来就是:客户端发送消息到指定的交换机,进入死信队列【死信队列没有消费者】,在死信队列里超时处理器等待消息超时,消息超时后转发消息给转发队列,转发队列的消息消费者监听到消息后进行对应的逻辑处理即可。
最近因为工作需要需要实现单例模式,考虑到单例模式最简单的实现方式是枚举实现,因此研究了下枚举实现单例模式的原理,下面将从原理、详解两个步骤说明:
一:原理
1、单例模式利用了“
【 “
2、枚举类的构造器是private私有的,保障了内存中只有枚举的一个实例;
二:详解
1)、为什么说枚举实例是单例且线程安全的?
就拿枚举来说,其实Enum就是一个普通的类,它继承自`java.lang.Enum类。
|
|
对Singleton进行反编译可以得到如下java类:
|
|
由反编译后的代码可知,SINGLETON被声明为 static 的【 public static final SingletonEnum SINGLETON<==>public static final SingletonEnum SINGLETON = New SingletonEnum ();】,根据类加载过程可以知道虚拟机会保证一个类的
2)、如何保证枚举实例的单例?
首先
的实质是:
|
|
因此外部无法通过构造器创建枚举类的实例,这也是枚举类通常用来保存常量的一个原因之一。
其次,枚举类的实例会在类加载的时候线程安全的进行初始实例化,在类加载的时候,JVM会对
综上所述,枚举保证了枚举实例的线程安全和单例性;
参考文章:
浅谈使用单元素的枚举类型实现单例模式
java回顾篇—static和非static的区别
一个简单java程序的运行全过程
static关键字的四种用法
最近大佬优化了一条SQL,讲100万条的数据查询速度从27s降低到了0.8S,因此研究了下MySQL优化的原理和方式。下面将从Mysql中索引的本质,原理等方面介绍下MySQL中的索引原理。
1、MySQL中数据的存储方式;
mysql的数据在磁盘上的存储:
A、数据块:
由多个磁盘block组成的块,存储引擎负责管理数据块。
磁盘是block块设备,数据在磁盘上的存放也是按照块存放的。
mysql读取表到内存的时候,也必然是按照一块一块的方式读取。假设 要查询的表在和其他表在都在同一个块内。加载块的时候除了读取要查询的表,其他表也一并被读取出来。
当一个块内的部分表被删除时,这是就是形成了碎片。这样会降低装载到内存的速度。
所以会生成一个块头,记录一个快内表的大小,有无空闲空间,空闲空间的位置。
B、散列文件组织:
人为将表分成多个部分,每个部分称为桶。根据行中的某个或某些字段做使用散列函数做哈希运算,运算结果属于某个范围的放在指定的桶中。多个桶组成一个表。
桶有可能溢出。所以要选定一个合适散列函数,让行平均在各个桶中。
2、什么是MySQL的索引;
引用块内容
MySQL官方对索引的定义为:索引(Index)是帮助MySQL高效获取数据的数据结构。我们可以简单理解为:快速查找排好序的一种数据结构。Mysql索引主要有两种结构:B+Tree索引和Hash索引。我们平常所说的索引,如果没有特别指明,一般都是指B树结构组织的索引(B+Tree索引)。索引也是一种数据,需要占用物理空间。B+Tree索引索引的数据结构如图所示:
3、MySQL中索引【hash和BTree】的实现原理;
……等我研究下两种算法再确定。
4、两种索引【hash和BTree】的区别和缺点;
一、BTree
BTree索引是最常用的mysql数据库索引算法,因为它不仅可以被用在=,>,>=,<,<=和between这些比较操作符上,而且还可以用于like操作符,只要它的查询条件是一个不以通配符开头的常量,例如:
select from user where name like ‘jack%’;
select from user where name like ‘jac%k%’;
如果一通配符开头,或者没有使用常量,则不会使用索引,例如:
select from user where name like ‘%jack’;
select from user where name like simply_name;
二、Hash
Hash索引只能用于对等比较,例如=,<=>(相当于=)操作符。由于是一次定位数据,不像BTree索引需要从根节点到枝节点,最后才能访问到页节点这样多次IO访问,所以检索效率远高于BTree索引。
但为什么我们使用BTree比使用Hash多呢?主要Hash本身由于其特殊性,也带来了很多限制和弊端:
- Hash索引仅仅能满足“=”,“IN”,“<=>”查询,不能使用范围查询。
- 联合索引中,Hash索引不能利用部分索引键查询。
对于联合索引中的多个列,Hash是要么全部使用,要么全部不使用,并不支持BTree支持的联合索引的最优前缀,也就是联合索引的前面一个或几个索引键进行查询时,Hash索引无法被利用。- Hash索引无法避免数据的排序操作
由于Hash索引中存放的是经过Hash计算之后的Hash值,而且Hash值的大小关系并不一定和Hash运算前的键值完全一样,所以数据库无法利用索引的数据来避免任何排序运算。- Hash索引任何时候都不能避免表扫描
Hash索引是将索引键通过Hash运算之后,将Hash运算结果的Hash值和所对应的行指针信息存放于一个Hash表中,由于不同索引键存在相同Hash值,所以即使满足某个Hash键值的数据的记录条数,也无法从Hash索引中直接完成查询,还是要通过访问表中的实际数据进行比较,并得到相应的结果。- Hash索引遇到大量Hash值相等的情况后性能并不一定会比BTree高
对于选择性比较低的索引键,如果创建Hash索引,那么将会存在大量记录指针信息存于同一个Hash值相关联。这样要定位某一条记录时就会非常麻烦,会浪费多次表数据访问,而造成整体性能底下。
5、参考文章。
1、 mysql之mysql数据在磁盘的储存方式
2、索引的优缺点
3、什么是索引?Mysql目前主要的几种索引类型
4、MySQL的索引是什么?怎么优化?
5、深入理解MySQL索引原理和实现——为什么索引可以加速查询?
6、数据库索引系列四:索引算法Hash与BTree的区别
7、MySQL优化原理
在切换 v-if 块时,Vue.js 有一个局部编译/卸载过程,因为 v-if 之中的模板也可能包括数据绑定或子组件。v-if 是真实的条件渲染,因为它会确保条件块在切换当中合适地销毁与重建条件块内的事件监听器和子组件。
v-if 也是惰性的:如果在初始渲染时条件为假,则什么也不做——在条件第一次变为真时才开始局部编译(编译会被缓存起来)。
相比之下,v-show 简单得多——元素始终被编译并保留,只是简单地基于 CSS 切换。
一般来说,v-if 有更高的切换消耗而 v-show 有更高的初始渲染消耗。因此,如果需要频繁切换 v-show 较好,如果在运行时条件不大可能改变 v-if 较好。
v-if 是动态添加,当值为 false 时,是完全移除该元素,即 dom 树中不存在该元素。
v-show 仅是隐藏 / 显示,值为 false 时,该元素依旧存在于 dom 树中。若其原有样式设置了 display: none 则会导致其无法正常显示。
参考文章:Vue.js 条件与循环
tag:
缺失模块。
1、请确保node版本大于6.2
2、在博客根目录(注意不是yilia根目录)执行以下命令:
npm i hexo-generator-json-content --save
3、在根目录_config.yml里添加配置:
jsonContent: meta: false pages: false posts: title: true date: true path: true text: false raw: false content: false slug: false updated: false comments: false link: false permalink: false excerpt: false categories: false tags: true