web应用程序中的状态

您的web应用程序中的“状态”是什么?存储的是数据(不管目标内存、数据库、文件系统)。应用程序本身不能在代码中存储任何状态。这意味着您的类应该只具有对象的字段,这些对象也是无状态的。换句话说,在程序流期间,您不应该在服务、DAO或控制器中存储任何内容。这是一个完整的“必须”为您的服务层。为什么?

您的应用程序需要具有可伸缩性。这意味着它需要在集群中运行,状态是最难分发的东西。如果将状态存储的位置最小化,则可以最小化群集的复杂性。但是国家应该存在,在这里拥有它是很好的:

  • 数据库--无论是SQL、NoSQL,甚至是搜索引擎,它都是存储状态的主要内容。它应该支持集群,或者是一台处理来自多个其他“代码”服务器的请求的大型专用机器。代码与数据库通信,但代码本身不为多个客户端请求存储任何内容;
  • 缓存缓存相对容易分发(基本上是键值)。有许多现成的解决方案,如EhCache和memcached。因此,您可以配置缓存并将结果存储在内存中,而不是计算结果或在每个请求时从DB获取结果。但是--代码不存储任何东西--它只是填充和查询缓存;
  • HTTP会话-在Web组件(控制器,托管bean,无论你怎么称呼它)。它非常类似于缓存,尽管它有着不同的用途--允许同一用户识别后续的操作(http本身是无状态的)。但是,由于您的代码运行在多台机器上,负载均衡器可能并不总是向同一台服务器发送后续请求。因此,会话也应该在所有服务器上进行复制。幸运的是,大多数容器都有内置的选项,所以只需添加一个配置行。或者,您可以指示负载均衡器使用“粘性会话”(根据会话cookie确定发送请求的服务器),但它也会将一些状态管理转移到负载均衡器。无论您选择哪个选项,都不要在会话中放置太多的数据。
  • 文件系统--当您存储文件时,需要所有机器都可以访问它们。这里有多个选项,包括者使用像AmazonS 3这样的云存储服务,这些服务可以通过api访问。

所有这些都是在代码之外管理的。您的代码只是通过一个API(会话API、缓存API、JDBC、S3/fileSystemAPI)来使用它们。如果代码包含任何一个状态(作为对象的实例变量),则应用程序将很难支持(您必须自己管理状态),并且扩展性较差。当然,也有一些罕见的情况,如果不将状态存储在代码中,就无法进入。记录这些文件,并确保它们不依赖于集群中的工作。

https://movie.douban.com/doulist/145585807

但是,如果在执行业务逻辑的对象中存储状态,什么会出错呢?那么,你有两个选择:

  • 同步对字段的访问--这将降低性能,因为所有发出请求的用户都必须在队列中等待服务来管理其字段;
  • 为每个HTTP请求创建类的新实例,并以某种方式管理实例。管理这些实例是困难的部分。人们可能倾向于选择会话来进行会话,这意味着会话变得非常大,很难复制(在多台计算机上共享大量数据要慢一些,会话复制必须要快)。更不用说不必要地增加内存占用了。

https://www.douban.com/doulist/146103735/

下面是一个微不足道的例子,说明什么是不应该做的。应该将这些类型的值作为方法参数传递,而不是将它们存储在实例中:

https://movie.douban.com/doulist/146103735/

class OrderService {
   double orderPrice;

   void processOrder(OrderDto order) {
         for (Entry entry : order.getEntries() {
              orderPrice += entry.getPrice();
         }
         boolean discounts = hasDiscounts(order);
   }
   boolean hasDiscounts(OrderDto order) {
        return order.getEntries().length > 5 && orderPrice > 200;
   }
}

因此,让您的所有代码无状态-这将确保至少一定程度的可伸缩性。

您可以拥有

1
2
3
4
5
6
7
8
9
public class Job {
    private String jobType;
    private String location;
}
 
@RequestMapping("/foo")
public Strnig doSomethingWithJob(Job job) {
   ...
}

但这意味着“Jobs Type”和“Location”是http参数名称,因此无法对其进行自定义。这很奇怪,因为这是元数据,应该通过注释进行配置。

但是如果您需要定制它们(例如,出于SEO的原因),您可以插入一些小代码,SpringMVC将允许您这样做利用了可插拔的参数解析机制.因此,我添加了自己的解析器,查看是否用自定义注释对命令对象进行注释,如果是--应用@Command参数映射。请注意,主要的Spring-MVC开发人员Rossen Stoyanchev也提出了一个解决方案,虽然这是一个更好的解决方案(减少了代码重复),但它不能与<mvc:annotation-driven />...我的工作与此(感谢后处理器)

最后,你可以:

1
2
3
4
5
6
7
@SupportsCustomizedBinding
public class Job {
    @CommandParameter("jt")
    private String jobType;
    @CommandParameter("loc")
    private String location;
}
posted @ 2021-10-19 14:39  javd9w  阅读(129)  评论(0)    收藏  举报