Hoverfly - 微服务虚拟化示例

微服务虚拟化是一种模拟基于异构组件的应用程序(如API驱动的应用程序,基于云的应用程序或面向服务的体系结构)中特定组件行为的技术。

Hoverfly是一个轻量的API服务模拟工具(有时候也被称作服务虚拟化工具)。 使用Hoverfly,您可以创建应用程序依赖的API的真实模拟。

什么是微服务虚拟化?

目前云应用程序大量使用微服务,这些微服务相互交互以实现某些业务能力。在开发这种类型的生态系统时,我们有时会遇到一些常见问题,这些问题通常会影响整个团队的生产力,例如:

  • 目前,生态系统中的所有服务可能都不可用。也许是其他团队正在开发着。
  • 一些遗留的服务不处于开发环境,并且由于显而易见的原因,我们无法使用生产版本进行测试。
  • 由于某些原因,某些服务可能会停机。
  • 管理测试数据的问题。通常,要编写适当的测试,您需要对模拟或存根中的数据进行细粒度控制。管理具有多个团队的大型项目的测试数据会带来影响交付时间的瓶颈。

因此,我们可以很容易地理解,上述问题将影响当前产品的开发,并可能影响交付时间表,这与该产品的开发成本成正比。那么有什么解决方案呢?

  • 我们可以考虑使用一些流行的模拟框架来mock这些服务。但它也有一些缺点,例如,模拟通常是特定于场景的,并且需要花费很多精力来为这些服务创建模拟响应,并且模拟最好只适用于单元测试阶段(Junit)。
  • 我们可以使用存根服务,我们将使用硬编码响应来开发一些虚假服务 - 同样这些也需要我们开发一些东西来使这项工作运转起来。
  • 现在,我们需要在开发过程中进行持续集成,在这些情况下,Mocking和Stubbed服务都不是很好的候选者。

由于模拟和存根服务在有效使用方面都存在一些缺陷,因此为了解决上述问题,我们有一种称为服务虚拟化的技术,我们可以捕获/模拟实际服务。Hoverfly就是一种这样的工具,它使用新GO语言开发,提供了非常简单和现实的步骤来解决问题。

Hoverfly的特征:

  • 创建可重复使用的虚拟服务,在CI环境中替代缓慢和不稳定的外部或第三方服务
  • 模拟网络延迟,随机故障或速率限制以测试边缘情况
  • 使用多种编程语言扩展和自定义, 包括Go,Java,Javascript,Python
  • 导出,共享,编辑和导入API模拟数据
  • Java和Python的语言绑定
  • REST API
  • 轻巧,高性能,随处运行
  • 采用 Apache 2许可证

下面我们通过序列图来更好地理解虚拟化服务的运行过程:

Hoverfly处于捕获模式 - 作为实际服务的代理服务

捕获模式
模拟模式下的Hoverfly - 直接响应,无需进入实际服务

模拟模式

演示说明

我们将按照一定的步骤演示Hoverfly作为服务虚拟化工具的用法。

  • 我们将创建一个小型微服务生态系统,它们将相互调用。
  • 我们将使用Hoverfly在捕获模式下拦截实际的请求/响应。
  • 最后,我们将看到Hoverfly如何充当服务虚拟化服务器,以便在模拟模式下发回已捕获的请求/响应。
  • 我们还将检查基础服务停机时对我们的开发没有太大影响。
  • 此外,我们将看到我们可以轻松切换Hoverfly以返回捕获模式并将请求传递给实际服务。

准备工作

首先需要下载Hoverfly。官网地址,提供MacOS、Windows、Linux版本以及Docker镜像。

Windows版本下载地址

Windows版本下载后解压到任意目录下即可。里面包含了两个可执行文件:hoverctl.exe 和 hoverfly.exe。

  • hoverctl是一个命令行工具,可用于配置和控制Hoverfly。它允许你将Hoverfly作为守护程序运行。
  • Hoverfly是完成大部分工作的应用程序。它提供代理服务器和Web服务器以及API端点。

可以使用以下命令查看版本号

hoverctl version
hoverfly -version

运行Hoverfly实例

hoverctl start

查看日志

hoverctl logs

