在上文Nginx+Tomcat關(guān)于Session的管理中簡(jiǎn)單介紹了如何使用redis來集中管理session,本文首先將介紹默認(rèn)的管理器是如何管理Session的生命周期的,然后在此基礎(chǔ)上對(duì)Redis集中式管理Session進(jìn)行分析。 Tomcat Manager介紹 上文中在Tomcat的context.xml中配置了Session管理器RedisSessionManager,實(shí)現(xiàn)了通過redis來存儲(chǔ)session的功能;Tomcat本身提供了多種Session管理器,如下類圖: 1.Manager接口類 定義了用來管理session的基本接口,包括:createSession,findSession,add,remove等對(duì)session操作的方法;還有g(shù)etMaxActive,setMaxActive,getActiveSessions活躍會(huì)話的管理;還有Session有效期的接口;以及與Container相關(guān)聯(lián)的接口; 2.ManagerBase抽象類 實(shí)現(xiàn)了Manager接口,提供了基本的功能,使用ConcurrentHashMap存放session,提供了對(duì)session的create,find,add,remove功能,并且在createSession中了使用類SessionIdGenerator來生成會(huì)話id,作為session的唯一標(biāo)識(shí); 3.ClusterManager接口類 實(shí)現(xiàn)了Manager接口,集群session的管理器,Tomcat內(nèi)置的集群服務(wù)器之間的session復(fù)制功能; 4.ClusterManagerBase抽象類 繼承了ManagerBase抽象類,實(shí)現(xiàn)ClusterManager接口類,實(shí)現(xiàn)session復(fù)制基本功能; 5.PersistentManagerBase抽象類 繼承了ManagerBase抽象類,實(shí)現(xiàn)了session管理器持久化的基本功能;內(nèi)部有一個(gè)Store存儲(chǔ)類,具體實(shí)現(xiàn)有:FileStore和JDBCStore; 6.StandardManager類 繼承ManagerBase抽象類,Tomcat默認(rèn)的Session管理器(單機(jī)版);對(duì)session提供了持久化功能,tomcat關(guān)閉的時(shí)候會(huì)將session保存到j(luò)avax.servlet.context.tempdir路徑下的SESSIONS.ser文件中,啟動(dòng)的時(shí)候會(huì)從此文件中加載session; 7.PersistentManager類 繼承PersistentManagerBase抽象類,如果session空閑時(shí)間過長,將空閑session轉(zhuǎn)換為存儲(chǔ),所以在findsession時(shí)會(huì)首先從內(nèi)存中獲取session,獲取不到會(huì)多一步到store中獲取,這也是PersistentManager類和StandardManager類的區(qū)別; 8.DeltaManager類 繼承ClusterManagerBase,每一個(gè)節(jié)點(diǎn)session發(fā)生變更(增刪改),都會(huì)通知其他所有節(jié)點(diǎn),其他所有節(jié)點(diǎn)進(jìn)行更新操作,任何一個(gè)session在每個(gè)節(jié)點(diǎn)都有備份; 9.BackupManager類 繼承ClusterManagerBase,會(huì)話數(shù)據(jù)只有一個(gè)備份節(jié)點(diǎn),這個(gè)備份節(jié)點(diǎn)的位置集群中所有節(jié)點(diǎn)都可見;相比較DeltaManager數(shù)據(jù)傳輸量較小,當(dāng)集群規(guī)模比較大時(shí)DeltaManager的數(shù)據(jù)傳輸量會(huì)非常大; 10.RedisSessionManager類 繼承ManagerBase抽象類,非Tomcat內(nèi)置的管理器,使用redis集中存儲(chǔ)session,省去了節(jié)點(diǎn)之間的session復(fù)制,依賴redis的可靠性,比起sessin復(fù)制擴(kuò)展性更好; Session的生命周期 1.解析獲取requestedSessionId 當(dāng)我們?cè)陬愔型ㄟ^request.getSession()時(shí),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已經(jīng)存在,則直接返回;如果不存在則判定requestedSessionId是否為空,如果不為空則通過requestedSessionId到Session manager中獲取session,如果為空,并且不是創(chuàng)建session操作,直接返回null;否則會(huì)調(diào)用Session manager創(chuàng)建一個(gè)新的session; 關(guān)于requestedSessionId是如何獲取的,Tomcat內(nèi)部可以支持從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); 可以發(fā)現(xiàn)首先去url解析sessionId,如果獲取不到則去cookie中獲取,此處的SessionUriParamName=jsessionid;在cookie被瀏覽器禁用的情況下,我們可以看到url后面跟著參數(shù)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之后,會(huì)通過此id去session Manager中獲取session,不同的管理器獲取的方式不一樣,已默認(rèn)的StandardManager為例: protected Map sessions = new ConcurrentHashMap (); public Session findSession(String id) throws IOException { if (id == null) { return null; } return sessions.get(id); } 3.createSession創(chuàng)建session 沒有獲取到session,指定了create=true,則創(chuàng)建session,已默認(rèn)的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會(huì)生成一個(gè)唯一的sessionId,具體可以參考類StandardSessionIdGenerator的generateSessionId方法;這里發(fā)現(xiàn)創(chuàng)建完session之后并沒有把session放入ConcurrentHashMap中,其實(shí)在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會(huì)定期檢測(cè)出不活躍的session,然后將其刪除,一方面session占用內(nèi)存,另一方面是安全性的考慮;啟動(dòng)tomcat的同時(shí)會(huì)啟動(dòng)一個(gè)后臺(tái)線程用來檢測(cè)過期的session,具體可以查看ContainerBase的內(nèi)部類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默認(rèn)值是10,也就是每10秒檢測(cè)一次,然后調(diào)用Container的backgroundProcess方法,此方法又調(diào)用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默認(rèn)值是6,那其實(shí)最后就是6*10=60秒執(zhí)行一次processExpires,具體如何檢測(cè)過期在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; } 主要是通過對(duì)比當(dāng)前時(shí)間到上次活躍的時(shí)間是否超過了maxInactiveInterval,如果超過了就做expire處理; Redis集中式管理Session分析 在上文中使用tomcat-redis-session-manager來管理session,下面來分析一下是如果通過redis來集中式管理Session的;圍繞session如何獲取,如何創(chuàng)建,何時(shí)更新到redis,以及何時(shí)被移除; 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不為空的情況下,會(huì)先比較sessionId是否等于currentSessionId中的sessionId,如果等于則從currentSession中取出session,currentSessionId和currentSession都是ThreadLocal變量,這里并沒有直接從redis里面取數(shù)據(jù),如果同一線程沒有去處理其他用戶信息,是可以直接從內(nèi)存中取出的,提高了性能;最后才從redis里面獲取數(shù)據(jù),從redis里面獲取的是一段二進(jìn)制數(shù)據(jù),需要進(jìn)行反序列化操作,相關(guān)序列化和反序列化都在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) { ...... } 二進(jìn)制數(shù)據(jù)中保存了2個(gè)對(duì)象,分別是SessionSerializationMetadata和RedisSession,SessionSerializationMetadata里面保存的是Session中的attributes信息,RedisSession其實(shí)也有attributes數(shù)據(jù),相當(dāng)于這份數(shù)據(jù)保存了2份; 2.如何創(chuàng)建 同樣RedisSessionManager重寫了createSession方法,2個(gè)重要的點(diǎn)分別: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 } 分布式環(huán)境下有可能出現(xiàn)生成的sessionId相同的情況,所以需要確保唯一性;保存session到redis中是最核心的一個(gè)方法,何時(shí)更新,何時(shí)過期都在此方法中處理; 3.何時(shí)更新到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中情況下需要保存數(shù)據(jù)到redis中,分別是:forceSave,redisSession.isDirty(),null == (isCurrentSessionPersisted = this.currentSessionIsPersisted.get()),!isCurrentSessionPersisted以及!Arrays.equals(originalSessionAttributesHash, (sessionAttributesHash = serializer.attributesHashFrom(redisSession)))其中一個(gè)為true的情況下保存數(shù)據(jù)到reids中; 3.1重點(diǎn)看一下forceSave,可以理解forceSave就是內(nèi)置保存策略的一個(gè)標(biāo)識(shí),提供了三種內(nèi)置保存策略:DEFAULT,SAVE_ON_CHANGE,ALWAYS_SAVE_AFTER_REQUEST
3.2redisSession.isDirty()檢測(cè)session內(nèi)部是否有臟數(shù)據(jù) public Boolean isDirty() { return Boolean.valueOf(this.dirty.booleanValue() || !this.changedAttributes.isEmpty()); } 每一個(gè)request請(qǐng)求后檢測(cè)是否有臟數(shù)據(jù),有臟數(shù)據(jù)才保存,實(shí)時(shí)性沒有SAVE_ON_CHANGE高,但是也沒有ALWAYS_SAVE_AFTER_REQUEST來的粗暴; 3.3后面三種情況都是用來檢測(cè)三個(gè)ThreadLocal變量; 4.何時(shí)被移除 上一節(jié)中介紹了Tomcat內(nèi)置看定期檢測(cè)session是否過期,ManagerBase中提供了processExpires方法來處理session過去的問題,但是在RedisSessionManager重寫了此方法
直接不做處理了,具體是利用了redis的設(shè)置生存時(shí)間功能,具體在saveInternal方法中:
總結(jié) 本文大致分析了Tomcat Session管理器,以及tomcat-redis-session-manager是如何進(jìn)行session集中式管理的,但是此工具完全依賴tomcat容器,如果想完全獨(dú)立于應(yīng)用服務(wù)器的方案, Spring session是一個(gè)不錯(cuò)的選擇。 |
免責(zé)聲明:本站部分文章和圖片均來自用戶投稿和網(wǎng)絡(luò)收集,旨在傳播知識(shí),文章和圖片版權(quán)歸原作者及原出處所有,僅供學(xué)習(xí)與參考,請(qǐng)勿用于商業(yè)用途,如果損害了您的權(quán)利,請(qǐng)聯(lián)系我們及時(shí)修正或刪除。謝謝!
始終以前瞻性的眼光聚焦站長、創(chuàng)業(yè)、互聯(lián)網(wǎng)等領(lǐng)域,為您提供最新最全的互聯(lián)網(wǎng)資訊,幫助站長轉(zhuǎn)型升級(jí),為互聯(lián)網(wǎng)創(chuàng)業(yè)者提供更加優(yōu)質(zhì)的創(chuàng)業(yè)信息和品牌營銷服務(wù),與站長一起進(jìn)步!讓互聯(lián)網(wǎng)創(chuàng)業(yè)者不再孤獨(dú)!
掃一掃,關(guān)注站長網(wǎng)微信
當(dāng)我們?cè)诠蚕砭W(wǎng)絡(luò)訪問的時(shí)候,可能會(huì)遇到提示指定的網(wǎng)絡(luò)名不再可用的問題,這可能是由于我們的共享網(wǎng)絡(luò)出現(xiàn)了錯(cuò)誤,也可能是被共享的對(duì)象所拒絕了。指定的網(wǎng)絡(luò)名 ......
文/曹楊 原標(biāo)題:誰還看電視? 爸爸戴一副老花鏡,媽媽戴一副近視鏡,一人坐在沙發(fā),一人躺在床上,各自刷著自己關(guān)注的博主更新的短視頻。電視也許開著,但只是背景。 這樣的畫面,幾乎成了洛奇家的常 ...
圖片來源于簡(jiǎn)書 文/郭開森 楊帆 陸玖財(cái)經(jīng)準(zhǔn)備開新欄目了,每周一創(chuàng)始人郭開森和楊帆合體郭德帆,對(duì)行業(yè)進(jìn)行一些觀察和評(píng)論,第一篇我們?nèi)允谴蛩銓懮鐓^(qū)團(tuán)購,這是當(dāng)下最火的話題。 來過陸玖財(cái)經(jīng)做客的朋友們...
1、首先進(jìn)入到“百度”軟件中, 2、然后在其中輸入“百度識(shí)圖”, 3、之后點(diǎn)擊圖中的“開始使用”按鈕, 4、緊接著點(diǎn)擊右下角的“相冊(cè)”功能, 5、在相冊(cè)下 ......
一、軟件沖突1、首先確認(rèn)是否是應(yīng)用程序沖突導(dǎo)致的。2、查看是否只有特定幾個(gè)游戲或應(yīng)用會(huì)導(dǎo)致該問題。3、如果是應(yīng)用沖突,那么只要卸載這些app就可以解決了。二 ......
電腦端:1、大家可以點(diǎn)擊右邊鏈接進(jìn)入網(wǎng)頁版的百度網(wǎng)盤,進(jìn)入之后點(diǎn)擊“去登錄”。https://pan.baidu.com/2、之后正確的輸入賬號(hào)密碼進(jìn)行登錄就好啦。手機(jī)端:1 ......
在填寫一些項(xiàng)目申請(qǐng)書中,總是免不了要選擇一些數(shù)字,但是在方框中如何插入數(shù)字,該怎么辦呢?那么下面就由學(xué)習(xí)啦小編給大家分享下word在方框里輸入數(shù)字的技巧, ......
8月15日消息 上周,有媒體報(bào)道前身為百度圖片的“榴蓮”APP含有大量不雅視頻內(nèi)容被用戶舉報(bào)。對(duì)此,百度圖片官方進(jìn)行了回應(yīng),百度圖片表示已經(jīng)對(duì)報(bào)道中所涉及的“生吃旋風(fēng)哥”等爭(zhēng)議內(nèi)容進(jìn)行了下線處理。 此外,百度...
一、N100對(duì)比intel i3 1、N100的跑分達(dá)到了147210分,這個(gè)數(shù)據(jù)可以達(dá)到i3的七代級(jí)別。 2、在跑分上也是超越了大部分的I3七代CPU,不過比I3八代要弱勢(shì)一些。 3 ......
WPS Office手機(jī)版怎么加橫線?很多用戶還不知道WPS Office手機(jī)版怎么加橫線,WPS Office手機(jī)版怎么加橫線,WPS Office手機(jī)版怎么打橫線,WPS Office手機(jī)版怎么弄 ......
迅雷前綴是什么 答:迅雷前綴是(magnet:?xt=urn:btih:)括號(hào)里的就是了。 我們只要在這段文字之后輸入后續(xù)的內(nèi)容,就可以創(chuàng)建下載鏈接了。 1、磁力鏈接不基于文 ......
一、內(nèi)容特權(quán)。 1、半價(jià)點(diǎn)播。 許多站內(nèi)視頻都需要付費(fèi)觀看,而大會(huì)員用戶可以直接半價(jià)享受; 購買成功后的48h內(nèi)無限次觀看。有部分的內(nèi)容是只限在中國大陸內(nèi)觀 ......
1、首先打開小米運(yùn)動(dòng)的“實(shí)驗(yàn)室功能”。 2、接著點(diǎn)擊“門卡模擬”。 3、然后點(diǎn)擊“我知道了”。 4、最后貼近就可以刷卡成功了。...
1、打開手機(jī)輕顏相機(jī)app,點(diǎn)擊“我的”,點(diǎn)擊“設(shè)置”,2、點(diǎn)擊“幫助與反饋”,3、點(diǎn)擊右下角“在線咨詢”即可聯(lián)系客服,詢問自己的問題啦!...
答:華為P系列: 華為p40,華為p40plus,華為p50,華為p50e,華為p60 華為mate系列: 華為mate40,華為mate50,華為mate50e,華為mate60 華為nova系列: 華為n ......
近期有用戶反映,電腦在更新Windows 11 Insider Preview 25252.1000后,出現(xiàn)了應(yīng)用和已壓縮的文件點(diǎn)擊毫無反應(yīng),拖拽都不行,只能從開始菜單打開的情況,這是怎 ......
可見單元格就是不包括隱藏或者篩選篩選后隱藏起來的單元格區(qū)域。方法:篩選或隱藏?cái)?shù)據(jù),復(fù)制需要粘貼的值,在目標(biāo)單元格區(qū)域左上角的第一個(gè)單元格處右擊,選擇【 ......
答:驍龍8+更好。 驍龍7+gen2實(shí)際上就是驍龍8+的低配版本。 在一些其他的核心架構(gòu)方面都是保持一致的,比如說CPU的架構(gòu)、GPU的架構(gòu)等等。 驍龍7+和驍龍8+具體 ......
文/黎明 一場(chǎng)針對(duì)中國互聯(lián)網(wǎng)巨頭的反壟斷風(fēng)暴正在醞釀,而且這次動(dòng)真格了。 11月10日,國家市場(chǎng)監(jiān)管總局發(fā)布《關(guān)于平臺(tái)經(jīng)濟(jì)領(lǐng)域的反壟斷指南(征求意見稿)》,要加大對(duì)互聯(lián)網(wǎng)巨頭涉嫌壟斷的調(diào)查和監(jiān)管。 ...
win11系統(tǒng)如何釋放掉系統(tǒng)默認(rèn)保留的存儲(chǔ)空間?一般情況下,Windows會(huì)保留一些存儲(chǔ)空間,以便設(shè)備獲得良好性能和成功更新。但是當(dāng)出現(xiàn)系統(tǒng)盤儲(chǔ)存空間不足時(shí),我們會(huì)將幾個(gè)G的保留空間釋放出來,以解燃眉之急。本期教...
文件被win10系統(tǒng)誤報(bào)病毒自動(dòng)刪除了如何進(jìn)行恢復(fù)?有用戶下載了某些破解軟件卻被Win10系統(tǒng)誤認(rèn)為是病毒文件而自動(dòng)刪除,當(dāng)然系統(tǒng)自帶殺毒軟件其實(shí)挺不錯(cuò)的,就是有時(shí)候會(huì)誤報(bào),大家遇到這種情況的時(shí)候就希望把誤刪的...
win11系統(tǒng)快速跳過聯(lián)網(wǎng)創(chuàng)建本地管理賬戶3種方法?現(xiàn)在市面上銷售的品牌筆記本和臺(tái)式機(jī)基本上都預(yù)裝Windows11家庭中文版正版操作系統(tǒng),聯(lián)網(wǎng)后系統(tǒng)會(huì)自動(dòng)激活。當(dāng)用戶拿到新機(jī)器后還需要按照cortana(小娜)的提示一步...
羅技g304dpi燈顏色代表什么:1、藍(lán)色:這種情況是正常工作的顯示,如果說是常亮或者閃爍,那都沒有問題這是在正常工作呢。2、紅色:如果說是紅燈閃爍的話那就是 ......
答:在3DMark壓力測(cè)試當(dāng)中,顯卡需要超高97%才能夠算合格,證明顯卡的穩(wěn)定性是過關(guān)的。 1、一般的默認(rèn)情況下在2500~3000分就算很正常的了。 2、分?jǐn)?shù)越高說明顯卡 ......
1、先打開機(jī)頂盒進(jìn)入主界面,并且使用遙控器打開設(shè)置。 2、然后選擇“賬號(hào)與安全”,并且進(jìn)入。 3、最后往下面翻就可以看到“ADB調(diào)試”的選項(xiàng),直接開啟就行了 ......
相信有非常多使用過筆記本的用戶都聽說過獨(dú)顯直連這個(gè)詞,但很多用戶并不了解獨(dú)顯直連是什么,又有什么用處,那么下面就和小編一起來看看什么是獨(dú)顯直連和開啟這 ......
win11系統(tǒng)開機(jī)總是自動(dòng)登錄OneDrive如何關(guān)閉?win11系統(tǒng)開機(jī)的時(shí)候,會(huì)自動(dòng)啟動(dòng)OneDrive,不想要啟動(dòng),該怎么操作呢?下面我們就來看看詳細(xì)的教程。 在OneDrive界面點(diǎn)小齒輪按鈕,下拉菜單中點(diǎn)【設(shè)置】。 單擊【...
答:中高端水平 i513500hx在處理器當(dāng)中是處于一個(gè)中高端的水平。 i513500hx是第十一代酷睿處理器系列的一員,基礎(chǔ)頻率為2.4GHz,表現(xiàn)十分的不錯(cuò)。 i513500hx介 ......
1、首先確認(rèn)手機(jī)型號(hào)是否支持無線充電功能,(可以在品牌官網(wǎng)找到手機(jī)信息查看)2、查看充電板的指示燈是否亮起。指示燈不亮檢查充電器、數(shù)據(jù)線、電源之間連接是 ......
背景 有時(shí)候我們需要獲取文件的創(chuàng)建時(shí)間。 例如: 我在研究 《xtrabackup 原理圖》的時(shí)候,想通過觀察確認(rèn) xtrabackup_log 是最早創(chuàng)建 并且是 最晚保存的 ......