我想创建一个服务,通过REST风格的控制器来管理集合。我在想,需要什么才能使这项服务免受多人同时攻击。
所以基本上是这样的。。。
@Transactional
class NoteService {
private static users = [:]
//This won't be so simple it the future
private static key = 0;
def get(id) {
log.debug("We are inside the get")
return users[id]
}
def create(obj){
log.debug("We are inside the create")
update(key++, obj)
}
def update(id, obj){
log.debug("We are inside the update")
users.put(id,obj)
}
def delete(id){
log.debug("We are inside the remove")
user.remove(id)
}
}
如果我有多个控制器请求同时命中它,这会起作用吗?我担心的是,如果两个客户试图同时攻击它,可能会出现问题。还有可能有一个更好的策略,也许使用承诺?我使用的是2.3+
不,这是坏的,不安全。
如果您没有可变状态,那么您总是线程安全的。当然,如果你根本没有状态变量,那就更好了,但为依赖注入的Spring Bean或记录器等提供状态变量也没关系。只要你不更改这些值,它们实际上是不可变的(它们在启动期间设置,之后不会更改),两个并发调用方不会相互干扰。
但是,对于并发访问来说,最有问题的是一个状态变量(在这种情况下,它是静态的,因为默认情况下Grails服务是singleton Spring bean,所以该映射可能是一个非静态的实例变量,并且存在相同的问题),您可以在多个方法中更改它。
最简单的方法是在地图上同步。你不能只同步方法——只有当一个方法访问了映射时,这才会起作用。使用synchronized
将序列化调用并保证没有并发访问。但是您可以从多个方法中读取和写入,因此对其中每个方法的调用进行序列化并不能帮助不同方法的并发调用之间的交互。即使同步了每个方法,偶尔也会有两个方法同时被调用的情况;同步没有帮助。
因此,您需要一种跨方法同步的机制,在这里您有点幸运,因为您只有一个可变字段,所以您可以在该字段上同步(当然,如果您有多个字段被更改,您可以始终创建一个伪"锁定"对象并在该对象上同步)。然后,通过"通过"映射序列化调用来保护对所有方法的所有访问(无论它们是否同步,现在您可以取消同步它们,因为这只会减慢速度)。
这是最简单的,但不是很有表现力。如果保持每个同步锁所花费的时间很短,那么您可能不会注意到太多问题。尝试使同步块尽可能短:
def update(id, obj) {
log.debug("We are inside the update")
synchronized(users) {
users.put(id,obj)
}
}
更好的解决方案是使用Java5中添加的java.util.concurrent.*
锁定和并发类。如果实现正确,这将是非常高性能的,但要了解如何使用这些API还需要一段时间。最好的资源是实践中的Java并发。它写于2006年,但仍然非常适用(它显然不包括更新的JDK中的更新,但1.5中提供的API和该书中描述的API足以满足许多用例)。这本书大约有400页,但材料很难(但解释得很好),所以计划几个月的时间框架:)
Venkat Subramaniam在JVM上的编程并发是另一个很好的资源。它比JCIP更新(2011年),也没有那么深入,所以它涵盖的内容更少,但更容易接近。它涵盖了包括Groovy在内的多种JVM语言。仍然是一个多月的时间框架,但更少的几个月。
如果这更像"Groovy"呢?我继续使用了你推荐的Gpars,我只是不确定我是否正确使用了它们:-p。我试过了,看起来还可以。
import grails.transaction.Transactional
import grails.converters.JSON
import groovyx.gpars.agent.Agent;
@Transactional
class NoteService {
private static key = 0
private final noteState = new Agent<Map<String, String>>([:])
def get(id) {
return noteState.val[id]
}
def create(obj){
noteState << { it[(key++).toString()] = obj }
}
def update(id, obj){
noteState << { it[id] = obj }
}
def delete(id){
noteState << { it.remove(id) }
}
}