❶ java里shiro的用法
好在配置簡單,以前做一個許可權模塊要寫好多代碼。現在spring security好像是集成了shiro的功能,實現了代碼量更少,高速開發的目的。
大致為兩種措施:
一、腳本同步:
1、自己寫腳本將資料庫數據寫入到redis/memcached。
2、這就涉及到實時數據變更的問題(mysql row binlog的實時分析),binlog增量訂閱Alibaba 的canal ,以及緩存層數據 丟失/失效 後的數據同步恢復問題。
二、業務層實現:
1、先讀取nosql緩存層,沒有數據再讀取mysql層,並寫入數據到nosql。
2、nosql層做好多節點分布式(一致性hash),以及節點失效後替代方案(多層hash尋找相鄰替代節點),和數據震盪恢復了。
❸ org.apache.shiro.util.bytesource 什麼類型
java.io.NotSerializableException: org.apache.shiro.util.SimpleByteSource是由於 SimpleByteSource 沒有實現序列化介面導致。在集成redis緩存, 開啟緩存認證時候報錯我的解決方案 寫一個新類去繼承SimpleByteSource,並實現序列化介面 public class MySimpleByteSource extends SimpleByteSource implements Serializable {public MySimpleByteSource(byte[] bytes) {super(bytes);// TODO Auto-generated constructor stub}} 在處理認證中 protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { return new SimpleAuthenticationInfo(model, user.getPassword(), new MySimpleByteSource(salt), getName()); }org.apache.shiro.util.bytesource 什麼類型
❹ org.apache.shiro.util.bytesource 什麼類型
java.io.NotSerializableException: org.apache.shiro.util.SimpleByteSource
是由於 SimpleByteSource 沒有實現序列化介面導致。
在集成redis緩存, 開啟緩存認證時候報錯
<!-- 啟用認證緩存,當用戶登錄一次後將不在查詢資料庫來獲取用戶信息,直接在從緩存獲取 -->
<property name="authenticationCachingEnabled" value="true" />
我的解決方案 寫一個新類去繼承SimpleByteSource,並實現序列化介面
public class MySimpleByteSource extends SimpleByteSource implements Serializable {
public MySimpleByteSource(byte[] bytes) {
super(bytes);
// TODO Auto-generated constructor stub
}
}
在處理認證中
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken token) throws AuthenticationException {
return new SimpleAuthenticationInfo(model, user.getPassword(), new MySimpleByteSource(salt), getName());
}
❺ 如何查詢redis的緩存文件路徑
1、首先找到redis的安裝目錄,如下圖測試環境目錄,進入到/opt/install/redis-2.8.19/src,如下圖所示。
❻ 我配置了redis註解緩存,為什麼不起作用
作為緩存伺服器,如果不加以限制內存的話,就很有可能出現將整台伺服器內存都耗光的情況,可以在redis的配置文件裡面設置:
example:
#
限定最多使用1.5GB內存
maxmemory
1536mb
如果內存到達了指定的上限,還要往redis裡面添加更多的緩存內容,需要設置清理內容的策略:
默認為0,沒有指定最大緩存,如果有新的數據添加,超過最大內存,則會使redis崩潰,所以一點要設置。
設置maxmemory之後,配合的要設置緩存數據回收策略。
❼ shiro+redis中redis在什麼地方調用
什麼是Redis Redis是一個開源的使用ANSI C語言編寫、支持網路、可基於內存亦可持久化的日誌型、Key-Value資料庫,並提供多種語言的API ---維基網路 1.與其他用戶狀態保存方案比較 一般開發中用戶狀態使用session或者cookie,兩種方式各種利弊
❽ web伺服器怎麼使用redis分步式緩存
Redis復制流程概述
Redis的復制功能是完全建立在之前我們討論過的基於內存快照的持久化策略基礎上的,也就是說無論你的持久化策略選擇的是什麼,只要用到了Redis的復制功能,就一定會有內存快照發生,那麼首先要注意你的系統內存容量規劃,原因可以參考我上一篇文章中提到的Redis磁碟IO問題。
Redis復制流程在Slave和Master端各自是一套狀態機流轉,涉及的狀態信息是:
Slave 端:
REDIS_REPL_NONEREDIS_REPL_CONNECTREDIS_REPL_CONNECTED
Master端:
REDIS_REPL_WAIT_BGSAVE_STARTREDIS_REPL_WAIT_BGSAVE_ENDREDIS_REPL_SEND_BULKREDIS_REPL_ONLINE
整個狀態機流程過程如下:
Slave端在配置文件中添加了slave of指令,於是Slave啟動時讀取配置文件,初始狀態為REDIS_REPL_CONNECT。
Slave端在定時任務serverCron(Redis內部的定時器觸發事件)中連接Master,發送sync命令,然後阻塞等待master發送回其內存快照文件(最新版的Redis已經不需要讓Slave阻塞)。
Master端收到sync命令簡單判斷是否有正在進行的內存快照子進程,沒有則立即開始內存快照,有則等待其結束,當快照完成後會將該文件發送給Slave端。
Slave端接收Master發來的內存快照文件,保存到本地,待接收完成後,清空內存表,重新讀取Master發來的內存快照文件,重建整個內存表數據結構,並最終狀態置位為 REDIS_REPL_CONNECTED狀態,Slave狀態機流轉完成。
Master端在發送快照文件過程中,接收的任何會改變數據集的命令都會暫時先保存在Slave網路連接的發送緩存隊列里(list數據結構),待快照完成後,依次發給Slave,之後收到的命令相同處理,並將狀態置位為 REDIS_REPL_ONLINE。
整個復制過程完成,流程如下圖所示:
Redis復制機制的缺陷
從上面的流程可以看出,Slave從庫在連接Master主庫時,Master會進行內存快照,然後把整個快照文件發給Slave,也就是沒有象MySQL那樣有復制位置的概念,即無增量復制,這會給整個集群搭建帶來非常多的問題。
比如一台線上正在運行的Master主庫配置了一台從庫進行簡單讀寫分離,這時Slave由於網路或者其它原因與Master斷開了連接,那麼當Slave進行重新連接時,需要重新獲取整個Master的內存快照,Slave所有數據跟著全部清除,然後重新建立整個內存表,一方面Slave恢復的時間會非常慢,另一方面也會給主庫帶來壓力。
所以基於上述原因,如果你的Redis集群需要主從復制,那麼最好事先配置好所有的從庫,避免中途再去增加從庫。
Cache還是Storage
在我們分析過了Redis的復制與持久化功能後,我們不難得出一個結論,實際上Redis目前發布的版本還都是一個單機版的思路,主要的問題集中在,持久化方式不夠成熟,復制機制存在比較大的缺陷,這時我們又開始重新思考Redis的定位:Cache還是Storage?
如果作為Cache的話,似乎除了有些非常特殊的業務場景,必須要使用Redis的某種數據結構之外,我們使用Memcached可能更合適,畢竟Memcached無論客戶端包和伺服器本身更久經考驗。
如果是作為存儲Storage的話,我們面臨的最大的問題是無論是持久化還是復制都沒有辦法解決Redis單點問題,即一台Redis掛掉了,沒有太好的辦法能夠快速的恢復,通常幾十G的持久化數據,Redis重啟載入需要幾個小時的時間,而復制又有缺陷,如何解決呢?
Redis可擴展集群搭建1. 主動復制避開Redis復制缺陷。
既然Redis的復制功能有缺陷,那麼我們不妨放棄Redis本身提供的復制功能,我們可以採用主動復制的方式來搭建我們的集群環境。
所謂主動復制是指由業務端或者通過代理中間件對Redis存儲的數據進行雙寫或多寫,通過數據的多份存儲來達到與復制相同的目的,主動復制不僅限於用在Redis集群上,目前很多公司採用主動復制的技術來解決MySQL主從之間復制的延遲問題,比如Twitter還專門開發了用於復制和分區的中間件gizzard(https://github.com/twitter/gizzard) 。
主動復制雖然解決了被動復制的延遲問題,但也帶來了新的問題,就是數據的一致性問題,數據寫2次或多次,如何保證多份數據的一致性呢?如果你的應用對數據一致性要求不高,允許最終一致性的話,那麼通常簡單的解決方案是可以通過時間戳或者vector clock等方式,讓客戶端同時取到多份數據並進行校驗,如果你的應用對數據一致性要求非常高,那麼就需要引入一些復雜的一致性演算法比如Paxos來保證數據的一致性,但是寫入性能也會相應下降很多。
通過主動復制,數據多份存儲我們也就不再擔心Redis單點故障的問題了,如果一組Redis集群掛掉,我們可以讓業務快速切換到另一組Redis上,降低業務風險。
2. 通過presharding進行Redis在線擴容。
通過主動復制我們解決了Redis單點故障問題,那麼還有一個重要的問題需要解決:容量規劃與在線擴容問題。
我們前面分析過Redis的適用場景是全部數據存儲在內存中,而內存容量有限,那麼首先需要根據業務數據量進行初步的容量規劃,比如你的業務數據需要100G存儲空間,假設伺服器內存是48G,那麼根據上一篇我們討論的Redis磁碟IO的問題,我們大約需要3~4台伺服器來存儲。這個實際是對現有業務情況所做的一個容量規劃,假如業務增長很快,很快就會發現當前的容量已經不夠了,Redis裡面存儲的數據很快就會超過物理內存大小,那麼如何進行Redis的在線擴容呢?
Redis的作者提出了一種叫做presharding的方案來解決動態擴容和數據分區的問題,實際就是在同一台機器上部署多個Redis實例的方式,當容量不夠時將多個實例拆分到不同的機器上,這樣實際就達到了擴容的效果。
拆分過程如下:
在新機器上啟動好對應埠的Redis實例。
配置新埠為待遷移埠的從庫。
待復制完成,與主庫完成同步後,切換所有客戶端配置到新的從庫的埠。
配置從庫為新的主庫。
移除老的埠實例。
重復上述過程遷移好所有的埠到指定伺服器上。
以上拆分流程是Redis作者提出的一個平滑遷移的過程,不過該拆分方法還是很依賴Redis本身的復制功能的,如果主庫快照數據文件過大,這個復制的過程也會很久,同時會給主庫帶來壓力。所以做這個拆分的過程最好選擇為業務訪問低峰時段進行。
Redis復制的改進思路
我們線上的系統使用了我們自己改進版的Redis,主要解決了Redis沒有增量復制的缺陷,能夠完成類似Mysql Binlog那樣可以通過從庫請求日誌位置進行增量復制。
我們的持久化方案是首先寫Redis的AOF文件,並對這個AOF文件按文件大小進行自動分割滾動,同時關閉Redis的Rewrite命令,然後會在業務低峰時間進行內存快照存儲,並把當前的AOF文件位置一起寫入到快照文件中,這樣我們可以使快照文件與AOF文件的位置保持一致性,這樣我們得到了系統某一時刻的內存快照,並且同時也能知道這一時刻對應的AOF文件的位置,那麼當從庫發送同步命令時,我們首先會把快照文件發送給從庫,然後從庫會取出該快照文件中存儲的AOF文件位置,並將該位置發給主庫,主庫會隨後發送該位置之後的所有命令,以後的復制就都是這個位置之後的增量信息了。
Redis與MySQL的結合
目前大部分互聯網公司使用MySQL作為數據的主要持久化存儲,那麼如何讓Redis與MySQL很好的結合在一起呢?我們主要使用了一種基於MySQL作為主庫,Redis作為高速數據查詢從庫的異構讀寫分離的方案。
為此我們專門開發了自己的MySQL復制工具,可以方便的實時同步MySQL中的數據到Redis上。
(MySQL-Redis 異構讀寫分離)
總結:
Redis的復制功能沒有增量復制,每次重連都會把主庫整個內存快照發給從庫,所以需要避免向在線服務的壓力較大的主庫上增加從庫。
Redis的復制由於會使用快照持久化方式,所以如果你的Redis持久化方式選擇的是日誌追加方式(aof),那麼系統有可能在同一時刻既做aof日誌文件的同步刷寫磁碟,又做快照寫磁碟操作,這個時候Redis的響應能力會受到影響。所以如果選用aof持久化,則加從庫需要更加謹慎。
可以使用主動復制和presharding方法進行Redis集群搭建與在線擴容。
❾ 怎麼去操作shiro跟redis集成的session
第一步:配置WEB.XML
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
第二步:SHIRO整合SPRING配置
applicationContext-shiro.xml 偽代碼:
<!--Session集群配置-->
<bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
<property name="globalSessionTimeout" value="3600000"/>
<property name="sessionDAO" ref="zkShiroSessionDAO"/>
<property name="sessionValidationScheler" ref="sessionValidationScheler"/>
<property name="" value="true"/>
<property name="sessionIdCookie" ref="wapsession"/>
</bean>
<!--
指定本系統SESSIONID, 默認為: JSESSIONID
問題: 與SERVLET容器名沖突, 如JETTY, TOMCAT 等默認JSESSIONID,
當跳出SHIRO SERVLET時如ERROR-PAGE容器會為JSESSIONID重新分配值導致登錄會話丟失!
-->
<bean id="wapsession" class="org.apache.shiro.web.servlet.SimpleCookie">
<constructor-arg name="name" value="WAPSESSIONID"/>
</bean>
<!--
定時清理僵屍session,Shiro會啟用一個後台守護線程定時執行清理操作
用戶直接關閉瀏覽器造成的孤立會話
-->
<bean id="sessionValidationScheler"
class="org.apache.shiro.session.mgt.">
<property name="interval" value="3600000"/>
<property name="sessionManager" ref="sessionManager"/>
</bean>
<!--由zk做session存儲容器-->
<bean id="zkShiroSessionDAO" class="b2gonline.incometaxexamine._systembase.shiro.ZKShiroSessionDAO">
<!--使用內存緩存登錄用戶信息,一次獲取用戶登錄信息後緩存到內存減少Shiro大量的讀取操作,用戶退出或超時後自動清除-->
<constructor-arg name="useMemCache" value="true"/>
<property name="zookeeperTemplate" ref="zookeeperTemplate"/>
<property name="shiroSessionZKPath" value="/SHIROSESSIONS"/>
<property name="sessionPrefix" value="session-"/>
</bean>
<!-- SHIRO安全介面 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
...
<property name="sessionManager" ref="sessionManager"/>
</bean>
第三步:Zookeeper對Shiro-SessionDao實現類
ZKShiroSessionDAO.JAVA偽代碼:
import bgonline.foundation.hadoop.zk.IZookeeperTemplate;
import bgonline.foundation.hadoop.zk.ZNode;
import org.apache.shiro.cache.AbstractCacheManager;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import org.apache.shiro.cache.MapCache;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.UnknownSessionException;
import org.apache.shiro.session.mgt.eis.AbstractSessionDAO;
import org.apache.shiro.session.mgt.eis.CachingSessionDAO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.SerializationUtils;
import java.io.Serializable;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* ZOOKEEPER實現SHIRO集群SESSION存儲
*
* @author aliencode
* @date 13-7-10
*/
public class ZKShiroSessionDAO extends CachingSessionDAO {
public ZKShiroSessionDAO() {
}
private boolean useMemCache = false;
/**
* SESSION ZK DAO 實例
* 如果開戶緩存
* 用戶登錄時自動緩存, 用戶登錄超時自動刪除
* 由於shiro的cacheManager是全局的, 所以這里使用setActiveSessionsCache直接設置Cache來本地緩存, 而不使用全局zk緩存.
* 由於同一用戶可能會被路由到不同伺服器,所以在doReadSession方法里也做了緩存增加.
*
* @param useMemCache 是否使用內存緩存登錄信息
*/
public ZKShiroSessionDAO(boolean useMemCache) {
this.useMemCache = useMemCache;
if (useMemCache) {
setActiveSessionsCache(
new MapCache<>(this.ACTIVE_SESSION_CACHE_NAME, new ConcurrentHashMap<Serializable, Session>())
);
}
}
Logger logger = LoggerFactory.getLogger(this.getClass());
/**
* ZK操作類
*/
private IZookeeperTemplate zookeeperTemplate;
/**
* 緩存根路徑, 結尾不加/
*/
private String shiroSessionZKPath = "/SHIROSESSIONS";
/**
* 緩存項前綴
*/
private String sessionPrefix = "session-";
/**
* 設置Shiro Session 前綴 默認 session-
*
* @param sessionPrefix
*/
public void setSessionPrefix(String sessionPrefix) {
this.sessionPrefix = sessionPrefix;
}
public void setZookeeperTemplate(IZookeeperTemplate zookeeperTemplate) {
this.zookeeperTemplate = zookeeperTemplate;
}
/**
* 設置Shiro在ZK伺服器存放根路徑
*
* @param shiroSessionZKPath 默認值:/SHIROSESSIONS/
*/
public void setShiroSessionZKPath(String shiroSessionZKPath) {
this.shiroSessionZKPath = shiroSessionZKPath;
}
/**
* session更新
*
* @param session
* @throws UnknownSessionException
*/
@Override
public void update(Session session) throws UnknownSessionException {
if (session == null || session.getId() == null) {
logger.error("session argument cannot be null.");
}
saveSession(session, "update");
}
@Override
protected void doUpdate(Session session) {
}
/**
* session刪除
*
* @param session
*/
@Override
public void delete(Session session) {
if (session == null || session.getId() == null) {
logger.error("session argument cannot be null.");
}
logger.debug("delete session for id: {}", session.getId());
zookeeperTemplate.deleteNode(getPath(session.getId()));
if (useMemCache) {
this.uncache(session);
}
}
@Override
protected void doDelete(Session session) {
}
/**
* 獲取當前活躍的session, 當前在線數量
*
* @return
*/
@Override
public Collection<Session> getActiveSessions() {
ZNode zNode = new ZNode();
zNode.setPath(shiroSessionZKPath);
Set<Session> sessions = new HashSet<Session>();
//讀取所有SessionID , 返回形如: session-9e3b5707-fa80-4d32-a6c9-f1c3685263a5
List<String> ss = zookeeperTemplate.getChildren(zNode);
for (String id : ss) {
if (id.startsWith(sessionPrefix)) {
String noPrefixId = id.replace(sessionPrefix, "");
Session session = doReadSession(noPrefixId);
if (session != null) sessions.add(session);
}
}
logger.debug("shiro getActiveSessions. size: {}", sessions.size());
return sessions;
}
/**
* 創建session, 用戶登錄
*
* @param session
* @return
*/
@Override
protected Serializable doCreate(Session session) {
Serializable sessionId = this.generateSessionId(session);
this.assignSessionId(session, sessionId);
saveSession(session, "create");
return sessionId;
}
/**
* session讀取
*
* @param id
* @return
*/
@Override
protected Session doReadSession(Serializable id) {
if (id == null) {
logger.error("id is null!");
return null;
}
logger.debug("doReadSession for path: {}", getPath(id));
Session session;
byte[] byteData = zookeeperTemplate.getData(getPath(id)).getByteData();
if (byteData != null && byteData.length > 0) {
session = (Session) SerializationUtils.deserialize(byteData);
if (useMemCache) {
this.cache(session, id);
logger.debug("doReadSession for path: {}, add cached !", getPath(id));
}
return session;
} else {
return null;
}
}
/**
* 生成全路徑
*
* @param sessID
* @return
*/
private String getPath(Serializable sessID) {
return shiroSessionZKPath + '/' + sessionPrefix + sessID.toString();
}
/**
* session讀取或更新
*
* @param session
* @param act update/save
*/
private void saveSession(Session session, String act) {
Serializable sessionId = session.getId();
ZNode sessionNode = new ZNode();
sessionNode.setByteData(SerializationUtils.serialize(session));
sessionNode.setPath(getPath(sessionId));
logger.debug("save session for id: {}, act: {}", sessionId, act);
if (act == "update")
zookeeperTemplate.setData(sessionNode);
else
zookeeperTemplate.createNode(sessionNode);
}
}