博客

  • 阿里云的函数计算

    阿里云的函数计算

    之前用过AWS的Lambda,当时感觉确实很新鲜,能够省一些费用,但是管理上又有一些复杂。

    阿里云现在也提供了类似的功能,叫函数计算,目前价格比较优惠,每个月前100万次调用免费。

    触发器包括对象存储,API网关,日志服务,表格存储,定时触发器等。如果是配合API网关使用的话,每G的费用大概0.72-0.8元之间,总的来说是非常适合一些特定适用场景。

    这里分享一个我自己的个人应用场景。

    用了小米路由器之后经常下载一些电影,但是手机上操作有点复杂,一般是百度搜索一下下载地址,可能是磁力链接,也可能是迅雷下载链接;找到链接以后需要到打开小米路由器的APP,添加下载。我期待的场景是输入关键字,然后返回一个链接,点开直接下载。

    这个场景真的适合函数计算,因为要做的事情无非就是发送请求到电影网站,然后抓取一下链接,然后调用小米远程下载的API生成一个链接。(这个链接在微信中打开会自动连接到小米的小程序)

    函数计算

    阿里云的函数计算提供了java,node和python运行环境。从AWS Lambda的经验中了解到Java启动原因可能不太适合serverless的使用。这次直接用node开工了。

    use strict';
    module.exports.handler = function(event, context, callback) {
        callback(null, 'hello world');
     };

    这个是我们需要实现的函数,event包含了调用器的输入,比如API网关的请求头和传参情况,它的类型是Buffer。一定要注意类型。。。不然要跳进坑里。

    context是函数计算自己的一些信息,比如AK和id什么的。

    callback接受两个参数,第一个是error,第二个是要返回的值。callback是必须调用的,不然就是超时。

    API网关和函数计算

    API网关作为触发器输入的event结构如下

    {
        "path":"api request path",
        "httpMethod":"request method name",
        "headers":{all headers,including system headers},
        "queryParameters":{query parameters},
        "pathParameters":{path 
  • Spring Rest文档生成

    写文档这个事情绝对是软件开发中最纠结的时候了,大部分时候你首先考虑的项目交付,但是从长远来看,一份文档,可以减少很多问题。

    Spring提供了一个Rest doc扩展,可以从单元测试中读取一些一些信息并生成文档。

    @Before
    public void setUp() {
    	this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
    			.apply(documentationConfiguration(this.restDocumentation)) 
    			.build();
    }

    首先配置上需要应用文档生成器,这个是一行代码的时候,无所谓。

    但是对于文档的内容,你其实需要从代码中注明,比如

    this.mockMvc.perform(get("/user/5").accept(MediaType.APPLICATION_JSON))
    		.andExpect(status().isOk())
    		.andDo(document("index",
    				responseFields( 
    						subsectionWithPath("contact")
    								.description("The user's contact details"))));

    andDo之后的和测试本身并无关联,只是为了文档生成。

    这个方案在我看来无非就是把文档写到了代码中,并没有根据测试的期待输入和期待输出直接生成文档。

    ScaCap的Auto-Restdocs项目是我目前找到的比较好的解决方案,它除了要求andDo之后表明文档名称以外不强制要求别的信息。

    它会从请求的字段中,还有请求的POJO中抽取字段和验证信息,文档本身还是使用的Spring Rest Docs的模板和样式。

    参考

    https://projects.spring.io/spring-restdocs/

    https://github.com/ScaCap/spring-auto-restdocs

    https://www.slideshare.net/fbenz/introducing-spring-auto-rest-docs…

  • 使用swagger-request-validator验证请求合法性

    使用swagger-request-validator验证请求合法性

    Swagger常常用在API设计上,很多应用框架还可以直接通过代码生成Swagger文件。

    有些时候我们自己可能也是一个服务的消费者,即我们需要发送请求给某个API。

    Swagger docs一般会包含请求的格式和要求,对于一般的GET请求还好,但是对于POST请求,有可能body本身挺复杂的,有时候就需要验证请求的本身是否符合Swagger doc的规定。

    swagger-request-validator是atlassian的一套swagger验证工具。支持Spring MVC等应用框架,当然也可以手动时候。

    首先引入依赖

    <dependency>
        <groupId>com.atlassian.oai</groupId>
        <artifactId>swagger-request-validator-core</artifactId>
        <version>${swagger-request-validator.version}</version>
    </dependency>

    其次通过Swagger定义文件生成验证器。

    final SwaggerRequestResponseValidator validator = SwaggerRequestResponseValidator
            .createFor(swaggerJsonUrl / swagger Json content)
            .withBasePathOverride(basePathOverride)
            .build;
    final ValidationReport report = validator.validate(request, response);

    验证器可以从网址或者定义文件内容直接生成。

    如果只需要验证请求,那么就调用validateRequest。

    这里的Request对象可以通过SimpleRequest.Builder直接构建的。

    参考

    https://bitbucket.org/atlassian/swagger-request-validator…

  • 线稿的自动上色

    线稿的自动上色

    有时候自己画了几张线稿,需要上色一下。

    上色的时候就有一些问题了,一是上色的专业知识欠缺,二是想尝试多种上色风格,费时费力。

    在AI上色领域,其实玩家并不是很多,最开始关注的是Style2paints,代码在Github上有,可以自己搭服务,其在线版本已经挂了一些日子了。其次是paintschainer,这是一个在线版本,可以上色,也可以处理线稿,只是上色风格比较上,更多是实验性质。

    以下是一个例子:

  • Java基准测试 — JMH

    JVM为了让Java代码更好更快的运行,做出了大量的改进和优化。虽然各种外部环境的变化让我们不再需要关注每一行代码的性能,但是也难免有需要自己衡量代码性能的时候。

    OpenJDK提供了JMH工具可以科学量化性能。

    对于一个已有的项目(比如Gradle项目),要使用JMH,首先要引入依赖

    compile group: 'org.openjdk.jmh', name: 'jmh-core', version: '1.20'
    compile group: 'org.openjdk.jmh', name: 'jmh-generator-annprocess', version: '1.20'
    

    在IDE中enable annprocess,然后新建类

    import org.openjdk.jmh.annotations.Benchmark;
    import org.openjdk.jmh.runner.Runner;
    import org.openjdk.jmh.runner.RunnerException;
    import org.openjdk.jmh.runner.options.Options;
    import org.openjdk.jmh.runner.options.OptionsBuilder;
    
    public class App {
    
        @Benchmark
        public void method() {
    
        }
    
        public 
  • Java 9 新特性 — 模块化Jar包(Multi-release JAR files)

    Java 9 新特性 — 模块化Jar包(Multi-release JAR files)

    Java 9的特性有一个标号238的特性,链接如下:JEP 238: Multi-Release JAR Files

    这个特性是有一定争议的,并没有在开源框架中大规模使用。

    简而言之,多版本jar允许打包同一个类的多个版本,供不同运行时使用。 例如,如果在JDK 8上运行,Java运行时将使用该类的Java 8版本,但如果在Java 9上运行,它将使用Java 9特定的实现。同样,如果为Java 10版本构建版本,则运行时将使用它来代替Java 9和默认(Java 8)版本。

    多版本JAR的用例

    优化的运行时间

    开发应用程序时,开发人员不知道将在哪个运行时执行。 但是对于某些运行时,可以实现相同类的优化版本。 例如通过查看java.version可以判断当前运行环境,然后通过一些技术手段来调用不同的实现。多版本Jar可以直接从JVM层提供这种特性的支持,不再需要自己实现。

    冲突的API

    例如需要支持2个不同的运行时,但有一个已弃用的API。 目前有两种广泛使用的解决方案:

    最直接的方法是使用反射。 例如,可以定义一个VersionProvider接口,然后定义2个具体的类Java8VersionProvider和Java9VersionProvider ,它们是在运行时加载。 这个解决方案的一个变体是只有一个类,但是有不同的方法,通过反射访问和调用不同的方法。
    如果技术上适用,更高级的解决方案是使用方法句柄。

    还有一种常见的方案就是直接提供两个不同的jar文件,比如guava-jdk8和guava-jdk9。这个方法简单有效,但是管理这种代码库会带来一些难度。

    多版本JAR的使用

    Multi-release JAR files的关键在于MANIFEST.MF中,首先要声明

    Multi-Release: true

    然后提供不同版本的class文件,大概目录如下

    Gradle作为常见的JVM平台项目构建工具,虽然目前没有对于多版本Jar的直接支持,但是由于灵活性大,可以直接自动配置一个。…

  • Github API和gatekeeper

    Github提供了大量API可供使用,但是大部分时候我们需要的可能只是一个简单的网页(client-side only)的应用,通过js直接调用Github API。

    但是由于安全限制,Github并不允许OAuth认证发生在这种应用上。这个时候我们就需要一个帮助我们去获取token的后端,而gatekeeper就可以完成这个任务。

    一般的验证流程是这样的

    1. 调用Github认证,https://github.com/login/oauth/authorize
    2. 在callback页面获取临时code
    3. 调用gatekeeper去换取可用token

    Gatekeeper是一个node应用,配置信息在config.json中。

    Gatekeeper可以很方便的部署到Heroku或者Azure上,对于一般使用,可以做到免费的。…

  • Serverless服务提供商

    今天去更新了一下serverless的代码,突然发现又多了几个Serverless服务商。

    之前场内的玩家有AWS Lamabda和Azure Function。然后IBM有一个类似的,可以自己托管的OpenWhisk。

    今天新看到了几个小一点的

    https://webtask.io/
    
    https://spotinst.com/
    
    http://kubeless.io/

    前两个都是服务商,第三个比较有意思,是基于Kubernetes的,开源可以自己托管。…

  • 再谈多多猫插件开发

    今天找漫画的时候又想到多多猫了,之前用过一下,当时写插件还是用的记事本在xml中直接编写js,那个体验很差,而且代码测试也很复杂。

    今天又需要插件了,自然想换点花样来做,首先考虑的就是使用构建工具来处理大部分问题。这里用的是Gulp。

    模板替换和文件生成

    我们的目标文件只有一个,那就是index.sited.xml。而这个文件又分了两个部分,首先是xml的声明,包含了一些插件信息,其次是我们的js代码。

    首先做的是将这两者分开,分别创建index.xml和index.js文件。

    <?xml version="1.0" encoding="utf-8"?>
    <sited ver="1" debug="1" engine="32" schema="1">
        <meta guid="<%=app.guid%>">
            <title><%= app.title %></title>
            <intro><%= app.intro %></intro>
            <author><%= app.author %></author>
            <url><%= app.url %></url>
            <encode>utf-8</encode>
            <expr><%= app.expr%></expr>
        </meta>
        <main dtype="1" durl="<%= app.url %>">
            <home>
                <hots cache="1d" title="首页" 
  • 单元测试的纯度和脆弱性风险

    客户公司新来了一个CTO,新官上任三把火,他首先盯上了单元测试,他要求只为单个类编写单元测试。所有依赖项都被模拟,简单而言就是单元测试不跨类。

    然后客户展示了一段代码,表示按照这个规范很难操作,因为现在有需求要业务重构B类。

    class A(val b: B) {
    
       fun doSomething() {
           b.doSomethingElse()
           b.doSomethingElse2()
           b.doSomethingElse3()
       }
    }

    我们来简单分析下这个代码和对应的规范:

    按照规范A类和B类都有自己的单元测试,然后根据新的业务需求,类B进行重构并隐藏在接口后面,因此从技术上讲,类A可能会根据场景获得不同的行为。

    现在的问题是,当我们想要遵循项目的指导方针时,我们还应该重构A的单元测试。之前有一个测试,测试对象是B(当然是被mock的)。现在,当对B的引用消失时,测试将被重构,以验证与这个新接口的调用。

    这种情况是在测试重构过程中发生的信息丢失。当在实际系统中存在此类调用时,由于单元测试的纯度,我们不再验证A->B之间的任何通信。

    我认为这种情况是对于单元测试纯度的过度要求,进而增加了单元测试的脆弱性。

    单元测试的纯度

    周围打听下这种纯度要求其实很常见,特别见于新开始建设单元测试的团队,项目领袖对于单元测试对于质量保障有较高的期望,进而导致对于纯度有近乎象牙塔的规范要求,从而导致建设失败。

    这种纯度的建设方式可以用于项目初期开荒,一锤子买卖,快速搞定上级下发的KPI,但是对于项目质量保障毫无价值。

    对于纯度的争议并不少见,我理解其实更多是对于Unit这个外来词的定义问题,机械的将编程语言的类和方法作为一个Unit就是一个高纯度的要求。

    还有一种原因我认为是对于单元测试本质目标的忽略,进而陷入定义的混乱,然而这不是重要的事情。

    不可否认关于Unit的确切含义存在一些分歧。但是,单元测试测试的目的并不是为了符合某些任意的定义。目的是检测错误。或者更一般地说:验证代码的行为是否符合预期。

    重构时,单元测试应该确保变更没有引入错误或更改行为。因此,测试不应该与测试代码中的实现细节耦合。事实上,应该可以完全重写类的实现,并让测试验证行为是否仍然相同。

    因此,从这个角度来看,测试并不关心被测试类是否调用了其他类。这是一个实现细节。事实上,典型的重构是将方法中的一些内部代码提取到一个单独的类中,而这正是测试应该能够验证重构没有改变行为的场景。

    还有一个更简单的例子,一个验证Email地址的Utils类,它是一个static方法,签名如下

    public static bool isValid(string email)

    高纯度的规范要求mock这个方法,你只需要和任何一个最近三个月写过单元测试的研发工程师聊一聊,他就能告诉这个规范是多么愚蠢。

    因此,底线是:避免mock(除了外部服务或非确定性输入的情况),并让测试联动尽可能多的类来验证行为。

    就我个人而言,我喜欢将“单元”视为“行为单元”——规范中可以独立测试的最小部分。但这只是我的定义。

    Mock对脆弱性的影响

    其实公正的讲,这个例子本身是特殊的,因为这个例子正好证明了一个中间问题。让我们来回想一下常见的改动情况。

    当你做出向后不兼容的改变时,依赖于你所改变的东西就会崩溃。所以,如果你的测试现在报错了,那就太好了——一切都在按预期进行。在这一点上,你有几个选择:…