安卓13优化更大,或摆脱杀后台问题
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了安卓13优化更大,或摆脱杀后台问题相关的知识,希望对你有一定的参考价值。
参考技术A安卓13优化更大,或摆脱杀后台问题
安卓13优化更大,或摆脱杀后台问题,新系统的推送名单中小米应属于首批国内厂商,华为、OPPO、VIVO等一众厂商目前还没有透露,安卓13优化更大,或摆脱杀后台问题。
安卓13优化更大,或摆脱杀后台问题1
android最令人沮丧的体验之一就是杀后台。后台应用被杀后通知可能会停止推送,虽说各家厂商也都有做类似的修复,不过还是要谷歌官方亲自出手才能解决这个问题。
一位Google工程师说,该公司在“一百万台” Android 设备上测试了MGLRU,其看到的结果是,看到了CPU利用率和内存压力方面的实质性改进,从而减少了OOM杀戮,降低了UI延迟。
原来,名为“Multi-Generational Least Recently Used”(MGLRU)的功能已经在Chrome OS上线,该公司在“4.14 和 5.15 之间的一些不同内核”上维护 MGLRU。现在看来,Google计划将 MGLRU 整合到 Android 系统中。
Android Gerrit上的一条commit,Google已经合并了Android 13的通用内核图像(GKI)的变化,另一个commit显示,很快甚至有可能通过adb启用它。
据悉,该功能实现了两个主要目标:第一个是Google发现kswapd的CPU使用量减少了40%,第二个是Google发现Android上的内存不足(OOM)的应用杀戮减少了18%。
安卓13优化更大,或摆脱杀后台问题2
近日,XDA发现,谷歌为Android 13内核添加了一段和内存、多任务优化有关的代码,主要涉及MGLRU(Multi-Generational Least Recently Used),即多页面回收策略,据悉可以极大改善小内存手机频繁杀后台的情况。
了解到,谷歌已经在Chrome OS引入这一策略,可以有效改善小内存设备在多任务运行时,由于CPU占用过高导致系统误杀后台应用的情况。根据谷歌测试,借助MGLRU策略,CPU使用率降低了40%。此外,在75%内存占用时减少了85%的后台误杀情况,在50%的内存占用时降低了18%的渲染延迟。
杀后台对于安卓手机用户,那是再正常不过,日常使用的过程中,我们常常会在后台留下很多App,比如最常用的QQ、微信等,把应用留在后台,目的就是为了能够在需要它的时候快速打开,不需要经过漫长的冷启动。
通常来说,内存比较小的手机,后台驻留能力都比较差,用户刚挂在后台的应用,可能没多久就被系统“杀”掉了,所以在消费者看来,智能手机的运行内存越大,越好用,或者越耐用,而厂商也经常在发布会上将手机的应用留存作为宣传点。
如今安卓阵营的运行内存也进一步升级,中低端机型都做到8GB起步,部分游戏手机甚至搭载了18GB。可惜的是,即便是12GB运存的安卓手机,后台驻留能力可能也不如6GB的iPhone,认为ios的后台管理策略十分优秀,当天挂在后台的大型游戏,过两天还可以做到热启动。
不少网友对此表示,这是iOS特有的“假后台”机制,不论如何,希望安卓系统也能提高运行内存的利用效率,别让大内存成为摆设。至于国内的定制ROM,不一定会照搬安卓13的内核代码,但谷歌这样的改动对于厂商优化后台策略也有益处,消费者也不需要盲目追求大内存的手机了。
距离谷歌I/O大会还有不到一个月了,不出意外谷歌会在大会上公布最新的Android 13系统,并进入Beta测试阶段,小米、OPPO等也有机会成为第一批吃上新系统的厂商。
安卓13优化更大,或摆脱杀后台问题3
根据网上对谷歌安卓13的曝光,安卓13将在5月进行推送,或许也有很多小伙伴也在关心自己的手机能否升级最新的Android13。根据爆料,小米或将成为首批安卓13系统升级的手机厂商,不少米粉也因此乐开花。
这次Android13系统的优化或许也没能能给予即将发布的.MIUI13.5太大的提升。安卓13与其说是升级不如说是安卓12的一次完善补丁,原本谷歌定档的Android12L最后也还是没能如期而至,最后选择直接发布Android13。
安卓13优化上更多是UI方面,基本在原来安卓12的基础上进行填补。新系统上用户可以根据壁纸颜色深度调整系统外观,使得桌面整体风格更加个性化和多样化。安卓13在用户隐私和安全上也做了优化升级,应用读取权限和授予权限变得更严格,连WiFi权限也需要得到授权方可调用。
这次新系统的推送名单中小米应属于首批国内厂商,华为、OPPO、VIVO等一众厂商目前还没有透露,时间线上应该也不会相隔太远。小米这次升级的机型中有最新的小米12系列以及11系列,基本都是这两年发布的新机型,红米Redmi机型也基本覆盖了2021到2022年发布的高端机型,中低端型号没能看到。
这次升级可以看出谷歌在系统上已经面临瓶颈状态,安卓这两年更多依靠的是厂商在硬件层面上的升级。从安卓7.0到安卓12大家都可以看到谷歌对系统上的调整和变化不是很大,基本都在UI设计的整改上以及用户隐私以及权限上进行升级。
同样的问题在iOS系统上也一样存在,近年iPhone在系统上变化也不大,更多在智能互连上进行升级,iPhone的RAM也从原来的2GB升级3GB再到现在6GB。
反观华为的鸿蒙HarmonyOS的升级在这两年可以用突飞猛进来形容,引领行业开创了多屏协同,还在智能家居互联互动上做出了跨时代的突破。据悉Harmony3.0将发布,华为的智能硬件调度又要在3.0时代上实现了。纵观每一年的安卓升级都让用户大失所望,难道谷歌安卓真的落后了,相信时间会给我们答案。
秒杀系统后台实现详解
秒杀后台实现
本文主要讲解项目实战中秒杀如何解决下面问题:
1)实现秒杀异步下单,掌握如何保证生产者&消费者消息不丢失
2)实现防止恶意刷单
3)实现防止相同商品重复秒杀
4)实现秒杀下单接口隐藏
5)实现下单接口限流
1 秒杀异步下单
用户在下单的时候,需要基于JWT令牌信息进行登陆人信息认证,确定当前订单是属于谁的。
针对秒杀的特殊业务场景,仅仅依靠对象缓存或者页面静态化等技术去解决服务端压力还是远远不够。
对于数据库压力还是很大,所以需要异步下单,异步是最好的解决办法,但会带来一些额外的程序上的
复杂性。
1.1 秒杀服务-下单实现
1)将tokenDecode工具类config放入秒杀服务并声明Bean
public static void main(String[] args){
SpringApplication.run(SeckillApplication,class,args);
}
@Bean
public TokenDecode tokenDecode(){
return new TokenDecode();
}
2)更新秒杀服务启动类,添加redis配置
/**
* 设置 redisTemplate 的序列化设置
* @param redisConnectionFactory
* @return
*/
@Bean
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
// 1.创建 redisTemplate 模版
RedisTemplate<Object, Object> template = new RedisTemplate<>();
// 2.关联 redisConnectionFactory
template.setConnectionFactory(redisConnectionFactory);
// 3.创建 序列化类
GenericToStringSerializer genericToStringSerializer = new GenericToStringSerializer(Object.class);
// 4.序列化类,对象映射设置
// 5.设置 value 的转化格式和 key 的转化格式
template.setValueSerializer(genericToStringSerializer);
template.setKeySerializer(new StringRedisSerializer());
template.afterPropertiesSet();
return template;
}
2)新建下单controller并声明方法
@RestController
@CrossOrigin
@RequestMapping("/seckillorder")
public class SecKillOrderController {
@Autowired
private TokenDecode tokenDecode;
@Autowired
private SecKillOrderService secKillOrderService;
/**
* 秒杀下单
* @param time 当前时间段
* @param id 秒杀商品id
* @return
*/
@RequestMapping("/add")
//获取当前登陆人
String username = tokenDecode.getUserInfo().get("username");
boolean result = secKillOrderService.add(id,time,username);
if (result){
return new Result(true, StatusCode.OK,"下单成功");
}else{
return new Result(false,StatusCode.ERROR,"下单失败");
}
}
}
3) 新建service接口
public interface SecKillOrderService {
/**
* 秒杀下单
* @param id 商品id
* @param time 时间段
* @param username 登陆人姓名
* @return
*/
boolean add(Long id, String time, String username);
}
4)更改预加载秒杀商品
当预加载秒杀商品的时候,提前加载每一个商品的库存信息,后续减库存操作也会先预扣减缓存中的库存再异步扣减mysql数据。
预扣减库存会基于redis原子性操作实现
for (SeckillGoods seckillGoods : seckillGoodsList) {
redisTemplate.boundHashOps(SECKILL_GOODS_KEY + redisExtName).put(seckillGoods.getId(),seckillGoods);
//预加载库存信息
redisTemplate.OpsForValue(SECKILL_GOODS_STOCK_COUNT_KEY+seckillGoods.getId(),se
ckillGoods.getStockCount());
}
6)秒杀下单业务层实现
业务逻辑:
获取秒杀商品数据与库存量数据,如果没有库存则抛出异常执行redis预扣减库存,并获取扣减之后的库存值如果扣减完的库存值<=0, 则删除redis中对应的商品信息与库存信息基于mq异步方式完成与mysql数据同步(最终一致性)
注意:库存数据从redis中取出,转换成String
@Service
public class SecKillOrderServiceImpl implements SecKillOrderService {
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private IdWorker idWorker;
@Autowired
private CustomMessageSender customMessageSender;
/**
* 秒杀下单
* @param id 商品id
* @param time 时间段
* @param username 登陆人姓名
* @return
*/
@Override
public boolean add(Long id, String time, String username) {
//获取商品数据
SeckillGoods goods = (SeckillGoods)
redisTemplate.boundHashOps("SeckillGoods_" + time).get(id);
String redisStock = (String) redisTemplate.boundValueOps("StockCount_" +
goods.getId()).get();
if(StringUtils.isEmpty(redisStock)){
return false;
}
int value=Integer.parseInt(redisStock);
//如果没有库存,则直接抛出异常
if(goods==null || value<=0){
return false;
}
//redis预扣库存
Long stockCount = redisTemplate.boundValueOps("StockCount_" +
id).decrement();
if (stockCount<=0){
//库存没了
//删除商品信息
redisTemplate.boundHashOps("SeckillGoods_" + time).delete(id);
//删除对应的库存信息
redisTemplate.delete("StockCount_" + goods.getId());
}
//有库存
//如果有库存,则创建秒杀商品订单
SeckillOrder seckillOrder = new SeckillOrder();
seckillOrder.setId(idWorker.nextId());
seckillOrder.setUserId(username);
seckillOrder.setSellerId(goods.getSellerId());
seckillOrder.setCreateTime(new Date());
seckillOrder.setStatus("0");
//发送消息
return true;
}
}
1.2 生产者保证消息不丢失
按照现有rabbitMQ的相关知识,生产者会发送消息到达消息服务器。但是在实际生产环境下,消息生产者发送的消息很有可能当到达了消息服务器之后,由于消息服务器的问题导致消息丢失,如宕机。因为消息服务器默认会将消息存储在内存中。一旦消息服务器宕机,则消息会产生丢失。因此要保证生产者的消息不丢失,要开始持久化策略。
rabbitMQ持久化:交换机持久化 队列持久化 消息持久化
但是如果仅仅只是开启这两部分的持久化,也很有可能造成消息丢失。因为消息服务器很有可能在持久化的过程中出现宕机。因此需要通过数据保护机制来保证消息一定会成功进行持久化,否则将一直进行消息发送。
事务机制
事务机制采用类数据库的事务机制进行数据保护,当消息到达消息服务器,首先会开启一个事务,接着进 行数据磁盘持久化,只有持久化成功才会进行事务提交,向消息生产者返回成功通知,消息生产者一旦接收成 功通知则不会再发送此条消息。当出现异常,则返回失败通知.消息生产者一旦接收失败通知,则继续发送该 条消息。
事务机制虽然能够保证数据安全,但是此机制采用的是同步机制,会产生系统间消息阻塞,影响整个系统 的消息吞吐量。从而导致整个系统的性能下降,因此不建议使用。
confirm机制
confirm模式需要基于channel进行设置, 一旦某条消息被投递到队列之后,消息队列就会发送一个确 认信息给生产者,如果队列与消息是可持久化的, 那么确认消息会等到消息成功写入到磁盘之后发出. confirm的性能高,主要得益于它是异步的.生产者在将第一条消息发出之后等待确认消息的同时也可以 继续发送后续的消息.当确认消息到达之后,就可以通过回调方法处理这条确认消息. 如果MQ服务宕机了,则会 返回nack消息. 生产者同样在回调方法中进行后续处理。
1.2.1 开启confifirm机制
1)更改秒杀服务配置文件
rabbitmq:
host: 192.168.200.128
publisher-confirms: true #开启confirm机制
2)开启队列持久化
@Configuration
public class RabbitMQConfig {
//秒杀商品订单消息
public static final String SECKILL_ORDER_KEY="seckill_order";
@Bean
public Queue queue(){
//开启队列持久化
return new Queue(SECKILL_ORDER_KEY,true);
}
}
3)消息持久化源码查看
4)增强rabbitTemplate
@Component
public class CustomMessageSender implements RabbitTemplate.ConfirmCallback {
static final Logger log = LoggerFactory.getLogger(CustomMessageSender.class);
private static final String MESSAGE_CONFIRM="message_confirm";
@Autowired
private RabbitTemplate rabbitTemplate;
@Autowired
private RedisTemplate redisTemplate;
public CustomMessageSender(RabbitTemplate rabbitTemplate) {
this.rabbitTemplate = rabbitTemplate;
rabbitTemplate.setConfirmCallback(this);
}
@Override
public void confirm(CorrelationData correlationData, boolean ack, String
cause) {
if (ack){
//返回成功通知
//删除redis中的相关数据
redisTemplate.delete(correlationData.getId());
redisTemplate.delete(MESSAGE_CONFIRM_+correlationData.getId());
}else{
//返回失败通知
Map<String,String> map =
(Map<String,String>)redisTemplate.opsForHash().entries(MESSAGE_CONFIRM_+correlationData.getId());
String exchange = map.get("exchange");
String routingKey = map.get("routingKey");
String sendMessage = map.get("sendMessage");
//重新发送
rabbitTemplate.convertAndSend(exchange,routingKey,
JSON.toJSONString(sendMessage));
}
}
//自定义发送方法
public void sendMessage(String exchange,String routingKey,String message){
//设置消息唯一标识并存入缓存
CorrelationData correlationData = new
CorrelationData(UUID.randomUUID().toString());
redisTemplate.opsForValue().set(correlationData.getId(),message);
Map<String, String> map = new HashMap<>();
map.put("exchange", exchange);
map.put("routingKey", routingKey);
map.put("sendMessage", message);
redisTemplate.opsForHash().putAll(MESSAGE_CONFIRM_+correlationData.getId(),map)
;
//携带唯一标识发送消息
rabbitTemplate.convertAndSend(exchange,routingKey,message,correlationData);
}
}
5)发送消息
更改下单业务层实现
@Autowired
private CustomMessageSender customMessageSender;
1.3 秒杀下单服务更新库存
1.3.1 异步下单服务service_consume
1)添加依赖
<dependencies>
<dependency>
<groupId>com.changgou</groupId>
<artifactId>changgou_common_db</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>com.changgou</groupId>
<artifactId>changgou_service_order_api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.changgou</groupId>
<artifactId>changgou_service_seckill_api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.changgou</groupId>
<artifactId>changgou_service_goods_api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId>
</dependency>
</dependencies>
2)新建application.yml
server:
port: 9022
spring:
jackson:
time-zone: GMT+8
application:
name: sec-consume
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://192.168.200.128:3306/changgou_seckill?
useUnicode=true&characterEncoding=utf-
8&useSSL=false&allowMultiQueries=true&serverTimezone=GMT%2b8
username: root
password: root
main:
allow-bean-definition-overriding: true #当遇到同样名字的时候,是否允许覆盖注册
redis:
host: 192.168.200.128
rabbitmq:
host: 192.168.200.128
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:6868/eureka
instance:
prefer-ip-address: true
feign:
hystrix:
enabled: true
client:
config:
default: #配置全局的feign的调用超时时间 如果 有指定的服务配置 默认的配置不会生效
connectTimeout: 60000 # 指定的是 消费者 连接服务提供者的连接超时时间 是否能连接
单位是毫秒
readTimeout: 20000 # 指定的是调用服务提供者的 服务 的超时时间() 单位是毫秒
#hystrix 配置
hystrix:
command:
default:
execution:
timeout:
#如果enabled设置为false,则请求超时交给ribbon控制
enabled: true
isolation:
strategy: SEMAPHORE
thread:
# 熔断器超时时间,默认:1000/毫秒
timeoutInMilliseconds: 20000
3)新建启动类
@SpringBootApplication
@EnableDiscoveryClient
@MapperScan(basePackages = {"com.changgou.consume.dao"})
public class OrderConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(OrderConsumerApplication.class,args);
}
}
1.3.2 消费者手动ACK下单实现
按照现有RabbitMQ知识,可以得知当消息消费者成功接收到消息后,会进行消费并自动通知消息服务器将该条消息删除。此种方式的实现使用的是消费者自动应答机制。但是此种方式非常的不安全。在生产环境下,当消息消费者接收到消息,很有可能在处理消息的过程中出现意外情况从而导致消息丢失,因为如果使用自动应答机制是非常不安全。我们需要确保消费者当把消息成功处理完成之后,消息服务器才会将该条消息删除。此时要实现这种效果的话,就需要将自动应答转换为手动应答,只有在消息消费者将消息处理完,才会通知消息服务器将该条消息删除。
1)更改配置文件
rabbitmq:
host: 192.168.200.128
listener:
simple:
acknowledge-mode: manual #手动
2)定义监听类
@Component
public class ConsumeListener {
@Autowired
private SecKillOrderService secKillOrderService;
@RabbitListener(queues = RabbitMQConfig.SECKILL_ORDER_KEY)
public void receiveSecKillOrderMessage(Channel channel, Message message){
//转换消息
SeckillOrder seckillOrder = JSON.parseObject(message.getBody(),
SeckillOrder.class);
//同步mysql订单
int rows = secKillOrderService.createOrder(seckillOrder);
if (rows>0){
//返回成功通知
try {
channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
} catch (IOException e) {
e.printStackTrace();
}
}else{
//返回失败通知
try {
//第一个boolean true所有消费者都会拒绝这个消息,false代表只有当前消费者拒
绝
//第二个boolean true当前消息会进入到死信队列,false重新回到原有队列中,默
认回到头部
channel.basicNack(message.getMessageProperties().getDeliveryTag(),false,false);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
3)定义业务层接口与实现类
public interface ConsumeService {
int handleCreateOrder(SeckillOrder order);
}
@Service
public class SecKillOrderServiceImpl implements SecKillOrderService { @Autowired
private SeckillGoodsMapper seckillGoodsMapper;
@Autowired
private SeckillOrderMapper seckillOrderMapper;
/**
* 添加订单
* @param seckillOrder
* @return
*/
@Override
@Transactional
public int createOrder(SeckillOrder seckillOrder) {
int result =seckillGoodsMapper.updateStockCount(seckillOrder.getSeckillId());
if (result<=0){
return result;
}
result =seckillOrderMapper.insertSelective(seckillOrder);
if (result<=0){
return result;
}return 1;
数据库字段unsigned介绍
unsigned-----无符号,修饰int 、char
ALTER TABLE tb_seckill_goods MODIFY COLUMN stock_count int(11) UNSIGNED DEFAULT NULL COMMENT '剩余库存数';
1.5 流量削峰
在秒杀这种高并发的场景下,每秒都有可能产生几万甚至十几万条消息,如果没有对消息处理量进行任何限制的话,很有可能因为过多的消息堆积从而导致消费者宕机的情况。因此官网建议对每一个消息消费者都设置处理消息总数(消息抓取总数)。
消息抓取总数的值,设置过大或者过小都不好,过小的话,会导致整个系统消息吞吐能力下降,造成性能浪费。过大的话,则很有可能导致消息过多,导致整个系统OOM。因此官网建议每一个消费者将该值设置在100-300之间。
1)更新消费者。
//设置预抓取总数
channel.basicQos(300);
1.6 秒杀渲染服务-下单实现
1)定义feign接口
@FeignClient(name="seckill")
public interface SecKillOrderFeign {
/**
* 秒杀下单
* @param time 当前时间段
* @param id 秒杀商品id
* @return
*/
@RequestMapping("/seckillorder/add")
public Result add(@RequestParam("time") String time, @RequestParam("id") Long id);
}
2)定义controller
@Controller
@CrossOrigin
@RequestMapping("/wseckillorder")
public class SecKillOrderController {
@Autowired
private SecKillOrderFeign secKillOrderFeign;
/**
* 秒杀下单
* @param time 当前时间段
* @param id 秒杀商品id
* @return
*/
@RequestMapping("/add")
@ResponseBody
public Result add(String time,Long id){
Result result = secKillOrderFeign.add(time, id);
return result;
}
}
2 防止恶意刷单解决
在生产场景下,很有可能会存在某些用户恶意刷单的情况出现。这样的操作对于系统而言,会导致业务出错、脏数据、后端访问压力大等问题的出现。
一般要解决这个问题的话,需要前端进行控制,同时后端也需要进行控制。后端实现可以通过Redisincrde 原子性递增来进行解决。
2.1 更新秒杀服务下单
2.2 防重方法实现
//防止重复提交
private String preventRepeatCommit(String username,Long id) {
String redisKey = "seckill_user_" + username+"_id_"+id;
long count = redisTemplate.opsForValue().increment(redisKey, 1);
if (count == 1){
//设置有效期五分钟
redisTemplate.expire(redisKey, 5, TimeUnit.MINUTES);
return "success";
}
if (count>1){
return "fail";
}
return "fail";
}
3 防止相同商品重复秒杀
3.1 修改下单业务层实现
3.2 dao层新增查询方法
public interface SeckillOrderMapper extends Mapper<SeckillOrder> {
/**
* 查询秒杀订单信息
* @param username
* @param id
* @return
*/
@Select("select * from tb_seckill_order where user_id=#{username} and seckill_id=#{id}")
SeckillOrder getSecKillOrderByUserNameAndGoodsId(String username, Long id); }
4 秒杀下单接口隐藏
在用户每一次点击抢购的时候,都首先去生成一个随机数并存入redis,接着用户携带着这个随机数去访问秒杀下单,下单接口首先会从redis中获取该随机数进行匹配,如果匹配成功,则进行后续下单操作,如果匹配不成功,则认定为非法访问。
4.1 将随机数工具类放入common工程中
public class RandomUtil {
public static String getRandomString() {
int length = 15;
String base = "abcdefghijklmnopqrstuvwxyz0123456789";
Random random = new Random();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < length; i++) {
int number = random.nextInt(base.length());
sb.append(base.charAt(number));
}
return sb.toString();
}
public static void main(String[] args) {
String randomString = RandomUtil.getRandomString();
}
4.2秒杀渲染服务定义随机数接口
/**
* 接口加密
* 生成随机数存入redis,10秒有效期
*/
@GetMapping("/getToken")
@ResponseBody
public String getToken(){
String randomString = RandomUtil.getRandomString();
String cookieValue = this.readCookie();
redisTemplate.boundValueOps("randomcode_"+cookieValue).set(randomString,10, TimeUnit.SECONDS);
return randomString;
}
//读取cookie private String readCookie(){
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String cookieValue = CookieUtil.readCookie(request, "uid").get("uid");
return cookieValue;
}
4.3 js修改
修改js下单方法
//秒杀下单
add:function(id){
app.msg ='正在下单';
//获取随机数
axios.get("/api/wseckillorder/getToken").then(function (response) {
var random=response.data;
axios.get("/api/wseckillorder/add? time="+moment(app.dateMenus[0]).format("YYYYMMDDHH")+"&id="+id+"&random="+random ).then(function (response) {
if (response.data.flag){
app.msg='抢单成功,即将进入支付!';
}else{app.msg='抢单失败';
}
})
})
}
4.4 秒杀渲染服务更改
修改秒杀渲染服务下单接口
/**
* 秒杀下单
* @param time 当前时间段
* @param id 秒杀商品id
* @return
*/
@RequestMapping("/add")
@ResponseBody
public Result add(String time,Long id,String random){
//校验密文有效
String randomcode = (String) redisTemplate.boundValueOps("randomcode").get(); if (StringUtils.isEmpty(randomcode) || !random.equals(randomcode)){
return new Result(false, StatusCode.ERROR,"无效访问");
}
Result result = secKillOrderFeign.add(time, id);
return result;
}
5 秒杀下单接口限流
因为秒杀的特殊业务场景,生产场景下,还有可能要对秒杀下单接口进行访问流量控制,防止过多的请求进入到后端服务器。对于限流的实现方式,我们之前已经接触过通过nginx限流,网关限流。但是他们都是对一个大的服务进行访问限流,如果现在只是要对某一个服务中的接口方法进行限流呢?这里推荐使用google提供的guava工具包中的RateLimiter进行实现,其内部是基于令牌桶算法进行限流计算
1)添加依赖
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>28.0-jre</version>
</dependency>
2)自定义限流注解
@Documented
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME)
public @interface AccessLimit {}
3)自定义切面类
@Component
@Scope
@Aspect
public class AccessLimitAop {
@Autowired
private HttpServletResponse httpServletResponse;
private RateLimiter rateLimiter = RateLimiter.create(20.0); @Pointcut("@annotation(com.changgou.webSecKill.aspect.AccessLimit)")
public void limit(){} @Around("limit()")
public Object around(ProceedingJoinPoint proceedingJoinPoint){
boolean flag = rateLimiter.tryAcquire();
Object obj = null;
try{
if (flag){
obj=proceedingJoinPoint.proceed();
}else{
String errorMessage = JSON.toJSONString(new Result(false,StatusCode.ERROR,"fail"));
outMessage(httpServletResponse,errorMessage);
}
}catch (Throwable throwable) { throwable.printStackTrace();
}return obj;
}
private void outMessage(HttpServletResponse response, String errorMessage) { ServletOutputStream outputStream = null;
try {
response.setContentType("application/json;charset=UTF-8");
outputStream = response.getOutputStream();
outputStream.write(errorMessage.getBytes("UTF-8"));
} catch (IOException e) {
e.printStackTrace();
}finally {
try {outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
4)使用自定义限流注解
欢迎观看并写出自己的见解!共同探讨!
编程·思维·职场
欢迎扫码关注
以上是关于安卓13优化更大,或摆脱杀后台问题的主要内容,如果未能解决你的问题,请参考以下文章