Play Framework2.1源码分析 - 架构设计及线程策略分析


转载请注明出处,保持署名    作者:joymufeng

1. 介绍

    大家翘首以盼的Play2.1终于发布了,目前可用版本是Play 2.1-RC4。在此感谢Play!开发团队付出的辛勤努力!Play2.1以后版本中将会加入导出符合Servlet3.1规范的War包功能,相信大家对这个功能也很期待。废话少说,回归正题,先了解一下,Play2.1中究竟有哪些新的变化:

  • Scala升级到2.10版本
            与时俱进,这是Play!框架的一贯作风
  • scala.concurrent.Future
            这是scala 2.10中最激动人心的API,并发中的生产者/消费者问题,寥寥几行代码就搞定,真心钦佩!
  • 模块化
            模块化使得开发者可以定制自己的框架。比如你可以将Play框架定制成一个高性能的、异步Http Server。
  • 允许Play项目模块化开发
  • 异步执行的代码可以操作Http.Context
  • 为Java开发者提供更好的线程模型
  • 开发者可以控制控制器类的实例化,更容易和Spring框架集成
  • 全新的Scala JSON API
  • 全新的Filter API和CSRF保护
  • RequireJS
  • 改进内容协商 

    详细信息请参考:http://www.playframework.org/documentation/2.1-RC3/Highlights

    Play2.1的架构图如下,核心组件仍然是Netty和AKKA,

    Play2.1架构

    线程是系统最宝贵的资源,也是影响系统性能最关键的因素。下面本文主要围绕“线程策略”这个主题,通过源码分析Play2.1的架构设计。 

2. Netty线程策略

2.1 源码分析

    Netty是一个异步的、基于事件驱动的高性能网络应用框架。非常适合编写Web服务器应用。在Play2.1中,Netty的入口类是play.core.server.NettyServer,该类中创建NioServerSocketChannelFactory代码如下:

?
1
2
3
new org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory(
       Executors.newCachedThreadPool(NamedThreadFactory( "netty-boss" )),
       Executors.newCachedThreadPool(NamedThreadFactory( "netty-worker" ))))

    这里使用 Executors的newCachedThreadPool方法创建线程池,该方法的文档描述如下:     

   Creates a thread pool that creates new threads as needed, but will reuse previously constructed threads when they are available. These pools will typically improve the performance of programs that execute many short-lived asynchronous tasks. Calls to execute will reuse previously constructed threads if available. If no existing thread is available, a new thread will be created and added to the pool. Threads that have not been used for sixty seconds are terminated and removed from the cache. Thus, a pool that remains idle for long enough will not consume any resources. Note that pools with similar properties but different details (for example, timeout parameters) may be created usingThreadPoolExecutor constructors.

    从文档描述中可以看出,newCachedThreadPool适合于大量短时间、异步任务的执行,在执行任务时,会重用之前创建的线程,如果没有可用的,则创建一个新的线程,并加入到线程池中。如果线程空闲超过60秒,则会被终止,并从线程池中移除。newCachedThreadPool的这些特性,刚好满足Netty的异步、事件驱动的需求。

2.2 Netty线程策略总结

    Netty中处理ServerSocketChannel的"Boss threads", 和处理客户端Channel的"Worker threads"都使用newCachedThreadPool,即根据需要创建新的线程。"Boss threads"的数量取决于Netty绑定的端口个数,例如80和443端口;"Worker threads"的数量则取决于客户端请求数量。当客户端请求逐渐增多时,系统将变的不可控。

3. AKKA线程策略

    AKKA中的actors实际执行的线程环境是由其关联的dispatcher管理的。所以这里我们只关注dispatcher的实现。 Play2.1中AKKA的线程策略和2.0版本相比,区别比较大。在play2.0中,actions、promises和websockets actors分别使用各自的dispatcher,其它actors使用默认的default-dispatcher。而在Play2.1中,所有的AKKA actors都使用默认的default-dispatcher,其配置如下:

?
1
2
3
4
5
6
7
default -dispatcher = {
     fork-join-executor { 
         parallelism-min = 8
         parallelism-factor = 1.0
         parallelism-max = 24
     }
}
从上面的配置可以看出,默认情况下,default-dispatcher只使用8个线程。


