股票交易行情数据接口,接口幂等性
股票交易行情数据接口,接口幂等性定义
在HTTP/1中,对幂等性进行了定义。它描述了一次和多次请求某一个资源对于资源本身应该具有同样的结果,即仅第一次请求会对资源产生副作用,后续的多个请求对于同一个资源不会产生副作用。
这里的副作用是不会对结果产生破坏或者产生不可预料的结果。也就是说,其任意多次执行对资源本身所产生的影响均与第一次执行的影响相同。
实现股票交易行情数据接口,接口幂等性的优劣
以下情况若不保证股票交易行情数据接口,接口幂等性,将会对系统产生不可预知的问题。
前端重复提交表单:用户提交表单时,很多时候会因网络抖动没有及时对用户做出提交成功响应,致使用户认为没有成功提交,然后一直点提交按钮,这时就会发生重复提交表单的请求。这种情况在电商系统中实现扣款逻辑时尤为重要。用户恶意进行刷单:例如投票功能,若用户针对一个用户进行重复提交投票,这样会导致股票交易行情数据接口,接口接收到用户重复提交的投票信息,这样会使投票结果与事实严重不符。股票交易行情数据接口,接口超时重复提交:很多时候HTTP客户端工具都默认开启超时重试的机制,尤其是第三方调用股票交易行情数据接口,接口时,为了防止网络抖动超时等造成的请求失败,都会添加重试机制,导致一个请求提交多次。消息重复消费:当使用MQ消息中间件时,若发生消息中间件出现错误未及时提交消费信息,会导致发生重复消费。
上述情况需保持股票交易行情数据接口,接口的幂等性,保证系统的健壮性。引入幂等性会增加了服务端的逻辑复杂性和成本,其主要是:
把并行执行的功能改为串行执行,降低了执行效率。增加了额外的控制幂等的业务逻辑,复杂化了业务功能。
需针对具体的业务场景,考虑是否有引入幂等性的必要,除了业务上的特殊要求外,一般情况下不需要引入的股票交易行情数据接口,接口幂等性。
RestfulAPI股票交易行情数据接口,接口的幂等性
Restful推荐的几种HTTP股票交易行情数据接口,接口方法中,分别存在幂等性与不能保证幂等性的方法,如下所示:
解决方案
唯一性约束
数据库唯一主键
该方案是在数据库层面防止重复,利用的是数据库的主键唯一约束的特性,解决了insert数据时的幂等性,比如以订单号为唯一约束,订单号相同的订单不可能存在两条记录的插入。此外,也适用于以主键作为唯一查询条件的删除操作。
此外,数据库唯一主键不能是使用数据库中的自增主键,而是使用分布式ID充当主键,这样才能保证在分布式环境下ID的全局唯一性。且若是在分库分表的情况下,路由规则要保证相同请求下,落地在同一个数据库和同一张表中,因为,不同的数据库和表主键是不相关的。
数据量较大,且只能被处理一次时,可对数据做MD5处理,并将其放入redis的set中。在每次处理数据前,先查看数据对应的MD5是否已经存在,若存在,则不处理。
锁机制
数据库悲观锁
使用forupdate来实现数据库的悲观锁,但是需要注意的是,在使用数据库悲观锁时,需要关闭数据库的自动提交事务功能autocommit。
此外,因为悲观锁使用时一般是伴随着事务一起使用的,数据锁定的时间可能会很长,需要根据实际情况选用。且id字段得是主键或唯一索引,否则会造成数据库不使用行锁,而使用表锁的情况,这会造成很大的性能损失,处理起来会较麻烦。
数据库乐观锁
数据库乐观锁通过在表结构中增加一个version字段或update_time字段实现,适用于更新的场景中,且是读多写少的场景。语句如下所示:
UPDATE [TABLE] SET count = #{count} - 1, version = #{version} + 1 WHERE id = #{id} ADN version = #{version}
根据version字段,即在操作数据库前,先获取当前记录的version值,然后在操作的时候携带该version值。具体的示例如下:
第一次操作数据库时,得到的version=调用库存服务时,version更新成了2;此时,返回给订单服务出现了问题,订单服务又一次的调用库存服务,订单服务还是会携带version=1去调用库存服务;此时再次执行sql语句时,就不会执行成功。因为version已经改变了。
token机制
针对客户端连续点击或者调用方的超时重试等情况,例如提交订单,此种操作就可以用Token的机制实现防止重复提交。
该方案的具体过程如下:
调用方在调用股票交易行情数据接口,接口的时候先向后端请求一个全局ID;调用方请求的时候携带这个全局ID一起请求;此时,后端需要对这个Token作为Key,用户信息作为Value到Redis中进行键值内容校验,用户数据量过大时,可对请求数据做MD5存储。若Key存在且Value匹配就执行删除命令,然后正常执行后面的业务逻辑。若不存在对应的Key或Value不匹配就返回重复执行的错误信息。
此外,需要注意,在并发情况下,执行Redis查找数据与删除需要保证原子性,否则很可能在并发下无法保证幂等性。其实现方法可以使用分布式锁或者使用Lua表达式来注销查询与删除操作。
全局请求唯一ID
调用请求股票交易行情数据接口,接口时,生成一个唯一id,redis将数据保存到set中,存在即表示已处理过。可使用nginx设置每一个请求的唯一id。
基于token令牌实现示例
示例使用maven作为项目依赖管理,项目结构如下所示:
poxml依赖文件如下:
org.springframework.boot
spring-boot-starter-data-redis
io.lettuce
lettuce-core
redis.clients
jedis
redis.clients
jedis
org.springframework.boot
spring-boot-starter-web
org.projectlombok
lombok
true
org.apache.commons
commons-pool2
org.springframework.boot
spring-boot-starter-test
test
yml文件配置如下:
server:
port: 9999
spring:
redis:
host: 192.168.125.140
port: 6379
timeout: 1000
database: 0
jedis:
pool:
max-active: 100
max-wait: -1
min-idle: 0
max-idle: 20
生成和验证Token的TokenService工具如下:
@Service
public class TokenService {
@Autowired
private StringRedisTemplate redisTemplate;
private static final String IDEMPOTENT_TOKEN_PREFIX = 'idempotent_token:';
public String generateToken(String value) {
// 当传入的 value 是较大的数据量或者 json 时,可对其进行 MD5 处理,再存储到 redis 中
String token = UUID.randomUUID().toString();
String key = IDEMPOTENT_TOKEN_PREFIX + token;
redisTemplate.opsForValue().set(key, value, 3, TimeUnit.MINUTES);
return token;
}
public boolean validToken(String token, String value) {
// 使用 LUA 脚本保证查找和删除的原子性
String script = 'if redis.call('get', KEYS[1]) == ARGV[1] ' +
'then return redis.call('del', KEYS[1]) ' +
'else return 0 end';
DefaultRedisScript redisScript = new DefaultRedisScript<>(script, Long.class);
String key = IDEMPOTENT_TOKEN_PREFIX + token;
Long result = redisTemplate.execute(redisScript, Arrays.asList(key), value);
if (Objects.nonNull(result) && result > 0) {
System.out.println(String.format('验证 token=%s,key=%s,value=%s 成功', token, key, value));
return true;
}
System.out.println(String.format('验证 token=%s,key=%s,value=%s 失败', token, key, value));
return false;
}
}
数据实体类OrderInfo如下:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class OrderInfo {
private String orderNum;
private String customer;
}
股票交易行情数据接口,接口请求控制器TokenController如下:
@RestController
public class TokenController {
@Autowired
private TokenService tokenService;
@GetMapping('/token/generate')
public ResponseEntity
测试结果
开启应用,并使用Postman进行测试。首先通过股票交易行情数据接口,接口/token/generate获取Token,再执行幂等性股票交易行情数据接口,接口/order/pay操作。
获取Token。
在Headrs中携带Token。
股票交易行情数据接口,接口幂等性验证。
文章为作者独立观点,不代表 股票程序化软件自动交易接口观点