Latke 配置剖析

本贴主要介绍了 Latke 的关键配置及其设计用意,所以无论你是 Latke 的 开发者 还是基于 Latke 开发的产品的 使用者,希望本贴能够帮助到你。

为了说明方便,我们会使用 Solo 博客系统作为主要的应用场景进行举例。

latke.properties

文件路径是 WEB-INF/classes/latke.properties,该配置文件是 必须的。通常情况其中的配置项比较少,最简化的情况是只需要配置 #### Server #### 部分的 3 个项,其他项都有默认值,不需要显示设置。

Server 部分

一些 Solo 用户在初始化时会遇到“配置错误”的问题,这是因为 latke.props 没有配置或配置不当造成的。该配置文件中 #### Server #### 部分的默认配置如下:

#### Server ####
# Browser visit protocol
serverScheme=http
# Browser visit domain name
serverHost=localhost
# Browser visit port, 80 as usual, THIS IS NOT SERVER LISTEN PORT!
serverPort=8080

这 3 个配置项需要配置为用户通过浏览器 访问时候 的值。换句话说,如果你的服务在本机启动,那么默认的配置是可以让你在本机通过 http://localhost:8080 访问时一切正常的;但非本机访问时(比如通过 http://domain-or-ip:8080) 就 不能 正常加载静态资源了。

解决方案:将这三个配置项的值调整为最终访问时候对应的样子。

