JUnit是xUnit在Java中的应用,是一种单元测试框架。JUnit在编译时以Jar的形式链接,根据2013年对github 10,000个项目的调查,Junit和slf4j并列为最受欢迎的库,出现率为30.7%。

单元测试,一致性测试和性能测试

单元测试

单元测试是一段会执行目标工程中指定功能(测试对象)的代码,并验证它的行为。

被测试的代码比例也叫做覆盖率。

单元测试只针对一小段代码(类,方法),外部依赖应该被移除,例如用测试框架模拟外部依赖。

单元测试并不适合复杂的UI或组件交互,这部分工作属于一致性测试。

一致性测试

旨在测试组件或组件间的一致性,有时也称为功能测试。

这种测试将用例转化为测试集,例如模拟一个用户与应用交互。

性能测试

反复进行基准测试。

Read More

第二节中我们学习了如何使用工作队列来在多个工作进程中分发费时的任务。

但是如果我们需要运行一个远程的函数并且需要等待返回结果会怎样?这是一种完全不同的机制,通常叫做RPC(Remote Procedure Call)远程方法调用。

这一节中我们将使用RabbitMQ来建造一个RPC系统:一个Client和一个可扩容的RPC Server。因为没有真正值得分布式化的任务,我们用Fibonacci数列来代替。

Client接口

为了演示RPC Service是如何使用的,我们将编写一个简单的Client类。它提供一个名为call的方法,发出RPC请求并在返回之前阻塞。

1
2
3
FibonacciRpcClient fibonacciRpc = new FibonacciRpcClient();
String result = fibonacciRpc.call("4");
System.out.println( "fib(4) is " + result);
注:虽然RPC很常见,但也备受批评。问题在于程序无法得知调用的方法是远程(较慢)还是本地,这种模糊将导致系统不可预期以及不必要的额外调试复杂度,可能导致不可维护的`意面代码`。
确保方法远程或本地的明确性,作好文档。使组件之间的依赖关系清晰。处理异常情况,比如RPC不可用。存疑时尽量避免使用RPC。如果可能,使用异步方式,用状态转移代替阻塞。

Read More

上一节中,使用Dircet Exchange结合routingKey及bindingKey可以实现选择性地接收消息。但是它也有局限性,无法基于多种尺度来路由。

比如说日志系统中我们可能不仅希望根据级别,还希望根据日志发出者的身份来进行路由。比如syslog unix工具,就是根据severity和facility(auth/cron/kern…)来进行路由的。

这各方式更具灵活性,比如我想接收来自cron的Error信息和来自Kern的所有信息。

要实现这样的功能我们需要学习更复杂的Topic exchange。

Topic exchange

发送给topic exchange的信息的routingKey必须是一个word的list,用.号分隔。习惯上用一些能够代表消息特征单词来作为word,比如stock.usd.nyse是一个合法的routingKey。word不限个数,但总长不能超过255 Byte。

bindingKey也必须是这样的形式,而路由逻辑则和Direct方式相似,消息将被发送给所有bindingKey和RoutingKey匹配的队列。但是有两wildcard:

\* 能替换成任何一个(单个)word
# 能替换成任意(0-n)个word

Read More

这节中我们将实现只订阅消息的一个子集。例如,只接收Error的日志保存到磁盘,而将debug类的信息直接在console打印。

Bindings

上一节已经提到Binding,代码如下:

1
channel.queueBind(queueName, EXCHANGE_NAME, "");

Binding是Exchange与Queue之间的关系,可以理解为:queue对Exchange中的消息感兴趣。

Binding可以添加额外的路由参数,为免混淆,这里称为bindingKey。以下是使用BindingKey来绑定:

1
channel.queueBind(queueName, EXCHANGE_NAME, "black");

BindingKey的解释取决于Exchange类型,fanout类Exchange直接无视BindingKey.

Read More

  这节中我们将学习如何将一个消息传递给多个consummer,所谓的发布/订阅模式。

