分布式架构中shiro
分布式架构中shiro
前言:
前段时间在搭建公司游戏框架安全验证的时候,就想到之前web最火的shiro框架,虽然后面实践发现在netty中不太适用,最后自己模仿shiro写了一个缩减版的,但是中间花费两天时间弄出来的shiro可不能白费,这里给大家出个简单的教程说明吧。
shiro的基本介绍这里就不再说了,可以自行翻阅博主之前写的shiro教程,这篇文章主要说明分布式架构下shiro的session共享问题。
一、原理描述
无论分布式、还是集群下,项目都需要获取登录用户的信息,而不可能做的就是让客户在每个系统或者每个模块中反复登录,也不存在让客户端存载用户信息给服务端,这是很常识的问题
而单机模式下,我们用shiro做了登录验证,他的主要方式就是在第一次登陆的时候,把我们设置的用户信息保存在cache(内存)中和自带的ehcahe(缓存管理器)中,然后给客户端一个cookie,在每次客户端访问时获取cookie值,从而得到用户信息。
好了,那么逻辑就清楚了,分布式架构下,要与多系统共享用户信息,其实就是共享shiro保存的cache。
要在多项目中共享,内存是不可能的了,ehcache对分布式支持不太好,或者说根本不支持。那么剩下只能是我么熟悉的mysql,redis,mongdb啥的数据库了。这么一对比,不用我说大家也明白了,最适合的无疑是redis了,速度快,主从啥的。
二、流程描述
查看源码我们可以知道,cacheManager最终会被set到sessionDAO中,所以我们要自己写sessionDAO。有两个类去操作保存的,那么我们只需要重写,实现这两个类,然后在注册的时候声明即可。
1.shiroCache:cache类,可以自己写一个定时消除的MAP存放更好,文章结尾我会给出map的代码。而这里的代码我是放在redis的。
package com.result.shiro.distributed;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import com.result.redis.RedisKey;
import com.result.redis.RedisUtil;
import com.result.tools.KyroUtil;
/**
* @author 作者 huangxinyu
* @version 创建时间:2018年1月8日 下午9:33:23
* cache共享
*/
@SuppressWarnings("unchecked")
public class ShiroCache<K, V> implements Cache<K, V> {
private static final String REDIS_SHIRO_CACHE = RedisKey.CACHEKEY;
private String cacheKey;
private long globExpire = 30;
@SuppressWarnings("rawtypes")
public ShiroCache(String name) {
this.cacheKey = REDIS_SHIRO_CACHE + name + ":";
}
@Override
public V get(K key) throws CacheException {
Object obj = RedisUtil.get(KyroUtil.serialization(getCacheKey(key)));
if(obj==null){
return null;
}
return (V) KyroUtil.deserialization((String)obj);
}
@Override
public V put(K key, V value) throws CacheException {
V old = get(key);
RedisUtil.setex(KyroUtil.serialization(getCacheKey(key)), 18000, KyroUtil.serialization(value));
return old;
}
@Override
public V remove(K key) throws CacheException {
V old = get(key);
RedisUtil.del(KyroUtil.serialization(getCacheKey(key)));
return old;
}
@Override
public void clear() throws CacheException {
for(String key : (Set<String>)keys()){
RedisUtil.del(key);
}
}
@Override
public int size() {
return keys().size();
}
@Override
public Set<K> keys() {
return (Set<K>) RedisUtil.keys(KyroUtil.serialization(getCacheKey("*")));
}
@Override
public Collection<V> values() {
Set<K> set = keys();
List<V> list = new ArrayList<>();
for (K s : set) {
list.add(get(s));
}
return list;
}
private K getCacheKey(Object k) {
return (K) (this.cacheKey + k);
}
}
2.session操作类:这里用来把用户信息存放在redis中共享的。
package com.result.shiro.distributed;
/**
* @author 作者 huangxinyu
* @version 创建时间:2018年1月6日 上午10:12:42
* redis实现共享session
*/
import java.io.Serializable;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.UnknownSessionException;
import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.result.redis.RedisKey;
import com.result.redis.RedisUtil;
import com.result.tools.KyroUtil;
import com.result.tools.SerializationUtil;
public class RedisSessionDao extends EnterpriseCacheSessionDAO {
private static Logger logger = LoggerFactory.getLogger(RedisSessionDao.class);
@Override
public void update(Session session) throws UnknownSessionException {
this.saveSession(session);
}
/**
* 删除session
*/
@Override
public void delete(Session session) {
if (session == null || session.getId() == null) {
logger.error("==========session或sessionI 不存在");
return;
}
RedisUtil.del(KyroUtil.serialization(RedisKey.SESSIONKEY + session.getId()));
}
/**
* 获取存活的sessions
*/
@Override
public Collection<Session> getActiveSessions() {
Set<Session> sessions = new HashSet<>();
Set<String> keys = RedisUtil.keys(KyroUtil.serialization(RedisKey.SESSIONKEY + "*"));
for(String key:keys){
sessions.add((Session)KyroUtil.deserialization((String)RedisUtil.get(key)));
}
return sessions;
}
/**
* 创建session
*/
@Override
protected Serializable doCreate(Session session) {
Serializable sessionId = this.generateSessionId(session);
this.assignSessionId(session, sessionId);
this.saveSession(session);
return sessionId;
}
/**
* 获取session
*/
@Override
protected Session doReadSession(Serializable sessionId) {
if(sessionId == null){
logger.error("==========session id 不存在");
return null;
}
Object obj = RedisUtil.get(KyroUtil.serialization(RedisKey.SESSIONKEY + sessionId));
if(obj==null){
return null;
}
Session s = (Session)KyroUtil.deserialization((String)obj);
return s;
}
/**
* 保存session并存储过期时间
* @param session
* @throws UnknownSessionException
*/
public static void saveSession(String sessionId,Object obj) throws UnknownSessionException{
if (obj == null) {
logger.error("要存入的session为空");
return;
}
//设置过期时间
int expireTime = 1800;
RedisUtil.setex(sessionId,expireTime,SerializationUtil.serializeToString(obj));
}
}
然后还有一个类也是必要的 package com.result.shiro.distributed;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import org.apache.shiro.cache.CacheManager;
/**
* @author 作者 huangxinyu
* @version 创建时间:2018年1月8日 下午9:32:41
* 类说明
*/
public class RedisCacheManager implements CacheManager {
@Override
public <K, V> Cache<K, V> getCache(String name) throws CacheException {
return new ShiroCache<K, V>(name);
}
}
三:辅助类说明
用户信息的session存放在redis中肯定是需要序列化的,然而用json这种可读性太强的东西安全性显得极低,而且长度太大,浪费存储空间和IO。所以需要找其他的序列化工具。
常规的好用的序列化工具有kyro,protobuff,这些是性能极高而且序列化之后长度极小的序列化工具,其中protobuf支持跨语言。不过这些在之后的文章再和大家介绍去了,因为~!!session不支持这两种操作(因为上面两个类中操作的session实际是一个接口)。
那么序列化用的什么,emmmm~一个很原生的东西,测试效率也挺高的,和protobuf差不太多。下面贴出的代码实际就是上面类中kyroUtils中的方法,因为shiro分布式在项目中被废掉了,我也没去改名字了。大家自己看仔细点就可以了。
被注释掉的代码是kyro的序列化工具。
package com.result.tools;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author 作者 huangxinyu
* @version 创建时间:2018年1月6日 下午2:22:14
* Kryo工具类
*/
public class KyroUtil {
private static Logger logger = LoggerFactory.getLogger(KyroUtil.class);
//private static KryoPool pool;
//原本打算使用kyro序列化session,后来发现kyro对session序列化不支持,反序列后得不到value。 这种out序列化测试性能消耗时间更短,但是长度变大4倍意思,待优化
// static{
// KryoFactory factory = new KryoFactory() {
// public Kryo create() {
// Kryo kryo = new Kryo();
// kryo.setReferences(false);
// //把shiroSession的结构注册到Kryo注册器里面,提高序列化/反序列化效率
// kryo.register(Session.class, new JavaSerializer());
// kryo.register(String.class, new JavaSerializer());
// kryo.register(User.class, new JavaSerializer());
// kryo.setInstantiatorStrategy(new StdInstantiatorStrategy());
// return kryo;
// }
// };
// pool = new KryoPool.Builder(factory).build();
// logger.info("KryoPool初始化成功====================================");
// }
/**
* 对象编码
* @param value
* @return
*/
public static String serialization(Object value) {
// String str ="";
// try {
// Kryo kryo = pool.borrow();
// ByteArrayOutputStream baos = new ByteArrayOutputStream();
// Output output = new Output(baos);
// kryo.writeClassAndObject(output, value);
// output.flush();
// output.close();
// byte[] b = baos.toByteArray();
// baos.flush();
// baos.close();
// str = new String(b, "ISO8859-1");
// } catch (IOException e) {
// e.printStackTrace();
// }
// return str;
//
ByteArrayOutputStream bos = null;
ObjectOutputStream oos = null;
try {
bos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(bos);
oos.writeObject(value);
return new String(bos.toByteArray(), "ISO8859-1");
} catch (Exception e) {
throw new RuntimeException("serialize session error", e);
} finally {
try {
oos.close();
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
// return new String(new Base64().encode(b));
}
/**
* 对象解码
* @param <T>
* @param <T>
* @param obj
* @param clazz
* @return
*/
public static Object deserialization(String obj) {
// try {
// Kryo kryo = pool.borrow();
// ByteArrayInputStream bais;
// bais = new ByteArrayInputStream(obj.getBytes("ISO8859-1"));
// //new Base64().decode(obj));
// Input input = new Input(bais);
// return kryo.readClassAndObject(input);
// } catch (UnsupportedEncodingException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
// return null;
ByteArrayInputStream bis = null;
ObjectInputStream ois = null;
try {
bis = new ByteArrayInputStream(obj.getBytes("ISO8859-1"));
ois = new ObjectInputStream(bis);
return ois.readObject();
} catch (Exception e) {
throw new RuntimeException("deserialize session error", e);
} finally {
try {
ois.close();
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
四、注册
好了,该重写的都重写了,那么最后一步就是整合spring的时候我们要告诉spring,我们要用的是我们重写过的sessiondao了。
我这里用的是代码的方式,因为某些原因在写框架的时候不太好用xml去整合。
反正原理都差不多,大家看看就明白了:
package com.business.shiro;
import java.util.LinkedHashMap;
import java.util.Map;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.session.mgt.ExecutorServiceSessionValidationScheduler;
import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.config.MethodInvokingFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import com.result.shiro.distributed.RedisCacheManager;
import com.result.shiro.distributed.RedisSessionDao;
/**
* @author 作者 huangxinyu
* @version 创建时间:2018年1月8日 下午8:29:12
* 类说明
*/
@Configuration
public class ShiroConfiguration {
private static Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
@Bean(name = "cacheShiroManager")
public CacheManager getCacheManage() {
return new RedisCacheManager();
}
@Bean(name = "lifecycleBeanPostProcessor")
public LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
@Bean(name = "sessionValidationScheduler")
public ExecutorServiceSessionValidationScheduler getExecutorServiceSessionValidationScheduler() {
ExecutorServiceSessionValidationScheduler scheduler = new ExecutorServiceSessionValidationScheduler();
scheduler.setInterval(900000);
return scheduler;
}
@Bean(name = "hashedCredentialsMatcher")
public HashedCredentialsMatcher getHashedCredentialsMatcher() {
HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
credentialsMatcher.setHashAlgorithmName("MD5");
credentialsMatcher.setHashIterations(1);
credentialsMatcher.setStoredCredentialsHexEncoded(true);
return credentialsMatcher;
}
@Bean(name = "sessionIdCookie")
public SimpleCookie getSessionIdCookie() {
SimpleCookie cookie = new SimpleCookie("sid");
cookie.setHttpOnly(true);
cookie.setMaxAge(-1);
return cookie;
}
@Bean(name = "rememberMeCookie")
public SimpleCookie getRememberMeCookie() {
SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
simpleCookie.setHttpOnly(true);
simpleCookie.setMaxAge(2592000);
return simpleCookie;
}
@Bean
public CookieRememberMeManager getRememberManager(){
CookieRememberMeManager meManager = new CookieRememberMeManager();
meManager.setCipherKey(Base64.decode("4AvVhmFLUs0KTA3Kprsdag=="));
meManager.setCookie(getRememberMeCookie());
return meManager;
}
@Bean(name = "sessionManager")
public DefaultWebSessionManager getSessionManage() {
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
sessionManager.setGlobalSessionTimeout(1800000);
sessionManager.setSessionValidationScheduler(getExecutorServiceSessionValidationScheduler());
sessionManager.setSessionValidationSchedulerEnabled(true);
sessionManager.setDeleteInvalidSessions(true);
sessionManager.setSessionIdCookieEnabled(true);
sessionManager.setSessionIdCookie(getSessionIdCookie());
RedisSessionDao cacheSessionDAO = new RedisSessionDao();
cacheSessionDAO.setCacheManager(getCacheManage());
sessionManager.setSessionDAO(cacheSessionDAO);
// -----可以添加session 创建、删除的监听器
return sessionManager;
}
@Bean(name = "myRealm")
public AuthorizingRealm getShiroRealm() {
MyRealm realm = new MyRealm();
// realm.setName("shiro_auth_cache");
// realm.setAuthenticationCache(getCacheManage().getCache(realm.getName()));
// realm.setAuthenticationTokenClass(UserAuthenticationToken.class);
return realm;
}
@Bean(name = "securityManager")
public DefaultWebSecurityManager getSecurityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setCacheManager(getCacheManage());
securityManager.setSessionManager(getSessionManage());
securityManager.setRememberMeManager(getRememberManager());
securityManager.setRealm(getShiroRealm());
return securityManager;
}
@Bean
public MethodInvokingFactoryBean getMethodInvokingFactoryBean(){
MethodInvokingFactoryBean factoryBean = new MethodInvokingFactoryBean();
factoryBean.setStaticMethod("org.apache.shiro.SecurityUtils.setSecurityManager");
factoryBean.setArguments(new Object[]{getSecurityManager()});
return factoryBean;
}
@Bean
@DependsOn("lifecycleBeanPostProcessor")
public DefaultAdvisorAutoProxyCreator getAutoProxyCreator(){
DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();
creator.setProxyTargetClass(true);
return creator;
}
@Bean
public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor(){
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(getSecurityManager());
return advisor;
}
/**
* @return
*/
@Bean(name = "shiroFilter")
public ShiroFilterFactoryBean getShiroFilterFactoryBean(){
ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
factoryBean.setSecurityManager(getSecurityManager());
factoryBean.setLoginUrl("/toLogin");
factoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return factoryBean;
}
}
优化:伪定时消除map,最好配合quartz清楚,不然内存中MAP如果不访问就不消除,容易累计。
package com.result.security;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import com.result.NettyGoConstant;
/**
* @author 作者 huangxinyu
* @version 创建时间:2018年1月29日 上午10:31:50 类说明
*/
public class ExpiryMap<K, V> extends HashMap<K, V> {
private static final long serialVersionUID = 1L;
/**
* default expiry time 2m
*/
private long EXPIRY = NettyGoConstant.LOGINSESSIONTIMEOUT;
private HashMap<K, Long> expiryMap = new HashMap<>();
public ExpiryMap() {
super();
}
public ExpiryMap(long defaultExpiryTime) {
this(1 << 4, defaultExpiryTime);
}
public ExpiryMap(int initialCapacity, long defaultExpiryTime) {
super(initialCapacity);
this.EXPIRY = defaultExpiryTime;
}
public V put(K key, V value) {
expiryMap.put(key, System.currentTimeMillis() + EXPIRY);
return super.put(key, value);
}
public boolean containsKey(Object key) {
return !checkExpiry(key, true) && super.containsKey(key);
}
/**
* @param key
* @param value
* @param expiryTime
* 键值对有效期 毫秒
* @return
*/
public V put(K key, V value, long expiryTime) {
expiryMap.put(key, System.currentTimeMillis() + expiryTime);
return super.put(key, value);
}
public int size() {
return entrySet().size();
}
public boolean isEmpty() {
return entrySet().size() == 0;
}
public boolean containsValue(Object value) {
if (value == null)
return Boolean.FALSE;
Set<java.util.Map.Entry<K, V>> set = super.entrySet();
Iterator<java.util.Map.Entry<K, V>> iterator = set.iterator();
while (iterator.hasNext()) {
java.util.Map.Entry<K, V> entry = iterator.next();
if (value.equals(entry.getValue())) {
if (checkExpiry(entry.getKey(), false)) {
iterator.remove();
return Boolean.FALSE;
} else
return Boolean.TRUE;
}
}
return Boolean.FALSE;
}
public Collection<V> values() {
Collection<V> values = super.values();
if (values == null || values.size() < 1)
return values;
Iterator<V> iterator = values.iterator();
while (iterator.hasNext()) {
V next = iterator.next();
if (!containsValue(next))
iterator.remove();
}
return values;
}
public V get(Object key) {
if (key == null)
return null;
if (checkExpiry(key, true))
return null;
return super.get(key);
}
/**
*
* @Description: 是否过期
* @param key
* @return null:不存在或key为null -1:过期 存在且没过期返回value 因为过期的不是实时删除,所以稍微有点作用
*/
public Object isInvalid(Object key) {
if (key == null)
return null;
if (!expiryMap.containsKey(key)) {
return null;
}
long expiryTime = expiryMap.get(key);
boolean flag = System.currentTimeMillis() > expiryTime;
if (flag) {
super.remove(key);
expiryMap.remove(key);
return -1;
}
return super.get(key);
}
public void putAll(Map<? extends K, ? extends V> m) {
for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
expiryMap.put(e.getKey(), System.currentTimeMillis() + EXPIRY);
super.putAll(m);
}
public Set<Map.Entry<K, V>> entrySet() {
Set<java.util.Map.Entry<K, V>> set = super.entrySet();
Iterator<java.util.Map.Entry<K, V>> iterator = set.iterator();
while (iterator.hasNext()) {
java.util.Map.Entry<K, V> entry = iterator.next();
if (checkExpiry(entry.getKey(), false))
iterator.remove();
}
return set;
}
/**
*
* @Description: 是否过期
* @author: qd-ankang
* @date: 2016-11-24 下午4:05:02
* @param expiryTime
* true 过期
* @param isRemoveSuper
* true super删除
* @return
*/
private boolean checkExpiry(Object key, boolean isRemoveSuper) {
if (!expiryMap.containsKey(key)) {
return Boolean.FALSE;
}
long expiryTime = expiryMap.get(key);
boolean flag = System.currentTimeMillis() > expiryTime;
if (flag) {
if (isRemoveSuper)
super.remove(key);
expiryMap.remove(key);
}
return flag;
}
/**
* 删除
* @param key
*/
public void del(Object key){
super.remove(key);
expiryMap.remove(key);
}
public static void main(String[] args) throws InterruptedException {
ExpiryMap<String, String> map = new ExpiryMap<>(10);
map.put("test", "ankang");
map.put("test1", "ankang");
map.put("test2", "ankang", 3000);
System.out.println("test1" + map.get("test"));
Thread.sleep(1000);
System.out.println("isInvalid:" + map.isInvalid("test"));
System.out.println("size:" + map.size());
System.out.println("size:" + ((HashMap<String, String>) map).size());
for (Map.Entry<String, String> m : map.entrySet()) {
System.out.println("isInvalid:" + map.isInvalid(m.getKey()));
map.containsKey(m.getKey());
System.out.println("key:" + m.getKey() + " value:" + m.getValue());
}
System.out.println("test1" + map.get("test"));
}
/**
* 是否超过过期的一半时间
* @param key
* @return
*/
public boolean isHalfExpiryTime(Object key ){
if (!expiryMap.containsKey(key)) {
return false;
}
long expiryTime = expiryMap.get(key);
boolean flag = System.currentTimeMillis()-(expiryTime-NettyGoConstant.LOGINSESSIONTIMEOUT)>=NettyGoConstant.LOGINSESSIONTIMEOUT/2;
return flag;
}
}
更多链接: http://www.droptb.com/article/detail/58164a980bd64e13b4a7c679bfa31435 欢迎点击参观博主的网站
分布式架构中shiro的更多相关文章
- shiro权限控制(二):分布式架构中shiro的实现
前言:前段时间在搭建公司游戏框架安全验证的时候,就想到之前web最火的shiro框架,虽然后面实践发现在netty中不太适用,最后自己模仿shiro写了一个缩减版的,但是中间花费两天时间弄出来的shi ...
- Zookeeper在分布式架构中的应用
Zookeeper 是一个高性能.高可靠的分布式协调系统,是 Google Chubby 的一个开源实现.Zookeeper 能够为分布式应用提供一致性服务,提供的功能包括:配置维护.域名服务.分布式 ...
- 分布式架构中一致性解决方案——Zookeeper集群搭建
当我们的项目在不知不觉中做大了之后,各种问题就出来了,真jb头疼,比如性能,业务系统的并行计算的一致性协调问题,比如分布式架构的事务问题, 我们需要多台机器共同commit事务,经典的案例当然是银行转 ...
- [转]Todd.log - a place to keep my thoughts on programming 分布式架构中的幂等性
Todd.log - a place to keep my thoughts on programming 理解HTTP幂等性 基于HTTP协议的Web API是时下最为流行的一种分布式服务提供方式. ...
- Zookeeper详细使用解析!分布式架构中的协调服务框架最佳选型实践
Zookeeper概念 Zookeeper是分布式协调服务,用于管理大型主机,在分布式环境中协调和管理服务是很复杂的过程,Zookeeper通过简单的架构和API解决了这个问题 Zookeeper实现 ...
- .net 分布式架构之分布式缓存中间件
开源git地址: http://git.oschina.net/chejiangyi/XXF.BaseService.DistributedCache 分布式缓存中间件 方便实现缓存的分布式,集群, ...
- 分布式架构下,session共享有什么方案么?
分布式架构下,session共享有什么方案么? 会点代码的大叔 科技领域创作者 分布式架构下的session共享,也可以称作分布式session一致性:关于这个问题,和大家说一说解决方案(如果有其他的 ...
- Java生鲜电商平台-SpringCloud微服务架构中分布式事务解决方案
Java生鲜电商平台-SpringCloud微服务架构中分布式事务解决方案 说明:Java生鲜电商平台中由于采用了微服务架构进行业务的处理,买家,卖家,配送,销售,供应商等进行服务化,但是不可避免存在 ...
- nginx+iis+redis+Task.MainForm构建分布式架构 之 (redis存储分布式共享的session及共享session运作流程)
本次要分享的是利用windows+nginx+iis+redis+Task.MainForm组建分布式架构,上一篇分享文章制作是在windows上使用的nginx,一般正式发布的时候是在linux来配 ...
随机推荐
- java 模拟ajax上传图片
1.maven 引入依赖 <!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpmime --> &l ...
- sql知识小记
1.在sql语句中,单引号嵌套时,使用单引号做转义
- 紫书 例题8-7 UVa 11572(滑动窗口)
滑动窗口这个方法名字非常形象, 先是窗口的右指针尽量往右滑, 滑不动了就滑窗口的左指针, 滑到右指针又可以开始滑动为止. 这道题是要记录滑的过程中最大的窗口长度, 限制条件是窗口中不能出现重复的值. ...
- 2.Maven特点,Maven约定,建立第一个Maven项目
1 Maven是跨平台的项目管理工具.主要服务于基于java平台的项目构建,依赖管理和项目信息管理. 项目构建 清理à编译à測试à报告à打包à部署 理想的项目构建: 高度自己主动化 跨平台 可重 ...
- 清华EMBA课程系列思考之六 -- 比較文明视野下的中华领导智慧、企业管理与经济解析
告别马年的最后一缕阳光,踏着猴年的钟声,度过了温馨的春节,已然开启了新学期的第一堂课.看题目其貌不扬,但一旦进入课堂,已然聚精会神.唯恐掉队,就请大家跟我一起进入四天的心路修炼旅程,開始我们的新一期思 ...
- xcode5. 安装cocos2d-x 学习中。。。
找了一些帖子 没搞出来,后来找到原因了 如今的cocos2d版本号在xcode.5上 没右模版了. 用命令行 来运行.看了官方的文档.最终攻克了--- 对于自己解决的问题都会感到点兴奋. .. ...
- 编写SDR SDRAM页突发模式控制器的注意点-下篇
本来是没打算写这些的,但是后面逐渐发现点问题,所以决定再写一个下篇来补充说明一下. 图一 细心的网友会发现上篇末尾的打印是有点问题的,因为我的数据产生器产生的是1-200,1-200,1-200,1- ...
- bzoj1005: [HNOI2008]明明的烦恼(prufer+高精度)
1005: [HNOI2008]明明的烦恼 题目:传送门 题解: 毒瘤题啊天~ 其实思考的过程还是比较简单的... 首先当然还是要了解好prufer序列的基本性质啦 那么和1211大体一致,主要还是利 ...
- IIS访问站点,出现connection refused
排查后,发现是因为使用了代理导致的. 需要设置 Don't use the proxy server for local addresses.
- python 统计文件top IP
lines = ''' 1.2.2.3 1.21.29.19.... ''' cnt = {} for line in lines.split(): if line not in cnt: cnt[l ...