cari
Rumahpangkalan dataRedisredis实现session共享的方法介绍

redis实现session共享的方法介绍

May 12, 2020 am 09:19 AM
redis

 

redis实现session共享的方法介绍

Session实现原理

session和cookie是我们做web开发中常用到的两个对象,它们之间会不会有联系呢?

php中文网学习专题php session (包含图文、视频、案例)

Cookie是什么? 

Cookie 是一小段文本信息,伴随着用户请求和页面在 Web 服务器和浏览器之间传递。Cookie 包含每次用户访问站点时 Web 应用程序都可以读取的信息。

注:Cookie 会随每次HTTP请求一起被传递服务器端,排除js,css,image等静态文件,这个过程可以从fiddler或者ie自带的网络监控里面分析到,考虑性能的化可以从尽量减少cookie着手

Cookie写入浏览器的过程:我们可以使用如下代码在Asp.net项目中写一个Cookie 并发送到客户端的浏览器(为了简单我没有设置其它属性)。

HttpCookie cookie = new HttpCookie("RedisSessionId", "string value");Response.Cookies.Add(cookie);

1.jpg

我们可以看到在服务器写的cookie,会通过响应头Set-Cookie的方式写入到浏览器。

Session是什么? 

Session我们可以使用它来方便地在服务端保存一些与会话相关的信息。比如常见的登录信息。

Session实现原理? 

HTTP协议是无状态的,对于一个浏览器发出的多次请求,WEB服务器无法区分 是不是来源于同一个浏览器。所以服务器为了区分这个过程会通过一个sessionid来区分请求,而这个sessionid是怎么发送给服务端的呢?

前面说了cookie会随每次请求发送到服务端,并且cookie相对用户是不可见的,用来保存这个sessionid是最好不过了,我们通过下面过程来验证一下。

Session["UserId"] = 123;

2.jpg

通过上图再次验证了session和cookie的关系,服务器产生了一次设置cookie的操作,这里的sessionid就是用来区分浏览器的。为了实验是区分浏览器的,可以实验在IE下进行登录,然后在用chrome打开相同页面,你会发现在chrome还是需要你登录的,原因是chrome这时没有sessionid。httpOnly是表示这个cookie是不会在浏览器端通过js进行操作的,防止人为串改sessionid。

asp.net默认的sessionid的键值是ASP.NET_SessionId,可以在web.config里面修改这个默认配置

<sessionState mode="InProc" cookieName="MySessionId"></sessionState>

服务器端Session读取

服务器端是怎么读取session的值呢 ,Session["键值"]。那么问题来了,为什么在Defaule.aspx.cs文件里可以获取到这个Session对象,这个Session对象又是什么时候被初始化的呢。

为了弄清楚这个问题,我们可以通过转到定义的方式来查看。

System.Web.UI.Page ->HttpSessionState(Session)

