-
動態服務發現
-
負載均衡
-
TLS 終止
-
HTTP / 2 和 gRPC 代理
-
斷路器
-
健康檢查
-
流量分割
-
故障註入
-
監控指標
route:
- destination:
host: productpage
port:
number: 9080
spec:
host: productpage
subsets:
- labels:
version: v1
name: v1
trafficPolicy:
connectionPool:
http:
http1MaxPendingRequests: 1
maxRequestsPerConnection: 1
tcp:
maxConnections: 1
tls:
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
name: external-svc-mongocluster
spec:
hosts:
- mymongodb.somedomain # not used
addresses:
- 192.192.192.192/24 # VIPs
ports:
- number: 27018
name: mongodb
protocol: MONGO
location: MESH_INTERNAL
resolution: STATIC
endpoints:
- address: 2.2.2.2
- address: 3.3.3.3
spec:
selector:
istio: ingressgateway
servers:
- hosts:
- '*'
port:
name: http
number: 80
protocol: HTTP
-
分散式事務監控
-
服務呼叫問題根因分析
-
服務依賴性分析
-
效能/延遲最佳化
-
Productpage 使用 Python 開發,負責展示書籍的名稱和書籍的簡介。
-
Details 使用 Ruby 開發,負責展示書籍的詳細資訊。
-
Reviews 使用 Java 開發,負責顯示書評。
-
Ratings 使用 Node.js 開發,負責顯示書籍的評星。
-
書籍的名稱:”The Comedy of Errors”,翻譯成中文是《錯誤的喜劇》。
-
書籍的簡介:Summary。翻譯成中文是:《錯誤的喜劇》是威廉·莎士比亞早期劇作之一。這是他最短的、也是他最喜歡的喜劇之一,除了雙關語和文字遊戲之外,幽默的主要部分來自於打鬧和錯誤的身份。
Type:
paperback
Pages:
200
Publisher:
PublisherA
Language:
English
ISBN-10:
1234567890
ISBN-13:
123-1234567890
An extremely entertaining play by Shakespeare. The slapstick humour is refreshing!
Absolutely fun and entertaining. The play lacks thematic depth when compared to other plays by Shakespeare.
def getProducts():
return [
{
'id': 0,
'title': 'The Comedy of Errors',
'descriptionHtml': 'Wikipedia Summary: The Comedy of Errors is one of < b>William Shakespeare\'s< /b> early plays. It is his shortest and one of his most farcical comedies, with a major part of the humour coming from slapstick and mistaken identity, in addition to puns and word play.'
}
details = {
"name" : "http://details{0}:9080".format(servicesDomain),
"endpoint" : "details",
"children" : []
}
ratings = {
"name" : "http://ratings{0}:9080".format(servicesDomain),
"endpoint" : "ratings",
"children" : []
}
reviews = {
"name" : "http://reviews{0}:9080".format(servicesDomain),
"endpoint" : "reviews",
"children" : [ratings]
}
productpage = {
"name" : "http://details{0}:9080".format(servicesDomain),
"endpoint" : "details",
"children" : [details, reviews]
}
service_dict = {
"productpage" : productpage,
"details" : details,
"reviews" : reviews,
}
private String getJsonResponse (String productId, int starsReviewer1, int starsReviewer2) {
String result = "{";
result += "\"id\": \"" + productId + "\",";
result += "\"reviews\": [";
// reviewer 1:
result += "{";
result += " \"reviewer\": \"Reviewer1\",";
result += " \"text\": \"An extremely entertaining play by Shakespeare. The slapstick humour is refreshing!\"";
if (ratings_enabled) {
if (starsReviewer1 != -1) {
result += ", \"rating\": {\"stars\": " + starsReviewer1 + ", \"color\": \"" + star_color + "\"}";
}
else {
result += ", \"rating\": {\"error\": \"Ratings service is currently unavailable\"}";
}
}
result += "},";
// reviewer 2:
result += "{";
result += " \"reviewer\": \"Reviewer2\",";
result += " \"text\": \"Absolutely fun and entertaining. The play lacks thematic depth when compared to other plays by Shakespeare.\"";
if (ratings_enabled) {
if (starsReviewer2 != -1) {
result += ", \"rating\": {\"stars\": " + starsReviewer2 + ", \"color\": \"" + star_color + "\"}";
}
else {
result += ", \"rating\": {\"error\": \"Ratings service is currently unavailable\"}";
}
}
result += "}";
result += "]";
result += "}";
return result;
}
-
star_color 表示評星的顏色(黑色和紅色)。
-
ratings_enabled 表示是否啟用評星。
private final static String star_color = System.getenv("STAR_COLOR") == null ? "black" : System.getenv("STAR_COLOR");
-
如果不指定 STARCOLOR 變數且 ratingsenabled 為 true,那麼評星預設為黑色。
-
如果指定 STARCOLOR 變數且 ratingsenabled 為 true,那麼評星顏色為傳入的顏色。
-
如果不指定 ratings_enabled 為 true,那麼將不會顯示評星。
#java build the app.
docker run --rm -v "$(pwd)":/home/gradle/project -w /home/gradle/project gradle:4.8.1 gradle clean build
pushd reviews-wlpcfg
#plain build -- no ratings
docker build -t "istio/examples-bookinfo-reviews-v1:${VERSION}" -t istio/examples-bookinfo-reviews-v1:latest --build-arg service_version=v1 .
#with ratings black stars
docker build -t "istio/examples-bookinfo-reviews-v2:${VERSION}" -t istio/examples-bookinfo-reviews-v2:latest --build-arg service_version=v2 \
--build-arg enable_ratings=true .
#with ratings red stars
docker build -t "istio/examples-bookinfo-reviews-v3:${VERSION}" -t istio/examples-bookinfo-reviews-v3:latest --build-arg service_version=v3 \
--build-arg enable_ratings=true --build-arg star_color=red .
-
V1:沒有評星(未指定 enable_ratings=true)。
-
V2:評星為黑色(指定 enableratings=true;未指定 starcolor 的變數,程式碼中預設的顏色為黑色)。
-
V3:評星為紅色(指定 enableratings=true;指定 starcolor 的變數為 red)。
#!/bin/sh
set -e
mongoimport --host localhost --db test --collection ratings --drop --file /app/data/ratings_data.json
{rating: 5}
{rating: 4}
# Initialize a mysql db with a 'test' db and be able test productpage with it.
# mysql -h 127.0.0.1 -ppassword < mysqldb-init.sql
CREATE DATABASE test;
USE test;
CREATE TABLE `ratings` (
`ReviewID` INT NOT NULL,
`Rating` INT,
PRIMARY KEY (`ReviewID`)
);
INSERT INTO ratings (ReviewID, Rating) VALUES (1, 5);
INSERT INTO ratings (ReviewID, Rating) VALUES (2, 4);
ReviewID | Rating |
1 | 5 |
2 | 4 |
* We default to using mongodb, if DB_TYPE is not set to mysql.
*/
if (process.env.SERVICE_VERSION === 'v2') {
if (process.env.DB_TYPE === 'mysql') {
var mysql = require('mysql')
var hostName = process.env.MYSQL_DB_HOST
var portNumber = process.env.MYSQL_DB_PORT
var username = process.env.MYSQL_DB_USER
var password = process.env.MYSQL_DB_PASSWORD
} else {
var MongoClient = require('mongodb').MongoClient
var url = process.env.MONGO_DB_URL
}
}
dispatcher.onGet(/^\/ratings\/[0-9]*/, function (req, res) {
var productIdStr = req.url.split('/').pop()
var productId = parseInt(productIdStr)
if (Number.isNaN(productId)) {
res.writeHead(400, {'Content-type': 'application/json'})
res.end(JSON.stringify({error: 'please provide numeric product ID'}))
} else if (process.env.SERVICE_VERSION === 'v2') {
var firstRating = 0
var secondRating = 0
if (process.env.DB_TYPE === 'mysql') {
var connection = mysql.createConnection({
host: hostName,
port: portNumber,
user: username,
password: password,
database: 'test'
})
connection.connect()
connection.query('SELECT Rating FROM ratings', function (err, results, fields) {
if (err) {
res.writeHead(500, {'Content-type': 'application/json'})
res.end(JSON.stringify({error: 'could not connect to ratings database'}))
} else {
if (results[0]) {
firstRating = results[0].Rating
}
if (results[1]) {
secondRating = results[1].Rating
}
var result = {
id: productId,
ratings: {
Reviewer1: firstRating,
Reviewer2: secondRating
}
}
res.writeHead(200, {'Content-type': 'application/json'})
res.end(JSON.stringify(result))
}
})
-
第一種:訪問 bookinfo 時(Productpage 呼叫的 Review V1),頁面沒有評星;
-
第二種:訪問 bookinfo 時(Productpage 呼叫的 Review V2),頁面是黑色的評星;
-
第三種:訪問 bookinfo 時(Productpage 呼叫的 Review V3),頁面是紅色的評星。
[root@master networking]# cat virtual-service-reviews-v3.yaml
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: reviews
spec:
hosts:
- reviews
http:
- route:
- destination:
host: reviews
subset: v3 version: v3
[root@master networking]# cat virtual-service-reviews-80-20.yaml
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: reviews
spec:
hosts:
- reviews
http:
- route:
- destination:
host: reviews
subset: v1
weight: 80
- destination:
host: reviews
subset: v2
weight: 20
[root@master ~]# cat speedrule.yaml
apiVersion: "config.istio.io/v1alpha2"
kind: memquota
metadata:
name: handler
namespace: myproject
spec:
quotas:
- name: requestcount.quota.myproject
# default rate limit is 5000qps
maxAmount: 5000
validDuration: 1s
# The first matching override is applied.
# A requestcount instance is checked against override dimensions.
overrides:
- dimensions:
destination: reviews
source: productpage
destinationVersion: v3
maxAmount: 1
validDuration: 1s
recommendation_rate_limit_handler.yml 檔案宣告了 requestcount quota。
[root@master ~]# cat recommendation_rate_limit_handler.yml
apiVersion: "config.istio.io/v1alpha2"
kind: quota
metadata:
name: requestcount
namespace: myproject
spec:
dimensions:
source: source.labels["app"] | source.service | "unknown"
sourceVersion: source.labels["version"] | "unknown"
destination: destination.labels["app"] | destination.service | "unknown"
destinationVersion: destination.labels["version"] | "unknown"
---
apiVersion: "config.istio.io/v1alpha2"
kind: rule
metadata:
name: quota
namespace: myproject
spec:
actions:
- handler: handler.memquota
instances:
- requestcount.quota
---
apiVersion: config.istio.io/v1alpha2
kind: QuotaSpec
metadata:
creationTimestamp: null
name: request-count
namespace: myproject
spec:
rules:
- quotas:
- charge: 1
quota: RequestCount
---
apiVersion: config.istio.io/v1alpha2
kind: QuotaSpecBinding
metadata:
creationTimestamp: null
name: request-count
namespace: myproject
spec:
quotaSpecs:
- name: request-count
namespace: myproject
services:
- name: productpage
namespace: myproject
- name: details
namespace: myproject
- name: reviews
namespace: myproject
while true; do curl http://istio-ingressgateway-istio-system.apps.example.com/productpage; sleep .1; done
spec:
host: productpage
subsets:
- labels:
version: v1
name: v1
trafficPolicy:
connectionPool:
http:
http1MaxPendingRequests: 1
maxRequestsPerConnection: 1
tcp:
maxConnections: 1
tls:
mode: ISTIO_MUTUAL
[root@master ~]# cat acl-blacklist.yml
apiVersion: "config.istio.io/v1alpha2"
kind: denier
metadata:
name: denycustomerhandler
spec:
status:
code: 7
message: Not allowed
---
apiVersion: "config.istio.io/v1alpha2"
kind: checknothing
metadata:
name: denycustomerrequests
spec:
---
apiVersion: "config.istio.io/v1alpha2"
kind: rule
metadata:
name: denycustomer
spec:
match: destination.labels["app"] == "details" && source.labels["app"]=="productpage"
actions:
- handler: denycustomerhandler.denier
instances: [ denycustomerrequests.checknothing ]