4. Play框架核心API的线程策略

     为了保证快速处理客户端请求,Play中很多操作都是异步完成的。这些异步操作的执行环境是由play.core.Execution提供的。该类中创建了internalContext: scala.concurrent.ExecutionContext, 作为隐式的ExecutionContext, 线程池大小为processors个数,即CPU的总核数。

?
1
2
3
4
lazy val internalContext : scala.concurrent.ExecutionContext = {
     val numberOfThreads = play.api.Play.maybeApplication.map( _ .configuration.getInt( "internal-threadpool-size" )).flatten.getOrElse(Runtime.getRuntime.availableProcessors)
     ExecutionContext.fromExecutorService(Executors.newFixedThreadPool(numberOfThreads, NamedThreadFactory( "play-internal-execution-context" )))
}
在Play2.1的核心API中,用到这个隐式参数的API大体如下:

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要将 Play Framework、MyBatis-Plus 和 Spring 集成起来,可以按照以下步骤进行操作: 1. 添加 MyBatis-Plus 和 Spring 依赖项到 Play Framework 项目中。 ``` libraryDependencies ++= Seq( "com.baomidou" %% "mybatis-plus" % "3.4.2", "org.springframework" % "spring-context" % "5.2.9.RELEASE" ) ``` 2. 创建一个 Spring Context 配置文件,例如 applicationContext.xml,并在文件中配置 MyBatis-Plus 和数据源。 ```xml <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mybatis-plus="http://mybatis.org/schema/mybatis-plus" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://mybatis.org/schema/mybatis-plus http://mybatis.org/schema/mybatis-plus/mybatis-plus.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- MyBatis-Plus 配置 --> <mybatis-plus:configuration> <mybatis-plus:global-config> <mybatis-plus:db-config id="dbConfig" /> </mybatis-plus:global-config> </mybatis-plus:configuration> <!-- 数据源配置 --> <bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource"> <property name="driverClassName" value="com.mysql.cj.jdbc.Driver" /> <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test" /> <property name="username" value="root" /> <property name="password" value="password" /> </bean> <!-- MyBatis-Plus SqlSessionFactory 配置 --> <bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="configLocation" value="classpath:mybatis-config.xml" /> <property name="mapperLocations" value="classpath*:mapper/**/*.xml" /> <property name="globalConfig" ref="dbConfig" /> </bean> <!-- MyBatis-Plus MapperScannerConfigurer 配置 --> <bean class="com.baomidou.mybatisplus.extension.spring.MybatisMapperScannerConfigurer"> <property name="sqlSessionFactory" ref="sqlSessionFactory" /> </bean> </beans> ``` 3. 创建 MyBatis-Plus 配置文件,例如 mybatis-config.xml,并在文件中配置 MyBatis-Plus 的一些参数和插件。 ```xml <?xml version="1.0" encoding="UTF-8"?> <configuration> <settings> <setting name="cacheEnabled" value="true" /> <setting name="lazyLoadingEnabled" value="true" /> <setting name="aggressiveLazyLoading" value="false" /> </settings> <plugins> <plugin interceptor="com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor" /> </plugins> </configuration> ``` 4. 在 Play Framework 中,可以使用 SpringApplicationBuilder 创建 Spring Context,并将其与应用程序一起启动。 ```java package controllers; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import play.mvc.Controller; import play.mvc.Result; public class HomeController extends Controller { public Result index() { // 使用 SpringApplicationBuilder 创建 Spring Context ApplicationContext context = new AnnotationConfigApplicationContext(); context.registerShutdownHook(); // 或者使用 ClassPathXmlApplicationContext 加载 applicationContext.xml 文件 // ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); // 获取 MyBatis-Plus Mapper 接口实例 UserMapper userMapper = context.getBean(UserMapper.class); // 调用接口方法,执行 SQL 操作 User user = userMapper.selectById(1L); return ok(user.toString()); } } ``` 这样,就可以在 Play Framework 中集成 MyBatis-Plus 和 Spring 了。当然,还需要根据项目实际情况进行进一步的配置和调整。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值