protected internal override HttpContext Context {
[System.Runtime.TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
  get {
       if (_context == null) {
           _context = HttpContext.Current;
       }
       return _context;
    }
 }
 public virtual HttpSessionState Session {
        get {
            if (!_sessionRetrieved) {
                /* try just once to retrieve it */
                _sessionRetrieved = true;

                try {
                    _session = Context.Session;
                }
                catch {
                    //  Just ignore exceptions, return null.
                }
            }

            if (_session == null) {
                throw new HttpException(SR.GetString(SR.Session_not_enabled));
            }

            return _session;
        }
    }

上面这一段是Page对象初始化Session对象的,可以看到Session的值来源于HttpContext.Current,而HttpContext.Current又是什么时候被初始化的呢,我们接着往下看。

public sealed class HttpContext : IServiceProvider, IPrincipalContainer
    {

        internal static readonly Assembly SystemWebAssembly = typeof(HttpContext).Assembly;
        private static volatile bool s_eurlSet;
        private static string s_eurl;

        private IHttpAsyncHandler  _asyncAppHandler;   // application as handler (not always HttpApplication)
        private AsyncPreloadModeFlags _asyncPreloadModeFlags;
        private bool               _asyncPreloadModeFlagsSet;
        private HttpApplication    _appInstance;
        private IHttpHandler       _handler;
        [DoNotReset]
        private HttpRequest        _request;
        private HttpResponse       _response;
        private HttpServerUtility  _server;
        private Stack              _traceContextStack;
        private TraceContext       _topTraceContext;
        [DoNotReset]
        private Hashtable          _items;
        private ArrayList          _errors;
        private Exception          _tempError;
        private bool               _errorCleared;
        [DoNotReset]
        private IPrincipalContainer _principalContainer;
        [DoNotReset]
        internal ProfileBase       _Profile;
        [DoNotReset]
        private DateTime           _utcTimestamp;
        [DoNotReset]
        private HttpWorkerRequest  _wr;
        private VirtualPath        _configurationPath;
        internal bool              _skipAuthorization;
        [DoNotReset]
        private CultureInfo        _dynamicCulture;
        [DoNotReset]
        private CultureInfo        _dynamicUICulture;
        private int                _serverExecuteDepth;
        private Stack              _handlerStack;
        private bool               _preventPostback;
        private bool               _runtimeErrorReported;
        private PageInstrumentationService _pageInstrumentationService = null;
        private ReadOnlyCollection<string> _webSocketRequestedProtocols;
}

HttpContext包含了我们常用的Request,Response等对象。HttpContext得从ASP.NET管道说起,以IIS 6.0为例,在工作进程w3wp.exe中,利用Aspnet_ispai.dll加载.NET运行时(如果.NET运行时尚未加载)。

IIS 6.0引入了应用程序池的概念,一个工作进程对应着一个应用程序池。一个应用程序池可以承载一个或多个Web应用,每个Web应用映射到一个IIS虚拟目录。与IIS 5.x一样,每一个Web应用运行在各自的应用程序域中。

如果HTTP.SYS接收到的HTTP请求是对该Web应用的第一次访问,在成功加载了运行时后,会通过AppDomainFactory为该Web应用创建一个应用程序域(AppDomain)。

随后,一个特殊的运行时IsapiRuntime被加载。IsapiRuntime定义在程序集System.Web中,对应的命名空间为System.Web.Hosting。

IsapiRuntime会接管该HTTP请求。IsapiRuntime会首先创建一个IsapiWorkerRequest对象,用于封装当前的HTTP请求,并将该IsapiWorkerRequest对象传递给ASP.NET运行时:HttpRuntime,从此时起,HTTP请求正式进入了ASP.NET管道。

根据IsapiWorkerRequest对象,HttpRuntime会创建用于表示当前HTTP请求的上下文(Context)对象:HttpContext。

至此相信大家对Session初始化过程,session和cookie的关系已经很了解了吧,下面开始进行Session共享实现方案。 

Session共享实现方案

一.StateServer方式

这种是asp.net提供的一种方式,还有一种是SQLServer方式(不一定程序使用的是SQLServer数据库,所以通用性不高,这里就不介绍了)。也就是将会话数据存储到单独的内存缓冲区中,再由单独一台机器上运行的Windows服务来控制这个缓冲区。

状态服务全称是“ASP.NET State Service ”(aspnet_state.exe)。它由Web.config文件中的stateConnectionString属性来配置。该属性指定了服务所在的服务器,以及要监视的端口。

<sessionState mode="StateServer"      stateConnectionString="tcpip=127.0.0.1:42424"     cookieless="false" timeout="20" />

在这个例子中,状态服务在当前机器的42424端口(默认端口)运行。要在服务器上改变端口和开启远程服务器的该功能,可编辑HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\aspnet_state\Parameters注册表项中的Port值和AllowRemoteConnection修改成1。

 显然,使用状态服务的优点在于进程隔离,并可在多站点中共享。 使用这种模式,会话状态的存储将不依赖于iis进程的失败或者重启,然而,一旦状态服务中止,所有会话数据都会丢失(这个问题redis不会存在,重新了数据不会丢失)。

这里提供一段bat文件帮助修改注册表,可以复制保存为.bat文件执行

reg add "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\aspnet_state\Parameters" /v "AllowRemoteConnection" /t REG_DWORD  /d 1 /f

reg add "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\aspnet_state\Parameters" /v "Port" /t REG_DWORD  /d 42424 /f

net stop aspnet_state
net start aspnet_state

pause

4.jpg

完成这些配置以后还是不能实现共享,虽然站点间的SessionId是一致的,但只有一个站点能够读取的到值,而其它站点读取不到。下面给出解决方案,在Global文件里面添加下面代码

public override void Init()
 {
      base.Init();

      foreach (string moduleName in this.Modules)
      {
           string appName = "APPNAME";
           IHttpModule module = this.Modules[moduleName];
           SessionStateModule ssm = module as SessionStateModule;
           if (ssm != null)
           {
                FieldInfo storeInfo = typeof(SessionStateModule).GetField("_store", BindingFlags.Instance | BindingFlags.NonPublic);
                FieldInfo configMode = typeof(SessionStateModule).GetField("s_configMode", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Static);

                SessionStateMode mode = (SessionStateMode)configMode.GetValue(ssm);
                if (mode == SessionStateMode.StateServer)
                {
                    SessionStateStoreProviderBase store = (SessionStateStoreProviderBase)storeInfo.GetValue(ssm);
                    if (store == null)//In IIS7 Integrated mode, module.Init() is called later
                    {
                        FieldInfo runtimeInfo = typeof(HttpRuntime).GetField("_theRuntime", BindingFlags.Static | BindingFlags.NonPublic);
                        HttpRuntime theRuntime = (HttpRuntime)runtimeInfo.GetValue(null);
                        FieldInfo appNameInfo = typeof(HttpRuntime).GetField("_appDomainAppId", BindingFlags.Instance | BindingFlags.NonPublic);
                        appNameInfo.SetValue(theRuntime, appName);
                    }
                    else
                    {
                       Type storeType = store.GetType();
                       if (storeType.Name.Equals("OutOfProcSessionStateStore"))
                       {
                           FieldInfo uribaseInfo = storeType.GetField("s_uribase", BindingFlags.Static | BindingFlags.NonPublic);
                           uribaseInfo.SetValue(storeType, appName);
                           object obj = null;
                           uribaseInfo.GetValue(obj);
                        }
                    }
                }
                break;
            }
        }
  }

二.redis实现session共享

下面我们将使用redis来实现共享,首先要弄清楚session的几个关键点,过期时间,SessionId,一个SessionId里面会存在多组key/value数据。基于这个特性我将采用Hash结构来存储,看看代码实现。用到了上一篇提供的RedisBase帮助类。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.SessionState;
using ServiceStack.Redis;
using Com.Redis;

namespace ResidSessionDemo.RedisDemo
{
    public class RedisSession
    {
        private HttpContext context;

        public RedisSession(HttpContext context, bool IsReadOnly, int Timeout)
        {
            this.context = context;
            this.IsReadOnly = IsReadOnly;
            this.Timeout = Timeout;
            //更新缓存过期时间
            RedisBase.Hash_SetExpire(SessionID, DateTime.Now.AddMinutes(Timeout));
        }

        /// <summary>
        /// SessionId标识符
        /// </summary>
        public static string SessionName = "Redis_SessionId";

        //
        // 摘要:
        //     获取会话状态集合中的项数。
        //
        // 返回结果:
        //     集合中的项数。
        public int Count
        {
            get
            {
                return RedisBase.Hash_GetCount(SessionID);
            }
        }

        //
        // 摘要:
        //     获取一个值,该值指示会话是否为只读。
        //
        // 返回结果:
        //     如果会话为只读,则为 true;否则为 false。
        public bool IsReadOnly { get; set; }

        //
        // 摘要:
        //     获取会话的唯一标识符。
        //
        // 返回结果:
        //     唯一会话标识符。
        public string SessionID
        {
            get
            {
                return GetSessionID();
            }
        }

        //
        // 摘要:
        //     获取并设置在会话状态提供程序终止会话之前各请求之间所允许的时间(以分钟为单位)。
        //
        // 返回结果:
        //     超时期限(以分钟为单位)。
        public int Timeout { get; set; }

        /// <summary>
        /// 获取SessionID
        /// </summary>
        /// <param name="key">SessionId标识符</param>
        /// <returns>HttpCookie值</returns>
        private string GetSessionID()
        {
            HttpCookie cookie = context.Request.Cookies.Get(SessionName);
            if (cookie == null || string.IsNullOrEmpty(cookie.Value))
            {
                string newSessionID = Guid.NewGuid().ToString();
                HttpCookie newCookie = new HttpCookie(SessionName, newSessionID);
                newCookie.HttpOnly = IsReadOnly;
                newCookie.Expires = DateTime.Now.AddMinutes(Timeout);
                context.Response.Cookies.Add(newCookie);
                return "Session_"+newSessionID;
            }
            else
            {
                return "Session_"+cookie.Value;
            }
        }

        //
        // 摘要:
        //     按名称获取或设置会话值。
        //
        // 参数:
        //   name:
        //     会话值的键名。
        //
        // 返回结果:
        //     具有指定名称的会话状态值;如果该项不存在,则为 null。
        public object this[string name]
        {
            get
            {
                return RedisBase.Hash_Get<object>(SessionID, name);
            }
            set
            {
                RedisBase.Hash_Set<object>(SessionID, name, value);
            }
        }

        // 摘要:
        //     判断会话中是否存在指定key
        //
        // 参数:
        //   name:
        //     键值
        //
        public bool IsExistKey(string name)
        {
            return RedisBase.Hash_Exist<object>(SessionID, name);
        }

        //
        // 摘要:
        //     向会话状态集合添加一个新项。
        //
        // 参数:
        //   name:
        //     要添加到会话状态集合的项的名称。
        //
        //   value:
        //     要添加到会话状态集合的项的值。
        public void Add(string name, object value)
        {
            RedisBase.Hash_Set<object>(SessionID, name, value);
        }
        //
        // 摘要:
        //     从会话状态集合中移除所有的键和值。
        public void Clear()
        {
            RedisBase.Hash_Remove(SessionID);
        }

        //
        // 摘要:
        //     删除会话状态集合中的项。
        //
        // 参数:
        //   name:
        //     要从会话状态集合中删除的项的名称。
        public void Remove(string name)
        {
            RedisBase.Hash_Remove(SessionID,name);
        }
        //
        // 摘要:
        //     从会话状态集合中移除所有的键和值。
        public void RemoveAll()
        {
            Clear();
        }
    }
}

下面是实现类似在cs文件中能直接使用Session["UserId"]的方式,我的MyPage类继承Page实现了自己的逻辑主要做了两件事  1:初始化RedisSession  2:实现统一登录认证,OnPreInit方法里面判断用户是否登录,如果没有登录了则跳转到登陆界面

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;

namespace ResidSessionDemo.RedisDemo
{
    /// <summary>
    /// 自定义Page 实现以下功能
    /// 1.初始化RedisSession
    /// 2.实现页面登录验证,继承此类,则可以实现所有页面的登录验证
    /// </summary>
    public class MyPage:Page
    {
        private RedisSession redisSession;

        /// <summary>
        /// RedisSession
        /// </summary>
        public RedisSession RedisSession
        {
            get
            {
                if (redisSession == null)
                {
                    redisSession = new RedisSession(Context, true, 20);
                }
                return redisSession;
            }
        }

        protected override void OnPreInit(EventArgs e)
        {
            base.OnPreInit(e);
            //判断用户是否已经登录,如果未登录,则跳转到登录界面
            if (!RedisSession.IsExistKey("UserCode"))
            {
                Response.Redirect("Login.aspx");
            }
        }
    }
}

我们来看看Default.aspx.cs是如何使用RedisSession的,至此我们实现了和Asp.netSession一模一样的功能和使用方式。

RedisSession.Remove("UserCode");

相比StateServer,RedisSession具有以下优点

1、redis服务器重启不会丢失数据  2.可以使用redis的读写分离个集群功能更加高效读写数据  

测试效果,使用nginx和iis部署两个站点做负载均衡,iis1地址127.0.0.1:8002 iis2地址127.0.0.1:9000  nginx代理服务地址127.0.0.1:8003,不懂如何配置的可以去阅读我的nginx+iis实现负载均衡这篇文章。我们来看一下测试结果。

访问127.0.0.1:8003 需要进行登录   用户名为admin  密码为123

5.jpg

登录成功以后,重点关注端口号信息

6.JPG

刷新页面,重点关注端口号信息

7.jpg

可以尝试直接访问iis1地址127.0.0.1:8002 iis2地址127.0.0.1:9000 这两个站点,你会发现都不需要登录了。至此我们的redis实现session功能算是大功告成了。

问题拓展

使用redis实现session告一段落,下面留个问题讨论一下方案。微信开发提供了很多接口,参考下面截图,可以看到获取access_token接口每日最多调用2000次,现在大公司提供的很多接口针对不对级别的用户接口访问次数限制都是不一样的,至于做这个限制的原因应该是防止恶意攻击和流量限制之类的。

那么我的问题是怎么实现这个接口调用次数限制功能。大家可以发挥想象力参与讨论哦,或许你也会碰到这个问题。

8.jpg

先说下我知道的两种方案:

1、使用流量整形中的令牌桶算法,大小固定的令牌桶可自行以恒定的速率源源不断地产生令牌。如果令牌不被消耗,或者被消耗的速度小于产生的速度,令牌就会不断地增多,直到把桶填满。后面再产生的令牌就会从桶中溢出。最后桶中可以保存的最大令牌数永远不会超过桶的大小。

说浅显点:比如上面的获取access_token接口,一天2000次的频率,即1次/分钟。我们令牌桶容量为2000,可以使用redis 最简单的key/value来存储 ,key为用户id,value为整形存储还可使用次数,然后使用一个定时器1分钟调用client.Incr(key) 实现次数自增;用户每访问一次该接口,相应的client.Decr(key)来减少使用次数。

但是这里存在一个性能问题,这仅仅是针对一个用户来说,假设有10万个用户,怎么使用定时器来实现这个自增操作呢,难道是循环10万次分别调用client.Incr(key)吗?这一点没有考虑清楚。

2、直接用户访问一次 先进行总次数判断,符合条件再就进行一次自增

9.jpg

更多redis知识请关注入redis门教程栏目。

 

Atas ialah kandungan terperinci redis实现session共享的方法介绍. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan
Artikel ini dikembalikan pada:博客园. Jika ada pelanggaran, sila hubungi admin@php.cn Padam
Redis: Meningkatkan prestasi aplikasi dan skalabilitiRedis: Meningkatkan prestasi aplikasi dan skalabilitiApr 17, 2025 am 12:16 AM

Redis meningkatkan prestasi aplikasi dan skalabiliti dengan data caching, melaksanakan penguncian dan ketekunan data yang diedarkan. 1) Data cache: Gunakan REDIS ke cache data yang sering diakses untuk meningkatkan kelajuan akses data. 2) Kunci yang diedarkan: Gunakan Redis untuk melaksanakan kunci yang diedarkan untuk memastikan keselamatan operasi dalam persekitaran yang diedarkan. 3) Kegigihan data: Memastikan keselamatan data melalui mekanisme RDB dan AOF untuk mencegah kehilangan data.