Exchange

  是时候学习完整的消息模型了。

  回顾一下之前的内容:

  1. producer是产生消息的用户进程。
  2. Queue是存储消息的缓存
  3. cunsumer是接收消息的用户进程

  切记消息从来不是直接发送给queue,实质上多数情况下producer甚至不知道有queue的存在。
  消息是直接发送给Exhange的。Exhange很简单,一方面它从producer接收消息并推送到Queue中,至于是将消息推送到特定的Queue,或所有QUEUE,或丢弃,这取决了Exchange的类型。

  有以下几种Exchange类型:direct,topic,headers和fanout。

Read More

  本节中我们将新建Work Queue用来在多个工作进程间分发比较耗时的任务。

  work queue的主要意图就是避免立即处理资源密集的任务并等待它完成,而是安排它稍后处理。将任务封闭为消息发送给queue,后台的工作进程会取得这个消息并处理它,如果有多个工作进程,它们分担任务。

  这种理念在web应用中非常有用,特别是在当无法在一个短时间窗内完成复杂请求时。

准备工作

  方便起见,我们将使用Thread.sleep()来代替一个真正的费时任务,使用.号来代表处理时长,比如消息hello...表示任务将费时3秒。

  对上一节的producer进行改动,使之能发送随机时长的任务。

Read More

  rabbitmq是AMQP的一种实现,可为分布式系统提供可靠的消息通信机制。它从生产者接收消息,并将其传递给消费者,并可二者之间可以提供路由、缓冲和存留功能。

queue一个无限缓冲,可以存储任意多的消息。多个producer可以将消息发送给同一queue,多个consumer也可以从同一queue接收消息。

  在使用RabbitMQ之前,需要有RabbitMQ-server,如果是ubuntu,可以用如下命令安装

1
sudo apt-get install rabbitmq-server

Hello World!

  以下使用Java编写一个Hello world例程,包含两个程序,一个producer和一个consumer。

  如果使用Maven工具,可以在pom.xml添加如下的依赖:

1
2
3
4
5
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>3.5.2</version>
</dependency>

Read More

  之前一篇写了如何反转链表以及定位链表的中点,现在来一个稍微复杂点的问题。

  这个面试题是我第一次参加实习生笔试时遇到的,当时并没有解出来。题目要求是检测单向链表是否存在环,并且只用常数的额外存储空间。

  简单直接的办法自然是将每一个指针记录下来,并且和新出现的节点的next指针比较,如果next指向之前出现过的节点,说明有环。但是这种方法需要至少线性的空间,不符合题目要求。而且每次都需要查找比较,性能很低。或者使用hash表,则空间消耗较list更大。

  出来之后我就开始搜索了,解法很好懂,但是如果没有学习过相关算法,要靠自己想出来还是挺有难度的。

  解法的奥秘全在下面这张图里了。

Read More

  Rest API中使用最多的参数类型是Json和xml,而在之前的项目一直接触的也是Json,实际上我也认为Json更简洁更直观。但是最近导师和某研究院签了一个合作项目,对方的前端使用的是xml,所以我也跟着查找了一下java对象与xml的转换方法。

  Servlet是自带json和xml两种返回格式的,在Get时返回xml非常简单,使用annotation指定xml即可。   

GET

1
2
3
4
5
@Get("xml")
public Object handleGetRequest() {
log.debug("received a Get Request");
return new Order("id123","user1",new Date(),"scan");
}

Read More

  前两天收到一个电话面试,期间问了一个关于链表的基础问题,而我竟然答错了。想想真是汗颜,一方面确实经验不足,有些紧张,另一方面也是太天真,总以为太过基础的内容不值花费太多时间。

  其实作为在校生,面试官不会对你有多高的期望,最看重的就是你的基本素质,而我恰恰忽略了这方面的准备。虽然项目期间写了不少代码,但是Java本身就提供种类容器,很少遇到要自己去处理链表、排序之类的操作。所以乍一问及,也没来得及细想,就凭印象给出了一个错误答案。
  
  所以后来找了一些关于链表的常见问题,亡羊补牢一下。   

链表反转

  所这个其实没有多少算法,凭直觉就能写出代码。考虑到笔试中使用C语言较多,所以这次用C来实现。说起来本科直接学的C++,与C的不同还是很明显的。

节点定义

链表名+next指针

1
2
3
4
5
6
#include <stdio.h>
typedef struct Node {
char *name;
struct Node *next;
} Node;

Read More