当前位置: 首页 > wzjs >正文

快速做课件的网站2022年seo最新优化策略

快速做课件的网站,2022年seo最新优化策略,b2c网站成本,广西执业药师培训网站一、获取用户 IP 并注入逻辑 在开发用户系统时,我们有时需要知道用户的真实 IP 地址,比如为了记录用户的登录地点。获取 IP 的最佳时机,就是用户刚发起请求的时候,而不是等到后面业务逻辑再来处理。 1. 在拦截器中获取 IP 我们通…

一、获取用户 IP 并注入逻辑

        在开发用户系统时,我们有时需要知道用户的真实 IP 地址,比如为了记录用户的登录地点。获取 IP 的最佳时机,就是用户刚发起请求的时候,而不是等到后面业务逻辑再来处理。

1. 在拦截器中获取 IP

        我们通常会在拦截器(Interceptor)中处理用户请求,比如过去我们在这里提取 token,现在我们可以在同样的位置提取用户 IP。

        不过需要注意的是,用户请求经过了 Nginx 转发,不能直接使用 request.getRemoteAddr() 来获取 IP,这样得到的是网关地址,不是真实 IP。

正确做法是让 Nginx 把用户 IP 放到请求头中,比如:

X-Real-IP: 用户真实 IP

我们只要在拦截器中读取这个请求头即可:

String ip = request.getHeader("X-Real-IP");

2. IP 信息暂存到 channel 附件中

拿到 IP 后,我们可以把它存到一个临时存储位置(通常叫做“附件”)中,后面的登录逻辑会用到它。

这里用的是 Netty 的 Channel 对象,我们可以像存储 token 一样,把 IP 放进去:

channel.attr(AttributeKey.valueOf("ip")).set(ip);

这样就把 IP 临时存好了。

3. 登录逻辑中使用 IP 后移除

这个 IP 只在登录逻辑中用一次,用完后就可以从 channel 附件中移除,避免多余数据残留。

小结

  • IP 读取要从 Nginx 的请求头中拿;

  • 拿到后存到 channel 附件中;

  • 登录用一次后就删掉;

  • 整个流程只做一次,简洁高效。

二、用户上线事件与 IP 信息封装

        IP 获取完后,下一步就是记录并封装 IP 信息,以便后续分析用户行为,比如判断是否异地登录、展示登录地点等。

1. 用户上线事件

用户成功登录后,我们会把这个“用户上线”的动作作为一个事件抛出去。为什么要这样做?

简单来说,这样可以解耦,比如登录逻辑专注于验证账号密码,IP 解析等事情可以通过事件异步处理,不影响主流程的速度。

抛出事件的代码很简单(示意):

eventBus.post(new UserOnlineEvent(userId));

2. 创建 IpInfo 实体类

        为了更方便管理 IP 数据,我们单独封装了一个 IpInfo 类,用来保存用户的 IP 信息。常见字段有:

  • createIp:用户注册时的 IP,只设置一次;

  • updateIp:用户每次登录时的 IP,会持续更新;

  • 其他解析出的归属地信息(如省份、城市、运营商等)。

结构大致如下:

public class IpInfo {private String createIp;private String updateIp;private String province;private String city;private String isp;// ...更多字段
}

3. 设置 IP 的时机

在用户登录成功的方法中,我们就来设置这些 IP 信息。

  • 如果是第一次登录(createIp 为空),就设置 createIp

  • 不管是不是第一次,都更新 updateIp

示意逻辑如下:

if (ipInfo.getCreateIp() == null) {ipInfo.setCreateIp(currentIp);
}
ipInfo.setUpdateIp(currentIp);

小结

  • 登录成功后抛出“上线事件”,用于异步处理;

  • 创建 IpInfo 类封装 IP 信息;

  • createIp 只记录一次,updateIp 每次都更新。

三、IP 解析框架设计

        拿到用户 IP 后,我们希望知道这个 IP 属于哪个省市、运营商。这就需要一个 IP 解析框架 —— 它的任务是根据 IP 地址查出归属地信息,并更新到用户资料中。