停止Hoverfly

hoverctl stop

创建微服务

让我们使用spring boot来创建服务,以加快开发速度。

应用服务

添加一个Rest端点

创建一个RestController类,公开端点/service/hoverfly。这个端点被我们在此之后开发的客户服务调用。为简单起见,我们只返回一些硬编码值,并在响应中添加响应时间。

@RestController
public class MyRestController {
	@RequestMapping(value = "/service/hoverfly")
	public HoverflyServiceResponse getSampleResponse() {
		System.out.println("Inside HoverflyActualServiceApplication::getSampleResponse()");
		return new HoverflyServiceResponse("returned value from HoverflyActualServiceApplication",
				new Date().toString(), UUID.randomUUID().toString());
	}
}

响应对象

public class HoverflyServiceResponse {
	private String message;
	private String responseTime;
	private String transactionid;

	public HoverflyServiceResponse(String message, String responseTime, String transactionid) {
		super();
		this.message = message;
		this.responseTime = responseTime;
		this.transactionid = transactionid;
	}

	public String getMessage() {
		return message;
	}

	public void setMessage(String message) {
		this.message = message;
	}

	public String getResponseTime() {
		return responseTime;
	}

	public void setResponseTime(String responseTime) {
		this.responseTime = responseTime;
	}

	public String getTransactionid() {
		return transactionid;
	}

	public void setTransactionid(String transactionid) {
		this.transactionid = transactionid;
	}
}

验证服务

将应用程序端口更改为9080。在application.properties文件中设置属性

server.port = 9080

启动服务后,通过浏览器访问http://localhost:9080/service/hoverfly可得到如下响应

{
    "message":"returned value from HoverflyActualServiceApplication",
    "responseTime":"Thu Jan 17 17:03:52 CST 2019",
    "transactionid":"56f3dec0-ce1f-4a94-95b7-2dee47b4b7fd"
}

客户服务

同样的方式新建一个消费者,创建一个RestController类公开端点/invoke。此端点方法将在内部调用我们刚刚开发的服务(hoverfly-actual-service)。

此外,我们在RestTemplate通过调用一个系统属性mode来在创建bean时添加了一定的逻辑。如果mode=proxy,那么所有的请求将首先通过Hoverfly代理进行路由。否则所有对此的请求将直接进入实际服务。

请仔细观察restTemplate()了解代理模式的方法。注意Hoverfly代理服务器将在http://localhost:8500上运行。

@RestController
public class ClientController {
	private static final int HOVERFLY_PORT = 8500;
	private static final String HOVERFLY_HOST = "localhost";
	private static final String PROXY = "proxy";
	
	@Autowired
	RestTemplate restTemplate;
	
	@RequestMapping("/invoke")
	public String invoke() {
		System.out.println("inside TestController::invoke()");
		String url = "http://localhost:9080/service/hoverfly";
		String response = restTemplate.exchange(url, HttpMethod.GET, null, new ParameterizedTypeReference<String>() {
		}).getBody();
		System.out.println("Actual Response : " + response);
		return response;
	}
	
	@Bean
	public RestTemplate restTemplate() {

		String mode = System.getProperty("mode");
		System.out.println("##################### Mode ################# " + mode);

		SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
		Proxy proxy = new Proxy(Type.HTTP, new InetSocketAddress(HOVERFLY_HOST, HOVERFLY_PORT));
		requestFactory.setProxy(proxy);
		RestTemplate template = null;

		if (mode != null && mode.equalsIgnoreCase(PROXY)) {
			System.out.println("######### Running application in PROXY mode so that we can use simulated hoveryfly server!!!!");
			template = new RestTemplate(requestFactory);
		} else {
			System.out.println("######### Running application in PRODUCTION mode so that we can use simulated hoveryfly server!!!!");
			template = new RestTemplate();
		}

		return template;
	}
}

验证服务
此服务在本地的8080端口中运行。启动时要注意带上参数-Dmode=proxy。
访问http://localhost:8080/invoke,得到响应:

{
    "message":"returned value from HoverflyActualServiceApplication",
    "responseTime":"Thu Jan 17 17:19:22 CST 2019",
    "transactionid":"1bc90f89-26cd-4bcd-87da-e6acb8649265"
}

