OctoPer的负载测试IDE(kraken)是具备多个前端应用程序的angular项目,javascript
这篇博文是想给每一个想要建立具备多个应用程序和库的Angular Workspace的开发人员一份参考指南。css
根据我建立Kraken的经验,首先说明如何使用Angular/Cli生成此类项目, 而后进行共享资源和单元测试,并给出有关部署的提示html
首先,让咱们要知道什么是angular Workspace Workspace是一组angular应用程序和库。angular.json
是Angular Workspace 的根级别文件为构建和开发工具提供了工做区范围和特定于项目(应用程序或库)的配置默认值。前端
但愿可使用Angular cli
生成Angular工做空间及其配置。就是本文章的主题:如何使用angular Cli 生成工做空间和其应用程序和库。java
请记住,在Angular术语中项目既能够是应用程序,也能够是库。二者都存储在projects工做区的文件夹中, 并在根angular.json
文件中进行配置。node
Angular须要Node.js版本10.9.0
或更高版本(运行node -v
查看当前版本)。 Angular cli生成的Angular应用程序都依赖于以npm软件包的形式提供的外部库。 Node.js包含一个npm软件包管理器。运行npm -v
以检查npm版本。我建议使用命令将npm更新到最新的可用版本 (在本博客发布时为6.10.0) npm install -g npm@latest
。nginx
最后,你须要Angular Cli, 使用如下命令在全局安装它:git
npm install -g @angular/cli
复制代码
ng new <name>
命令用户生成工做空间github
ng new kraken --createApplication=false --directory=frontend --interactive=false
复制代码
--createApplication=false
参数避免建立初始应用程序(默认值为true)。不然,Angular CLI src在新工做空间的文件夹中建立一个应用程序。在工做空间的子文件夹(projects)中生成应用程序。--interactive=false
参数用在此处,用以免angular cli建立项目时提示无用的参数,例如初始应用程序(咱们不生成)是否应包含路由模块或要使用的CSS预处理程序。--directory=frontend
参数是初始化工做空间的目录名称。默认为工做空间名称。 正如咱们在上面的屏幕截图中看到的那样,此命令在frontend
文件夹中生成了几个文件chrome
第一个是Angular Workspace配置文件 angular.json
。目前,它仅包含有关项目(应用程序和库) 位置的信息。一旦咱们生成了一些东西,它将变得很是复杂。
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {}
}
复制代码
该package.json文件列出了Angular所需的全部依赖关系。 因为该ng new命令还安装了npm依赖项,所以将生成package-lock.json文件 以及包含下载的依赖项的node_modules文件夹。
tsconfig.json文件指定打字稿编译器选项(anuglar项目使用TS)。 而tslint.json文件配置打字稿短绒,是一个工具,用于分析源代码, 标记编程错误,错误,文体错误,以及可疑的构造。
最后,README.md还会生成一个文件。 阅读该文档以获取与Angular CLI用法有关的信息:如何构建,服务和测试新生成的应用程序
ng generate application <name>
命令用于projects在工做区的子文件夹中建立新的应用程序。 运行如下命令以生成两个应用程序:Administration和Gatling:
cd frontend
ng generate application administration --style=scss --routing=true
ng generate application gatling --style=scss --routing=true
复制代码
这些命令在子文件夹projects生成administration、gatling两个项目,其中包含的文件以下(和常规的angular cli生成的项目,缺乏package.json文件)
命令也更新了根angular.json文件,添加了两个应用程序配置:
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"administration": {
"projectType": "application",
[...]
},
"gatling": {
"projectType": "application",
[...]
}
},
"defaultProject": "administration"
}
复制代码
咱们稍后将了解针对每一个应用程序项目的配置,由于咱们将必须更新它们以共享资源。
ng generate library <name>
命令用于生成库。 在工做空间文件夹中(frontend若是您遵循本教程的介绍),请运行如下命令:
ng generate library tools
ng generate library vendors
复制代码
再次在这里,angular.json使用两个新建立的库更新文件:
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"administration": {
[...]
},
"gatling": {
[...]
},
"tools": {
"projectType": "library",
[...]
}
},
"vendors": {
"projectType": "library",
[...]
}
},
"defaultProject": "administration"
}
复制代码
它还为每一个库projects/tools和projects/vendors建立相应的文件。文件与应用程序生成时的文件相似。
要注意的一件事是,在生成库时不能指定样式预处理器。angular.json库的工做空间配置(文件)中没有与此相关的配置。您每次在库中生成组件时都必须指定样式,例如使用command ng generate component my-component --style=scss --project=tools
。
这里的想法是在库中生成服务,并在应用程序中使用它。 让咱们在tools库中建立一个虚拟服务:
ng generate service hello-world --project=tools
复制代码
语法是,ng generate service <service-name>
而且该参数--project是必需的,以指定在那个库中生成服务。
这将建立一个文件projects/tools/src/hello-world.service.ts及其单元测试(projects/tools/src/hello-world.service.spec.ts)。更新它以建立一个简单的getter:
import {Injectable} from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class HelloWorldService {
get message(): string {
return 'Hello World!';
}
}
复制代码
注意:该服务经过providedIn: 'root'选项声明为可注入。这意味着该服务是单例。它能够在任何组件或服务中使用,而无需提供它。不管您在哪里使用它,它始终是同一实例。
若是没有此选项,咱们可能会使用每一个实例的语法为每一个实例注入一个实例@Component({providers: [HelloWorldService]})。咱们也可使用语法在模块级别执行相同的操做@NgModule({providers: [HelloWorldService]})。
在Angular文档中阅读有关依赖注入的更多信息。
复制代码
更新AppComponent projects/administration/src/app/app.component.ts
使用上面的服务
import {Component} from '@angular/core';
import {HelloWorldService} from 'projects/tools/src/lib/hello-world.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
title = 'administration';
constructor(helloWorld: HelloWorldService) {
this.title = helloWorld.message;
}
}
复制代码
若是使用的是IntelliJIdea
或Webstorm
,则HelloWorld
服务的默认导入为 import {HelloWorldService} from '../../../tools/src/lib/hello-world.service';
。 若是您四处移动文件,这将很丑陋且难以维护。您能够打开项目设置,而后选择TypeScript>导入>“ 使用相对于tsconfig.json的路径”选项:
Angular Material UI
Angular Material是一组遵循Material Design设置的约定的组件。 借助Angular CLI,只需运行如下命令便可在两个应用程序上安装Angular Material。
ng add @angular/material
ng add @angular/material --project=gatling
复制代码
注意:第一个命令不须要--project=administration参数,由于它是工做区配置中的默认项目("defaultProject": "administration"在angular.json文件中)。
复制代码
这两个命令在package.json文件中添加依赖项@angular/material,还自动更新各类项目文件,添加适当的样式,导入字体和NgModules:
UPDATE projects/administration/src/main.ts (391 bytes)
UPDATE projects/administration/src/app/app.module.ts (502 bytes)
UPDATE angular.json (10132 bytes)
UPDATE projects/administration/src/index.html (482 bytes)
UPDATE projects/administration/src/styles.scss (181 bytes)
复制代码
我发现将外部依赖项从新组合到单个NgModule中更加干净。 这就是为何咱们以前建立了一个vendors库。
所以,让咱们更新VendorsModule文件projects/vendors/src/lib/vendors.module.ts以导入和导出MatButtonModule,从而在一个地方从新组合咱们的外部依赖项:
import {NgModule} from '@angular/core';
import {MatButtonModule} from '@angular/material';
@NgModule({
imports: [MatButtonModule],
exports: [MatButtonModule]
})
export class VendorsModule { }
复制代码
而后,咱们在AppModule中导入VendorsModule projects/administration/src/app/app.module.ts:
@NgModule({
[...]
imports: [
VendorsModule,
],
[...]
})
export class AppModule { }
复制代码
最后,使用mat-raised-buttonAppComponent中的指令 projects/administration/src/app/app.component.html:
<div style="text-align:center">
<h1>
Welcome to {{ title }}!
</h1>
<div><img width="300" alt="Angular Logo" src="data:image/svg+xml;base64,..."></div>
<button mat-raised-button color="primary" >Click Me!</button>
</div>
复制代码
要为Angular应用程序提供服务,需运行命令ng serve --project=administration。
而后,您能够在http://localhost:4200/
上打开网页:
若是按照本教程的每一个步骤进行操做,则应该看到“欢迎使用Hello World !”消息以及提出的材料“ Click Me!”按钮。
不然,您能够下载源代码(仅npm install在启动应用程序以前运行)。
若是您想看一个更复杂的应用程序,请转到“ 完整的应用程序示例”一章。
另请注意,若是要同时启动administration和gatling应用程序,则会看到错误消息:
Port 4200 is already in use. Use '--port' to specify a different port.
Error: Port 4200 is already in use. Use '--port' to specify a different port.
复制代码
须要使用--port选项 具体请官方文档或者阅读“ 如何使用HAProxy服务多个角度的应用程序(下文有介绍)”一章以了解如何设置应用程序使用的默认端口。
让咱们回到负载测试IDE。将像Kraken这样的大型应用程序拆分为几个模块和库是很不错的。 它简化了维护并提升了代码的可重用性。
关于可重用性,跨组件库共享CSS样式和资产,拥有彻底隔离的模块可能会更干净一些, 可是因为我全部的库和应用程序都依赖于Angular Material, 由于我不想为每一个项目重复导入代码。
在上面的介绍中,咱们导入了默认的Angular Material主题之一。 为了使Kraken具备IDE的外观和感受,我想使用自定义Angular Material主题,其主题为:
Kraken
的用户界面要建立此UI,我必须建立几个组件库:
它们都使用位于工做区根目录styles文件夹中的通用CSS 。 例如,自定义材质主题SCSS文件将初始化Material Design并声明自定义颜色:
@import '../node_modules/@angular/material/theming';
[...]
$app-primary: mat-palette($mat-blue);
$app-accent: mat-palette($mat-green);
$app-info: mat-palette($mat-light-blue);
$app-success: mat-palette($mat-light-green);
$app-error: mat-palette($mat-deep-orange);
$app-background: mat-palette($mat-gray);
[...]
$mat-theme: (
[...]
);
@include angular-material-theme($mat-theme);
复制代码
而且紧凑的Scss文件,声明了用于更改组件密度的自定义CSS类:
@import 'app-padding';
@import 'app-font';
// Must also disable the ripple effect
@mixin compact-checkbox($size: 34px) {
$half: $size / 2;
.mat-checkbox-inner-container {
height: $half;
width: $half;
}
}
@mixin compact-button($size: 34px) {
.mat-icon-button {
width: $size;
height: $size;
line-height: $size * 0.9;
.ng-fa-icon {
font-size: $size * 0.5;
}
}
}
[...]
复制代码
为了在管理和加特林负载测试应用程序中使用这些SCSS文件, 咱们必须在Angular的工做区配置中声明它们angular.json:
"stylePreprocessorOptions": {
"includePaths": [
"styles"
]
},
复制代码
在stylePreprocessorOptions
必须加入到projects > administration / gatling > architect > build > options
。
在运行单元测试时,若是某些被测组件须要通用样式,则可能会出现如下错误:
ERROR in ../icon/src/lib/icon/icon.component.scss
Module build failed (from /home/kojiro/workspaces/kraken/frontend/node_modules/sass-loader/lib/loader.js):
@import 'app-margin';
^
Can't find stylesheet to import.
复制代码
在这种状况下,只需将添加stylePreprocessorOptions
到 projects > my-project > architect > test > options
工做空间配置部分。
除了公共CSS文件外,您可能还但愿在应用程序和库之间共享资产。
例如,在Kraken的资产中,咱们存储:
"assets": [
"projects/gatling/src/favicon.ico",
"projects/gatling/src/assets",
{
"glob": "**/*",
"input": "assets/",
"output": "assets/"
},
{
"glob": "**/worker-*.js",
"input": "node_modules/ace-builds/src-min/",
"output": "assets/ace/"
},
],
复制代码
它首先声明特定于Gatling应用程序的资产。 而后,它将使用语法"glob": "**/*"
将其包含在根资产文件夹中的全部文件。 最后,全部Ace方法都被复制到中assets/ace/
。
咱们还使用许多其余与Ace代码编辑器相关的javascript文件。 这些主题,模式和代码段必须在Architect选项的scripts部分中声明build:
"scripts": [
"node_modules/ace-builds/src-min/mode-xml.js",
"node_modules/ace-builds/src-min/mode-yaml.js",
"node_modules/ace-builds/src-min/ext-searchbox.js",
"node_modules/ace-builds/src-min/ext-language_tools.js",
"node_modules/ace-builds/src-min/ext-modelist.js",
"ext/mode-log.js",
"ext/theme-kraken.js",
"ext/snippets/scala.js"
]
复制代码
不管您要使用资产和外部脚本实现什么目标,建议您浏览一下Angular-Cli文档页面: Stories Asset Configuration。
Angular CLI容许您使用如下语法在特定项目上运行单元测试:
ng test --project=<my-project>
复制代码
默认状况下,它将连续运行单元测试,让您知道它们是成功仍是失败。 若是是使用fit而不是it在单元测试定义上运行该特定测试(您也能够fdescribe在测试套件上使用)。
它会在带有选项的文件夹中自动生成测试覆盖率报告。 /coverage/my-project--watch=false --codeCoverage=true
我但愿有一个报告能够从新组合全部覆盖率信息,并经过并行运行单元测试来加快测试执行速度。 所以,我建立了如下脚本:
#!/bin/bash
rm -rf coverage
rm -rf coverage-all
for dir in projects/*; do
if [["$dir" != *-e2e]]
then
prefix="projects/";
project=${dir#$prefix}; #Remove prefix
echo "$project"
ng test --watch=false --codeCoverage=true --sourceMap=true --project=$project &
fi
done
wait # Wait for all tasks to complete
./node_modules/istanbul/lib/cli.js report --dir ./coverage-all/ html
google-chrome ./coverage-all/index.html &
复制代码
它对每一个不以结尾结尾的项目-e2e(这些都是端到端测试项目)并行运行单元测试, 而且覆盖范围平行。而后,使用Istanbul client汇总全部报告。 警告:若是您有多个项目,则此脚本将同时运行多个Web浏览器。它可能会在计算机运行时冻结您的计算机!
Istanbul须要JSON格式的报告才能进行汇总.所以,也要更新karma.conf.js文件。它们位于每一个应用程序或库的根目录。只需添加json报告:
coverageIstanbulReporter: {
dir: require('path').join(__dirname, '../../coverage/analysis'),
reports: ['html', 'lcovonly', 'json'],
fixWebpackSourcePaths: true
},
复制代码
若是您想知道如何编写单元测试,请查看有关测试的Angular文档。
在Kraken中,咱们有两个Angular应用程序:Administration
和Gatling
。
这个想法是要在不一样的端口和base Href
上服务;而后将它们打包为在相同端口上使用但使用不一样的基本Href的docker映像。 将它们打包为在相同端口上使用,但使用不一样的base Href
的docker映像。
base Href
上部署Angular应用默认状况下,Angular在相同的URL和端口上为全部应用程序提供服务:http://localhost:4200
。
为了可以在开发过程当中同时启动两个应用程序, 请在Angular工做区配置angular.json文件中更改其中一个的端口。 例如,在Kraken中,Gatling应用程序的serve架构师配置为在端口4222上为其提供服务:
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "gatling:build",
"port": 4222
},
"configurations": {
"production": {
"browserTarget": "gatling:build:production"
}
}
},
复制代码
而后,ng serve使用如下参数运行命令:
ng serve --open --project=gatling --baseHref /gatling/
复制代码
因为参数和配置的缘由,它将经过URL http://localhost:4222/gatling
打开Web浏览器。
借助Angular-Cli ng build命令能够构建Angular应用程序:
ng build gatling --prod --baseHref /gatling/
复制代码
一样,这里--baseHref /gatling/
用于指定base Href。 该应用程序内置于文件夹中dist/gatling
。 如下DOCKERFILE生成一个NGINX Docker映像,其中包含已构建的应用程序:
FROM nginx:1.15.9-alpine
ARG APPLICATION
COPY nginx.conf /etc/nginx/nginx.conf
WORKDIR /usr/share/nginx/html
COPY dist/$APPLICATION .
RUN ls -laR .
EXPOSE 80
复制代码
ng build命令的输出将复制到/usr/share/nginx/html图像的文件夹中,并使用如下Nginx配置:
worker_processes 1;
events {
worker_connections 1024;
}
http {
server {
listen 80 default_server;
server_name localhost;
root /usr/share/nginx/html;
index index.html index.htm;
include /etc/nginx/mime.types;
gzip on;
gzip_min_length 1000;
gzip_proxied expired no-cache no-store private auth;
gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript;
location / {
try_files $uri $uri/ /index.html;
}
}
}
复制代码
运行如下命令以生成Docker映像:
docker build --rm=true -t gatling-ui:latest --build-arg APPLICATION=gatling .
复制代码
HAProxy是一种开源软件,可为基于TCP和HTTP的应用程序提供高可用性代理服务器。 对于Kraken,咱们使用它在适当的Frontend上重定向HTTP请求,具体取决于base Href
(感谢path_beg/administration关键字)。 这是配置文件:
global
defaults
mode http
option forwardfor
option http-server-close
# Set the max time to wait for a connection attempt to a server to succeed
timeout connect 30s
# Set the max allowed time to wait for a complete HTTP request
timeout client 50s
# Set the maximum inactivity time on the server side
timeout server 50s
# handle the situation where a client suddenly disappears from the net
timeout client-fin 30s
frontend http-in
bind *:80
mode http
acl has_administration path_beg /administration
acl has_gatling path_beg /gatling
use_backend kraken-administration if has_administration
use_backend kraken-gatling if has_gatling
backend kraken-administration
server kraken-administration-ui kraken-administration-ui:80 check fall 3 rise 2
reqrep ^([^\ ]*\ /)administration[/]?(.*) \1\2
backend kraken-gatling
server kraken-gatling-ui kraken-gatling-ui:80 check fall 3 rise 2
reqrep ^([^\ ]*\ /)gatling[/]?(.*) \1\2
复制代码
启动它的最简单方法是使用HAProxy Docker映像。
要查看完整的示例(使用启动两个前端应用程序,多个后端服务器和HAProxy docker-compose)。
我但愿这篇博文对但愿尝试Angular开发复杂的多项目工做区的人有所帮助。
能够在GitHub上找到Kraken的Angular前端的源代码,请查看完整的最新代码示例。若是您认为我错过了Angular开发的重要部分或犯了一个错误,请给我写评论。
最后,若是您想了解有关负载测试IDE的Kraken的更多信息,请查看其GitHub页面。
博文地址:地址