在上文Nginx+Tomcat關于Session的管理中簡單介紹了如何使用redis來集中管理session,本文首先將介紹默認的管理器是如何管理Session的生命周期的,然后在此基礎上對Redis集中式管理Session進行分析。 Tomcat Manager介紹 上文中在Tomcat的context.xml中配置了Session管理器RedisSessionManager,實現了通過redis來存儲session的功能;Tomcat本身提供了多種Session管理器,如下類圖:
1.Manager接口類 定義了用來管理session的基本接口,包括:createSession,findSession,add,remove等對session操作的方法;還有getMaxActive,setMaxActive,getActiveSessions活躍會話的管理;還有Session有效期的接口;以及與Container相關聯的接口; 2.ManagerBase抽象類 實現了Manager接口,提供了基本的功能,使用ConcurrentHashMap存放session,提供了對session的create,find,add,remove功能,并且在createSession中了使用類SessionIdGenerator來生成會話id,作為session的唯一標識; 3.ClusterManager接口類 實現了Manager接口,集群session的管理器,Tomcat內置的集群服務器之間的session復制功能; 4.ClusterManagerBase抽象類 繼承了ManagerBase抽象類,實現ClusterManager接口類,實現session復制基本功能; 5.PersistentManagerBase抽象類 繼承了ManagerBase抽象類,實現了session管理器持久化的基本功能;內部有一個Store存儲類,具體實現有:FileStore和JDBCStore; 6.StandardManager類 繼承ManagerBase抽象類,Tomcat默認的Session管理器(單機版);對session提供了持久化功能,tomcat關閉的時候會將session保存到javax.servlet.context.tempdir路徑下的SESSIONS.ser文件中,啟動的時候會從此文件中加載session; 7.PersistentManager類 繼承PersistentManagerBase抽象類,如果session空閑時間過長,將空閑session轉換為存儲,所以在findsession時會首先從內存中獲取session,獲取不到會多一步到store中獲取,這也是PersistentManager類和StandardManager類的區別; 8.DeltaManager類 繼承ClusterManagerBase,每一個節點session發生變更(增刪改),都會通知其他所有節點,其他所有節點進行更新操作,任何一個session在每個節點都有備份; 9.BackupManager類 繼承ClusterManagerBase,會話數據只有一個備份節點,這個備份節點的位置集群中所有節點都可見;相比較DeltaManager數據傳輸量較小,當集群規模比較大時DeltaManager的數據傳輸量會非常大; 10.RedisSessionManager類 繼承ManagerBase抽象類,非Tomcat內置的管理器,使用redis集中存儲session,省去了節點之間的session復制,依賴redis的可靠性,比起sessin復制擴展性更好; Session的生命周期 1.解析獲取requestedSessionId 當我們在類中通過request.getSession()時,tomcat是如何處理的,可以查看Request中的doGetSession方法: protected Session doGetSession(boolean create) {
// There cannot be a session if no context has been assigned yet
Context context = getContext();
if (context == null) {
return (null);
}
// Return the current session if it exists and is valid
if ((session != null) && !session.isValid()) {
session = null;
}
if (session != null) {
return (session);
}
// Return the requested session if it exists and is valid
Manager manager = context.getManager();
if (manager == null) {
return null; // Sessions are not supported
}
if (requestedSessionId != null) {
try {
session = manager.findSession(requestedSessionId);
} catch (IOException e) {
session = null;
}
if ((session != null) && !session.isValid()) {
session = null;
}
if (session != null) {
session.access();
return (session);
}
}
// Create a new session if requested and the response is not committed
if (!create) {
return (null);
}
if ((response != null) &&
context.getServletContext().getEffectiveSessionTrackingModes().
contains(SessionTrackingMode.COOKIE) &&
response.getResponse().isCommitted()) {
throw new IllegalStateException
(sm.getString("coyoteRequest.sessionCreateCommitted"));
}
// Re-use session IDs provided by the client in very limited
// circumstances.
String sessionId = getRequestedSessionId();
if (requestedSessionSSL) {
// If the session ID has been obtained from the SSL handshake then
// use it.
} else if (("/".equals(context.getSessionCookiePath())
&& isRequestedSessionIdFromCookie())) {
/* This is the common(ish) use case: using the same session ID with
* multiple web applications on the same host. Typically this is
* used by Portlet implementations. It only works if sessions are
* tracked via cookies. The cookie must have a path of "/" else it
* won't be provided for requests to all web applications.
*
* Any session ID provided by the client should be for a session
* that already exists somewhere on the host. Check if the context
* is configured for this to be confirmed.
*/
if (context.getValidateClientProvidedNewSessionId()) {
boolean found = false;
for (Container container : getHost().findChildren()) {
Manager m = ((Context) container).getManager();
if (m != null) {
try {
if (m.findSession(sessionId) != null) {
found = true;
break;
}
} catch (IOException e) {
// Ignore. Problems with this manager will be
// handled elsewhere.
}
}
}
if (!found) {
sessionId = null;
}
}
} else {
sessionId = null;
}
session = manager.createSession(sessionId);
// Creating a new session cookie based on that session
if ((session != null) && (getContext() != null)
&& getContext().getServletContext().
getEffectiveSessionTrackingModes().contains(
SessionTrackingMode.COOKIE)) {
Cookie cookie =
ApplicationSessionCookieConfig.createSessionCookie(
context, session.getIdInternal(), isSecure());
response.addSessionCookieInternal(cookie);
}
if (session == null) {
return null;
}
session.access();
return session;
}如果session已經存在,則直接返回;如果不存在則判定requestedSessionId是否為空,如果不為空則通過requestedSessionId到Session manager中獲取session,如果為空,并且不是創建session操作,直接返回null;否則會調用Session manager創建一個新的session; 關于requestedSessionId是如何獲取的,Tomcat內部可以支持從cookie和url中獲取,具體可以查看CoyoteAdapter類的postParseRequest方法部分代碼: String sessionID;
if (request.getServletContext().getEffectiveSessionTrackingModes()
.contains(SessionTrackingMode.URL)) {
// Get the session ID if there was one
sessionID = request.getPathParameter(
SessionConfig.getSessionUriParamName(
request.getContext()));
if (sessionID != null) {
request.setRequestedSessionId(sessionID);
request.setRequestedSessionURL(true);
}
}
// Look for session ID in cookies and SSL session
parseSessionCookiesId(req, request);可以發現首先去url解析sessionId,如果獲取不到則去cookie中獲取,此處的SessionUriParamName=jsessionid;在cookie被瀏覽器禁用的情況下,我們可以看到url后面跟著參數jsessionid=xxxxxx;下面看一下parseSessionCookiesId方法: String sessionCookieName = SessionConfig.getSessionCookieName(context);
for (int i = 0; i < count; i++) {
ServerCookie scookie = serverCookies.getCookie(i);
if (scookie.getName().equals(sessionCookieName)) {
// Override anything requested in the URL
if (!request.isRequestedSessionIdFromCookie()) {
// Accept only the first session id cookie
convertMB(scookie.getValue());
request.setRequestedSessionId
(scookie.getValue().toString());
request.setRequestedSessionCookie(true);
request.setRequestedSessionURL(false);
if (log.isDebugEnabled()) {
log.debug(" Requested cookie session id is " +
request.getRequestedSessionId());
}
} else {
if (!request.isRequestedSessionIdValid()) {
// Replace the session id until one is valid
convertMB(scookie.getValue());
request.setRequestedSessionId
(scookie.getValue().toString());
}
}
}
}sessionCookieName也是jsessionid,然后遍歷cookie,從里面找出name=jsessionid的值賦值給request的requestedSessionId屬性; 2.findSession查詢session 獲取到requestedSessionId之后,會通過此id去session Manager中獲取session,不同的管理器獲取的方式不一樣,已默認的StandardManager為例: protected Map
sessions = new ConcurrentHashMap
();
public Session findSession(String id) throws IOException {
if (id == null) {
return null;
}
return sessions.get(id);
}3.createSession創建session 沒有獲取到session,指定了create=true,則創建session,已默認的StandardManager為例: public Session createSession(String sessionId) {
if ((maxActiveSessions >= 0) &&
(getActiveSessions() >= maxActiveSessions)) {
rejectedSessions++;
throw new TooManyActiveSessionsException(
sm.getString("managerBase.createSession.ise"),
maxActiveSessions);
}
// Recycle or create a Session instance
Session session = createEmptySession();
// Initialize the properties of the new session and return it
session.setNew(true);
session.setValid(true);
session.setCreationTime(System.currentTimeMillis());
session.setMaxInactiveInterval(((Context) getContainer()).getSessionTimeout() * 60);
String id = sessionId;
if (id == null) {
id = generateSessionId();
}
session.setId(id);
sessionCounter++;
SessionTiming timing = new SessionTiming(session.getCreationTime(), 0);
synchronized (sessionCreationTiming) {
sessionCreationTiming.add(timing);
sessionCreationTiming.poll();
}
return (session);
}如果傳的sessionId為空,tomcat會生成一個唯一的sessionId,具體可以參考類StandardSessionIdGenerator的generateSessionId方法;這里發現創建完session之后并沒有把session放入ConcurrentHashMap中,其實在session.setId(id)中處理了,具體代碼如下: public void setId(String id, boolean notify) {
if ((this.id != null) && (manager != null))
manager.remove(this);
this.id = id;
if (manager != null)
manager.add(this);
if (notify) {
tellNew();
}
}4.銷毀Session Tomcat會定期檢測出不活躍的session,然后將其刪除,一方面session占用內存,另一方面是安全性的考慮;啟動tomcat的同時會啟動一個后臺線程用來檢測過期的session,具體可以查看ContainerBase的內部類ContainerBackgroundProcessor: protected class ContainerBackgroundProcessor implements Runnable {
@Override
public void run() {
Throwable t = null;
String unexpectedDeathMessage = sm.getString(
"containerBase.backgroundProcess.unexpectedThreadDeath",
Thread.currentThread().getName());
try {
while (!threadDone) {
try {
Thread.sleep(backgroundProcessorDelay * 1000L);
} catch (InterruptedException e) {
// Ignore
}
if (!threadDone) {
Container parent = (Container) getMappingObject();
ClassLoader cl =
Thread.currentThread().getContextClassLoader();
if (parent.getLoader() != null) {
cl = parent.getLoader().getClassLoader();
}
processChildren(parent, cl);
}
}
} catch (RuntimeException e) {
t = e;
throw e;
} catch (Error e) {
t = e;
throw e;
} finally {
if (!threadDone) {
log.error(unexpectedDeathMessage, t);
}
}
}
protected void processChildren(Container container, ClassLoader cl) {
try {
if (container.getLoader() != null) {
Thread.currentThread().setContextClassLoader
(container.getLoader().getClassLoader());
}
container.backgroundProcess();
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
log.error("Exception invoking periodic operation: ", t);
} finally {
Thread.currentThread().setContextClassLoader(cl);
}
Container[] children = container.findChildren();
for (int i = 0; i < children.length; i++) {
if (children[i].getBackgroundProcessorDelay() <= 0) {
processChildren(children[i], cl);
}
}
}
}backgroundProcessorDelay默認值是10,也就是每10秒檢測一次,然后調用Container的backgroundProcess方法,此方法又調用Manager里面的backgroundProcess: public void backgroundProcess() {
count = (count + 1) % processExpiresFrequency;
if (count == 0)
processExpires();
}
/**
* Invalidate all sessions that have expired.
*/
public void processExpires() {
long timeNow = System.currentTimeMillis();
Session sessions[] = findSessions();
int expireHere = 0 ;
if(log.isDebugEnabled())
log.debug("Start expire sessions " + getName() + " at " + timeNow + " sessioncount " + sessions.length);
for (int i = 0; i < sessions.length; i++) {
if (sessions[i]!=null && !sessions[i].isValid()) {
expireHere++;
}
}
long timeEnd = System.currentTimeMillis();
if(log.isDebugEnabled())
log.debug("End expire sessions " + getName() + " processingTime " + (timeEnd - timeNow) + " expired sessions: " + expireHere);
processingTime += ( timeEnd - timeNow );
}processExpiresFrequency默認值是6,那其實最后就是6*10=60秒執行一次processExpires,具體如何檢測過期在session的isValid方法中: public boolean isValid() {
if (!this.isValid) {
return false;
}
if (this.expiring) {
return true;
}
if (ACTIVITY_CHECK && accessCount.get() > 0) {
return true;
}
if (maxInactiveInterval > 0) {
long timeNow = System.currentTimeMillis();
int timeIdle;
if (LAST_ACCESS_AT_START) {
timeIdle = (int) ((timeNow - lastAccessedTime) / 1000L);
} else {
timeIdle = (int) ((timeNow - thisAccessedTime) / 1000L);
}
if (timeIdle >= maxInactiveInterval) {
expire(true);
}
}
return this.isValid;
}主要是通過對比當前時間到上次活躍的時間是否超過了maxInactiveInterval,如果超過了就做expire處理; Redis集中式管理Session分析 在上文中使用tomcat-redis-session-manager來管理session,下面來分析一下是如果通過redis來集中式管理Session的;圍繞session如何獲取,如何創建,何時更新到redis,以及何時被移除; 1.如何獲取 RedisSessionManager重寫了findSession方法 public Session findSession(String id) throws IOException {
RedisSession session = null;
if (null == id) {
currentSessionIsPersisted.set(false);
currentSession.set(null);
currentSessionSerializationMetadata.set(null);
currentSessionId.set(null);
} else if (id.equals(currentSessionId.get())) {
session = currentSession.get();
} else {
byte[] data = loadSessionDataFromRedis(id);
if (data != null) {
DeserializedSessionContainer container = sessionFromSerializedData(id, data);
session = container.session;
currentSession.set(session);
currentSessionSerializationMetadata.set(container.metadata);
currentSessionIsPersisted.set(true);
currentSessionId.set(id);
} else {
currentSessionIsPersisted.set(false);
currentSession.set(null);
currentSessionSerializationMetadata.set(null);
currentSessionId.set(null);
}
}sessionId不為空的情況下,會先比較sessionId是否等于currentSessionId中的sessionId,如果等于則從currentSession中取出session,currentSessionId和currentSession都是ThreadLocal變量,這里并沒有直接從redis里面取數據,如果同一線程沒有去處理其他用戶信息,是可以直接從內存中取出的,提高了性能;最后才從redis里面獲取數據,從redis里面獲取的是一段二進制數據,需要進行反序列化操作,相關序列化和反序列化都在JavaSerializer類中: public void deserializeInto(byte[] data, RedisSession session, SessionSerializationMetadata metadata)
throws IOException, ClassNotFoundException {
BufferedInputStream bis = new BufferedInputStream(new ByteArrayInputStream(data));
Throwable arg4 = null;
try {
CustomObjectInputStream x2 = new CustomObjectInputStream(bis, this.loader);
Throwable arg6 = null;
try {
SessionSerializationMetadata x21 = (SessionSerializationMetadata) x2.readObject();
metadata.copyFieldsFrom(x21);
session.readObjectData(x2);
} catch (Throwable arg29) {
......
}二進制數據中保存了2個對象,分別是SessionSerializationMetadata和RedisSession,SessionSerializationMetadata里面保存的是Session中的attributes信息,RedisSession其實也有attributes數據,相當于這份數據保存了2份; 2.如何創建 同樣RedisSessionManager重寫了createSession方法,2個重要的點分別:sessionId的唯一性問題和session保存到redis中; // Ensure generation of a unique session identifier.
if (null != requestedSessionId) {
sessionId = sessionIdWithJvmRoute(requestedSessionId, jvmRoute);
if (jedis.setnx(sessionId.getBytes(), NULL_SESSION) == 0L) {
sessionId = null;
}
} else {
do {
sessionId = sessionIdWithJvmRoute(generateSessionId(), jvmRoute);
} while (jedis.setnx(sessionId.getBytes(), NULL_SESSION) == 0L); // 1 = key set; 0 = key already existed
}分布式環境下有可能出現生成的sessionId相同的情況,所以需要確保唯一性;保存session到redis中是最核心的一個方法,何時更新,何時過期都在此方法中處理; 3.何時更新到redis 具體看saveInternal方法 protected boolean saveInternal(Jedis jedis, Session session, boolean forceSave) throws IOException {
Boolean error = true;
try {
log.trace("Saving session " + session + " into Redis");
RedisSession redisSession = (RedisSession)session;
if (log.isTraceEnabled()) {
log.trace("Session Contents [" + redisSession.getId() + "]:");
Enumeration en = redisSession.getAttributeNames();
while(en.hasMoreElements()) {
log.trace(" " + en.nextElement());
}
}
byte[] binaryId = redisSession.getId().getBytes();
Boolean isCurrentSessionPersisted;
SessionSerializationMetadata sessionSerializationMetadata = currentSessionSerializationMetadata.get();
byte[] originalSessionAttributesHash = sessionSerializationMetadata.getSessionAttributesHash();
byte[] sessionAttributesHash = null;
if (
forceSave
|| redisSession.isDirty()
|| null == (isCurrentSessionPersisted = this.currentSessionIsPersisted.get())
|| !isCurrentSessionPersisted
|| !Arrays.equals(originalSessionAttributesHash, (sessionAttributesHash = serializer.attributesHashFrom(redisSession)))
) {
log.trace("Save was determined to be necessary");
if (null == sessionAttributesHash) {
sessionAttributesHash = serializer.attributesHashFrom(redisSession);
}
SessionSerializationMetadata updatedSerializationMetadata = new SessionSerializationMetadata();
updatedSerializationMetadata.setSessionAttributesHash(sessionAttributesHash);
jedis.set(binaryId, serializer.serializeFrom(redisSession, updatedSerializationMetadata));
redisSession.resetDirtyTracking();
currentSessionSerializationMetadata.set(updatedSerializationMetadata);
currentSessionIsPersisted.set(true);
} else {
log.trace("Save was determined to be unnecessary");
}
log.trace("Setting expire timeout on session [" + redisSession.getId() + "] to " + getMaxInactiveInterval());
jedis.expire(binaryId, getMaxInactiveInterval());
error = false;
return error;
} catch (IOException e) {
log.error(e.getMessage());
throw e;
} finally {
return error;
}
}以上方法中大致有5中情況下需要保存數據到redis中,分別是:forceSave,redisSession.isDirty(),null == (isCurrentSessionPersisted = this.currentSessionIsPersisted.get()),!isCurrentSessionPersisted以及!Arrays.equals(originalSessionAttributesHash, (sessionAttributesHash = serializer.attributesHashFrom(redisSession)))其中一個為true的情況下保存數據到reids中; 3.1重點看一下forceSave,可以理解forceSave就是內置保存策略的一個標識,提供了三種內置保存策略:DEFAULT,SAVE_ON_CHANGE,ALWAYS_SAVE_AFTER_REQUEST
3.2redisSession.isDirty()檢測session內部是否有臟數據 public Boolean isDirty() {
return Boolean.valueOf(this.dirty.booleanValue() || !this.changedAttributes.isEmpty());
}每一個request請求后檢測是否有臟數據,有臟數據才保存,實時性沒有SAVE_ON_CHANGE高,但是也沒有ALWAYS_SAVE_AFTER_REQUEST來的粗暴; 3.3后面三種情況都是用來檢測三個ThreadLocal變量; 4.何時被移除 上一節中介紹了Tomcat內置看定期檢測session是否過期,ManagerBase中提供了processExpires方法來處理session過去的問題,但是在RedisSessionManager重寫了此方法
直接不做處理了,具體是利用了redis的設置生存時間功能,具體在saveInternal方法中:
總結 本文大致分析了Tomcat Session管理器,以及tomcat-redis-session-manager是如何進行session集中式管理的,但是此工具完全依賴tomcat容器,如果想完全獨立于應用服務器的方案, Spring session是一個不錯的選擇。 |
免責聲明:本站部分文章和圖片均來自用戶投稿和網絡收集,旨在傳播知識,文章和圖片版權歸原作者及原出處所有,僅供學習與參考,請勿用于商業用途,如果損害了您的權利,請聯系我們及時修正或刪除。謝謝!

始終以前瞻性的眼光聚焦站長、創業、互聯網等領域,為您提供最新最全的互聯網資訊,幫助站長轉型升級,為互聯網創業者提供更加優質的創業信息和品牌營銷服務,與站長一起進步!讓互聯網創業者不再孤獨!
掃一掃,關注站長網微信
大家都在看當我們在共享網絡訪問的時候,可能會遇到提示指定的網絡名不再可用的問題,這可能是由于我們的共享網絡出現了錯誤,也可能是被共享的對象所拒絕了。指定的網絡名 ......
1、首先進入到“百度”軟件中, 2、然后在其中輸入“百度識圖”, 3、之后點擊圖中的“開始使用”按鈕, 4、緊接著點擊右下角的“相冊”功能, 5、在相冊下 ......
一、軟件沖突1、首先確認是否是應用程序沖突導致的。2、查看是否只有特定幾個游戲或應用會導致該問題。3、如果是應用沖突,那么只要卸載這些app就可以解決了。二 ......
文/曹楊 原標題:誰還看電視? 爸爸戴一副老花鏡,媽媽戴一副近視鏡,一人坐在沙發,一人躺在床上,各自刷著自己關注的博主更新的短視頻。電視也許開著,但只是背景! ∵@樣的畫面,幾乎成了洛奇家的常 ...
圖片來源于簡書 文/郭開森 楊帆 陸玖財經準備開新欄目了,每周一創始人郭開森和楊帆合體郭德帆,對行業進行一些觀察和評論,第一篇我們仍是打算寫社區團購,這是當下最火的話題! 磉^陸玖財經做客的朋友們...
一、N100對比intel i3 1、N100的跑分達到了147210分,這個數據可以達到i3的七代級別。 2、在跑分上也是超越了大部分的I3七代CPU,不過比I3八代要弱勢一些。 3 ......
電腦端:1、大家可以點擊右邊鏈接進入網頁版的百度網盤,進入之后點擊“去登錄”。https://pan.baidu.com/2、之后正確的輸入賬號密碼進行登錄就好啦。手機端:1 ......
在填寫一些項目申請書中,總是免不了要選擇一些數字,但是在方框中如何插入數字,該怎么辦呢?那么下面就由學習啦小編給大家分享下word在方框里輸入數字的技巧, ......
8月15日消息 上周,有媒體報道前身為百度圖片的“榴蓮”APP含有大量不雅視頻內容被用戶舉報。對此,百度圖片官方進行了回應,百度圖片表示已經對報道中所涉及的“生吃旋風哥”等爭議內容進行了下線處理。 此外,百度...
迅雷前綴是什么 答:迅雷前綴是(magnet:?xt=urn:btih:)括號里的就是了。 我們只要在這段文字之后輸入后續的內容,就可以創建下載鏈接了。 1、磁力鏈接不基于文 ......
WPS Office手機版怎么加橫線?很多用戶還不知道WPS Office手機版怎么加橫線,WPS Office手機版怎么加橫線,WPS Office手機版怎么打橫線,WPS Office手機版怎么弄 ......
一、內容特權。 1、半價點播。 許多站內視頻都需要付費觀看,而大會員用戶可以直接半價享受; 購買成功后的48h內無限次觀看。有部分的內容是只限在中國大陸內觀 ......
藍奏云怎么打開別人分享的鏈接1、當我們獲得了鏈接以后,首先需要把鏈接粘貼進瀏覽器里回車進入。2、進入以后就會轉到這樣一個頁面,如果有密碼的話我們就輸入給 ......
1、首先打開小米運動的“實驗室功能”。 2、接著點擊“門卡模擬”。 3、然后點擊“我知道了”。 4、最后貼近就可以刷卡成功了。...
答:華為P系列: 華為p40,華為p40plus,華為p50,華為p50e,華為p60 華為mate系列: 華為mate40,華為mate50,華為mate50e,華為mate60 華為nova系列: 華為n ......
近期有用戶反映,電腦在更新Windows 11 Insider Preview 25252.1000后,出現了應用和已壓縮的文件點擊毫無反應,拖拽都不行,只能從開始菜單打開的情況,這是怎 ......
可見單元格就是不包括隱藏或者篩選篩選后隱藏起來的單元格區域。方法:篩選或隱藏數據,復制需要粘貼的值,在目標單元格區域左上角的第一個單元格處右擊,選擇【 ......
答:驍龍8+更好。 驍龍7+gen2實際上就是驍龍8+的低配版本。 在一些其他的核心架構方面都是保持一致的,比如說CPU的架構、GPU的架構等等。 驍龍7+和驍龍8+具體 ......
1、先打開機頂盒進入主界面,并且使用遙控器打開設置。 2、然后選擇“賬號與安全”,并且進入。 3、最后往下面翻就可以看到“ADB調試”的選項,直接開啟就行了 ......
答:在3DMark壓力測試當中,顯卡需要超高97%才能夠算合格,證明顯卡的穩定性是過關的。 1、一般的默認情況下在2500~3000分就算很正常的了。 2、分數越高說明顯卡 ......
文件被win10系統誤報病毒自動刪除了如何進行恢復?有用戶下載了某些破解軟件卻被Win10系統誤認為是病毒文件而自動刪除,當然系統自帶殺毒軟件其實挺不錯的,就是有時候會誤報,大家遇到這種情況的時候就希望把誤刪的...
win11系統如何釋放掉系統默認保留的存儲空間?一般情況下,Windows會保留一些存儲空間,以便設備獲得良好性能和成功更新。但是當出現系統盤儲存空間不足時,我們會將幾個G的保留空間釋放出來,以解燃眉之急。本期教...
文/黎明 一場針對中國互聯網巨頭的反壟斷風暴正在醞釀,而且這次動真格了! 11月10日,國家市場監管總局發布《關于平臺經濟領域的反壟斷指南(征求意見稿)》,要加大對互聯網巨頭涉嫌壟斷的調查和監管。 ...
我們經常用WPS的時候,如果需要輸入波浪號~,會發現鍵盤上的波浪號輸入之后在最上面,但是為了美觀,我們希望波浪號顯示在中間。這里總結了三個方法分享給大家 ......
答:不一樣。 字類不同、數量不同、使用也不同。 1、字類不同 仿宋gb2312只包含符合GB2312標準的簡體字,仿宋包含所有的簡體字,包括特殊的繁體字。 2、版本不 ......
1、下載番茄作家助手,登錄你自己的賬號進入。2、在首頁這里可以看到你創建的書籍,或者新建一個小說,點擊這個黃色的編輯圖標。3、進入以后,再點擊“創建章節 ......
答:中高端水平 i513500hx在處理器當中是處于一個中高端的水平。 i513500hx是第十一代酷睿處理器系列的一員,基礎頻率為2.4GHz,表現十分的不錯。 i513500hx介 ......
羅技g304dpi燈顏色代表什么:1、藍色:這種情況是正常工作的顯示,如果說是常亮或者閃爍,那都沒有問題這是在正常工作呢。2、紅色:如果說是紅燈閃爍的話那就是 ......
打開軟件,直接填寫就可以。 1、下載安裝easyconnect軟件, 2、打開easyconnect應用,在如圖所示的“服務器地址”一欄輸入如圖所示網址,點擊“連接”, 3、等 ......