为什么
搭建VSCode+Docker搭建PHP远程开发环境搭建,主要解决以下几个问题:
微信开发调试问题。
随时随地办公问题(具体参看code-server)。
减少本地环境污染,PHP与Node版本可能冲突(dyld: Library not loaded: /usr/local/opt/icu4c/lib/libicui18n.64.dylib)。
减少硬盘使用。
搭建VSCode+Docker搭建PHP远程开发环境搭建,主要解决以下几个问题:
微信开发调试问题。
随时随地办公问题(具体参看code-server)。
减少本地环境污染,PHP与Node版本可能冲突(dyld: Library not loaded: /usr/local/opt/icu4c/lib/libicui18n.64.dylib)。
减少硬盘使用。
现在的脑科学认为,人脑关于记忆主要分为两种功能:存储功能和提取功能。在《如何学习:成就你的终身学习力》中有一段话是这么写的:
记忆是不会“丢失”的,不会像我们以为的那样越变越淡,直至踪迹全无。准确地说,“丢失”——其实只是我们一时无法提取出来记忆而已,它的提取能力在当下很低,低到几乎为零。
作者还举了一个在老式图书馆搜集材料,因为霉味而勾起自己在1982在图书馆工作一段回忆的例子。如果换成触景生情的说法,想来也是有碰到过的。
有些记忆大师公开说过,会在记忆的过程中会将要记忆的内容,配合图像(或故事)一起编码来加速和巩固记忆。
Jfinal已经集成了EhCache缓存,只要简单配置就可以使用EhCache。最近的一个字典表数据量比较大,于是开启了EhCache,页面加载速度明显变快。昨日服务突然无法正常启动,错误如下(非法完整错误日志):
1 | net.sf.ehcache.CacheException: java.io.InvalidClassException: com.xxx.model.Concept; local class incompatible: stream classdesc serialVersionUID = -1234, local class serialVersionUID = -5678 |
看提示我们知道是serialVersionUID版本引起的问题。我们知道ehcache就会进行存盘的,如果保存的对象版本不一致就有可能出现这个问题,此时回忆起对Concept所在的表做了修改和重新生成。
问题基本清晰了,那解决方法其实是相对简单的,把缓存的文件删除就可以了。
具体做法是这样的,找到ehcache.xml配置文件的的存盘位置,这里我用了java.io.tmpdir,不同操作系统类型此路径可能不一致,可以在程序中打印出来看下, 我这里是/var/folders/d0/..。
1 | System.out.println(System.getProperty("java.io.tmpdir")); |
删除临时文件,需要根据自己的实际路径进行删除。
1 | cd /var/folders |
重新启动服务,正常启动。
因为系统是可以正常使用的,可以编写个清理的接口,这样可以在数据结构调整时及时清理缓存。
我们需要先了解下认证和授权,因为这两个概念常常被混淆。认证是鉴定用户身份的过程,它通常使用一个标识符 (如用户名或电子邮件地址)和一个加密令牌(比如密码或者存取令牌)来 鉴别用户身份(处理我是谁的问题)。授权是指验证用户是否允许做某件事的过程(处理我能做什么的问题)。更详细描述可以参看这里。
接下来说说,我们可能碰到的问题,一个产品可能会发布多个前端应用,比如Web,APP,小程序(微信,百度等),管理后台,不同的前端应用可能会有不同需求的。比如微信的授权认证和App的认证就不一致,Web和APP因为能力不同,需要的接口也可能不同,而管理后台可能要基于角色进行授权。
面对这些问题的时候我们使用Jfinal该如何解决呢?
首先需要参看文档,配置路由,Routes 级别拦截器。
我们可以针对不同的前端应用进行路由拆分和模块化,同时为不同的Routes添加各自的拦截器,实现不同的级别的认证与授权。
1 | public class WebRoutes extends Routes { |
以上代码并非完整代码,也没有实现实际的拦截器逻辑,只是为了简要的说明问题,如果你在实际的实现过程中碰到问题,可以和我联系,或许能帮上忙。通过配置不同的路由和拦截器,我们还可以尽可能多的复用代码(Controller和Service)。
对于前后端分离的项目,无论是开发还是部署,可能都会碰到CORS问题。采用Jfinal-undertow方式发布的应用来说,通常是不需要考虑CORS的,因为前端与后端是一起发布的,但在开发时或采用分离部署时,还是会碰到的这个问题,因此在这里按开发和部署两部分讲解如何解决CORS问题。
首先我们简单的了解下什么是CORS,完整的资料可以自己查看wikipedia。
跨域资源共享(CORS,Cross-origin resource sharing),它是一种允许受限的访问第一个访问资源域以外的资源(如字体)的机制。一张网页通常包含了很多跨域的图片,样式,脚本,iframes,或者视频,但一些跨域请求,尤其是Ajax请求,会因为默认的同源安全策略(简单来说就是绝对路径,需要是协议,域名,端口号三者与第一个访问的资源(浏览器中输入的URL)相同,才被认为是同源)而被禁止访问。
CORS定义了浏览器和服务器如何来决定一个跨域请求是安全的。它比完全同源请求有更多的自由和功能,但不要只是简单地允许所有跨源请求。CORS规范最初是作为W3C推荐标准发布的,但该文档已经过时。 Fetch Living Standard是当前维护活跃的规范。
对于程序开发来说,可能知道与CORS相关的请求头与响应头,已经够用了。如果需要更细的控制,可以进一步的阅读文档。
Origin
Access-Control-Request-Method
Access-Control-Request-Headers
Access-Control-Allow-Origin
Access-Control-Allow-Credentials
Access-Control-Expose-Headers
Access-Control-Max-Age
Access-Control-Allow-Methods
Access-Control-Allow-Headers
我们通常使用的是Access-Control-Allow-Origin: *。
如果后端没有做任何处理的情况,前端通常使用设置Chrome跨域选项和配置vue-cli代理模式这两种方式。推荐使用配置vue-cli代理模式,因为测试人员就可以不需要修改选项了。
对于Windows用户来说,创建个Chrome的快捷方式,target后中增加--disable-web-security --user-data-dir=即可,如:
1 | "...\chrome.exe" --disable-web-security --user-data-dir= |
对于MacOS用户来说,需要创建好用户数据目录,否则可能无法打开。
1 | open -n /Applications/Google\ Chrome.app/ --args --disable-web-security --user-data-dir=/Users/work/ChromeDevUserData |
参照vue-cli3的文档中的devServer.proxy配置即可。
1 | module.exports = { |
当然我们是希望前端不需要做任何配置,此时后端需要做些开发。实现的方式有很多,这里只展示一种方式简单的实现方式,通过扩展Interceptor实现CorsInterceptor,代码如下:
1 | public class CorsInterceptor implements Interceptor { |
jfinal-undertow推荐采用目录结构部署系统,当然也支持fatjar打包,可以参看jfinal-undertow 下部署。以下是就打包好后的目录格式。
1 | .app |
采用这种方式部署是没有跨域问题的。
正式环境更多的可能会用Nginx来做反向代理,将不同域名整一起。以下是简单示例,仅供参考:
1 | server { |
本指南只针对使用jfinal开发后端,vue开发前端,使用undertow方式部署的项目。对于非undertow部署项目,可能有所区别,可参考本文自行调整。对使用了Ningx的用户,可以参看Vue文档进行配置。
本文主要针对vue-router采用不同的HTML5 History 模式时的不同处理方法。此处假定你已按undertow方式正常运行后台项目。
今天看到一段Python程序,打印Python的保留关键字。
1 | import keyword |
程序写了这么多年,也接触了一些语言(C++、Go、Java、Javascript(TypeScript)、Kotlin、Pyton),印象中都是说语言规范,记下来就好。突然就好奇了,有多少语言能利用自己的内置类库完成这个事情呢?于是拿自己接触的这些语言来做个对比。
其实在StackOverflow上也有人有类似的问题,比如:
做了个简单的搜索,整理出下面这张表格,表格内容不一定准确,欢迎纠正。
| Programming Languages | Can print keys by SelfLib | Keywords |
|---|---|---|
| C++ | No | https://en.cppreference.com/w/cpp/keyword |
| Go | No | https://golang.org/ref/spec#Keywords |
| Java | No | https://docs.oracle.com/javase/tutorial/java/nutsandbolts/_keywords.html |
| JavaScript | No | https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar |
| TypeScript | No | https://github.com/Microsoft/TypeScript/issues/2536 |
| Kotlin | No | https://kotlinlang.org/docs/reference/keyword-reference.html |
| Python | Yes | https://docs.python.org/3/library/keyword.html |
一些补充:
SourceVersion.isKeyword(str)。在找不同语言关键字的时候找到这样一个统计库。A list and count of keywords in programming languages
The number of keywords in a programming language can be an indication to it’s simplicity/complexity, and that can impact the simplicity/complexity of the solutions that developers produce using it. Complex solutions can be more expensive to maintain and difficult to hire for. However, this is dependent on many factors and keyword count is only one; language idioms also play a massive part.
提供丰富的类库,在意细节,是否会是Python能这么流行的原因呢?
上一篇《基于ElasticSearch的OLAP框架思路》简单介绍了ES的存储结构,本文对ES的数据建模做些整理,是对上文的补充。
在关系型数据库中会强调范式化设计(Normalization),其主要目标是“减少不必要的更新”,但也就带了一个问题“查询缓慢”,因为数据库越范式化,就需要Join越多的表。
关系型数据库设计时,通常设计时做到3NF,但在必要时进行数据的冗余。以下是范式的简要描述:
范式化的另一个好处呢,就是减少了存储空间(也有可能是早些年存储成本大,才有了范式化设计)。但在存储越来越便宜和数据量越来越大,查询需求也越来越多的情况下,关系型数据库就越来越受到挑战,非关系型数据库不断的涌现出来。其实可以理解来范式化设计与反范式化设计的交锋和解决不同的应用场景问题。
反范式化设计(Denormalization),就是尽量不去使用关联关系,而是在文档中保存存冗余的数据,将数据扁平化(Flattening)。这样做的优点很明显,就是数据读取性能好,缺点也很明显,不适合在数据频繁修改的场景,一条数据改动,会引起很多数据的更新,对数据一致性提出更高要求。
ES数据库设计时通常会采用反范式化设计,因为ES并不擅长处理关联关系,一般采用以下四种方法来处理关联:
其实应用端关联不属于ES设计的部分,在本文中不再赘述,另外基础的数据类型对数据建模影响不大,需要时参看ES相关文档进行设计即可。
ES采用JSON的文档化存储,而JSON文档天然就是有层级的,文档内可以包含对象(普通对象和数组对象),如果存储数组对象并需要进行查询或统计时,计算结果可能是错误的,这个时候就需要用到嵌套对象。
嵌套对象是对象类型的一个特例,允许对象数组中的对象独立索引,Nested文档会被保存在两个Lucene文档中,在查询时做Join处理,因此每次更新,需要重新索引整个对象(包括根对象和嵌套对象)。
ES提供了类似关系型数据库中Join的实现。使用Join数据类型实现,可能过维护Parent/Child的关系,从而分离两个对象。这样呢,父文档和子文档是两个独立的文档,更新父文档时无需重新索引子文档,子文档被添加,更新或者删除时也不会影响父文档和其他的子文档。需要额外的内存维护,读取性能相对差。
null值无论是在关系型数据库,还是在非关系型数据库,都是被特殊对待的。在ES中可以通过设置null_value,来指定字段为null时的默认值。
Mappings设置无论从功能,还是性能方面来说都非常的重要,Mappings的每一次变更的都可能造成功能和性能的变化或者数据迁移,因此需要增加Meta(版本)信息并利用版本化控制工具(如Git)进行管理。
可以通过update_by_query来对历史数据的新增字段进行索引的重建。因为ES不允许对已有字段修改数据类型,只能通过reindex的方式将一个索引重新索引到一个新的索引上,但可能通过指定别名的方法来使得减少应用程序的修改。
对于OLAP来说,数据的清洗和转换是不可避免的,而通过Ingest Node和Painless Script。相当于关系型数据库的存储过程或函数。
Ingest Node:Pipeline&Processor(可实现插件)。
Painless Script:默认缓存100个。
https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping.html
《Elasticsearch核心技术与实战》

我们采用开发直接上线署的方式来快速开发,但因客户已经开始正式使用我司开发的小程序了,为了减少对客户的影响,现在需要额外部署一套开发环境。这个时候首先想到的就是通过Nginx来进行反向代理,开发环境使用二级域名,(如https://dev.zai500.com),方便开发小程序时候使用。因为中间有一些部署前没有碰过的问题,记录下来,以便后期项目时注意。
公司项目是从SpringBoot转换到jfinal的,所以之前使用的是JKS格式的证书,需要进行转换,当然如果可以重新申请也能获得不同格式的证书。
1 | keytool -importkeystore -srckeystore server.keystore -destkeystore server.p12 -srcalias serverkey -destalias serverkey -srcstoretype jks -deststoretype pkcs12 -srcstorepass 111111 -deststorepass 111111 -noprompt |
如果不清楚scralias可能通过如下命令来查看
1 | keytool -list -keystore server.jks |
因为我们的应用是使用Docker部署的,我们很自然的,使用Docker来部署Nginx。
1 | docker run -p 80:80 -p 443:443 --name nginx -it nginx:stable |
以上命令用将nginx的配置信息拷贝到宿主机的当前目录,之后可以将nginx改成conf
现在映射配置目录,不采用文件映射的方式,因为我们需要配置证书。
1 | docker stop nginx |
注意这里不使用文件的直接映射,因为需要配置证书。
conf目录下1 | - zai500.com |
1 | server { |
注意事项
ip addr show docker0 来查看,其中inet 172.17.0.1/16部分可获取。本次Nginx的调整除了方便之后开发外,也加强了自己对Ng配置的理解。中间因为Mixed Content的处理多花费了不少时间。

https://blog.csdn.net/liuchuan_com/article/details/54376258