*:first-child {
margin-top: 0 !important;
}

body>*:last-child {
margin-bottom: 0 !important;
}

/* BLOCKS
=============================================================================*/

p, blockquote, ul, ol, dl, table, pre {
margin: 15px 0;
}

/* HEADERS
=============================================================================*/

h1, h2, h3, h4, h5, h6 {
margin: 20px 0 10px;
padding: 0;
font-weight: bold;
-webkit-font-smoothing: antialiased;
}

h1 tt, h1 code, h2 tt, h2 code, h3 tt, h3 code, h4 tt, h4 code, h5 tt, h5 code, h6 tt, h6 code {
font-size: inherit;
}

h1 {
font-size: 28px;
color: #000;
}

h2 {
font-size: 24px;
border-bottom: 1px solid #ccc;
color: #000;
}

h3 {
font-size: 18px;
}

h4 {
font-size: 16px;
}

h5 {
font-size: 14px;
}

h6 {
color: #777;
font-size: 14px;
}

body>h2:first-child, body>h1:first-child, body>h1:first-child+h2, body>h3:first-child, body>h4:first-child, body>h5:first-child, body>h6:first-child {
margin-top: 0;
padding-top: 0;
}

a:first-child h1, a:first-child h2, a:first-child h3, a:first-child h4, a:first-child h5, a:first-child h6 {
margin-top: 0;
padding-top: 0;
}

h1+p, h2+p, h3+p, h4+p, h5+p, h6+p {
margin-top: 10px;
}

/* LINKS
=============================================================================*/

a {
color: #4183C4;
text-decoration: none;
}

a:hover {
text-decoration: underline;
}

/* LISTS
=============================================================================*/

ul, ol {
padding-left: 30px;
}

ul li > :first-child,
ol li > :first-child,
ul li ul:first-of-type,
ol li ol:first-of-type,
ul li ol:first-of-type,
ol li ul:first-of-type {
margin-top: 0px;
}

ul ul, ul ol, ol ol, ol ul {
margin-bottom: 0;
}

dl {
padding: 0;
}

dl dt {
font-size: 14px;
font-weight: bold;
font-style: italic;
padding: 0;
margin: 15px 0 5px;
}

dl dt:first-child {
padding: 0;
}

dl dt>:first-child {
margin-top: 0px;
}

dl dt>:last-child {
margin-bottom: 0px;
}

dl dd {
margin: 0 0 15px;
padding: 0 15px;
}

dl dd>:first-child {
margin-top: 0px;
}

dl dd>:last-child {
margin-bottom: 0px;
}

/* CODE
=============================================================================*/

pre, code, tt {
font-size: 12px;
font-family: Consolas, "Liberation Mono", Courier, monospace;
}

code, tt {
margin: 0 0px;
padding: 0px 0px;
white-space: nowrap;
border: 1px solid #eaeaea;
background-color: #f8f8f8;
border-radius: 3px;
}

pre>code {
margin: 0;
padding: 0;
white-space: pre;
border: none;
background: transparent;
}

pre {
background-color: #f8f8f8;
border: 1px solid #ccc;
font-size: 13px;
line-height: 19px;
overflow: auto;
padding: 6px 10px;
border-radius: 3px;
}

pre code, pre tt {
background-color: transparent;
border: none;
}