比如我的博客域名是 myblog.com,该域名已经正常解析到服务器 IP,此时只需要将 serverHost 的值设置为 myblog.com 就可以通过 http://myblog.com:8080 访问了。要进一步消除后面的端口有两种方式:

  1. 前置 HTTP 服务器做反向代理(比如通过 NGINX
  2. 将 Solo 启动时监听端口调整为 80/443

推荐第一种方式,因为:Servlet 容器主要是 Java Web 应用的处理环境,主要处理的是动态请求。HTTPS 或者是静态资源请求应该交由更专业的 HTTP 处理引擎来做,这样能减少很多复杂的配置(比如配置 Java 的 SSL 证书),也能充分优化性能(比如静态资源由 NGINX 处理,配置 Cache、限流等)。

一个正确的配置 样例 如下:

#### Server ####
# Browser visit protocol
serverScheme=http
# Browser visit domain name
serverHost=myblog.com
# Browser visit port, 80 as usual, THIS IS NOT SERVER LISTEN PORT!
serverPort=

请注意其中 serverPort 项并没有赋值,因为这样就能够使用 HTTP 的浏览器默认端口 80 了,HTTPS 也可以这样留空。

Static Server 部分

该部分通常情况是没有显示配置的(比如 Solo 里面默认的配置文件就没有出现这部分),需要显示配置的情况是需要做 动静分离 的应用场景。

前面我们提到过可以通过 NGINX 作为前置服务器,将静态资源请求分离出来进行处理。其具体的分离规则可以按 path 或后缀。这个动静分离的层次是在具体服务器节点上的分离,是用户请求到达内部网络后的处理。

我们可以在用户浏览器请求时就进行动静分离,将静态资源请求分发到配置好的 CDN 服务上,这个 CDN 服务一般是一个域名地址。而 staticServerSchemestaticServerHoststaticServerPort这三项就是用来配置该地址的,如果没有显示配置,则这三项会分别使用 serverSchemeserverHostserverPort 的值。

其他配置项

下面这些配置项也是在某些场景才会用到,一般来说也不用单独配置的。

说明 默认值 备注
runtimeMode 运行模式,可选值 DEVELOPMENTPRODUCTION PRODUCTION 线上环境一定要使用 PRODUCTION
staticResourceVersion 静态资源版本号,主要用于刷缓存 默认取服务启动的时间毫秒
contextPath 容器上下文路径 自动获取部署上下文路径 会和 虚拟目录 的配置相关
staticPath 静态资源服务的上下文路径 contextPath 显示配置的话一般都是留空

其中“上下文路径”是 Java Servlet 中的概念,指的是请求 URL 上第一层目录路径,比如对于 http://mydomain.com/solo/login,/solo 就是上下文路径,但该值也和部署目录相关,本例需要应用部署在 tomcat/webapps/solo 目录中,如果应用部署在 ROOT 目录下,那上下文路径就是空字符串。

顺便吐槽一下,“上下文路径”这个概念是 Java Servlet 规范里面特有的,是一个很奇葩的设计,标准的 HTTP 协议是没有这个设定的,并且 Servlet API 中定义的 Request URI 和 URI RFC 定义的完全不是一个东西,关于 URI 和 URL 的正解请看这里

local.properties

文件路径是 WEB-INF/classes/local.properties,该文件是 必须的,主要用于配置数据库。比如在 Solo 中默认的配置如下:

#### H2 runtime ####
runtimeDatabase=H2
jdbc.username=root
jdbc.password=
jdbc.driver=org.h2.Driver
jdbc.URL=jdbc:h2:~/solo_h2/db
jdbc.pool=h2

#### MySQL runtime ####
#runtimeDatabase=MYSQL
#jdbc.username=root
#jdbc.password=
#jdbc.driver=com.mysql.jdbc.Driver
#jdbc.URL=jdbc:mysql://localhost:3306/solo?useUnicode=yes&characterEncoding=utf8
#jdbc.pool=druid

# The minConnCnt MUST larger or equal to 3
jdbc.minConnCnt=5
jdbc.maxConnCnt=10

# Be care to change the transaction isolation 
jdbc.transactionIsolation=REPEATABLE_READ

# The specific table name prefix
jdbc.tablePrefix=b3_solo

其中 #### H2 runtime ######## MySQL runtime #### 只能启用一种,该部分配置了使用哪一种数据库实现。

其他的配置项比较好识别,一般来说使用默认值就好。

H2

默认使用应用内嵌的 H2 数据库,其中 jdbc.URL 中可以配置数据持久化的文件路径,比如 jdbc:h2:~/solo_h2/db 使用的路径就是 操作系统用户 home (即 ~ )下的 solo_h2/db 目录,在 Java 中对于的系统变量是 ${user.home},如果你不大熟悉 Unix-like 的表示法,也可以将该路径配置成完整的绝对路径,比如 jdbc:h2:D:/solo_h2/db

mail.properties

文件路径是 WEB-INF/classes/mail.properties,该配置文件是可选的。在 Solo 中如果正确进行了配置,将会在有文章评论时发送邮件,下面是默认配置:

mail.user=b3log.solo@gmail.com
mail.password=
mail.debug=false
mail.smtp.host=smtp.gmail.com
mail.smtp.port=587
mail.smtp.socketFactory.class=javax.net.ssl.SSLSocketFactory
mail.smtp.socketFactory.fallback=false
mail.smtp.socketFactory.port=465

和前面的配置项类似,邮件的配置也有一些隐式默认项:

说明 默认值
mail.smtp.auth 是否启用身份验证 true
mail.smtp.starttls.enable 是否启用 SSL 连接 true

static-resources.xml

文件路径是 WEB-INF/static-resources.xml,该配置文件是 必须的,用于配置静态资源路径。

每款 Servlet 容器都有一个“默认的 Servlet”来处理请求,并包括了对静态资源 IO 的优化实现,所以我们需要根据请求路径将静态资源请求分发到该 Servlet 上。

路径的匹配模式是 Ant-style 匹配,简单说来就是:

  • * 匹配多个字符
  • ? 匹配单个字符
  • ** 匹配多层目录

如果你需要 添加自己的静态资源(比如 HTML、MP3 等)就需要修改一下该文件。

Latke 对于“默认的 Servlet”支持如下:

  • Tomcat、Jetty、JBoss、GlassFish:default
  • Resin:resin-file
  • WebLogic:FileServlet
  • WebSphere:SimpleFileServlet

“恰到好处”的框架

在 HTTP 层,我们希望 Latke 是一个对 Servlet 的轻薄封装,这需要开发者对 Servlet 规范有一定了解,以最大的自由度来组合自己熟悉的工具库,比如:

  • 没有封装请求:数据格式、提交方式没有限制,开发者可以使用自己最擅长的方式进行参数处理
  • 适度封装响应:内置支持渲染 HTML、JSON(P)、图片等

在实体模型 / 持久化层,Latke 做了一个较创新的设计尝试,即使用 JSON 进行贯穿、无实体类。较理想的应用实现场景是前端提交的请求数据以 JSON 作为格式,该 JSON 在 Controller、Service 中做处理(校验、增减字段等)后就能直接保存到关系型数据库中,免去很多无谓的类型转换和复杂的 ORM。

除了实体模型,其他的类对象(控制器、服务、切面、事件、插件、定时任务、缓存等)都是使用 IoC 容器进行管理的,这一点和 Spring 核心原理一致。因为 IoC / AOP 确实有用,也是业界主流技术。

Latke 整体的设计理念是 在不影响开发效率的前提下,让开发者懂得 Java Web 的基本原理

框架只是辅助,原理才是根本。一个框架如果让开发者 知其然而不知其所以然 表面上是提高了生产效率,但本质上是在坑害它的使用者。Latke 并不是快餐,而是可以细细品味的粗茶淡饭。