这个过程我们设计为异步处理,避免影响登录速度。

1. 在用户上线处理器中触发异步解析

        在“用户上线事件”被监听后,我们会触发一个处理器,负责进行 IP 解析。这一步是后台执行的,用户无感知。

为了不阻塞主线程,我们用线程池来异步执行解析任务。

2. 自定义线程池

我们自己定义一个专用线程池,主要考虑:

  • 线程命名明确,方便排查问题;

  • 核心线程数和最大线程数都设置为 1,表示串行处理;

  • 所有任务都用异步方式提交。

示意代码如下:

ExecutorService ipParserPool = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<>(),new NamedThreadFactory("ip-parser")
);

提交任务:

ipParserPool.submit(() -> parseIp(userId, currentIp));

3. 是否需要解析的判断逻辑

每次上线都解析 IP 会浪费资源。所以我们先对比:

  • 用户资料中 updateIp 是否和当前 IP 相同;

  • 如果一样,就不解析;

  • 如果不一样,说明 IP 变了,就执行解析逻辑。

判断示例:

if (!currentIp.equals(ipInfo.getUpdateIp())) {// IP 变了,去解析
}

4. 解析 IP 的具体逻辑

解析 IP 主要包括这几步:

(1)重试机制

有时解析服务会不稳定,为了提高成功率,我们设置 最多重试 3 次,每次失败 休眠 2 秒,并打印错误日志。

示意代码:

