同步操作将从 Jerusalem/seckill 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
本地在项目根目录下使用mvn clean package打包生成seckill-1.0.0-SNAPSHOT.jar文件
将jar包服务上传到服务端上并编写额外的application.properties配置文件
编写deploy.sh脚本文件启动对应的项目
source /etc/profile; //使java环境生效
nohup java -Xms2048m -Xmx2048m -XX:NewSize=1024m -XX:MaxNewSize=1024m -jar seckill-1.0.0-SNAPSHOT.jar --spring.config.addition-location=/usr/local/seckill/application.properties
授权并执行使用
chmod 777 deploy.sh
./deploy.sh &
启动应用程序
查看日志
tail -f nohup.out
打开阿里云的网络安全组配置,将80端口开放给外网可访问
参数说明
查看服务器性能
top -H
查看某个进程
ps -ef | grep java
通过端口查看进程
netstat -anp | grep 5240
查看某进程的线程
pstree -p [progressId] wc -l
统计某进程的线程数量
pstree -p [progressId] wc -l
杀死进程
kill 5240
server.tomcat.accept-count:等待队列长度,默认100
server.tomcat.max-connections:最大可被连接数,默认10000
server.tomcat.max-threads:最大工作线程数,默认200
server.tomcat.min-spare-threads:最小工作线程数,默认10
默认配置下,链接超10000后出现拒绝链接情况
默认配置下,发出的请求超过200+100后拒绝处理
修改配置如下
server.tomcat.accept-count: 1000
server.tomcat.max-connections: 10000
server.tomcat.max-threads: 800
server.tomcat.min-spare-threads: 10000
对4核8G的服务器来说,经验上最好的max-threads是800
@Component
public class WebServerConfiguration implements WebServerFactoryCustomizer<ConfigurableWebServerFactory> {
@Override
public void customize(ConfigurableWebServerFactory configurableWebServerFactory) {
//使用对应工厂类提供给我们的接口定制化我们的tomcat connector
((TomcatServletWebServerFactory)configurableWebServerFactory).addConnectorCustomizers(new TomcatConnectorCustomizer() {
@Override
public void customize(Connector connector) {
Http11NioProtocol protocol = (Http11NioProtocol) connector.getProtocolHandler();
//定制化keepalivetimeout(设置30秒内没有请求则服务端自动断开keepalive链接)
protocol.setKeepAliveTimeout(30000);
//当客户端发送超过10000个请求则自动断开keepalive链接
protocol.setMaxKeepAliveRequests(10000);
}
});
}
}
说明:
数据库查询尽量使用到主键查询和唯一索引查询,如果非唯一索引查询在数据达到一定数量级后,则要进行分表分库来优化。
如果对Nginx开发有特殊要求或者OpenResty的Nginx达不到我们的需求,可以到Nginx官网下载,操作之后得到所需的Nginx替换掉openResty/sbin目录下的nginx即可。
1.先行条件,需要在linux安装pcre,openssl,gcc,curl等
apt install PCRE
apt install pcre-devel openssl-devel gcc curl
2.下载openresty 下载页面 http://openresty.org/cn/download.html
3.上传并解压
tar -xvzf openresty**.tar.gz
4.解压后执行如下命令
./configure
make
make install
安装完成,nginx默认安装在 /usr/local/openresty/nginx目录下
修改本地和阿里云服务器的host路径,以便于统一访问
1.静态资源部署
进入nginx根目录下的html下,新建resources目录用于存放前端静态资源
设置指向resources目录下的location可以访问对应的html下的静态资源文件
设置upStream sserver
反向代理配置,配置一个backend server,可以用于指向后端不同的server集群,配置内容为server集群的局域网ip,以及轮训的权重值,并且配置一个location,当访问规则命中location任何一个规则的时候则可以进入反向代理规则
upstream backend_server{
server 192.168.75.180 weight=1;
server 192.168.75.181 weight=1;
}
location / {
proxy_pass http://backend_server;
#设置请求头
proxy_set_header Host $http_host:$proxy_port;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
#日志开关
server.tomcat.accesslog.enabled=true
#日志格式
server.tomcat.accesslog.pattern=%h %l %u %t "%r" %s %b %D
server.tomcat.accesslog.directory=/usr/local/seckill/logs
upstream backend_server{
server 192.168.75.180 weight=1;
server 192.168.75.181 weight=1;
keepalive_timeout 30; //添加此行
}
location / {
proxy_pass http://backend_server;
#设置请求头
proxy_set_header Host $http_host:$proxy_port;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# 添加如下两行
proxy_http_version 1.1;
#允许重新定义或追加字段到传递给代理服务器的请求头信息(默认是close)
proxy_set_header Connection "";
}
epoll多路复用
master-worker进程模型
引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
<version>2.0.5.RELEASE</version>
</dependency>
配置类
@Component
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 3600)
public class RedisSessionConfig {
}
//生成登录凭证token,UUID
String uuidToken = UUID.randomUUID().toString();
uuidToken = uuidToken.replace("-","");
//建议token和用户登陆态之间的联系
redisTemplate.opsForValue().set(uuidToken,userModel);
redisTemplate.expire(uuidToken,1, TimeUnit.HOURS);
//下发token
return CommonReturnType.create(uuidToken);
//存入Redis
redisTemplate.opsForValue().set("item_"+id,itemModel);
redisTemplate.expire("item_"+id,10, TimeUnit.MINUTES);
//序列化
public class JodaDateTimeJsonSerializer extends JsonSerializer<DateTime> {
@Override
public void serialize(DateTime dateTime, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeString(dateTime.toString("yyyy-MM-dd HH:mm:ss"));
}
}
//反序列化
public class JodaDateTimeJsonDeserializer extends JsonDeserializer<DateTime> {
@Override
public DateTime deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
String dateString =jsonParser.readValueAs(String.class);
DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss");
return DateTime.parse(dateString,formatter);
}
}
//自定义RedisTemplates
@Component
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 3600)
public class RedisSessionConfig {
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory){
RedisTemplate redisTemplate = new RedisTemplate();
redisTemplate.setConnectionFactory(redisConnectionFactory);
//解决key的序列化方式 -> String
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
redisTemplate.setKeySerializer(stringRedisSerializer);
//解决value的序列化方式 -> Json
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
SimpleModule simpleModule = new SimpleModule();
simpleModule.addSerializer(DateTime.class,new JodaDateTimeJsonSerializer());
simpleModule.addDeserializer(DateTime.class,new JodaDateTimeJsonDeserializer());
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
objectMapper.registerModule(simpleModule);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
return redisTemplate;
}
}
lua协程机制
nginx协程机制
Nginx处理阶段:
nginx lua插载点
function get_from_cache(key)
local cache_ngx = ngx.shared.my_cache
local value = cache_ngx:get(key)
return value
end
function set_to_cache(key,value,exptime)
if not exptime then
exptime = 0
end
local cache_ngx = ngx.shared.my_cache
local succ,err,forcible = cache_ngx:set(key,value,exptime)
return succ
end
local args = ngx.req.get_uri_args()
local id = args["id"]
local item_model = get_from_cache("item_"..id)
if item_model == nil then
local resp = ngx.location.capture("/item/get?id="..id)
item_model = resp.body
set_to_cache("item_"..id,item_model,1*60)
end
ngx.say(item_model)
加入shared dictionary的扩展,声明128m的共享字典的访问内存
lua_shared_dict my_cache 128m;
location /itemlua/get {
default_type 'application/json';
content_by_lua_file '/usr/local/openresty/lua/itemsharedic.lua';
}
**适用场景:**更新较少,热点数据
local args = ngx.req.get_uri_args()
local id = args[ "id"]
local redis= require "resty.redis"
local cache= redis:new()
local ok,err = cache:connect("192.168.75.140",6379) //连接只读Redis,只读操作
local item_model= cache:get("item_"..id)
if item_model == ngx.null or item_model = nil then
local resp = ngx.location.capture("/item/get?id="..id)
item_model= resp.body
end
ngx.say(item_model)
location /itemlua/get {
default_type 'application/json';
content_by_lua_file '/usr/local/openresty/lua/itemredis.lua';
}
**适用场景:**更新较多,非热点数据
申明一个cache缓存节点的路径
proxy_cache_path /usr/local/openresty/nginx/cache_temp levels=1:2 keys_zone=tmp_cache:100m inactive=7d max_size=100g;
参数说明:
location内加入
proxy_cache tmp_cache; //缓存节点名称
proxy_cache_valid 200 206 304 302 7d; //只缓存指定的状态码的请求
proxy_cache_key $request_uri; //将请求uri作为缓存的key
这种缓存方法读取的Nginx的本地文件,并没有将文件缓存到Nginx的内存中,所以效果并不好,不推荐。
总结:
在大型的应用集群中若对Redis访问过度依赖,会因为应用服务器到Redis之间的网络带宽产生瓶颈
针对读请求导致的性能瓶颈,只需要在数据写入过程中复制一份,数据就变成两份,数据读能力就扩展了一倍。 针对写请求,redis是key-value存储结构,通过对写请求key做sharding,分散到不同的master实例上,解决写的瓶颈问题。
架构的越顶层性能越高,占用的系统资源越昂贵,更新机制越难
架构的底层更容易集中式的存储数据,但是性能最差
所以,无通用的解决方案,结合场景选择合适的架构
协商机制:比较Last-modified和ETag到服务端,若服务端判断没变化则304不返回数据,否则200返回数据
回车刷新或a链接
F5刷新或command+R刷新
ctrl+F5或command+shift+R刷新
(一)
css,js,img等元素使用版本号部署(不便利,维护困难)
css,js,img等元素使用带摘要部署(存在先部署html还是先部署资源的覆盖问题)
css,js,img等元素使用摘要做文件名部署,新老版本并存且可回滚,资源部署完后在部署html(推荐)
(二)
(三)
在服务端完成html,css,甚至js的load渲染成纯html文件后,直接以静态资源的方式部署到cdn上。
用户风控策略优化:策略缓存模型化
活动校验策略优化:引入活动发布流程,模型缓存化,紧急下线功能
具体实现见代码优化
扣减库存缓存化
异步同步数据库
库存数据库最终一致性保证
具体实现见代码优化
version: '3.3'
services:
rmqnamesrv:
image: foxiswho/rocketmq:server
container_name: rmqnamesrv
ports:
- 9876:9876
volumes:
- ./data/logs:/opt/logs
- ./data/store:/opt/store
networks:
rmq:
aliases:
- rmqnamesrv
rmqbroker:
image: foxiswho/rocketmq:broker
container_name: rmqbroker
ports:
- 10909:10909
- 10911:10911
volumes:
- ./data/logs:/opt/logs
- ./data/store:/opt/store
- ./data/brokerconf/broker.conf:/etc/rocketmq/broker.conf
environment:
NAMESRV_ADDR: "rmqnamesrv:9876"
JAVA_OPTS: " -Duser.home=/opt"
JAVA_OPT_EXT: "-server -Xms128m -Xmx128m -Xmn128m"
command: mqbroker -c /etc/rocketmq/broker.conf
depends_on:
- rmqnamesrv
networks:
rmq:
aliases:
- rmqbroker
rmqconsole:
image: styletang/rocketmq-console-ng
container_name: rmqconsole
ports:
- 8080:8080
environment:
JAVA_OPTS: "-Drocketmq.namesrv.addr=rmqnamesrv:9876 -Dcom.rocketmq.sendMessageWithVIPChannel=false"
depends_on:
- rmqnamesrv
networks:
rmq:
aliases:
- rmqconsole
networks:
rmq:
driver: bridge
docker update --restart=always XXX
具体实现见代码优化
private ExecutorService executorService;
/***
* 同步调用线程池的submit方法
* 拥塞窗口为20的等待队列,实现队列泄洪
*/
@PostConstruct
public void init(){
executorService = Executors.newFixedThreadPool(20);
}
/***
* 限流
*/
@PostConstruct
public void init(){
orderCreateRateLimiter = RateLimiter.create(300);
}
限制一个会话在一定时间内接口调用次数(无法解决多会话接入的问题)
限制一个ip在一定时间内接口调用次数(不好控制,容易误伤)
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。