一、IP 获取方式
在用户访问你的网站时,系统需要获取用户的 IP 地址,这个地址可以帮助你了解用户的地理位置、设备信息等。获取用户的 IP 地址通常有以下几种方式:
HTTP 请求中的 IP 地址
当用户访问网站时,浏览器会通过 HTTP 请求发送信息到服务器,其中就包含了用户的 IP 地址。你可以直接从 HTTP 请求中提取这个地址。
例如,在 Java 中可以通过
request.getRemoteAddr()
来获取访问者的 IP 地址。
使用 Hutool 工具类
如果你使用的是 Java 开发,可以通过一个非常方便的工具库叫 Hutool 来简化 IP 获取的过程。它提供了
IpUtil.getIpAddr()
方法,直接帮你提取用户的 IP 地址,避免了很多复杂的操作。
Nginx 代理场景下的 IP 获取
如果你的服务器前面有 Nginx 作为反向代理,Nginx 会把用户的原始 IP 地址存储在请求头部的
X-Forwarded-For
字段中。你需要从这个字段中提取真实的 IP 地址。例如,在代码中可以通过
request.getHeader("X-Forwarded-For")
获取到这个字段。
二、WebSocket 场景处理
WebSocket 是一种可以在客户端和服务器之间建立持久连接的协议,常用于实时通信(例如,聊天应用、实时通知等)。与普通的 HTTP 请求不同,WebSocket 连接一旦建立,双方可以持续交换数据。这里我们要处理的是如何获取用户的 IP 地址。
WebSocket 初次连接时获取 IP
当用户通过浏览器建立 WebSocket 连接时,连接过程会先经过 HTTP 协议进行“升级”(upgrade)。这时,你可以从 HTTP 请求中获取到用户的 IP 地址。
也就是说,WebSocket 的建立过程和普通的 HTTP 请求是类似的,你依然可以使用前面提到的方法从请求中获取 IP。
在连接建立时保存 IP
用户的 IP 地址在 WebSocket 连接建立时会被记录下来,这样你就知道这个用户在该连接期间的 IP 地址。这个 IP 可以用来后续的用户行为追踪或安全防护等。
三、IP 存储设计
在我们的系统中,每个用户的 IP 地址需要保存,以便以后使用或分析。为了有效管理 IP 信息,我们设计了两个字段来存储用户的 IP 地址:createIp
和 updateIp
。
createIp
- 用户注册时记录的 IP当用户注册账户时,我们会记录下用户当时的 IP 地址,称之为
createIp
。这个 IP 地址代表了用户最初创建账户时使用的设备和网络。这个 IP 是比较固定的,它通常不会随便改变,除非用户换了设备或网络。
updateIp
- 用户每次登录或连接时更新的 IP用户每次登录系统或重新连接 WebSocket 时,我们会更新他们的 IP 地址,称之为
updateIp
。这个 IP 地址可能会发生变化,因为用户可能在不同的设备、不同的网络下登录。updateIp
是动态更新的,每次用户活动时都会记录。
扩展信息存储
用户的 IP 信息通常是与其他用户信息一起存储的。我们可以使用 JSON 格式 来保存这些额外的信息,方便扩展。
MySQL 数据库支持对 JSON 字段进行查询筛选,所以我们可以根据需要轻松提取某些字段,比如获取某个用户的注册 IP 或最后一次更新的 IP。
四、IP 归属地解析方案
获取了用户的 IP 地址后,我们常常需要知道这个 IP 地址对应的地理位置,比如用户在哪个城市、国家。这就是所谓的 IP 归属地解析。常见的解决方案有两种:使用本地的数据库和调用外部接口。
ip2region 本地库
ip2region 是一个开源的 IP 归属地查询工具,它通过本地的数据库来查询 IP 的地理位置。使用这种方式,查询速度非常快,因为不需要每次都访问网络。
但是,这个工具有个缺点:它不会主动更新,需要定期手动更新数据库。此外,启动时需要加载较大的数据,所以会消耗一些内存。
淘宝开放接口
淘宝提供了一个免费的 IP 归属地查询接口,用户可以通过访问该接口来获得 IP 地址的归属地信息。它的数据较为精准,且使用方便,不需要维护本地数据库。
不过,淘宝对接口的访问频率有一定限制。如果请求太多,会被限制访问。所以在高频率访问的情况下,需要特别注意接口的调用次数。
五、解析请求管理框架
当我们使用外部接口(比如淘宝的 IP 归属地接口)进行 IP 解析时,经常会遇到高并发的情况——多个请求同时发起,可能会导致接口被频繁访问,从而触发访问限制。这时候,我们需要一个管理框架来优化请求的处理。
并发请求排队处理
为了避免接口被过度访问,我们可以使用 请求排队 的方式,将多个请求按顺序处理。每个请求在发起时,会被放入一个队列,依次进行处理。这样就能有效避免接口请求过多导致的错误。
任务重试机制
如果某次请求失败(例如,由于接口访问限制而返回错误),我们可以设置一个 重试机制。系统会自动重试请求,直到达到最大重试次数为止。这样可以提高系统的稳定性。
当然,为了避免无限重试,我们会设置一个 最大重试次数,超过次数后就不再重试,以免造成资源浪费。
异步执行
请求的处理可以使用 异步 的方式进行。也就是说,系统可以在处理请求时,不会阻塞其他操作,允许其他请求继续进行。这样可以提高系统的响应速度,避免因一个请求而拖慢整个系统的运行。
使用线程池
为了高效管理这些并发请求和任务重试,我们可以使用 线程池。线程池可以提前创建一定数量的线程,当有请求需要处理时,可以直接从线程池中获取线程,而不需要每次都创建新的线程。这样可以减少线程创建的开销,提升处理效率。