Hoverfly演示

我们现在启动本地Hoverfly,并将测试不同的模式,并将了解它在服务停机时的实际帮助。Hoverfly提供6种不同模式Capture,Simulate,Spy,Modify,Synthesize和Diff。我们只会在此演示中展示Capture和Simulate模式。

在捕获模式下启动Hoverfly

打开Hoverfly目录(解压缩目录)的cmd窗口并键入hoverctl start命令。它将在本地工作站中启动hoverfly,proxy mode在8500端口中启动代理服务器,并将在8888端口启动管理UI 。

现在键入命令以更改要捕获的hoverfly模式。

hoverctl mode capture

现在转到浏览器http://localhost:8888/dashboard,它将显示管理UI,我们也可以在其中更改模式,还可以看到已捕获或模拟了多少请求。

管理控制台

捕获请求

现在在浏览器窗口中运行客户端服务几次,而hoverfly处于捕获模式。再次转到管理界面,注意Capture计数器已增加到你在浏览器中访问客户端服务应用程序的次数。

捕获请求

导出/导入捕获的请求

将模拟请求和响应存储在其他地方是个好主意,我们不需要一直运行Hoverfly。每当我们需要它时,我们将导回已保存的请求/响应并开始模拟服务。

要导出,打开Hoverfly命令窗口并输入命令:

hoverctl export simulations.json

这将导出已捕获的(在我们的情况下为3个事务)事务以及所有URL,请求等。一旦导出,文件将在主目录中创建json文件simulations.json。

{
	"data": {
		"pairs": [
			{
				"request": {
					"path": [
						{
							"matcher": "exact",
							"value": "/service/hoverfly"
						}
					],
					"method": [
						{
							"matcher": "exact",
							"value": "GET"
						}
					],
					"destination": [
						{
							"matcher": "exact",
							"value": "localhost:9080"
						}
					],
					"scheme": [
						{
							"matcher": "exact",
							"value": "http"
						}
					],
					"body": [
						{
							"matcher": "exact",
							"value": ""
						}
					],
					"query": {}
				},
				"response": {
					"status": 200,
					"body": "{\"message\":\"returned value from HoverflyActualServiceApplication\",\"responseTime\":\"Thu Jan 17 15:34:29 CST 2019\",\"transactionid\":\"012fe955-2259-494d-a548-88c232c35881\"}",
					"encodedBody": false,
					"headers": {
						"Content-Length": [
							"167"
						],
						"Content-Type": [
							"application/json;charset=UTF-8"
						],
						"Date": [
							"Thu, 17 Jan 2019 07:34:29 GMT"
						],
						"Hoverfly": [
							"Was-Here"
						]
					},
					"templated": false
				}
			}
		],
		"globalActions": {
			"delays": []
		}
	},
	"meta": {
		"schemaVersion": "v5",
		"hoverflyVersion": "v0.17.7",
		"timeExported": "2019-01-17T15:38:30+08:00"
	}
}

要导入simulations.json文件,可以键入命令以导入捕获的定义。

hoverctl import simulations.json

在模拟模式下测试

可使用命令转到模拟模式,也可以在控制台切换

hoverctl mode simulate

然后可以做以下的步骤

  • 在浏览器中打开客户端应用程序并点击刷新按钮,看到浏览器中的响应没有变化(通知响应时间和事务ID字段),这意味着Hoverfly处于活动状态并发送与导入文件匹配的所有URL模式的响应。
  • 转到Hoverfly管理界面,看到模拟计数器已增加到在模拟模式下访问客户端应用程序的次数。
  • 现在停止服务提供者并点击客户端应用程序,你可以很容易地看到Hoverfly模拟服务做出响应。在我们想要在实际服务停机时进行测试的实际场景中,这非常有用。

Hoverfly模拟了响应

总结

今天我们已经学习了如何有效和轻松地使用服务虚拟化工具Hoverfly,并将其集成到我们的微服务生态系统中。我们只看到了Hoverfly的一些功能,更多细节请访问官方文档

猜你喜欢

转载自blog.csdn.net/peterwanghao/article/details/86531160