Redis: Meneroka Model dan Struktur DatanyaRedis: Meneroka Model dan Struktur DatanyaApr 16, 2025 am 12:09 AM

Model dan struktur data Redis termasuk lima jenis utama: 1. String: Digunakan untuk menyimpan teks atau data binari, dan menyokong operasi atom. 2. Senarai: Koleksi Elemen yang Diarahkan, sesuai untuk beratur dan susunan. 3. Set: unsur -unsur unik yang tidak teratur ditetapkan, menyokong operasi set. 4. Diarahkan Set (SortedSet): Satu set elemen yang unik dengan skor, sesuai untuk kedudukan. 5. Jadual Hash (Hash): Koleksi pasangan nilai utama, sesuai untuk menyimpan objek.

Redis: mengklasifikasikan pendekatan pangkalan datanyaRedis: mengklasifikasikan pendekatan pangkalan datanyaApr 15, 2025 am 12:06 AM

Kaedah pangkalan data Redis termasuk pangkalan data dalam memori dan penyimpanan nilai utama. 1) Redis menyimpan data dalam ingatan, dan membaca dan menulis dengan cepat. 2) Ia menggunakan pasangan nilai utama untuk menyimpan data, menyokong struktur data kompleks seperti senarai, koleksi, jadual hash dan koleksi yang diperintahkan, sesuai untuk pangkalan data cache dan NoSQL.