for (int i = 0; i < 3; i++) {try {// 发起解析请求break; // 成功就退出循环} catch (Exception e) {log.warn("解析失败,第 {} 次", i + 1, e);Thread.sleep(2000);}
}

(2)发起请求并反序列化

我们使用 Hutool 工具类来发起 HTTP 请求获取 IP 信息:

String response = HttpUtil.get("http://xxx.com/ip?ip=" + currentIp);

拿到返回的数据后,用 JsonUtil 工具类把它转成 Java 对象:

IpInfo info = JsonUtil.toBean(response, IpInfo.class);

最后,把解析结果更新到用户资料中即可。

小结

  • 用户上线后触发异步解析;

  • 解析框架用单线程池,串行执行;

  • 先判断 IP 是否已变,避免重复解析;

  • 最多重试 3 次,每次失败等待 2 秒;

  • 使用 Hutool 发请求,JsonUtil 解析数据,更新用户信息。

四、IP 框架吞吐量测试

        我们已经完成了 IP 解析框架的基本逻辑。那接下来就要测试一下它的性能 —— 比如:一秒钟能处理多少个 IP?这就是所谓的吞吐量测试

1. 为什么要测试?

  • 看看框架能不能扛住大量用户同时登录;

  • 帮助我们找到性能瓶颈,是否需要优化;

  • 为未来扩容做准备。

2. 怎么测?

我们写一个简单的测试方法,做两件事:

  1. 循环调用解析方法,比如连续解析 10 次;

  2. 记录开始时间和结束时间,算出每次大概需要多久。

示意代码如下:

long start = System.currentTimeMillis();for (int i = 0; i < 10; i++) {try {parseIp(userId, testIp); // 调用 IP 解析方法System.out.println("第 " + (i + 1) + " 次解析成功:" + System.currentTimeMillis());} catch (Exception e) {e.printStackTrace();}
}long end = System.currentTimeMillis();
System.out.println("总耗时:" + (end - start) + "ms");

测试结果显示:差不多每秒解析 1 个 IP,当然这也跟你网络、第三方服务快慢有关。

3. 测试中的异常处理

在测试过程中,如果解析失败,我们不希望影响整个流程。

所以加上简单的异常捕获,一旦失败,就返回 null,方便继续测试:

try {return parseIp(ip);
} catch (Exception e) {return null;
}

4. 优雅停机:关闭线程池

测试完毕后,别忘了关闭线程池,不然程序可能无法正常退出。

步骤如下:

  1. 调用 shutdown(),不再接受新任务;

  2. 设置最大等待时间,比如 10 秒;

  3. 如果还没完成,强制关闭。

示意代码:

ipParserPool.shutdown();
if (!ipParserPool.awaitTermination(10, TimeUnit.SECONDS)) {ipParserPool.shutdownNow();
}

小结

  • 吞吐量测试可以估算框架性能;

  • 循环调用解析方法,记录时间;

  • 异常要处理,不能中断整个测试;

  • 测试后要关闭线程池,程序才能优雅停机。

五、线程池优雅停机

        我们在 IP 解析中使用了线程池来异步执行任务。程序运行时一切正常,但如果线程池不关闭,程序可能无法退出或者资源一直占用

所以我们需要在系统关闭时,把线程池优雅地停掉

1. 什么是“优雅停机”?

优雅停机的意思是:

  • 不再接受新的任务;

  • 把正在执行的任务做完;

  • 给任务一点时间完成;

  • 超时没完成,就强制停止。

这样既不会浪费资源,也不会留下“未完成的事情”。

2. 停机代码怎么写?

假设我们的线程池叫 ipParserPool,优雅停机步骤如下:

// 1. 通知线程池:别接新任务了
ipParserPool.shutdown();// 2. 等最多 10 秒,看看线程池能不能处理完现有任务
if (!ipParserPool.awaitTermination(10, TimeUnit.SECONDS)) {// 3. 如果超过 10 秒还没停下来,那就强制关闭ipParserPool.shutdownNow();
}

解释一下:

  • shutdown():相当于说“停止接活”;

  • awaitTermination():最多等 10 秒,看有没有彻底停下来;

  • shutdownNow():如果还不行,直接打断所有线程,强制停机。

3. 这个操作放在哪?

一般放在项目的 @PreDestroy 方法或 Spring 的销毁钩子里。比如:

@PreDestroy
public void destroy() {// 上面的优雅停机代码就写在这里
}

Spring 容器关闭时,就会自动调用这个方法。

小结

  • 用了线程池,一定要在项目关闭时优雅停机

  • 做法是先 shutdown(),再 awaitTermination(),最后 shutdownNow()

  • 推荐把停机逻辑写在 @PreDestroy 方法中,自动触发。

http://www.dtcms.com/wzjs/293201.html

相关文章:

  • 制作小程序代码晨阳seo
  • 短视频运营公司网站建设无锡哪里有做网站的
  • WordPress减少数据库占有正规网络公司关键词排名优化
  • 化工销售怎么做网站网站策划方案书
  • 知名网站的org域名网站网络营销公司
  • app脚本制作教程优化软件有哪些
  • 怎样自己做电影网站产品网络推广方式
  • 嘉兴平湖网站建设深圳google推广
  • 完善网站建设通知简述网站建设流程
  • 网站建设的策划书今晚赛事比分预测
  • 在线生成多款表白网站是怎么做的seo运营是什么
  • 网站页尾的作用企业培训公司
  • frontpage做的社交网站做网站公司哪家比较好
  • 上海恒鑫网站建设一个网站可以优化多少关键词
  • logo免费自动生成器app杭州排名优化软件
  • 备案网站制作百度一下你就知道官网网页
  • 网站用花生壳nas做存储网络优化大师手机版
  • 供应邯郸做网站上海牛巨微seo
  • 单页网站域名seo词条
  • 中国河北建设银行官网招聘网站软文范例大全
  • html网站的上传seo优化的作用
  • 怎么做网站门户软文推广营销服务平台
  • 彩票网站开发搭建要看网的域名是多少
  • 做论坛网站怎么赚钱吗网站推广方案策划书2000
  • 建设网站征集图片的通知百度竞价代运营托管
  • 做水果的网站有哪些爱奇艺科技有限公司
  • 基于5G的网站设计seo关键词排名优化专业公司
  • 网站开发质保抖音广告投放代理商
  • 网站设计就业压力最新的域名网站
  • 网站开发招聘简历模板优化师培训