最后
最后,强调几点:
- 1. 一定要谨慎对待写在简历上的东西,一定要对简历上的东西非常熟悉。因为一般情况下,面试官都是会根据你的简历来问的; 能有一个上得了台面的项目也非常重要,这很可能是面试官会大量发问的地方,所以在面试之前好好回顾一下自己所做的项目;
- 2. 和面试官聊基础知识比如设计模式的使用、多线程的使用等等,可以结合具体的项目场景或者是自己在平时是如何使用的;
- 3. 注意自己开源的Github项目,面试官可能会挖你的Github项目提问;
我个人觉得面试也像是一场全新的征程,失败和胜利都是平常之事。所以,劝各位不要因为面试失败而灰心、丧失斗志。也不要因为面试通过而沾沾自喜,等待你的将是更美好的未来,继续加油!
以上面试专题的答小编案整理成面试文档了,文档里有答案详解,以及其他一些大厂面试题目。
面试答案
—启动调试,正式环境请注释
local mobdebug = require(“luaScript.initial.mobdebug”);
mobdebug.start();
–导入自定义的RedisOperator模块
local redisOp = require(“luaScript.redis.RedisOperator”);
–创建自定义的redis操作对象
local red = redisOp:new();
–打开连接
red:open();
–获取访问次数
local visitCount = red:incrValue(“demo:visitCount”);
if visitCount == 1 then
–10秒内过期
red:expire(“demo:visitCount”, 10);
end
–将访问次数设置到Nginx变量
ngx.var.count = visitCount;
–归还连接到连接池
red:close();
在nginx-redis-demo.conf配置文件中编写一个location配置块来使用该脚本,建议将该脚本执行于access阶段而不是content阶段,具体代码如下:
#点击次数统计的演示
location /visitcount {
#定义一个Nginx变量,用于在Lua脚本中保存访问次数
set $count 0;
access_by_lua_file luaScript/redis/RedisVisitCount.lua;
echo "10s内总的访问次数为: " $count;
}
修改nginx-redis-demo.conf文件后重启Openrestry,然后使用浏览器访问其地址/visitcount,并且在浏览器中不断刷新,发现每刷新一次,页面的统计次数会加一,其结果如图8-23所示。
图8-23 访问统计效果图
Nginx+Redis+Java容器实现高并发访问
==========================
在不需要高速访问的场景下,运行在Java后端的容器(如Tomcat)会直接从DB数据库(如MySQL)查询数据,然后返回给客户端。
由于数据库的连接数限制、网络传输延迟、数据库的IO频繁等多方面的原因,Java后端容器直接查询DB的性能会很低,这时会进行架构的调整,采用“Java容器+Redis+DB”的查询架构。针对数据一致性要求不是特别高但是访问频繁的API接口(实际上大部分都是),可以将DB数据放入Redis缓存,Java API可以优先查询Redis,如果缓存未命中,就回源到DB查询,从DB查询成功后再将数据更新到Redis缓存。
“Java容器+Redis+DB”的查询架构既起到Redis分流大量查询请求的作用,又大大提升了API接口的处理性能,可谓一举两得。该架构的请求处理流程如图8-24所示。
图8-24 “Java容器+Redis+DB”查询架构的请求处理流程
大家知道,常用的后端Java容器(如Tomcat、Jetty等)的性能其实不是太高,QPS性能指标一般会在1000以内。从笔者经历过的很多次性能攻关的数据来看,Nginx的性能是Java容器的10倍左右(甚至以上),并且稳定性更强,还不存在FullGC卡顿。
为了应对高并发,可以将“Java容器+Redis+DB”架构优化为“Nginx+Redis+Java容器”查询架构。新架构将后端Java容器的缓存判断、缓存查询前移到反向代理Nginx,通过Nginx直接进行Redis缓存判断、缓存查询。
“Nginx+Redis+Java容器”的查询架构不仅为Java容器减少了很多请求,而且能够充分发挥Nginx的高并发优势和稳定性优势。该架构的请求处理流程如图8-25所示。
图8-25 “Nginx+Redis+Java容器”查询架构的请求处理流程
这里以秒杀系统的商品数据查询为例提供一个“Nginx+Redis+Java容器”查询架构的参考实现。首先定义两个接口:一个模拟Java容器的商品查询接口;另一个模拟供外部调用的商品查询接口:
·模拟Java容器的商品查询接口:/java/good/detail。·模拟供外部调用的商品查询接口:/good/detail。
然后提供一个Lua操作缓存的类RedisCacheDemo,主要定义如下3个方法:
(1)getCache(self,goodId):根据商品id取得Redis商品缓存。
(2)goUpstream(self):通过capture内部请求访问上游接口获取商品数据。
(3)setCache(self,goodId,goodString):设置商品缓存,此方法用于模拟后台Java代码。
缓存操作类RedisCacheDemo的核心代码如下:
—启动调试,正式环境请注释
local mobdebug = require(“luaScript.initial.mobdebug”);
mobdebug.start();
–导入自定义的基础模块
local basic = require(“luaScript.module.common.basic”);
–导入自定义的RedisOperator模块
local redisOp = require(“luaScript.redis.RedisOperator”);
local PREFIX = “GOOD_CACHE:”
–RedisCacheDemo类
local _RedisCacheDemo = { }
_RedisCacheDemo.__index = _RedisCacheDemo
–类的方法new
function _RedisCacheDemo.new(self)
local object = {}
setmetatable(object, self)
return object;
end
–根据商品id取得缓存
function _RedisCacheDemo.getCache(self,goodId)
–创建自定义的redis操作对象
local red = redisOp:new();
–打开连接
if not red:open() then
basic:error(“redis连接失败”);
return nil;
end
–获取缓存数据 local json = red:getValue(PREFIX … goodId);
red:close();
if not json or json==ngx.null then
basic:log(goodId … “的缓存没有命中”);
return nil;
end
basic:log(goodId … “缓存成功命中”);
return json;
end
–通过capture方法回源上游接口
function _RedisCacheDemo.goUpstream(self)
local request_method = ngx.var.request_method
local args = nil
–获取参数的值
if “GET” == request_method then
args = ngx.req.get_uri_args()
elseif “POST” == request_method then
ngx.req.read_body()
args = ngx.req.get_post_args()
end
–回源上游接口,比如Java后端rest接口
local res = ngx.location.capture(“/java/good/detail”,{
method = ngx.HTTP_GET,
args = args --重要:将请求参数原样向上游传递
})
basic:log(“上游数据获取成功”);
–返回上游接口的响应体body
return res.body;
end
–设置缓存,此方法主要用于模拟Java后台代码
function _RedisCacheDemo.setCache(self, goodId ,goodString)
–创建自定义的redis操作对象
local red = redisOp:new();
–打开连接
if not red:open() then
basic:error(“redis连接失败”);
return nil;
end
–set缓存数据
red:setValue(PREFIX … goodId,goodString);
–60秒内过期
red:expire(PREFIX … goodId, 60);
basic:log(goodId … “缓存设置成功”);
–归还连接到连接池
red:close();
return json;
end
return _RedisCacheDemo;
在nginx-redis-demo.conf配置文件中编写一个location配置块来使用该脚本,该配置块是提供给外部调用的商品查询接口/good/detail,具体代码如下:
首先从缓存中查询商品
未命中再回源到
后台 #首先从缓存中查询商品,未命中再回源到Java后台
location = /good/detail {
content_by_lua_block {
local goodId=ngx.var.arg_goodid;
–判断goodId参数是否存在
if not goodId then
ngx.say(“请输入goodId”);
return;
end
–首先从缓存中根据id查询商品
local RedisCacheDemo = require “luaScript.redis.RedisCacheDemo”;
local redisCacheDemo = RedisCacheDemo:new();
local json = redisCacheDemo:getCache(goodId);
–判断缓存是否被命中
if not json then
ngx.say(“缓存是否被命中,回源到上游接口
”);
–若没有命中缓存,则回源到上游接口
json = redisCacheDemo:goUpstream();
else
ngx.say(“缓存已经被命中
”);
end
ngx.say(“商品信息:”,json);
}
}
出于调试方便,在nginx-redis-demo.conf配置文件中再编写一个location配置块来模拟Java容器的后台商品查询接口/java/good/detail。
理论上,后台接口的业务逻辑是从数据库查询商品信息并缓存到Redis,然后返回商品信息。这里为了方便演示对其进行简化,具体的代码如下:
#模拟Java后台接口查询商品,然后设置缓存
location = /java/good/detail {
#指定规则为internal内部规则,防止外部请求命中此规则
internal;
content_by_lua_block {
local RedisCacheDemo = require “luaScript.redis.RedisCacheDemo”;
–Java后台将从数据库查找商品,这里简化成模拟数据
local json=‘{goodId:商品id,goodName:商品名称}’;
–将商品缓存到Redis
local redisCacheDemo = RedisCacheDemo:new();
redisCacheDemo:setCache(ngx.var.arg_goodid, json);
–返回商品到下游网关
ngx.say(json);
}
}
}
修改了nginx-redis-demo.conf文件后重启OpenRestry,然后使用浏览器访问商品查询外部接口/good/detail,并且多次刷新,发现从二次请求开始就能成功命中缓存,其结果如图8-26所示。
图8-26 使用浏览器访问商品查询外部接口/good/detail的结果
Nginx+Redis实现黑名单拦截
==================
我们在日常维护网站时经常会遇到这样一个需求,对于黑名单之内的IP需要拒绝提供服务。实现IP黑名单拦截有很多途径,比如以下方式:
最后
笔者已经把面试题和答案整理成了面试专题文档
[外链图片转存中…(img-lohJWkWX-1715250092507)]
[外链图片转存中…(img-UwIkKkUP-1715250092507)]
[外链图片转存中…(img-YAO9pnvq-1715250092508)]
[外链图片转存中…(img-xgxzaDnk-1715250092508)]
[外链图片转存中…(img-WSZoM9Mb-1715250092508)]
[外链图片转存中…(img-YVoO56Xd-1715250092509)]