Mengapa menggunakan Redis? Faedah dan kelebihanMengapa menggunakan Redis? Faedah dan kelebihanApr 14, 2025 am 12:07 AM

REDIS adalah penyelesaian pangkalan data yang kuat kerana ia menyediakan prestasi cepat, struktur data yang kaya, ketersediaan dan skalabilitas yang tinggi, keupayaan kegigihan, dan pelbagai sokongan ekosistem. 1) Prestasi yang sangat cepat: Data Redis disimpan dalam ingatan dan mempunyai kelajuan membaca dan menulis yang sangat cepat, sesuai untuk aplikasi kesesuaian yang tinggi dan rendah. 2) Struktur data yang kaya: Menyokong pelbagai jenis data, seperti senarai, koleksi, dan lain -lain, yang sesuai untuk pelbagai senario. 3) Ketersediaan dan skalabilitas yang tinggi: Menyokong replikasi master-hamba dan mod kluster untuk mencapai ketersediaan yang tinggi dan berskala mendatar. 4) Kegigihan dan keselamatan data: Ketekunan data dicapai melalui RDB dan AOF untuk memastikan integriti dan kebolehpercayaan data. 5) Sokongan ekosistem dan komuniti yang luas: dengan ekosistem yang besar dan komuniti aktif,

Memahami NoSQL: Ciri Utama RedisMemahami NoSQL: Ciri Utama RedisApr 13, 2025 am 12:17 AM

