|
现代浏览器普遍都支持了video
的tag,所以只需要在video的tag里面设置source为SpringMVC的流响应接口即可完成通过浏览器源生播放器进行播放,当然要想实现如倍速、快捷键等更多功能也可以通过其他开源播放器项目进行定制。
作为消息系统(Kafka as a Messaging System)
作为存储系统(Kafka as a Storage System)
流处理(Kafka for Stream Processing)
主题是会被推送记录的一个类别或订阅名称
对于每一个主题,Kafka维护了一个分区日志群集!
每个分区是一个有序的,可以不断追加消息的消息序列。分区中的每个消息都会分配一个在分区内是唯一的序列号,这个序列号叫做偏移量(offset)
允许应用推送流记录到一个或多个Kafka主题上。
生产者向所选的主题发布数据。生产者负责选择哪些消息应该分配到主题内的哪个分区。这种选择分区方式,可以使用简单的循环方式负载均衡; 也可以通过一些语义分区函数实现(如:基于消息的key进行划分)。
允许应用程序订阅一个或多个主题并且并处理产生的流记录
每个消费者都属于一个消费组,每一条被推送到主题的记录被传递给订阅该主题的消费组的其中一个消费者。消费者可以在不同进程或者不同的机器上。
如果所有的消费者实例有相同的消费组,消息将会有效地负载平衡给这些消费者实例。
允许应用程序作为一个流处理器,从一个或多个主题获取流数据,然后输出流数据到一个或多个主题,有效地将输入流转换为输出流。
如果所有的消费者实例在不同的消费组中,那么每一条消息将会被广播给所有的消费者处理。
允许构建和运行可重用的生产者(Producer)或消费者(Consumer)连接Kafka与现有应用程序或数据系统。例如,一个连接器(connector)在关系数据库中可能获取每个表变化。
预写式日志(Write-ahead logging,缩写 WAL)是关系数据库系统中用于提供原子性和持久性(ACID属性中的两个)的一系列技术。在使用WAL的系统中,所有的修改在提交之前都要先写入log文件中。
MemStore:每个Store中有一个MemStore实例。数据写入WAL之后就会被放入MemStore。MemStore是内存的存储对象,只有当MemStore满了的时候才会将数据刷写(flush)到HFile中。
HFile:在Store中有多个HFile。当MemStore满了之后HBase就会在HDFS上生成一个新的HFile,然后把MemStore中的内容写到这个HFile中。HFile直接跟HDFS打交道,它是数据的存储实体。接下来我们来解剖一下单个HFile中的成分。
WAL是存储在HDFS上的,Memstore是存储在内存中的,HFile又是存储在HDFS上的。
数据是先写入WAL,再被放入Memstore,最后被持久化到HFile中。
WAL:数据被发出之后第一时间被写入WAL。由于WAL是基于HDFS来实现的,所以也可以说现在单元格就已经被持久化了,但是WAL只是一个暂存的日志,它是不区分Store的。这些数据是不能被直接读取和使用。
Memstore:数据随后会立即被放入Memstore中进行整理。Memstore会负责按照LSM树的结构来存放数据。这个过程就像我们在打牌的时候,抓牌之后在手上对牌进行整理的过程。
HFile:最后,当Memstore太大了达到尺寸上的阀值,或者达到了刷写时间间隔阀值的时候,HBaes会被这个Memstore的内容刷写到HDFS系统上,称为一个存储在硬盘上的HFile文件。至此,我们可以称为数据真正地被持久化到硬盘上,就算宕机,断电,数据也不会丢失了。
HBase实际的读取顺序是先从BlockCache中找数据,找不到了再去Memstore和HFile中查询数据。
-ROOT-是一张存储META的表,META是一张存储所在region的简要信息,如起止行等信息。
从0.96版本之后这个三层查询架构被改成了二层查询架构。-ROOT-表被去掉了,同时zk中的/hbase/root-region-server也被去掉了。这回直接把.META.表所在的RegionServer信息存储到了zk中的/hbase/meta-region-server去了。再后来引入了namespace,.META.表这样别扭的名字被修改成了hbase:meta。
]]>UniversalDetector
调用handleData(buf, 0, read)
需要传入一个byte buffer,故大致写了如下一个实现试图从MultipartFile获取InputSteam然后读取buffer。
|
看上去简单明了,不过部署后发现程序似乎进入了infinite loop没有进行正常响应,调试进入发现程序果然陷入第5行持续循环无法结束,经过watcher发现每次loop都只read到了文件开头的字节,并未如预期read完整个Stream,难道是MultipartFile.getInputStream()
实现和预想的不一样?遂step into MultipartFile.getInputStream()
发现调用的是StandardMultipartHttpServletRequest
的实现,StandardMultipartHttpServletRequest
继承自AbstractMultipartHttpServletRequest
,是对当前HttpServletRequest
的封装。这看上去没什么问题,调用的是part
的getInputStream()
实现,没错,这里的part是javax.servlet.http.Part
的JavaEE接口,具体逻辑由容器的实现。
继续跟进,发现part果然进入了org.apache.catalina.core.ApplicationPart
的tomcat实现
这里的fileItem应该是容器对于上传表单中文件的封装,继续跟进fileItem进入了org.apache.tomcat.util.http.fileupload.disk.DiskFileItem
实现,下面是DiskFileItem
的一些关键方法,其中dfos是一个DeferredFileOutputStream
接口。
继续step into进入了org.apache.tomcat.util.http.fileupload.ThresholdingOutputStream
,ThresholdingOutputStream
是DeferredFileOutputStream
的一个实现。果然在上传文件大小大于阈值时候tomcat会将内容放置在一个临时目录里,并不会持续将文件保留在内存中。有趣的是默认的threshold配置是0,换言之就是默认情况下对于上传的文件,只要存在内容,tomcat总会将其持久化,并在调用getInputStream()
方法的时候创建FileInputStream返回。换言之,就是每次调用getInputStream()
返回的都是一个全新的FileInputStream,也解释了为什么在调用getInputStream()
总是只能读取前buffer长度的字节,一直无法读取完全的问题。其中ThresholdingOutputStream
的关键代码如下。
JShell是一个能够执行Java代码片段、表达式的Shell,从外表看上去像是一个Java的解释器,实际上它是一个Read-Eval-Print Loop,接受命令、代码片段输入,输出运算结果或者一个变更状态。
l10n_zh_CN.properties
的多语言支持。\help
便可以获得命令列表。\evn
指令导入的合法classpath下的任意jar。也就是说导入JShell编译器内存文件API的管理器,同时具有一个OutputMemoryJavaFileObject的Map用于类文件的缓存。
编译器用于解析、分析、在内存编译class文件的API基本接口,其中CompileTask、AnalyzeTask、ParseTask均继承自BaseTask,这几个Task均为TaskFactory的内部类,通过TaskFactory的工厂方法产生对应的实例。
赋值引擎,将source 封装成方法、属性等等,在外部封装了imports和class,为整个JShell的核心部分
编译 声明 重定义 替换 执行
代表传递的Java代码片段,同一个片段是不可变的,这一特性也就意味着它的任意方法都会有相同的返回,并且是线程安全的。
Snippet为抽象类,只有继承终点为实现类,其中派生Snippet结构如图所示。
ExpressionSnippet为表达式片段,StatementSnippet为声明片段,ErroneousSnippet为非法片段,PersistentSnippet为被存储影响后续代码结果的片段。DeclarationSnippet为值片段,其下属值、类型、函数都是其实现类。
直接/间接通过 JShell.eval(String)或者JShell.drop(Snippet), 或者Snippet被直接生成的关于这个变化描述的片段
继承自com.sun.tools.javac.parser.ParserFactory
解析器工厂,通过com.sun.tools.javac.util.Context
初始化了,com.sun.tools.javac.parser.ScannerFactory
用于产生Scanner。
一个存放Snippet诊断信息的向量,这些包含是否有不可达、未声明、无法解析等错误。
对一个输入的String,通过Context隐藏其注释和修饰符细节。
将源输入包装成方法,成员变量,等等。
由于时间有限,这里先把程序的主要执行逻辑写在这里,以后有时间绘成流程图。
通过以上源码分析,我们我们可以通过建立一个JShell对象来尝试执行一个简单的Java程序块。
|
虽然执行过程稍微长了一点,但是程序成功输出了xdsjsd,实验成功。
JShell为开发者提供了一个稳定的交互式终端,程序开发者可以通过JShell进行一些简单的程序执行结果验证,同时它还赋予了Java应用程序动态执行Java代码的能力,如果不出意外接下来会有很多基于JShell机制的Virtual Runtime,OJ平台出现,大家拭目以待吧!
]]>JDK在java.security
下有一个KeyStore
的工具类,可以通过工厂方法getInstance(String type, String provider)
初始化一个密钥存储对象,该对象对主流的密钥格式,如PKCS12、PEM都有了默认支持。
以下是初始化密钥的方法描述。
有了密钥,在应用上加解密的配置便完成了,可以使用密钥工厂对SSLSocket进行适配,产生安全连接。使用SSLContext
可以初始化一个SSLSocketFactory。
至此,整个SSL连接已经构建完成。
OkHttpClient.Builder()
提供了一个public Builder sslSocketFactory(SSLSocketFactory sslSocketFactory, X509TrustManager trustManager)
方法可以给OKhttp配置一个SSL连接工厂,所有通过该Client的Request都会附带认证信息,其中X509TrustManager为X509密钥标准的抽象管理类。
OKhttp的文档中提供了X509TrustManager的初始化方法。
最后通过OkHttpClient okHttpClient = new OkHttpClient.Builder().sslSocketFactory(sslSocketFactory,trustManager).build()
便可以完成整个Client的初始化工作。
作为后起之秀,OkHttp对于Java原有的证书授信机制有着深入的理解,并提供了使用SSLSocketFactory的适配方法,整个API设计简洁自然,杜绝了重复的设计。当然,没有所谓的绝对安全,对于重要的证书密钥应该妥善保管,并且及时更新,避免泄露带来的连锁反应。
]]>根据数据库结构生成MyBatis可以使用的ORM映射文件的工具有很多,当然最著名的是MyBatis官方的项目MyBatis Generator,该工具提供了一个可定制的ORM生成器,可以自定义生成ORM类型(XML还是基于注解)的方式,可以对表/列进行特殊定制,还可以定义数据库支持,基本主流的数据库都进行支持,项目文档也详细说明了各项配置,下面简单说几个比较常用的配置,其余细节可以参阅文档。
|
该节点是<table>
节点的子节点。
通过该项配置在生成的select语句会生成keyProperty="id"
属性,即在调用insert(T t)
或者insertSelective(T t)
方法后T会在插入成功后同时被设置主键,方便后续进行updateByPrimeryKey(T t)
的操作。
|
该节点是<context>
的子节点,通过该项设置可以避免生成很多无用的generate by MyBatis generator xxx
之类的注解,当然如果使用maven deploy的话有没有注释影响也不是很大。
|
该节点是<context>
的子节点,为生成器提供的插件支持,也是本文所介绍的重点内容,所谓插件为MyBatis Generator提供的自定义最灵活的地方,通过插件接口可以对生成的类文件、XML文件提供强大的自定义功能。
插件接口为org.mybatis.generator.api.Plugin
当然这是一个最上层的接口,在同层包下提供了org.mybatis.generator.api.PluginAdapter
一个适配器,可以看到官方的一些插件(位于org.mybatis。
.generator.plugins
包下)基本都继承自这个抽象类,我们这里主要研究的对象也是PluginAdapter
。
首先看下基本方法,对于Plugin
的各类方法,PluginAdapter
都进行了基础实现,抽象的方法仅仅只有boolean validate(List<String> warnings)
一个,看名字可以猜出这是一个校验方法,用于传入warnnings来检查是否插件可用。
由于方法众多,故只寻找有代表的两个讲下,更多细节可以参考官方文档。
sqlMap???ElementGenerated(XmlElement element, IntrospectedTable introspectedTable)
该方法在基础XML生成后调用,可以看到入参有XmlElement,拿到XmlElement后就可以操作生成的XML了。
modelExampleClassGenerated(TopLevelClass, IntrospectedTable)
该方法在生成modelExample时调用,如果要修改生成的modelExample内容,可以使用TopLevelClass进行反射操作。
剩下的方法基本根据命名规则都可以猜到是干什么,对于类和XML的函数参数都是一样的,只要有参数,就可以进行定制。
|
|
常有一些使用场景,比如操作远程服务失败,需要在指定时间后重试。业务方面,比如有订单在X分钟后没有完成操作则变更状态等需求,这里使用RedisTemplete,利用expired
消息实现了一个基于Redis在指时间后进行操作的服务实现。
redis-cli -h {redis_host} -p {redis_port}
配置
修改Redis配置文件/etc/redis/redis.conf,找到bind那行配置:
指定配置文件然后重启Redis服务即可:
$ sudo redis-server /etc/redis/redis.conf
这里需要配置 notify-keyspace-events 的参数为 “Ex”。x 代表了过期事件。 notify-keyspace-events "Ex"
添加键值事件订阅127.0.0.1:6379> psubscribe __keyevent@0__:expired
1s,10s,1min,10min,1h,3h,6h
的间隔重试。RedisSerializer<T>
接口实现序列化与反序列化过程,并在使用RedisTemplete
或者初始化时候setKeySerializer(RedisSerializer<?> serializer)
和setValueSerializer(RedisSerializer<?> serializer)
要监听过期事件,需要配置RedisMessageListenerContainer
MessageListener是接受message的一个回调接口,在public void onMessage(Message message, byte[] bytes)
中回调接收到的消息,message有两个属性,一个是’byte[]’的MessageBody,一个也是byte[]
的MessageChannel,该属性表示该message发生的时间频段,在这个项目下为"__keyevent@0__:expired"
,即为0号库的过期事件。
这里使用了类名+Tag的方式标注一个unique的重试实体,在分发过程中检索对应的RetryListener
初始化传入的类名进行消息分发。
由于项目使用的FastJson,就用FastJson实现了一个简单的序列化与反序列化,当然在初始化传入了class,以便在反序列化时使用
自定义的一个消息分发,分发给指定的订阅任务,而避免了全局的广播
定义了一个基础的retry方法,提供了尝试重试等基础方法的实现,实现类自需要定义setMessageListener()
并在初始化后调用便可以接收到指定Entity的消息。
|
为了简单起见,这里使用了String作为Entity,通常建议使用自定义bean作为Entity
至此,对于使用HttpGetRequestRetry
的makeRequest(String url, String tag)
的Http请求便可以在失败时完成重试了。
A library for composing asynchronous and event-based programs using observable sequences for the Java VM”
一个在 Java VM 上使用可观测的序列来组成异步的、基于事件的程序的库。
Observable-可观察者,即被观察者
Observer-观察者
subscribe-订阅事件。Observable 和 Observer 通过 subscribe() 。
onNext(T t)正常的回调事件
onError(Throwable e) 异常发生时的回调事件
create(new Observable.OnSubscribe
基础的构造方法
just(T…)
等同于onNext(t1,t2..)的调用
from(T[])
等同于
的调用
|
Action1、Action2、……、ActionN 是RxJava的一系列接口,只包含一个call()方法,Subscriber可以接受这一些列参数构造,产生不完整的回调。
示例
此外,还有三种方法重载
分别为支持onError、onComplted()的构造方式,可以看出,第二个方法和new Subscriber()已经并无区别了
如同Action,Func1,Func2,…,FuncN为Rx提供的变换函数,所谓变换就是处理加工数据的过程,如把传入的URI转换成response或是将传入的资源转换为Bitmap
|
为Observable设置Fuc的方法,一个具体的示例如下
此外,还有public final <R> Observable<R> flatMap(Func1<? super T, ? extends Observable<? extends R>> func)
这个方法来设置将T 转换为R[]的一转多方法。
Scheduler为RxJava的线程调度起,指定任务执行的线程
几个内置的Scheduler
|
分别为指定被观察者call()方法发生线程和订阅者事件回调线程。
]]>对于如何保存页面状态获取需要Session认证的方法,可以使用OkHttp的拦截器,当然,OKHttp提供了一个 CookieJar 的接口可以方便完成这个任务,这里示例是一个没有做持久化存储Cookie的示例。
在构建请求Builder的时候设置实现的CookieJar
使用Jsoup构造Document对象,然后就可以像JavaScript操作DOM内容了。
|
到这里就已经拿到了所有完成请求的参数信息了,剩下的就不用说了吧。这个小玩具已经被归进了SequariusToys_AAOClient项目中。
]]>Hash是什么? redis的文档解释是
Redis hashes look exactly how one might expect a “hash” to look, with field-value pairs:
实际是一种散列集合结构,结构也如下:
{key} {domain1} {value1} {domain 2} {value2} … {domain n} {valuen}
同时将多个 field-value (域-值)对设置到哈希表 key 中。
此命令会覆盖哈希表中已存在的域。
如果 key 不存在,一个空哈希表被创建并执行 HMSET 操作。
将哈希表 key 中的域 field 的值设为 value 。
如果 key 不存在,一个新的哈希表被创建并进行 HSET 操作。
如果域 field 已经存在于哈希表中,旧值将被覆盖。
上述版本的不覆盖set版本 如果存在返回为false
返回哈希表 key 中,一个或多个给定域的值。
如果给定的域不存在于哈希表,那么返回一个 nil 值。
HSET key field value
将哈希表 key 中的域 field 的值设为 value 。
如果 key 不存在,一个新的哈希表被创建并进行 HSET 操作。
如果域 field 已经存在于哈希表中,旧值将被覆盖。
返回哈希表 key 中,所有的域和值。
在返回值里,紧跟每个域名(field name)之后是域的值(value),所以返回值的长度是哈希表大
小的两倍。
为哈希表 key 中的域 field 的值加上增量 increment 。
增量也可以为负数,相当于对给定域进行减法操作。
如果 key 不存在,一个新的哈希表被创建并执行 HINCRBY 命令。
如果域 field 不存在,那么在执行命令前,域的值被初始化为 0 。
上面命令的float版本
上面命令的增加指定增量版本
返回哈希表 key 中域的数量。
返回哈希表 key 中的所有域/值。
是一种线性的集合结构,支持常见的push/pop等操作
弹入弹出
阻塞版弹出/弹入原语
可以在最后加入超时时间
弹出List尾部元素,在尾部弹入
返回列表 key 中,下标为 index 的元素。
下标
将值 value 插入到列表 key 当中,位于值 pivot 之前或之后。
当 pivot 不存在于列表 key 时,不执行任何操作。
当 key 不存在时, key 被视为空列表,不执行任何操作。
如果 key 不是列表类型,返回一个错误。
返回列表 key 中指定区间内的元素,区间以偏移量 start 和 stop 指定。
根据参数 count 的值,移除列表中与参数 value 相等的元素。
将列表 key 下标为 index 的元素的值设置为 value 。
当 index 参数超出范围,或对一个空列表( key 不存在)进行 LSET 时,返回一个错误。
对一个列表进行修剪(trim),就是说,让列表只保留指定区间内的元素,不在指定区间之内的
元素都将被删除。
Redis Sets are unordered collections of strings. The SADD command adds new elements to a set. It’s also possible to do a number of other operations against sets like testing if a given element already exists, performing the intersection, union or difference between multiple sets, and so forth.
将一个或多个 member 元素加入到集合 key 当中,已经存在于集合的 member 元素将被忽
略。
集合中元素的数量。
返回一个集合的全部成员,该集合是所有给定集合之间的差(补)集。
不存在的 key 被视为空集。
上一个命令的保留到destination集合的版本
返回一个集合的全部成员,该集合是所有给定集合的交集。
不存在的 key 被视为空集。
当给定集合当中有一个空集时,结果也为空集(根据集合运算定律)。.
上一个命令的保留到destination集合的版本
返回一个集合的全部成员,该集合是所有给定集合的并集。
不存在的 key 被视为空集。
上一个命令的保留到destination集合的版本
判断 member 元素是否集合 key 的成员。
返回集合 key 中的所有成员。
不存在的 key 被视为空集合。
将 member 元素从 source 集合移动到 destination 集合。
如果命令执行时,只提供了 key 参数,那么返回集合中的一个随机元素。
移除并返回集合中的一个随机元素。
如果只想获取一个随机元素,但不想该元素从集合中被移除的话,可以使用
SRANDMEMBER 命令。
移除集合 key 中的一个或多个 member 元素,不存在的 member 元素会被忽略。
当 key 不是集合类型,返回一个错误。
有序集合是一种带Socore的集合,可以根据score进行一些排序,查找工作。
将一个或多个 member 元素及其 score 值加入到有序集 key 当中。
如果某个 member 已经是有序集的成员,那么更新这个 member 的 score 值,并通过重新
插入这个 member 元素,来保证该 member 在正确的位置上。
返回有序集 key 的基数。
返回有序集 key 中, score 值在 min 和 max 之间(默认包括 score 值等于 min 或
max )的成员的数量。
为有序集 key 的成员 member 的 score 值加上增量 increment 。
返回有序集 key 中,指定区间内的成员。
其中成员的位置按 score 值递增(从小到大)来排序。
具有相同 score 值的成员按字典序( lexicographical order )来排列。
返回有序集 key 中,所有 score 值介于 min 和 max 之间(包括等于 min 或 max )的成
员。有序集成员按 score 值递增(从小到大)次序排列。
返回有序集 key 中成员 member 的排名。其中有序集成员按 score 值递增(从小到大)顺序
排列。
移除有序集 key 中的一个或多个成员,不存在的成员将被忽略。
当 key 存在但不是有序集类型时,返回错误。
移除有序集 key 中,指定排名(rank)区间内的所有成员。
区间分别以下标参数 start 和 stop 指出,包含 start 和 stop 在内。
移除有序集 key 中,所有 score 值介于 min 和 max 之间(包括等于 min 或 max )的成
员。
返回有序集 key 中,指定区间内的成员。
其中成员的位置按 score 值递减(从大到小)来排列。具有相同 score 值的成员按字典序的
逆序( reverse lexicographical order)排列。
返回有序集 key 中,成员 member 的 score 值。
如果 member 元素不是有序集 key 的成员,或 key 不存在,返回 nil 。
[AGGREGATE SUM|MIN|MAX]
计算给定的一个或多个有序集的并集,其中给定 key 的数量必须以 numkeys 参数指定,并
将该并集(结果集)储存到 destination 。
[AGGREGATE SUM|MIN|MAX]
计算给定的一个或多个有序集的交集,其中给定 key 的数量必须以 numkeys 参数指定,并
将该交集(结果集)储存到 destination 。
redis-doc
为准。
存取值的通用方法
序列化给定 key ,并返回被序列化的值,使用 RESTORE 命令可以将这个值反序列化为Redis 键。
序列化生成的值有以下几个特点:
1.它带有 64 位的校验和,用于检测错误, RESTORE 在进行反序列化之前会先检查校验和。
2.值的编码格式和 RDB 文件保持一致。
3.RDB 版本会被编码在序列化值当中,如果因为 Redis 的版本不同造成 RDB 格式不兼容,那么 Redis 会拒绝对这个值进行反序列化操作。
1.为给定 key 设置生存时间,当 key 过期时(生存时间为 0 ),它会被自动删除。
2.在 Redis 中,带有生存时间的 key 被称为『易失的』(volatile)。
3.生存时间可以通过使用 DEL 命令来删除整个 key 来移除,或者被 SET 和 GETSET 命令覆写(overwrite),这意味着,如果一个命令只是修改(alter)一个带生存时间的 key 的值而不是用一个新的 key 值来代替(replace)它的话,那么生存时间不会被改变。如果使用 RENAME 对一个 key 进行改名,那么改名后的 key 的生存时间和改名前一样。RENAME 命令的另一种可能是,尝试将一个带生存时间的 key 改名成另一个带生存时间的another_key ,这时旧的 another_key (以及它的生存时间)会被删除,然后旧的 key 会改名为 another_key ,因此,新的 another_key 的生存时间也和原本的 key 一样。同时,对已有生存时间的key执行expire命令可以更新生存时间
4.所以若不过期 返回为-1 否则返回过期时间为剩余的秒时间值,可以使用ttl查询键的生存时间
EXPIREAT 的作用和 EXPIRE 类似,都用于为 key 设置生存时间。
不同在于 EXPIREAT 命令接受的时间参数是 UNIX 时间戳(unix timestamp)。
如果生存时间设置成功,返回 1 。当 key 不存在或没办法设置生存时间,返回 0 。
将 key 原子性地从当前实例传送到目标实例的指定数据库上,一旦传送成功, key 保证会出现在目标实例上,而当前实例上的 key 会被删除。
原理还是使用DUMP、RESTORE进行序列化和反序列化
由于这是一个原子操作,所以是阻塞执行。
将当前数据库的 key 移动到给定的数据库 db 当中
如果当前数据库(源数据库)和给定数据库(目标数据库)有相同名字的给定 key ,或者 key 不存在于当前数据库,那么 MOVE 没有任何效果
OBJECT 命令允许从内部察看给定 key 的 Redis 对象。
-REFCOUNT 引用数目
-IDLETIME 空闲时间
-ENCODING 编码方式
移除指定key的TTL,让它获得永生。
与EXPIRE相同作用相同,只是以毫秒为时间单位
同PEXPIRE与EXPIRE的关系
TTL的毫秒版
随机返回一个key,数据库为空时返回nil,抽奖程序知道怎么写了才不会被喷了吧?
改名,
1.当 key 和 newkey 相同,或者 key 不存在时,返回一个错误。
2.当 newkey 已经存在时, RENAME 命令将覆盖旧值。
当且仅当 newkey 不存在时,将 key 改名为 newkey 。
当 key 不存在时,返回一个错误。
反序列化
SORT key 返回键值从小到大排序的结果。
SORT key DESC 返回键值从大到小排序的结果。
SORT {SET} ALPHA 按字符对set排序
LIMIT offset cout 结果集数量
SORT numbers STORE sorted-numbers 讲结果集保存在指定的键上
返回key的类型
none (key不存在) string (字符串) list (列表) set (集合) zset (有序集) hash (哈希表)
SCAN cursor [MATCH pattern] [COUNT count]
SCAN 命令及其相关的 SSCAN 命令、 HSCAN 命令和 ZSCAN 命令都用于增量地迭代(incrementally iterate)一集元素(a collection of elements):
SCAN 命令用于迭代当前数据库中的数据库键。
SSCAN 命令用于迭代集合键中的元素。
HSCAN 命令用于迭代哈希键中的键值对。
ZSCAN 命令用于迭代有序集合中的元素(包括元素成员和元素分值)。
SCAN 命令是一个基于游标的迭o代器(cursor based iterator): SCAN 命令每次被调用之后, 都会向用户返回一个新的游标, 用户在下次迭代时需要使用这个新游标作为 SCAN 命令的游标参数, 以此来延续之前的迭代过程。当 SCAN 命令的游标参数被设置为 0 时, 服务器将开始一次新的迭代, 而当服务器向用户返回值为 0 的游标时, 表示迭代已结束。
-COUNT 选项 调整一次迭代的数量
-MATCH 选项 scan {SET_NAME} {cursor} {RegularExpression}
如果 key 已经存在并且是一个字符串, APPEND 命令将 value 追加到 key 原来的值的末尾。如果 key 不存在, APPEND 就简单地将给定 key 设为 value ,就像执行SET key value 一样。
计算给定字符串中,被设置为 1 的比特位的数量。
一般情况下,给定的整个字符串都会被进行计数,通过指定额外的 start 或 end 参数,可以让计数只在特定的位上进行。
返回 key 中字符串值的子字符串,字符串的截取范围由 start 和 end 两个偏移量决定(包括 start 和 end 在内)。
对一个或多个保存二进制位的字符串 key 进行位元操作,并将结果保存到 destkey 上。
将 key 中储存的数字值减一。
1.如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 DECR 操作。
2.如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。
3.本操作的值限制在 64 位(bit)有符号数字表示之内。
DECR减去指定数值的版本
对 key 所储存的字符串值,获取指定偏移量上的位(bit)。
1.将给定 key 的值设为 value ,并返回 key 的旧值(old value)。
2.当 key 存在但不是字符串类型时,返回一个错误。
3.与INCR key联用可以做计数器~
将 key 中储存的数字值增一。
为 key 中所储存的值加上浮点数增量 increment 。
返回所有(一个或多个)给定 key 的值。
同时设置一个或多个 key-value 对。
同时设置一个或多个 key-value 对,当且仅当所有给定 key 都不存在。
这个命令和 SETEX 命令相似,但它以毫秒为单位设置 key 的生存时间,而不是像 SETEX命令那样,以秒为单位。
1.将字符串值 value 关联到 key 。
2.如果 key 已经持有其他值, SET 就覆写旧值,无视类型。
3.对于某个原本带有生存时间(TTL)的键来说, 当 SET 命令成功在这个键上执行时, 这个键原有的 TTL 将被清除。
1.将值 value 关联到 key ,并将 key 的生存时间设为 seconds (以秒为单位)。
2如果 key 已经存在, SETEX 命令将覆写旧值。
3.这个命令类似于以下两个命令:
SET key value
EXPIRE key seconds 设置生存时间
1.将 key 的值设为 value ,当且仅当 key 不存在。
2.若给定的 key 已经存在,则 SETNX 不做任何动作。
3.SETNX 是『SET if Not eXists』(如果不存在,则 SET)的简写。
用 value 参数覆写(overwrite)给定 key 所储存的字符串值,从偏移量 offset 开始。
不存在的 key 当作空白字符串处理。
返回 key 所储存的字符串值的长度。
当 key 储存的不是字符串值时,返回一个错误。
最近在倒腾Spring全家桶的应用,在阅读官方文档19.5节的时候无意中发现了了一个被称作hot-swapping的技术,这类在java5代理技术出现之后出现的字节码热交换技术其实也不是新鲜事,但是对于spring这样application动不动启动10来秒的框架如若使用热交换对开发效率无疑有一个质的提升,于是就根据文档踩了下坑。
|
如果作为gradle引入的话要稍微麻烦一点
首先需要加入依赖
运行时加入VM参数
如果想完成静态资源静态替换,则需要在application.properties中加入
完成上述配置之后便可以使用热交换插件了
1.使用调试模式运行Spring boot application,笔者使用的IDEA为图示按钮
2.修改字节码后使用上图左侧make按钮(默认快捷键alt+f9)编译新的项目,由于插件的存在,会自动将output中的代码进行热替换而无需重新启动整个application。
3.模版引擎在make之后也能马上变化(非Spring Loaded特性,无论是否配置Spring Loaded,静态资源替换在make候会自动生效)
1.如果是一般类/方法修改热替换,代理技术会起到很好的作用,但是需要注意我是我们使用的是Ioc容器,如springmvc的注解映射依赖于启动时扫描,假如替换了映射的类,springmvc并不知道这一变化,而会寻找之前的方法,这时候就会抛出找不到方法异常
简单看下示例
此时我们如果把该方法改为
虽然字节码被热替换了,但是框架抛出了异常
对于普通类的修改,spring loaded确实是是一个很好的工具,整个make/替换过程短暂极大提升了开发效率,但对于部分ioc容器,依旧需要重启appliction才能完全看到修改后的代码。
]]> 即使android提供了像素无关密度(dp),但是由于不同设备的dpi,也不能实现不同设备上的一致效果。从初始的角度上是看,dp的初衷并不是让显示的效果在不同屏幕上一致,google 官方也是推荐尽量使用match-parent、weight、加以以dp为单位的边距来进行布局,然而,这对于某些工程来说,也没有达到完美适配的目的。所以邪教的适配方式也层出不穷。
例如:
采用比例兑换的对应分辨率下的sp,产生了大量了values文件,据说优酷客户端就是这样干的。
android借鉴了CSS的精髓,提供了一个百分比布局support库[com.android.support:percent]
,但依赖于替换原有的布局标签,同样,你如果想让一个view支持百分比,就必须在外面嵌套一个百分比布局。
android设备可谓百花齐放,然而并不是所有的设备都和Google Api设备一样,所以适配成了日常开发过程中必须要踩的坑。所谓适配,适配大致分为ROM适配,设备适配、屏幕适配。这里来记录下最近比较火的国产AndroidAutoLayout适配方案。
其实整个实现的代码也比较简单,总的说来就是算的过程。花了大概半个小时看完。
主包也只有三个,分别是:attr存放布局属性,config存放配置实现了初始化读取manifest
、屏幕参数,utils定义了AutoLayoutHelper
辅助布局进行参数重绘,算是这个项目的核心所在、AutoUtils
实现了属性参数换算、ScreenUtils
测量屏幕分辨率。
整理实现思路大致如下:
通过AutoLayoutActivity
的onCreateView
方法获得根布局属性,替换为Auto***Layout
,layout在onMesure
时候调用AutoLayoutHelper的adjustChildren()
方法重新调整布局属性,遍历子控件,运算重写布局间距参数。
|
|
Activity
继承于AutoLayoutActivity
.或者覆写自定义的Activity
中onCreateView(String name, Context context, AttributeSet attrs)
方法
|
对于一些继承自基础布局的的布局控件,可以复写generateLayoutParams(AttributeSet attrs)
、onMeasure(int widthMeasureSpec, int heightMeasureSpec)
方法让其支持布局自动调整
1.ListView
类因为外层布局是ListView
所以要在获得view的时候调用AutoUtils.autoSize(View view)
方法达到自适应的效果。
2.看issue据说Bitmap
有OMM情况,这个我没验证。
3.作者提供了
这两个方法实现了布局“高度与宽度相等”、“宽度与高度相等”在解决一些图片空间确实有一定作用。
4.关于性能
理论上,整个适配过程只是一个修改宽高参数的过程,并未对控件进行重绘,理论上损失的只是计算参数时的性能。
|
Spring Boot使开发独立的,产品级别的基于Spring的应用变得非常简单,你只需”just run”。 我们为Spring平台及第三方库提供开箱即用的设置,这样你就可以有条不紊地开始。多数Spring Boot应用需要很少的Spring配置。
你可以使用Spring Boot创建Java应用,并使用java -jar启动它或采用传统的war部署方式。我们也提供了一个运行”spring脚本”的命令行工具。
我们主要的目标是:
1.为所有的Spring开发提供一个从根本上更快的和广泛使用的入门经验。
2.开箱即用,但你可以通过不采用默认设置来摆脱这种方式。
3.提供一系列大型项目常用的非功能性特征(比如,内嵌服务器,安全,指标,健康检测,外部化配置)。
4.绝对不需要代码生成及XML配置。
参照Gradle构建spring-boot 1.3.0 项目 基础配置
1.类名上加入@SpringBootApplication 注解
2.为了使其能够接受http请求加入@RestController注解
3.写一个方法加入@@RequestMapping(URL_MAPPING)处理请求路由
4.在main函数创建SpringApplication实例,调用其run(String[] args)方法
5.Run
NOTE:由于spring-boot内嵌了tomcat,所以无需web容器也可以启动web application
@RestController。这被称为一个构造型(stereotype)注解。它为阅读代码的人们提供建议。对于Spring,该类扮演了一个特殊角色。在本示例中,我们的类是一个web @Controller,所以当处理进来的web请求时,Spring会询问它。
@RequestMapping注解提供路由信息。它告诉Spring任何来自”/“路径的HTTP请求都应该被映射到home方法。@RestController注解告诉Spring以字符串的形式渲染结果,并直接返回给调用者。
Note:@RestController和@RequestMapping注解是Spring MVC注解(它们不是Spring Boot的特定部分)。具体查看Spring参考文档的MVC章节。
@EnableAutoConfiguration 这个注解告诉Spring Boot根据添加的jar依赖猜测你想如何配置Spring。由于spring-boot-starter-web添加了Tomcat和Spring MVC,所以auto-configuration将假定你正在开发一个web应用并相应地对Spring进行设置。
@ConfigurationSpring Boot提倡基于Java的配置。尽管你可以使用一个XML源来调用SpringApplication.run(),我们通常建议你使用@Configuration类作为主要源。一般定义main方法的类也是主要@Configuration的一个很好候选。
Note:很多使用XML配置的Spring配置示例已经被发布到网络上。你应该总是尽可能的使用基于Java的配置。搜索查看enable*注解就是一个好的开端。
@SpringBootApplication很多Spring Boot开发者总是使用@Configuration,@EnableAutoConfiguration和@ComponentScan注解他们的main类。由于这些注解被如此频繁地一块使用(特别是你遵循以上最佳实践时),Spring Boot提供一个方便的@SpringBootApplication选择。
该@SpringBootApplication注解等价于以默认属性使用@Configuration,@EnableAutoConfiguration和@ComponentScan。
|
Hexo是一款基于node.js的博客框架,使用MarkDown语法进行内容编辑,与传统的wordpress等动态网站相比,博客的渲染生成均在本地完成,所生成的静态页面可以部署到GithubPages等网站,无需数据库,甚至主机主机支持。目前,hexo在github已拥有7000多start,并拥有许多实用插件以及主题元素,足以见得其受欢迎程度,本来网上已有足够多的教学帖,但大多使用的版本较老或者过为冗余,本文秉承快速入门的初衷,记录了下我在安装部署hexo的经过以及踩过的那些坑。
note:很多童鞋因为SSHkey的关系弄了很久没有成功便放弃了,本文介绍的方法是脱离SSHkey利用https的方式的部署,如需ssh部署的请自行参阅官方文档
Reference:[项目地址] [官方文档]
①登录github帐号,新建一个版本仓库。仓库名字为USER_NAME.github.com 这个仓库名称既为你的pages访问链接->PAGES_URL。
②我选择了https的方式进行项目部署,每次部署需要输入github帐号密码进行认证,当然你也可以选择ssh的方式,已经有很多人写过了类似的教程,如若需要请自行查阅,我就不再赘述了。
进入主页,copy到Http的Clone地址CLONE_URL(即后缀为.git的地址)
准备完成。
1)git
根据系统版本到git下载页下载并安装.
2)nodejs
根据系统版本到nodejs下载页下载并安装.
3)配置环境变量,将git nodejs npm配入环境变量。配置成功后确认能执行git node npm指令
3)hexo 包
①安装hexo
②初始化hexo路径
③早期的hexo包是全家桶,我找了很多之前的教学攻略都是只安装了一个hexo导致组件不全无法预览、部署,查阅了官方的文档发现了解决方案
consle得到如下输出即为成功
④生成页面
note: hexo 启动的时候如果修改了markdown文件会自动生成,也就是说静态页面在启动服务器之后保存修改的文件内容会自动生成,不需要重复④步骤,直接刷新在浏览器中刷新即可查看效果,如果效果不对很有可能是markdown语法错误,这时候查看下console的输出信息。
⑤ 访问http://localhost:4000/ 你就发现新的大陆了。
|
执行之后则会在/HEXO_HOME/source/_posts/POST_NAME.md生成一个markdown文件。
打开后已经具有了一个基础模版,在这里加入一个hello wold。
启动服务器,新建的post便出现了
①打开/HEXO_HOME/_config.yml
在中间找到 deploy内容修改并保存
|
②部署
输入自己的github帐号密码等待push成功即可
使用浏览器中访问http://USER_NAME.github.io/ (既PAGES_URL) 整个部署过程就完成了,是不是简单到没朋友?
Markdown 是一种轻量级标记语言,语法也十分简单易学。
关于Markdown的语法学习推荐一个我无意中发现的网站Cmd Markdown,左边代码右边效果同步预览上手很快!
坑就填到这里,enjoy!