变通实现微服务的per request以提高IO效率(二)
*: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效率(二)的更多相关文章
- 变通实现微服务的per request以提高IO效率(三)
*:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: 0 !important; } /* ...
- 变通实现微服务的per request以提高IO效率
*:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: 0 !important; } /* ...
- Spring cloud微服务安全实战-4-9Zuul网关安全开发(二)
把在微服务里面写的安全的相关逻辑挪到网关里面来.这样把安全逻辑和业务逻辑解耦开.那么这些问题就都解决了. 先来看下之前的安全的代码,首先在之类做了认证,认证服务器去认证,拿这个token去换用户信息. ...
- 认证鉴权与API权限控制在微服务架构中的设计与实现(四)
引言: 本文系<认证鉴权与API权限控制在微服务架构中的设计与实现>系列的完结篇,前面三篇已经将认证鉴权与API权限控制的流程和主要细节讲解完.本文比较长,对这个系列进行收尾,主要内容包括 ...
- 阶段5 3.微服务项目【学成在线】_day17 用户认证 Zuul_16-网关-过虑器
4.5 过虑器 Zuul的核心就是过虑器,通过过虑器实现请求过虑,身份校验等. 4.5.1 ZuulFilter 自定义过虑器需要继承 ZuulFilter,ZuulFilter是一个抽象类,需要覆盖 ...
- 与IBM的Lin Sun关于Istio 1.0和微服务的问答
北京时间 7 月 31 日,Istio 正式发布了 1.0 版本,并表示已经可用于生产环境.该版本的主要新特性包括跨集群 mesh 支持.细粒度流量控制以及在一个 mesh 中增量推出 mutual ...
- springcloud+gateway微服务整合swagger
单一的微服务集成swagger: maven: <dependency> <groupId>io.springfox</groupId> <artifactI ...
- Kubernetes才是微服务和DevOps的桥梁
一.从企业上云的三大架构看容器平台的三种视角 一切都从企业上云的三大架构开始. 如图所示,企业上的三大架构为IT架构,应用架构和数据架构,在不同的公司,不同的人,不同的角色,关注的重点不同. 对于大部 ...
- Spring cloud微服务安全实战_汇总
Spring cloud微服务安全实战 https://coding.imooc.com/class/chapter/379.html#Anchor Spring Cloud微服务安全实战-1-1 课 ...
随机推荐
- MVC validate.js下使用 ajaxSubmit
首页定义验证实体 using System.ComponentModel.DataAnnotations; using System.Web.Mvc; namespace MvcApplication ...
- SQL 截取字符串
以下代码演示的是除去字符串后四位 ,) 下面是SUBSTRING的解释 SUBSTRING ( expression ,start , length ) 第一个参数是字符串,第二个是起始位置,第三个是 ...
- 自己实现简单的string类
1.前言 最近看了下<C++Primer>,觉得受益匪浅.不过纸上得来终觉浅,觉知此事须躬行.今天看了类类型,书中简单实现了String类,自己以前也学过C++,不过说来惭愧,以前都是用C ...
- ((uchar*)(Img1->imageData + Img1->widthStep*pt.y))[pt.x] 的 具体含义
widthstep是指图像每行所占的字节数. 主要要和width区别: width是表示图像的每行像素数,widthStep指表示存储一行像素需要的字节数. 在OpenCV里边,widthStep必须 ...
- 基于android studio编译工具下的android开发之IBeacon 例子
想直接看主要内容的请调到红字下面. 之所以会接触到android下的IBeacon,是因为我自己导师给的任务.一个网址http://estimote.com/和一句话:看看这个网站,然后试下在安卓手机 ...
- SharePoint 2013 托管导航 无法被开启的解决办法
在阅读了园子中霖雨的一片博文<SharePoint 2013 托管导航及相关配置>之后,非常想尝试一下SharePoint 2013 中的这个新功能,但是我的网站集包括样式是从2010升级 ...
- Linux:Ubuntu16.04下创建Wifi热点
Linux:Ubuntu16.04下创建Wifi热点 说明: 1.Ubuntu16.04里面可以直接创建热点,而不用像以前的版本,还要其他辅助工具. 2.本篇文章转载自编程人生 具体步骤如下: 1. ...
- 重温JSP学习笔记--JSTL标签库
以前写jsp的时候对jstl标签库是有些抵触的,因为我觉得嵌入java代码的方式几乎无往不利,没有必要使用标签库,不过这次复习还是好好地学习了一下,发现这个还是很有用处的,用得好能省不少事,JSTL是 ...
- [AngularJS] AngularJS系列(5) 中级篇之动画
目录 CSS定义 JS定义 ng动画实际帮我们在状态切换的时候 添加特定的样式 从而实现动画效果. 一般我们会通过C3来实现具体的动画. CSS定义 ng-if 图(实际上,图并不能展现出什么): H ...
- 自己封装的一个JS分享组件
因为工作的需求之前也封装过一个JS分享插件,集成了我们公司常用的几个分享平台. 但是总感觉之前的结构上很不理想,样式,行为揉成一起,心里想的做的完美,实际上总是很多的偏差,所以这次我对其进行了改版. ...