kbd {
-moz-border-bottom-colors: none;
-moz-border-left-colors: none;
-moz-border-right-colors: none;
-moz-border-top-colors: none;
background-color: #DDDDDD;
background-image: linear-gradient(#F1F1F1, #DDDDDD);
background-repeat: repeat-x;
border-color: #DDDDDD #CCCCCC #CCCCCC #DDDDDD;
border-image: none;
border-radius: 2px 2px 2px 2px;
border-style: solid;
border-width: 1px;
font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
line-height: 10px;
padding: 1px 4px;
}

/* QUOTES
=============================================================================*/

blockquote {
border-left: 4px solid #DDD;
padding: 0 15px;
color: #777;
}

blockquote>:first-child {
margin-top: 0px;
}

blockquote>:last-child {
margin-bottom: 0px;
}

/* HORIZONTAL RULES
=============================================================================*/

hr {
clear: both;
margin: 15px 0;
height: 0px;
overflow: hidden;
border: none;
background: transparent;
border-bottom: 4px solid #ddd;
padding: 0;
}

/* IMAGES
=============================================================================*/

img {
max-width: 100%
}
-->

效率

变通实现微服务的per request以提高IO效率中提到的同一请求过程中对于同一个方法的多次读取只实际调用一次,其余的读取均从第一次读取的结果缓存中获取,以提高读取的效率。实现方案是引入了Context对象,可以理解成上下文的一个环境变量,业务方法在获取数据时先从Context中取,如果取不到从数据库中加载并将结果写入Context中,而Context是通过ThreadLocal来存储。但实现有点复杂需要寻找优化方案。

Context方案的缺点

  • 复杂度增强,因为需要引入Context的特殊概念
  • 复用性比较低。需要在Context中为每种数据维护一个属性。比如存储用户,产品,价格等。当然也可以利用Map,这样会导致复杂度会更加强,在缓存清除的时候也会随着数据存储结构的复杂度提升而提升:之前是为每种数据定义一个ThreadLocal。
private ThreadLocal<CiaUserInfo> ciaUserInfoThreadLocal=new ThreadLocal<>();
  • 需要在业务方法中嵌入操作Context的方法,具备比较强的代码侵入性。下面代码中的getCiauserInfoFromCache就嵌入在业务代码中。
public CiaUserInfo getTokenInfo(String token) throws Exception {

        CiaUserInfo result = this.productContext.getCiaUserInfoFromCache(token);
if(null!=result){
return result;
}
else {
result=new CiaUserInfo();
} //...get user from db
this.productContext.setCiaUserInfoToCache(result);
return result;
}
  • 需要自己实现缓存KEY生成器,如果是多参数,复杂对象会导致编写KEY的难度成倍提升

基于Spring Cache来实现

创建Context的目的无非就是将数据存储在ThreadLocal中充当请求级别的缓存,如果缓存是基于Spring Cache,那么上面的缺点就会不攻自破。

我找了下并没有找到基于ThreadLocal实现的缓存,大家如果有找到的可以发给我。

由于我们只关心存储(过期策略本文暂时还是延用上一篇的利用注解拦截形式去手工释放),所以实现难度并不大,因为我们完全可以参考Spring Cache的默认提供的内存级别的缓存ConcurrentMapCacheManager,整体效果如下图所示。

创建ThreadLocalCacheManager

只需要实现CacheManager这个接口即可,所有的缓存都放在一个Map中管理。

  • getCache,这个方法非常重要,用来获取缓存对象,如果容器中不存在则自动创建并更新到容器中。

这个方法中展现了并发情况下的操作,面试时这种题经常会被问到,看看Spring Cache的实现还是很有帮助的。

  • 构建函数支持无参也支持可以传入缓存名称的有参函数。有参的作用时提前初始化缓存,无参就只能等到真正调用缓存时才会创建。
  • getCacheNames,返回容器中所有缓存的名称,这点在释放整个缓存容器的缓存时非常重要。
public class ThreadLocalCacheManager implements CacheManager {

    private final ConcurrentMap<String, Cache> cacheMap = new ConcurrentHashMap<String, Cache>(16);

    public ThreadLocalCacheManager(){

    }

    public ThreadLocalCacheManager(String... cacheNames) {
setCacheNames(Arrays.asList(cacheNames));
} protected Cache createConcurrentMapCache(String name) {
return new ThreadLocalCache(name);
} public void setCacheNames(Collection<String> cacheNames) {
if (cacheNames != null) {
for (String name : cacheNames) {
this.cacheMap.put(name, createConcurrentMapCache(name));
}
}
} @Override
public Cache getCache(String name) {
Cache cache = this.cacheMap.get(name);
if (cache == null) {
synchronized (this.cacheMap) {
cache = this.cacheMap.get(name);
if (cache == null) {
cache = createConcurrentMapCache(name);
this.cacheMap.put(name, cache);
}
}
}
return cache;
} @Override
public Collection<String> getCacheNames() {
return Collections.unmodifiableSet(this.cacheMap.keySet());
}
}

创建ThreadLocalCache

这个类是真正的缓存实现,继承AbstractValueAdaptingCache这个抽象类即可。相比内存缓存实现主要的区别就是存储的介质由ConcurrentMap变更为ThreadLocal,目的是每个线程单独一份缓存。

  • init,这是个协助函数,主要是将一个Map对象存入ThreadLocal中
  • lookup,根据key从缓存中取得对象,但不是很理解这个方法为什么叫lookup,看着别扭
  • getName,获取当前缓存的名称,一般在设置缓存名称时我的习惯时类名+方法名
  • getNativeCache,返回缓存的具体实现
  • put,就是更新或者插入缓存对象
  • putIfAbsent,put操作时先判断是否存在,如果存在返回缓存中的值,如果不存在就插入
  • evict,移除某个KEY
  • clear,清除缓存中的所有内容
public class ThreadLocalCache extends AbstractValueAdaptingCache {

    private final String name;

    private final ThreadLocal<ConcurrentMap> storeThreaLocal=new ThreadLocal<>();

    private void init(){
if(null==this.storeThreaLocal.get()){
this.storeThreaLocal.set(new ConcurrentHashMap());
}
} public ThreadLocalCache(String name){
this(name,true);
} protected ThreadLocalCache(String name,boolean allowNullValues) {
super(allowNullValues);
this.name=name; this.init(); } @Override
protected Object lookup(Object key) {
return this.storeThreaLocal.get().get(key);
} @Override
public String getName() {
return this.name;
} @Override
public Object getNativeCache() {
return this.storeThreaLocal.get();
} @Override
public void put(Object key, Object value) {
this.storeThreaLocal.get().put(key,value); } @Override
public ValueWrapper putIfAbsent(Object key, Object value) {
Object existing = this.storeThreaLocal.get().putIfAbsent(key, toStoreValue(value));
return toValueWrapper(existing);
} @Override
public void evict(Object key) {
this.storeThreaLocal.get().remove(key);
} @Override
public void clear() {
this.storeThreaLocal.get().clear();
}
}

配置缓存

配置非常简单,启动缓存注解,指定缓存容器。

    <cache:annotation-driven cache-manager="cacheManager"/>

    <bean id="cacheManager" class="core.cache.ThreadLocalCacheManager">

    </bean>

代码中增加Cache注解

增加@Cacheable注解,指定缓存名称以及缓存容器名称即可。相比Context方案就解决了缓存代码侵入性的问题,而且可以利用SpringCache的众多优点,比如缓存条件,缓存KEY的生成规则等等。

    @Cacheable(value = "CiaService.getCiaUserInfo",cacheManager = "cacheManager")
@Override
public CiaUserInfo getCiaUserInfo(String token) {
//...
}

释放缓存

由于有线程池的存在,所以如果不手动清除存储于ThreadLocal中的缓存数据,那么会影响我们最初的需求:请求级缓存。暂时还是通过特殊的注解来完成。通过cacheManager获取所有的缓存,然后依次执行释放操作。

 @Autowired
private CacheManager cacheManager;
@After("pointCut()")
public void after(JoinPoint joinPoint) throws ProductServiceException {
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Method targetMethod = methodSignature.getMethod(); LocalCacheContext localCacheContext= targetMethod.getAnnotation(LocalCacheContext.class);
if(null!=localCacheContext){
Collection<String> cacheNames= this.cacheManager.getCacheNames();
if(null!=cacheNames) {
for(String cacheName :cacheNames) {
this.cacheManager.getCache(cacheName).clear();
}
}
}
}

如何优雅的释放缓存

上面的缓存释放是通过注解来完成的,这个注解只能加在入口函数上,是有一定限制的,如果加错了缓存就有可能在请求的中途被错误的清除。像Web容器就有非常多的方案,比如HandleInterceptor是请求级别的,可以非常方便的在请求前请求后增加一些自定义的功能。由于我这边的微服务是dubbo实现,所以可以在dubbo提供的方案中找一找,也许会有收获。

变通实现微服务的per request以提高IO效率(二)的更多相关文章

  1. 变通实现微服务的per request以提高IO效率(三)

    *:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: 0 !important; } /* ...

  2. 变通实现微服务的per request以提高IO效率

    *:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: 0 !important; } /* ...

  3. Spring cloud微服务安全实战-4-9Zuul网关安全开发(二)

    把在微服务里面写的安全的相关逻辑挪到网关里面来.这样把安全逻辑和业务逻辑解耦开.那么这些问题就都解决了. 先来看下之前的安全的代码,首先在之类做了认证,认证服务器去认证,拿这个token去换用户信息. ...

  4. 认证鉴权与API权限控制在微服务架构中的设计与实现(四)

    引言: 本文系<认证鉴权与API权限控制在微服务架构中的设计与实现>系列的完结篇,前面三篇已经将认证鉴权与API权限控制的流程和主要细节讲解完.本文比较长,对这个系列进行收尾,主要内容包括 ...

  5. 阶段5 3.微服务项目【学成在线】_day17 用户认证 Zuul_16-网关-过虑器

    4.5 过虑器 Zuul的核心就是过虑器,通过过虑器实现请求过虑,身份校验等. 4.5.1 ZuulFilter 自定义过虑器需要继承 ZuulFilter,ZuulFilter是一个抽象类,需要覆盖 ...

  6. 与IBM的Lin Sun关于Istio 1.0和微服务的问答

    北京时间 7 月 31 日,Istio 正式发布了 1.0 版本,并表示已经可用于生产环境.该版本的主要新特性包括跨集群 mesh 支持.细粒度流量控制以及在一个 mesh 中增量推出 mutual ...

  7. springcloud+gateway微服务整合swagger

    单一的微服务集成swagger: maven: <dependency> <groupId>io.springfox</groupId> <artifactI ...

  8. Kubernetes才是微服务和DevOps的桥梁

    一.从企业上云的三大架构看容器平台的三种视角 一切都从企业上云的三大架构开始. 如图所示,企业上的三大架构为IT架构,应用架构和数据架构,在不同的公司,不同的人,不同的角色,关注的重点不同. 对于大部 ...

  9. Spring cloud微服务安全实战_汇总

    Spring cloud微服务安全实战 https://coding.imooc.com/class/chapter/379.html#Anchor Spring Cloud微服务安全实战-1-1 课 ...

随机推荐

  1. CSS3总结 (帅哥)

    第1章CSS3简介 如同人类的的进化一样,CSS3是CSS2的"进化"版本,在CSS2基础上,增强或新增了许多特性, 弥补了CSS2的众多不足之处,使得Web开发变得更为高效和便捷 ...

  2. Resource governor2:Configuration query

    SQL Server Engine 当前使用的configuration,称作 In-memory configuration,使用DMV:sys.dm_resource_governor_XXX查看 ...

  3. JAVA的静态变量、静态方法、静态类

    静态变量和静态方法都属于静态对象,它与非静态对象的差别需要做个说明. (1)Java静态对象和非静态对象有什么区别? 比对如下: 静态对象                                ...

  4. 在浏览器中输入URL按下回车键后发生了什么

    在浏览器中输入URL按下回车键后发生了什么 [1]解析URL[2]DNS查询,解析域名,将域名解析为IP地址[3]ARP广播,根据IP地址来解析MAC地址[4]分别从应用层到传输层.网络层和数据链路层 ...

  5. Android图片处理

    相信做Android开发的小伙伴对于Android图片压缩.裁剪一定有很深的印象,今天我将带领大家一起学习一下这个看着高深莫测的知识,以便再以后的学习.工作中可以帮助到大家. 首先我们看一下这个问题出 ...

  6. JS验证控件jQuery Validate

    jQuery Validate 插件为表单提供了强大的验证功能,让客户端表单验证变得更简单,同时提供了大量的定制选项,满足应用程序各种需求.该插件捆绑了一套有用的验证方法,包括 URL 和电子邮件验证 ...

  7. Util应用程序框架公共操作类(十二):Lambda表达式公共操作类(三)

    今天在开发一个简单查询时,发现我的Lambda操作类的GetValue方法无法正确获取枚举类型值,以至查询结果错误. 我增加了几个单元测试来捕获错误,代码如下. /// <summary> ...

  8. UIScrollView的delaysContentTouches与canCencelContentTouches属性

    UIScrollView有一个BOOL类型的tracking属性,用来返回用户是否已经触及内容并打算开始滚动,我们从这个属性开始探究UIScrollView的工作原理: 当手指触摸到UIScrollV ...

  9. 【jQuery小实例】---2自定义动画

    ---本系列文章所用使用js均可在本博客文件中找到 本节用jQuery完一个简易的动画效果,一个小驴跑跑的效果.和一个类似qq面板效果.大致也分为三步:添加jquery-1.8.3.js文件.这个是不 ...

  10. 整合struts2+hibernate详细配置步骤及注意事项

    刚刚学完这两个框架,就迫不及待的做了一个例子,在整合两个框架的时候,也碰到了一些小问题,下面介绍一下配置的步骤: 1.创建一个自定义的struts2和hibernate的类库 因为之前写例子都是直接将 ...