目前很多服务都是分布式开发的,就算自己做项目,练手,也都是采用的分模块,切分端口号的操作,那么这样就必然涉及一个远程调用的问题。那么一但涉及远程调用,就需要考虑一个性能以及安全的问题,本文就是初步讲解安全的问题
数据库表
这里的数据库表只是简单模拟,没有正式开发的数据要求,id(订单ID),name(订单描述之类),status(订单状态),version(订单版本)
实体类
public class Order {
private int id;
private String name;
private int status;
private int version;
//省略getset方法
}
Mapper
public interface OrderMapper {
@Select("select * from table_order where id=#{id}")
Order querryById(int id);
@Insert("insert into table_order values(#{id},#{name},#{status},#{version})")
void insertOrder(Order order);
@Update("update table_order set status=#{status} where id=#{id}")
int updateOrder(Order order);
@Update("update table_order set version=#{version} where id=#{id}")
int updateOrderByVersion(Order order);
}
由于模仿项目,直接通过接口注解操作,方便
Service层
@Service
public class OrderServiceImpl implements OrderService {
private Logger logger= LoggerFactory.getLogger(getClass());
@Autowired
private OrderMapper orderMapper;
@Autowired
private InvokeService invokeService;
private final static String url="http://localhost:8080/deliver?id=";
@Override
public Order querryById(int id) {
Order order=orderMapper.querryById(id);
if (order!=null)
return order;
return null;
}
@Override
public void insertOrder(Order order) {
orderMapper.insertOrder(order);
}
@Override
@Transactional
public String sendOrder(Order order) {
int id=order.getId();
String status=invokeService.invoke(url,id);
order=new Order();
order.setStatus(1);
order.setId(id);
orderMapper.updateOrder(order);
return status;
}
}
由于是跨域请求,所以我们分离一个service出来,专门做跨域问题(invokeService)
invokeService
@Service
public class InvokeServiceImpl implements InvokeService {
@Autowired
private RestTemplate restTemplate;
@Override
public String invoke(String url, int id) {
String body = restTemplate.getForEntity(url + id, String.class).getBody();
return body;
}
}
跨域处理,端口和之前不一样哦
@RestController
public class DeliveController {
private Logger logger= LoggerFactory.getLogger(getClass());
@RequestMapping("/deliver")
public String deliver(int id){
logger.info("===========接受订单指令,准备发货");
try {
TimeUnit.SECONDS.sleep(10);
}catch (InterruptedException e){
e.printStackTrace();
return "-1";
}
return "1";
}
}
当前服务器Controller的代码就不贴出来了,就是一个调用service的代码,这里可以很明显的看出一个业务逻辑了
解析
@Autowired
private TransactionTemplate transactionTemplate;
public String sendOrderByTemplate(Order order) {
int id=order.getId();
String status=invokeService.invoke(url,id);
transactionTemplate.execute(new TransactionCallback
通过以上的这个代码,就可以明显的优化第二个问题给我们带来的影响了,因为我们的跨域请求和数据库访问是分开的,是两个独立的代码,不会导致数据库链接资源被浪费掉。
但是,我们这个还是不能解决我们多线程的问题,因为只要是高并发,就会同一时间怼到我们方法上面来,就会同一时间去请求跨域信息,已导致“发货系统”出现问题,那么如何解决呢?
public String sendOrderBySingle(Order order) {
int id=order.getId();
boolean flag=(boolean)transactionTemplate.execute(new TransactionCallback
我们通过以上的方法,就可以很好的避免这个问题了,因为你有一个标志位以及版本信息存在,所有的请求,都要先走上面的方法,然后标志位就更新了,这样就不会导致所有请求都访问跨域了。
是不是感觉这样的逻辑理解起来很容易呢?下面总体总结一下
分析
我们都知道,数据库有乐观锁与悲观锁吧。悲观锁很好理解就是锁住每一次的请求,而乐观锁就不会。
然后乐观锁的实现方式就是CAS算法,这个算法就是比较并且替换。我们首先比较版本号,如果版本号一样就操作,否则不会做出操作,因为一旦版本号不一样,就说明线程不同步了,就说明数据的有效信息不对了。
这就是第三种优化的核心思想,就是一个版本的控制