Memcached 简洁而强大。它的简洁设计便于快速开发,减轻开发难度,解决了大数据量缓存的很多问题。它的 API 兼容大部分流行的开发语言。本质上,它是一个简洁的 key-value 存储系统。
Memcached 特性
memcached 作为高速运行的分布式缓存服务器,具有以下的特点。
协议简单
基于 libevent 的事件处理
内置内存存储方式
memcached 不互相通信的分布式
Memcached 命令
可以通过 telnet 命令并指定主机 ip 和端口来连接 Memcached 服务。
1 2 3 4 5 6 7 8 9 10 11 12 13
telnet 127.0.0.111211
Trying 127.0.0.1... Connected to 127.0.0.1. Escape character is '^]'. set foo 0 0 3 保存命令 bar 数据 STORED 结果 get foo 取得命令 VALUE foo 0 3 数据 bar 数据 END 结束行 quit 退出
Java 连接 Memcached
使用 Java 程序连接 Memcached,需要在你的 classpath 中添加 Memcached jar 包。
<!-- Add an EnvEntry only valid for this webapp --> <Newid="gargle"class="org.eclipse.jetty.plus.jndi.EnvEntry"> <Arg>gargle</Arg> <Argtype="java.lang.Double">100</Arg> <Argtype="boolean">true</Arg> </New>
<!-- Add an override for a global EnvEntry --> <Newid="wiggle"class="org.eclipse.jetty.plus.jndi.EnvEntry"> <Arg>wiggle</Arg> <Argtype="java.lang.Double">55.0</Arg> <Argtype="boolean">true</Arg> </New>
HandlerCollection 其实维护了一个 Handler 数组。这是为了同时支持多个 Web 应用,如果每个 Web 应用有一个 Handler 入口,那么多个 Web 应用的 Handler 就成了一个数组,比如 Server 中就有一个 HandlerCollection,Server 会根据用户请求的 URL 从数组中选取相应的 Handler 来处理,就是选择特定的 Web 应用来处理请求。
将 I/O 事件检测和业务处理这两种工作分开的思路也有缺点:当 Selector 检测读就绪事件时,数据已经被拷贝到内核中的缓存了,同时 CPU 的缓存中也有这些数据了,我们知道 CPU 本身的缓存比内存快多了,这时当应用程序去读取这些数据时,如果用另一个线程去读,很有可能这个读线程使用另一个 CPU 核,而不是之前那个检测数据就绪的 CPU 核,这样 CPU 缓存中的数据就用不上了,并且线程切换也需要开销。
因此 Jetty 的 Connector 做了一个大胆尝试,那就是把 I/O 事件的生产和消费放到同一个线程来处理,如果这两个任务由同一个线程来执行,如果执行过程中线程不阻塞,操作系统会用同一个 CPU 核来执行这两个任务,这样就能利用 CPU 缓存了。
ProduceExecuteConsume:任务生产者开启新线程来运行任务,这是典型的 I/O 事件侦测和处理用不同的线程来处理,缺点是不能利用 CPU 缓存,并且线程切换成本高。同样我们通过一张图来理解,图中的棕色表示线程切换。
ExecuteProduceConsume:任务生产者自己运行任务,但是该策略可能会新建一个新线程以继续生产和执行任务。这种策略也被称为“吃掉你杀的猎物”,它来自狩猎伦理,认为一个人不应该杀死他不吃掉的东西,对应线程来说,不应该生成自己不打算运行的任务。它的优点是能利用 CPU 缓存,但是潜在的问题是如果处理 I/O 事件的业务代码执行时间过长,会导致线程大量阻塞和线程饥饿。
// without annotation, we'd get "theName", but we want "name": @JsonProperty("name") public String getTheName() { return _name; }
// note: it is enough to add annotation on just getter OR setter; // so we can omit it here publicvoidsetTheName(String n) { _name = n; } }
@JsonIgnoreProperties 和 @JsonIgnore
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
// means that if we see "foo" or "bar" in JSON, they will be quietly skipped // regardless of whether POJO has such properties @JsonIgnoreProperties({ "foo", "bar" }) publicclassMyBean { // will not be written as JSON; nor assigned from JSON: @JsonIgnore public String internal;
// no annotation, public field is read/written normally public String external;
@JsonIgnore publicvoidsetCode(int c) { _code = c; }
// note: will also be ignored because setter has annotation! publicintgetCode() { return _code; } }
//不强制要求注册类(注册行为无法保证多个 JVM 内同一个类的注册编号相同;而且业务系统中大量的 Class 也难以一一注册) kryo.setRegistrationRequired(false); //默认值就是 false,添加此行的目的是为了提醒维护者,不要改变这个配置
//Fix the NPE bug when deserializing Collections. ((DefaultInstantiatorStrategy) kryo.getInstantiatorStrategy()) .setFallbackInstantiatorStrategy(newStdInstantiatorStrategy());
@java.lang.Override publicbooleanequals(final java.lang.Object o) { if (o == this) returntrue; if (o == null) returnfalse; if (o.getClass() != this.getClass()) returnfalse; if (!super.equals(o)) returnfalse; finalPersonother= (Person)o; if (this.name == null ? other.name != null : !this.name.equals(other.name)) returnfalse; if (this.gender == null ? other.gender != null : !this.gender.equals(other.gender)) returnfalse; if (this.ssn == null ? other.ssn != null : !this.ssn.equals(other.ssn)) returnfalse; returntrue; }
@java.lang.Override publicinthashCode() { finalintPRIME=31; intresult=1; result = result * PRIME + super.hashCode(); result = result * PRIME + (this.name == null ? 0 : this.name.hashCode()); result = result * PRIME + (this.gender == null ? 0 : this.gender.hashCode()); result = result * PRIME + (this.ssn == null ? 0 : this.ssn.hashCode()); return result; } }
Exception in thread "main" com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `io.github.dunwu.javatech.bean.lombok.BuilderDemo01` (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator) at [Source: (String)"{"name":"demo01"}"; line: 1, column: 2] at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:63) at com.fasterxml.jackson.databind.DeserializationContext.reportInputMismatch(DeserializationContext.java:1432) at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1062) at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1297) at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:326) at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:159) at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4218) at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3214) at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3182) at io.github.dunwu.javatech.bean.lombok.BuilderDemo01.main(BuilderDemo01.java:22)