Ciri -ciri utama Redis termasuk kelajuan, fleksibiliti dan sokongan struktur data yang kaya. 1) Kelajuan: Redis adalah pangkalan data dalam memori, dan membaca dan menulis operasi hampir seketika, sesuai untuk pengurusan cache dan sesi. 2) Fleksibiliti: Menyokong pelbagai struktur data, seperti rentetan, senarai, koleksi, dan lain -lain, yang sesuai untuk pemprosesan data yang kompleks. 3) Sokongan Struktur Data: Menyediakan rentetan, senarai, koleksi, jadual hash, dan lain -lain, yang sesuai untuk keperluan perniagaan yang berbeza.

Redis: Mengenal pasti fungsi utamanyaRedis: Mengenal pasti fungsi utamanyaApr 12, 2025 am 12:01 AM

Fungsi teras Redis adalah sistem penyimpanan dan pemprosesan data berprestasi tinggi. 1) Akses data berkelajuan tinggi: Redis menyimpan data dalam memori dan menyediakan kelajuan membaca dan menulis tahap mikrosecond. 2) Struktur Data Kaya: Menyokong rentetan, senarai, koleksi, dan lain -lain, dan menyesuaikan diri dengan pelbagai senario aplikasi. 3) Kegigihan: Data berterusan ke cakera melalui RDB dan AOF. 4) Menerbitkan langganan: boleh digunakan dalam beratur mesej atau sistem komunikasi masa nyata.

