用户模块 - IP归属地技术方案
一、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 解析时,经常会遇到高并发的情况——多个请求同时发起,可能会导致接口被频繁访问,从而触发访问限制。这时候,我们需要一个管理框架来优化请求的处理。
-
并发请求排队处理
-
为了避免接口被过度访问,我们可以使用 请求排队 的方式,将多个请求按顺序处理。每个请求在发起时,会被放入一个队列,依次进行处理。这样就能有效避免接口请求过多导致的错误。
-
-
任务重试机制
-
如果某次请求失败(例如,由于接口访问限制而返回错误),我们可以设置一个 重试机制。系统会自动重试请求,直到达到最大重试次数为止。这样可以提高系统的稳定性。
-
当然,为了避免无限重试,我们会设置一个 最大重试次数,超过次数后就不再重试,以免造成资源浪费。
-
-
异步执行
-
请求的处理可以使用 异步 的方式进行。也就是说,系统可以在处理请求时,不会阻塞其他操作,允许其他请求继续进行。这样可以提高系统的响应速度,避免因一个请求而拖慢整个系统的运行。
-
-
使用线程池
-
为了高效管理这些并发请求和任务重试,我们可以使用 线程池。线程池可以提前创建一定数量的线程,当有请求需要处理时,可以直接从线程池中获取线程,而不需要每次都创建新的线程。这样可以减少线程创建的开销,提升处理效率。
-