Redis: Panduan untuk struktur data popularRedis: Panduan untuk struktur data popularApr 11, 2025 am 12:04 AM

Redis menyokong pelbagai struktur data, termasuk: 1. String, sesuai untuk menyimpan data nilai tunggal; 2. Senarai, sesuai untuk beratur dan susunan; 3. Tetapkan, digunakan untuk menyimpan data yang tidak duplikasi; 4. Diarahkan set, sesuai untuk senarai ranking dan beratur keutamaan; 5. Jadual hash, sesuai untuk menyimpan objek atau data berstruktur.

Cara Melaksanakan Kaunter RedisCara Melaksanakan Kaunter RedisApr 10, 2025 pm 10:21 PM

Kaunter Redis adalah satu mekanisme yang menggunakan penyimpanan pasangan nilai utama REDIS untuk melaksanakan operasi pengiraan, termasuk langkah-langkah berikut: mewujudkan kekunci kaunter, meningkatkan tuduhan, mengurangkan tuduhan, menetapkan semula, dan mendapatkan tuduhan. Kelebihan kaunter Redis termasuk kelajuan cepat, konkurensi tinggi, ketahanan dan kesederhanaan dan kemudahan penggunaan. Ia boleh digunakan dalam senario seperti pengiraan akses pengguna, penjejakan metrik masa nyata, skor permainan dan kedudukan, dan pengiraan pemprosesan pesanan.

See all articles

Alat AI Hot

Undresser.AI Undress

Undresser.AI Undress

Apl berkuasa AI untuk mencipta foto bogel yang realistik

AI Clothes Remover

AI Clothes Remover

Alat AI dalam talian untuk mengeluarkan pakaian daripada foto.

Undress AI Tool

Undress AI Tool

Gambar buka pakaian secara percuma

Clothoff.io

Clothoff.io

Penyingkiran pakaian AI

AI Hentai Generator

AI Hentai Generator

Menjana ai hentai secara percuma.

Artikel Panas

R.E.P.O. Kristal tenaga dijelaskan dan apa yang mereka lakukan (kristal kuning)
1 bulan yang laluBy尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Tetapan grafik terbaik
1 bulan yang laluBy尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Cara Memperbaiki Audio Jika anda tidak dapat mendengar sesiapa
1 bulan yang laluBy尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Arahan sembang dan cara menggunakannya
1 bulan yang laluBy尊渡假赌尊渡假赌尊渡假赌

Alat panas

Hantar Studio 13.0.1

Hantar Studio 13.0.1

Persekitaran pembangunan bersepadu PHP yang berkuasa

SublimeText3 Linux versi baharu

SublimeText3 Linux versi baharu

SublimeText3 Linux versi terkini

Muat turun versi mac editor Atom

Muat turun versi mac editor Atom

Editor sumber terbuka yang paling popular

SublimeText3 versi Mac

SublimeText3 versi Mac

Perisian penyuntingan kod peringkat Tuhan (SublimeText3)

VSCode Windows 64-bit Muat Turun

VSCode Windows 64-bit Muat Turun

Editor IDE percuma dan berkuasa yang dilancarkan oleh Microsoft