MySQL json 数据类型

必须要5.7以上版本才能使用
写在开头

mysql json 的功能很强大,只是用来当一个储存数据的字段 就没什么意义了。

使用proto做交互的话,只要JSON 写得好 用proro.Unmarshal() 就可以很方便的转换类型 可以精简很多代码

JSON path 是以 $ 开头,之后就是JSON的层级使用。

$
$[0].name
$[0][1]
//*表示所有
$[*]

数据比较

json中的数据可以用 =, <, <=, >, >=, <>, !=, and <=> 进行比较。

json里的数据类型是多样的,在不同类型之间进行比较时,高优先级的要大于低优先级的(可以用JSON_TYPE()函数查看类型)。

优先级从高到低如下:

BLOB > BIT > OPAQUE > DATETIME > TIME > DATE > BOOLEAN > ARRAY > OBJECT > STRING > INTEGER >DOUBLE > NULL

函数整理

创建函数
  • JSON_ARRAY: JSON_ARRAY(val1,val2,val3…)

  • JSON_OBJECT: 生成一个包含指定K-V对的json object。如果有key为NULL或参数个数为奇数,则抛出异常。

  • JSON_QUOTE:JSON_QUOTE(json_val) 将json_val用””号括起来。

  • CONVERT: CONVERT(json_string,JSON)

查询函数
  • JSON_CONTAINS:JSON_CONTAINS(json_doc,var,[path])

    查询json文档是否在指定path包含指定的数据,包含则返回1,否则返回0. 如果有参数为null或者path不存在则返回null

  • JSON_CONTAINS_PATH:JSON_CONTAINS_PATH(json_doc,one_or_all,path,path…..)

    查询是否存在指定路径,存在则返回1,否则返回0.如果有参数为NULL,则返回NULL。

    one_or_all只能取值“one”或”all”,one表示只要有一个存在即可;all表示所有的都存在才行。

  • JSON_EXTRACT:JSON_EXTRACT(json_doc, path[, path] …)

    5.7.9及以上版本可以用”->”替代JSON_EXTRACT

    从json文档里抽取数据。如果有参数有NULL或path不存在,则返回NULL。如果抽取出多个path,则返回的数据封闭在一个json array里。

  • JSON_UNQUOTE:表示去掉抽取结果的”号

    • JSON_UNQUOTE( JSON_EXTRACT(column, path) )
    • JSON_UNQUOTE(column -> path)
    • column->>path
  • JSON_KEYS:JSON_KEYS(json_doc[, path])

    获取json文档在指定路径下的所有键值,返回一个json array。如果有参数为NULL或path不存在,则返回NULL

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
        mysql> SELECT JSON_KEYS('{"a": 1, "b": {"c": 30}}');
    +---------------------------------------+
    | JSON_KEYS('{"a": 1, "b": {"c": 30}}') |
    +---------------------------------------+
    | ["a", "b"] |
    +---------------------------------------+
    mysql> SELECT JSON_KEYS('{"a": 1, "b": {"c": 30}}', '$.b');
    +----------------------------------------------+
    | JSON_KEYS('{"a": 1, "b": {"c": 30}}', '$.b') |
    +----------------------------------------------+
    | ["c"] |
    +----------------------------------------------+
    ```
    - JSON_SEARCH:JSON_SEARCH(json_doc, one_or_all, search_str[, escape_char[, path] ...])

    查询包含指定字符串的paths,并作为一个json array返回。如果有参数为NUL或path不存在,则返回NULL。

    one_or_all:"one"表示查询到一个即返回;"all"表示查询所有。

    search_str:要查询的字符串。 可以用LIKE里的'%'或‘_’匹配。

    path:在指定path下查。
    ```mysql
    mysql> SET @j = '["abc", [{"k": "10"}, "def"], {"x":"abc"}, {"y":"bcd"}]';

    mysql> SELECT JSON_SEARCH(@j, 'one', 'abc');
    +-------------------------------+
    | JSON_SEARCH(@j, 'one', 'abc') |
    +-------------------------------+
    | "$[0]" |
    +-------------------------------+

    mysql> SELECT JSON_SEARCH(@j, 'all', 'abc');
    +-------------------------------+
    | JSON_SEARCH(@j, 'all', 'abc') |
    +-------------------------------+
    | ["$[0]", "$[2].x"] |
    +-------------------------------+
    ```

    ##### 修改函数

    - JSON_ARRAY_APPEND:JSON_ARRAY_APPEND(json_doc, path, val[, path, val] ...)

    在指定path的json array尾部追加val。如果指定path是一个json object,则将其封装成一个json array再追加。如果有参数为NULL,则返回NULL
    ```mysql
    mysql> SET @j = '["a", ["b", "c"], "d"]';
    mysql> SELECT JSON_ARRAY_APPEND(@j, '$[1]', 1);
    +----------------------------------+
    | JSON_ARRAY_APPEND(@j, '$[1]', 1) |
    +----------------------------------+
    | ["a", ["b", "c", 1], "d"] |
    +----------------------------------+
    mysql> SELECT JSON_ARRAY_APPEND(@j, '$[0]', 2);
    +----------------------------------+
    | JSON_ARRAY_APPEND(@j, '$[0]', 2) |
    +----------------------------------+
    | [["a", 2], ["b", "c"], "d"] |
    +----------------------------------+
    mysql> SELECT JSON_ARRAY_APPEND(@j, '$', 'z');
    +----------------------------------+
    | JSON_ARRAY_APPEND(@j, '$', z) |
    +----------------------------------+
    | ["a", ["b", "c"], "d","z"] |
    +----------------------------------+
  • JSON_ARRAY_INSERT:JSON_ARRAY_INSERT(json_doc, path, val[, path, val] …)

    在path指定的json array元素插入val,原位置及以右的元素顺次右移。如果path指定的数据非json array元素,则略过此val;

    如果指定的元素下标超过json array的长度,则插入尾部。

  • JSON_INSERT:JSON_INSERT(json_doc, path, val[, path, val] …)

    在指定path下插入数据,如果path已存在,则忽略此val(不存在才插入)。

  • JSON_REPLACE:JSON_REPLACE(json_doc, path, val[, path, val] …)

    替换指定路径的数据,如果某个路径不存在则略过(存在才替换)。如果有参数为NULL,则返回NULL。

  • JSON_SET :JSON_SET(json_doc, path, val[, path, val] …)

    设置指定路径的数据(不管是否存在)。如果有参数为NULL,则返回NULL。

  • JSON_MERGE:JSON_MERGE(json_doc, json_doc[, json_doc] …)

    • 如果都是json array,则结果自动merge为一个json array;
    • 如果都是json object,则结果自动merge为一个json object;
    • 如果有多种类型,则将非json array的元素封装成json array再按照规则一进行mege。
  • JSON_REMOVE: JSON_REMOVE(json_doc, path[, path] …)

    移除指定路径的数据,如果某个路径不存在则略过此路径。如果有参数为NULL,则返回NULL。

特性查询
  • JSON_DEPTH:JSON_DEPTH(json_doc)

    获取json文档的深度。如果参数为NULL,则返回NULL。

    空的json array、json object或标量的深度为1。

  • JSON_LENGTH: JSON_LENGTH(json_doc[, path])

    获取指定路径下的长度。如果参数为NULL,则返回NULL。

    • 标量的长度为1
    • json array的长度为元素的个数
    • json object的长度为key的个数
  • JSON_TYPE: JSON_TYPE(json_val)

    获取json文档的具体类型。如果参数为NULL,则返回NULL。

  • JSON_VALID: JSON_VALID(val)

    判断val是否为有效的json格式,是为1,不是为0。如果参数为NUL,则返回NULL。

Kubernetes 学习笔记 (kubectl)

指令

  • kubectl get 展示资源
1
2
3
4
5
6
7
8
获取命名空间
kubectl get namespaces

获取Pods
kubectl get pods

查看服务状态
kubectl get services
  • kubectl describe 资源详情

    1
    2
    3

    获取Pods详细信息
    kubectl describe pods
  • kubectl logs 打印容器日志

    1
    kubectl logs $POD_NAME
  • kubectl exec 在容器上执行命令

1
2
3
4
5
在Pod中执行命令
kubectl exec $POD_NAME env

启动容器中的bash
kubectl exec -ti $POD_NAME bash
  • 其他指令

– 对外部暴露服务

1
2
3
4
kubectl expose deployment/kubernetes-bootcamp --type="NodePort" --port 8080 service "kubernetes-bootcamp" exposed
$ kubectl get servicesNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 2m
kubernetes-bootcamp NodePort 10.99.175.225 <none> 8080:32172/TCP 5s

– 通过标签查询Pod和Service

$ kubectl get pods -l run=kubernetes-bootcamp
NAME READY STATUS RESTARTS AGE
kubernetes-bootcamp-5c69669756-hmc69 1/1 Running 0 8m
$ kubectl get services -l = run=kubernetes-bootcamp
error: name cannot be provided when a selector is specified
$ kubectl get services -l run=kubernetes-bootcamp
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes-bootcamp NodePort 10.99.175.225 8080:32172/TCP 6m

tl label pod $POD_NAME app=v1 pod “kubernetes-bootcamp-5c69669756-hmc69” labeled

1
2

-- 新增标签

$ kubectl label pod $POD_NAME app=v1
pod “kubernetes-bootcamp-5c69669756-hmc69” labeled
$ kubectl describe pods $POD_NAME
Name: kubernetes-bootcamp-5c69669756-hmc69
Namespace: default
Node: minikube/172.17.0.11
Start Time: Tue, 17 Jul 2018 05:20:35 +0000
Labels: app=v1
pod-template-hash=1725225312
run=kubernetes-bootcamp
$ kubectl get pods -l app=v1
NAME READY STATUS RESTARTS AGE
kubernetes-bootcamp-5c69669756-hmc69 1/1 Running 0 11m

1
2

-- 删除服务

$ kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 443/TCP 12m
kubernetes-bootcamp NodePort 10.99.175.225 8080:32172/TCP 10m
$ kubectl delete service -l run=kubernetes-bootcamp
service “kubernetes-bootcamp” deleted
$ kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 443/TCP 12m
`

MobX 状态管理

Github 项目

一直以来Vue的状态管理就一直让人很混乱,多人合作开发的大项目对状态管理会让人很烦躁。
无意间发现了Mobx这个项目,他能很好的解决此部分问题。
使Vue变成单纯的视图层工具,将数据状态剥离出框架,以便更好的管理与切换前端技术栈。

建议使用Mobx 前需要对 vue-class-component 和 装饰器 有所了解。(虽然也支持传统写法)
有同事吐槽过 这样结合把VUE变成了react,但是本人倾向于各部分只专注做一件事,把数据层完全和UI层玻璃出来,会大大降低维护成本与代码可读性
这样 vue 就只是一个单纯的动态模板渲染引擎,就像 react 一样。

安装

npm i mobx-vue -S

定义ViewModel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import { action, computed, observable } from "mobx";
export default class ViewModel {
@observable age = 10;
@observable users = [];

@computed get computedAge() {
return this.age + 1;
}

@action.bound setAge() {
this.age++;
}

@action.bound async fetchUsers() {
this.users = await http.get('/users')
}
}

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<template>
<section>
<p v-text="state.age"></p>
<p v-text="state.computedAge"></p>
<p v-for="user in state.users" :key="user.name">{{user.name}}</p>
<button @click="state.setAge"></button>
</section>
</template>

<script lang="ts">
import Vue from "vue";
import Component from "vue-class-component";
import { Observer } from "mobx-vue";
import ViewModel from "./ViewModel";

@Observer
@Component
export default class App extends Vue {
state = new ViewModel()
mounted() {
this.state.fetchUsers();
}
}
</script>

或者
<script lang="ts">
@Connect(new ViewModel())
@Component()
export default class App extends Vue {

mounted() {
this.fetchUsers();
}
}
</script>

微服务架构(初探)

微服务架构(一)

WHY?

服务拆解,每个服务拆解成之做自己的业务事物,形成高度内聚的自治性。
2.0+采用模块化的分层式架构,所有的业务逻辑代码最终会打进一个代码库中统一部署。
目前遇到的问题
1、全部开发人员会共享一个代码库,不同模块的边界模糊,实现高内聚、松耦合极其困难。(我们现在发版经常遇到要选择部分功能上线 GIT分支合并上传就会很麻烦)
2、处理遗留系统,尝试去做重构改进时,会动一发而牵全身。
3、模块的边界轻易被穿透。

微服务的主要特性

微服务架构强调每个服务一个进程。
独立开发与演进,隔离代码库至少让同一应用系统不同层次的开发人员享有自己完全自治的领地,每个微服务都有一个掌控者。
通过分解巨大单体式应用为多个服务方法解决了复杂性问题。
在功能不变的情况下,应用被分解为多个可管理的分支或服务。
微服务架构模式是每个微服务独立的部署。
开发者不再需要协调其它服务部署对本服务的影响。
这种改变可以加快部署速度。UI 团队可以采用 AB 测试,快速的部署变化。
微服务架构模式使得持续化部署成为可能。
你可以根据每个服务的规模来部署满足需求的规模。甚至于,你可以使用更适合于服务资源需求的硬件。
微服务架构模式使得每个服务独立扩展。

成本

把 1 个应用进程部署到 1 台主机,部署复杂度是 1 x 1 = 1,我们有 200 台主机,那么部署复杂度是 1 x 200 = 200。
把 1 个应用进程拆分成了 50 个微服务进程,则部署复杂度变成了 50 x 200 = 10000。
部署变得复杂和麻烦多了,同时监控的进程数也大幅增加,监控的复杂度也上升了很多。
所以实施微服务架构是有很高成本的,只有系统的规模到了一定程度才适合。
微服务推崇一切自动化的文化,这也是因为其运维复杂度的乘数级飙升,
从开发之后的构建、测试、部署都需要一个高度自动化的环境来支撑才能有效降低边际成本。

优点

每个微服务都很小,这样能聚焦一个指定的业务功能或业务需求。
微服务能够被小团队单独开发,这个小团队是2到5人的开发人员组成。
微服务是松耦合的,是有功能意义的服务,无论是在开发阶段或部署阶段都是独立的。
微服务能使用不同的语言开发。
微服务允许容易且灵活的方式集成自动部署,通过持续集成工具,如Jenkins, bamboo 。
一个团队的新成员能够更快投入生产。
微服务易于被一个开发人员理解,修改和维护,这样小团队能够更关注自己的工作成果。无需通过合作才能体现价值。
微服务允许你利用融合最新技术。
微服务只是业务逻辑的代码,不会和HTML,CSS 或其他界面组件混合。
微服务能够即时被要求扩展。
微服务能部署中低端配置的服务器上。
易于和第三方集成。
每个微服务都有自己的存储能力,可以有自己的数据库。也可以有统一数据库。

缺点

微服务架构可能带来过多的操作。
需要DevOps技巧 (http://en.wikipedia.org/wiki/DevOps).

  1. 开发(软件工程)、技术运营和质量保障(QA)三者的交集。
  2. 是一组过程、方法与系统的统称,用于促进开发(应用程序/软件工程)、技术运营和质量保障(QA)部门之间的沟通、协作与整合。它的出现是由于软件行业日益清晰地认识到:为了按时交付软件产品和服务,开发和运营工作必须紧密合作。[1]

可能双倍的努力。
分布式系统可能复杂难以管理。
因为分布部署跟踪问题难。
当服务数量增加,管理复杂性增加。

实施要点

1.自动化文化与环境:自动构建、自动测试、自动部署。
2.围绕业务能力建模服务,松耦合、高内聚、暴露接口而隐藏实现细节。
3.服务协作模型:中心化(乐队模型:中心指挥)和去中心化(舞蹈模型:群舞自组织),各自场景不同。
4.服务交互方式:RPC/REST/WS 技术很多但考虑统一。
5.服务部署的独立性、失败隔离性、可监控性。
6.服务流控:降级、限流
7.服务恢复:多考虑故障发生如何快速恢复而非如何避免发生故障。
8.服务发布:灰度。
9.服务部署:一服务一主机模型,需要虚拟化(Hypervisor)、容器化(LXC, Docker)等技术支持,实现硬件资源隔离。
10.服务配置:中心化配置服务支持
11.康威定律:任何设计系统的组织,最终产生的设计等同于组织之内、之间的沟通结构。系统架构的设计符合组织沟通结构取得的收益最大。
12.伯斯塔尔法则:服务健壮性原则 —— 发送时要保守,接收时要开放。

网关

实现一个API网关作为所有客户端的唯一入口。API网关有两种方式来处理请求。有些请求被简单地代理/路由到合适的服务上,其他的请求被转给到一组服务。

相比于提供普适的API,API网关根据不同的客户端开放不同的API。比如,Netflix API网关运行着客户端特定的适配器代码,会向客户端提供最适合其需求的API。
API网关也可以实现安全性,比如验证客户端是否被授权进行某请求。

需要考虑的问题

单个微服务代码量小,易修改和维护。但是,系统复杂度的总量是不变的,每个服务代码少了,但服务的个数肯定就多了。就跟拼图游戏一样,切的越碎,越难拼出整幅图。一个系统被拆分成零碎的微服务,最后要集成为一个完整的系统,其复杂度肯定比大块的功能集成要高很多。
单个微服务数据独立,可独立部署和运行。虽然微服务本身是可以独立部署和运行的,但仍然避免不了业务上的你来我往,这就涉及到要对外通信,当微服务的数量达到一定量级的时候,如何提供一个高效的集群通信机制成为一个问题。
单个微服务拥有自己的进程,进程本身就可以动态的启停,为无缝升级的打好了基础,但谁来启动和停止进程,什么时机,选择在哪台设备上做这件事情才是无缝升级的关键。这个能力并不是微服务本身提供的,而是需要背后强大的版本管理和部署能力。
多个相同的微服务可以做负载均衡,提高性能和可靠性。正是因为相同微服务可以有多个不同实例,让服务按需动态伸缩成为可能,在高峰期可以启动更多的相同的微服务实例为更多用户服务,以此提高响应速度。同时这种机制也提供了高可靠性,在某个微服务故障后,其他相同的微服务可以接替其工作,对外表现为某个设备故障后业务不中断。同样的道理,微服务本身是不会去关心系统负载的,那么什么时候应该启动更多的微服务,多个微服务的流量应该如何调度和分发,这背后也有一套复杂的负载监控和均衡的系统在起作用。
微服务可以独立部署和对外提供服务,微服务的业务上线和下线是动态的,当一个新的微服务上线时,用户是如何访问到这种新的服务?这就需要有一个统一的入口,新的服务可以动态的注册到这个入口上,用户每次访问时可以从这个入口拿到系统所有服务的访问地址。这个统一的系统入口并不是微服务本身的一部分,所以这种能力需要系统单独提供。
还有一些企业级关注的系统问题,比如,安全策略如何集中管理?系统故障如何快速审计和跟踪到具体服务?整个系统状态如何监控?服务之间的依赖关系如何管理?等等这些问题都不是单个微服务考虑的范畴,而需要有一个系统性的考虑和设计,让每个微服务都能够按照系统性的要求和约束提供对应的安全性,可靠性,可维护性的能力。
服务容错:当企业微服务化以后,服务之间会有错综复杂的依赖关系,例如,一个前端请求一般会依赖于多个后端服务,技术上称为1 -> N扇出. 在实际生产环境中,服务往往不是百分百可靠,服务可能会出错或者产生延迟,如果一个应用不能对其依赖的故障进行容错和隔离,那么该应用本身就处在被拖垮的风险中。在一个高流量的网站中,某个单一后端一旦发生延迟,可能在数秒内导致所有应用资源(线程,队列等)被耗尽,造成所谓的雪崩效应(Cascading Failure),严重时可致整个网站瘫痪。

服务依赖

总结

微服务的实施是有一定的先决条件:基础的运维能力(如监控、快速配置、快速部署)需提前构建,否则就会陷入如我们般被动的局面。推荐采用基础设施及代码的实践,通过代码来描述计算和网络基础设施的方法,使得图案度i可以快速安全的搭建和处理由新的配置代替的服务器,服务器之间可以拥有更高的一致性,降低了在“我的环境工作,而你的环境不工作”的可能,也是为后续的发布策略和运维提供更好的支撑。
其他要点
系统支撑

基础

日志和审计,主要是日志的汇总,分类和查询
监控和告警,主要是监控每个服务的状态,必要时产生告警
消息总线,轻量级的MQ或HTTP
注册发现
负载均衡
部署和升级
事件调度机制
资源管理,如:底层的虚拟机,物理机和网络管理

画龙点睛

认证和鉴权
微服务统一代码框架,支持多种编程语言
统一服务构建和打包
统一服务测试
微服务CI/CD流水线
服务依赖关系管理
统一问题跟踪调试框架,俗称调用链
灰度发布
蓝绿部署

容器(Docker)与微服务

•容器够小
–解决微服务对机器数量的诉求
•容器独立
–解决多语言问题
•开发环境与生产环境相同
–单机开发、提升效率
•容器效率高
–省钱
•代码/image一体化
–可复用管理系统
•容器的横向与纵向扩容
–可复制
–可动态调节CPU与内存
•Image管理
•系统安全管理
•授权管理
•系统成熟度
•社区成熟度
由于Docker引入,不同的微服务可以使用不同的技术架构,比如Node.js Java PHP Python等,这些单个的服务都可以独立完成交付生命周期
微服务+API + 平台的开发模式,容器化微服务的持续交付概念。

首先需要考虑构建DevOps能力,这是保证微服务架构在持续交付和应对复杂运维问题的动力之源;
其次保持服务持续演进,使之能够快速、低成本地被拆分和合并,以快速响应业务的变化;
同时要保持团队和架构对齐。微服务貌似是技术层面的变革,但它对团队结构和组织文化有很强的要求和影响。识别和构建匹配架构的团队是解决问题的另一大支柱。
最后,打造持续改进的自组织文化是实施微服务的关键基石。只有持续改进,持续学习和反馈,持续打造这样一个文化氛围和团队,微服务架构才能持续发展下去,保持新鲜的生命力,从而实现我们的初衷。

参考资料
1、微服务架构下的开发部署实践
https://zhuanlan.zhihu.com/p/21563604
2、微服务与SOA:与其重用+不如抓住敏捷性http://www.searchsoa.com.cn/showcontent_88178.htm
3、Red Hat:API层是微服务架构成功的关键https://searchcloudcomputing.techtarget.com.cn/5-11978/
4、SOA和微服务架构的区别
https://www.zhihu.com/question/37808426
5、解析微服务架构
https://kb.cnblogs.com/page/520922/

微服务框架技术栈之Protocol Buffer

微服务框架技术栈之Protocol Buffer

本文主要介绍 Protocol Buffers 的安装使用,以及语法结构,并结合 gRPC 来讲解如何应用 .proto 文件

一. 简介

Protocol Buffer 是 google 的一种数据交换的格式,它独立于语言,独立于平台。google 提供了多种语言的实现:java、c#、c++、go 和 python,每一种实现都包含了相应语言的编译器以及库文件。由于它是一种二进制的格式,比使用 xml 进行数据交换快许多。可以把它用于分布式应用之间的数据通信或者异构环境下的数据交换。作为一种效率和兼容性都很优秀的二进制数据传输格式,可以用于诸如网络传输、配置文件、数据存储等诸多领域。

二. 为什么使用Protocol Buffer

除了我们在前面讲到的 protobuf 拥有非常好的性能之外,还有以下几个优点:

  1. gRPC 使用 protobuf 非常高效,允许使用 protobuf 定义服务以及使用 protobuf 进行数据交换。
  2. 通过 protobuf 定义的 .proto 文件,允许通过 protobuf 编译器生成不同编程语言的代码来读写这个数据结构,目前 protobuf 提供了多种编程语言的支持。

总结:由于微服务架构的跨平台特性,以及 gRPC 对其的良好支持,所以使用 protobuf 可以作为微服务之间进行数据交换的标准之一

三. 编写 .proto 文件

由于 gRPC 仅支持 proto3,所以本文以 proto3 版本来进行讲解

文件名

一个比较好的习惯是认真对待 proto 文件的文件名。比如将命名规则定于如下:

{packageName}.{MessageName}.proto

结构体

Protocol Buffer 中有两种结构体。

1. Message 结构体

可以使用 Message 定义程序中需要处理的结构化数据。类似 java 和 C 语言的数据定义。

1
2
3
4
5
6
7
8
9
10
syntax = "proto3";

package book;

message BookStruct
{
int64 isbn = 1;
string title = 2;
string author = 3;
}

在上例中,package 名字叫做 book,定义了一个结构体 BookStruct,该消息有三个成员,类型为 int64 的 isbn,一个类型为 string 的 title,还有一个类型未 string 的 author。这三个成员都是可选的。

只有 proto2 版本才允许通过 required 和 optional 配置成员是否可选。

2. Service结构体(定义一个 RPC 接口)

如果想要将消息类型用在RPC中,可以在 .proto 文件中定义一个 RPC 服务接口,protobuf 编译器将会根据所选择的不同语言生成对应语言的接口代码以及Stub。

1
2
3
service BookService {
rpc PostBook (BookStruct) returns (BookStruct) {}
}

3. Service结构体(定义一个 HTTP 接口)

在实际应用中,我们不但需要定义 RPC 接口,还需要定义 HTTP 接口。

通过 Google 提供的标准接口 google/api/annotations.proto ,我们可以对 protobuf 服务描述其相应的 HTTP接口形式。通过使用 proto 编译器可以生成相应的 HTTP JSON 的接口实现。

1
2
3
4
5
6
7
8
9
10
import "google/api/annotations.proto";

service BookService {
rpc PostBookApi (BookStruct) returns (BookStruct) {
option (google.api.http) = {
post: "/v1/postBook"
body: "*"
};
}
}

gRPC-Gateway

更多参考

Protobuf3 语法指南

Google Protocol Buffer 的使用和原理

四. 编译 .proto 文件,生成代码

###安装 protoc 编译器

随 Google Protocol Buffer 源代码一起发布的编译器 protoc,支持多种编程语言。但使用 Google Protocol Buffer 的 Compiler 包,您可以开发出支持其他语言的新的编译器。

Language Source
C++ (include C++ runtime and protoc) src
Java java
Python python
Objective-C objectivec
C# csharp
JavaScript js
Ruby ruby
Go golang/protobuf
PHP php
Dart dart-lang/protobuf

Protoc 命令

安装好 protoc 编译器后,就可以使用 protoc 命令了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
protoc -h
Usage: /usr/bin/protoc [OPTION] PROTO_FILES
Parse PROTO_FILES and generate output based on the options given:
-IPATH, --proto_path=PATH Specify the directory in which to search for
imports. May be specified multiple times;
directories will be searched in order. If not
given, the current working directory is used.
--version Show version info and exit.
-h, --help Show this text and exit.
--encode=MESSAGE_TYPE Read a text-format message of the given type
from standard input and write it in binary
to standard output. The message type must
be defined in PROTO_FILES or their imports.
--decode=MESSAGE_TYPE Read a binary message of the given type from
standard input and write it in text format
to standard output. The message type must
be defined in PROTO_FILES or their imports.
--decode_raw Read an arbitrary protocol message from
standard input and write the raw tag/value
pairs in text format to standard output. No
PROTO_FILES should be given when using this
flag.
--descriptor_set_in=FILES Specifies a delimited list of FILES
each containing a FileDescriptorSet (a
protocol buffer defined in descriptor.proto).
The FileDescriptor for each of the PROTO_FILES
provided will be loaded from these
FileDescriptorSets. If a FileDescriptor
appears multiple times, the first occurrence
will be used.
-oFILE, Writes a FileDescriptorSet (a protocol buffer,
--descriptor_set_out=FILE defined in descriptor.proto) containing all of
the input files to FILE.
--include_imports When using --descriptor_set_out, also include
all dependencies of the input files in the
set, so that the set is self-contained.
--include_source_info When using --descriptor_set_out, do not strip
SourceCodeInfo from the FileDescriptorProto.
This results in vastly larger descriptors that
include information about the original
location of each decl in the source file as
well as surrounding comments.
--dependency_out=FILE Write a dependency output file in the format
expected by make. This writes the transitive
set of input file paths to FILE
--error_format=FORMAT Set the format in which to print errors.
FORMAT may be 'gcc' (the default) or 'msvs'
(Microsoft Visual Studio format).
--print_free_field_numbers Print the free field numbers of the messages
defined in the given proto files. Groups share
the same field number space with the parent
message. Extension ranges are counted as
occupied fields numbers.

--plugin=EXECUTABLE Specifies a plugin executable to use.
Normally, protoc searches the PATH for
plugins, but you may specify additional
executables not in the path using this flag.
Additionally, EXECUTABLE may be of the form
NAME=PATH, in which case the given plugin name
is mapped to the given executable even if
the executable's own name differs.
--cpp_out=OUT_DIR Generate C++ header and source.
--csharp_out=OUT_DIR Generate C# source file.
--java_out=OUT_DIR Generate Java source file.
--javanano_out=OUT_DIR Generate Java Nano source file.
--js_out=OUT_DIR Generate JavaScript source.
--objc_out=OUT_DIR Generate Objective C header and source.
--php_out=OUT_DIR Generate PHP source file.
--python_out=OUT_DIR Generate Python source file.
--ruby_out=OUT_DIR Generate Ruby source file.

从上述帮助文件中,我们可以看到 protoc 命令可以添加以下参数

  • –cpp_out 导出 C++ 代码
  • –csharp_out 导出 C#代码
  • –java_out 导出 Java 代码
  • –javanano_out 导出 Javanano 代码
  • –js_out 导出 JavaScript 代码
  • –objc_out 导出 ObjectC 代码
  • –php_out 导出 PHP 代码
  • –python_out 导出 Python 代码
  • –ruby_out 导出 Ruby 代码

举例:

1
2
3
4
5
6
protoc \
--proto_path=IMPORT_PATH \
--cpp_out=DST_DIR \
--java_out=DST_DIR \
--python_out=DST_DIR \
IMPORT_PATH/file.proto

Protoc 命令的 plugin 参数

除了可以导出上述代码之外,我们还可以使用 plugin 参数加载第三方插件,导出其他任何我们想要的代码。例如:Swagger JSON Schema。

举例:

1
2
3
4
5
6
7
8
9
# Install protoc plugin:protoc-gen-swagger
go get -u github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger

# Export swagger json schema
protoc \
--plugin=protoc-gen-swagger=`which protoc-gen-swagger`
--proto_path=IMPORT_PATH \
--swagger_out=DST_DIR \
IMPORT_PATH/file.proto

微服务架构(选型方案)

微服务选型方案

微服务架构的定义:

1、一些列的独立的服务共同组成系统
2、单独部署,跑在自己的进程中
3、每个服务为独立的业务开发
4、分布式管理
5、非常强调隔离性

其标准是:

1、分布式服务组成的系统
2、按照业务,而不是技术来划分组织
3、做有生命的产品而不是项目
4、强服务个体和弱通信( Smart endpoints and dumb pipes )
5、自动化运维( DevOps )
6、高度容错性
7、快速演化和迭代

我们现在的业务很复杂,系统项目也很多。而微服务的学习和落地拥有一定的成本,所以我这边将实施分为三个阶段

1、准备:

①选择切入点
②技术选型

2、开始:

①解决客户端如何访问这些服务
②每个服务之间的通信
③解决服务的容器管理,水平部署,负载均衡等

3、管理:

①优化填坑
②自动部署、自动发布、自动注册、自动发现
③监控服务
④应急机制

准备:

微服务架构有七个关键点:
服务框架
运行时支撑服务
服务安全
后台服务
服务容错
服务监控
服务部署平台
我们需要选择几个关键点进行研究,以“最小可用”为目标开始,逐步优化架构已完成所有微服务架构指标。

服务框架

gRPC:是谷歌近年新推的一套 RPC 框架,基于 protobuf 的强契约编程模型,能自动生成各种语言客户端,且保证互操作。支持 HTTP2 是 gRPC 的一大亮点,通讯层性能比 HTTP 有很大改进。

Protobuf: 是 Google 的一种数据交换的格式,是一套结构数据序列化机制。用户通过编写 proto 文件来定义 protobuf 的数据结构,一个简单的 proto 文件是由一个 message 结构体和一个 service 结构体组成。

运行时支撑服务

服务注册中心:在Docker技术的基础上,为容器化的应用提供部署运行、资源调度、服务发现和动态伸缩等一系列完整功能,提高了大规模容器集群管理的便捷性。
Etcd:一个高可用,分布式,一致的key-value存储,用来共享配置和服务发现(把缓存服务器和数据库从docker分离出去)
k8s:具有完备的集群管理能力,多扩多层次的安全防护和准入机制、多租户应用支撑能力、透明的服务注册和发现机制、內建智能负载均衡器、强大的故障发现和自我修复能力、服务滚动升级和在线扩容能力、可扩展的资源自动调度机制以及多粒度的资源配额管理能力。(做微服务的管理)

服务路由网管
建立服务网关
服务网关 = 路由转发 + 过滤器

1、路由转发:接收一切外界请求,转发到后端的微服务上去;
2、过滤器:在服务网关中可以完成一系列的横切功能,例如权限校验、限流以及监控等,这些都可以通过过滤器完成(其实路由转发也是通过过滤器实现的)

集中式配置中心

选型一个合格的配置中心,至少需要满足如下4个核心需求:
1.非开发环境下应用配置的保密性,避免将关键配置写入源代码
2.不同部署环境下应用配置的隔离性,比如非生产环境的配置不能用于生产环境
3.同一部署环境下的服务器应用配置的一致性,即所有服务器使用同一份配置
4.分布式环境下应用配置的可管理性,即提供远程管理配置的能力
该部分有很多方法达成,比如jenkins的环境部署,docker配置文件等等,所以在一开始不需要考虑这部分

服务监控

主要包括日志监控,调用链监控,Metrics 监控,健康检查和告警通知等。
服务容错
①重试机制
②限流
③熔断机制
④负载均衡
⑤降级(本地缓存)
后台服务
后台服务主要包括消息系统,分布式缓存,分布式数据访问层和任务调度系统。
服务安全选型
1.使用支持 OAuth 2.0 和 OpenID Connect 标准协议的授权服务器(个人建议定制自研);
2.使用 API 网关作为单一访问入口,统一实现安全治理;
3.客户在访问微服务之前,先通过授权服务器登录获取 access token,然后将 access token 和请求一起发送到网关;
4.网关获取 access token,通过授权服务器校验 token,同时做 token 转换获取 JWT token。
5.网关将 JWT Token 和请求一起转发到后台微服务;
6.JWT 中可以存储用户会话信息,该信息可以传递给后台的微服务,也可以在微服务之间传递,用作认证授权等用途;
7.每个微服务包含 JWT 客户端,能够解密 JWT 并获取其中的用户会话信息。
8.整个方案中,access token 是一种 by reference token,不包含用户信息可以直接暴露在公网上;JWT token 是一种 by value token,可以包含用户信息但不暴露在公网上。

### 服务部署平台选型

容器已经被社区接受为交付微服务的一种理想手段,可以实现不可变(immutable)发布模式。
一个轻量级的基于容器的服务部署平台主要包括容器资源调度,发布系统,镜像治理,资源治理和 IAM 等模块。
1.简化发布流程如下:
2.应用通过 CI 集成后生成镜像,用户将镜像推到镜像治理中心;
3.用户在资产治理中心申请发布,填报应用,发布和配额相关信息,然后等待审批通过;
4.发布审批通过,开发人员通过发布控制台发布应用;
5.发布系统通过查询资产治理中心获取发布规格信息;
6.发布系统向容器云发出启动容器实例指令;
7.容器云从镜像治理中心拉取镜像并启动容器;
8.容器内服务启动后自注册到服务注册中心,并保持定期心跳;
9.用户通过发布系统调用服务注册中心调拨流量,实现蓝绿,金丝雀或灰度发布等机制;
10.网关和内部微服务客户端定期同步服务注册中心上的服务路由表,将流量按负载均衡策略分发到新的服务实例上。

另外,持续交付流水线(CD Pipeline)也是微服务发布重要环节,这块主要和研发流程相关,一般需要企业定制,下面是一个可供参考的流水线模型,在镜像治理中心上封装一些轻量级的治理流程,例如只有通过测试环境测试的镜像才能升级发布到 UAT 环境,只有通过 UAT 环境测试的镜像才能升级发布到生产环境,通过在流水线上设置一些质量门,保障应用高质量交付到生产。

开始:

使用 Kubernetes来管理 Docker 集群,当 Kubernetes 满足不了需求时,可在部署平台开发相应的功能来满足开发查看日志、监控和报警等需求,尽量避免登录主机和容器。
实现了基于Docker集群的部署系统,便于开发者便捷地部署自己的应用程序。最终达到部署环境干净一致,可重复部署、迅速扩容和回滚。
容器管理平台构架图
容器管理平台主要功能有集群管理和状态展示、灰度发布和代码回退、组件模板、应用管理、镜像仓库和权限管理等。它采用前后端分离的架构,前端使用 JS 渲染,后端使用 Python 提供 API。这样开发者可以快速的进行发布和回退操作。
容器管理平台在应用发布流程,集群调度策略,k8s节点网络架构,阿里云支持,基础监控指标等方面进行了优化改进。

应用发布流程
新的发布系统是用户提交代码后,在发布系统选择要部署的commit,点击构建以后,系统会自动编译,打包成镜像,推送镜像仓库。如果构建成功,用户点击发布新版本的实例,灰度没有问题,全量,下线老版本的实例。回退时代码不需要构建,直接发布老版本实例。在某段时间内,新老版本是同时存在的。

Hello World

Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub.

Quick Start

Create a new post

1
$ hexo new "My New Post"

More info: Writing

Run server

1
$ hexo server

More info: Server

Generate static files

1
$ hexo generate

More info: Generating

Deploy to remote sites

1
$ hexo deploy

图片

1.首先把blog(hexo)目录下的_config.yml里的psot_asset_folder:设置为true

2.在blog(hexo)目录下执行:

npm install hexo-asset-image –save

3.在blog(hexo)目录下Git Bash Here,运行hexo n “博客名”来生成md博客时,会在_post目录下看到一个与博客同名的文件夹。

4.将想要上传的图片先扔到文件夹下,然后在博客中使用markdown的格式引入图片:

你想要输入的替代文字

More info: Deployment

istio ymal笔记

唯一性判断

apiVersion + kind + name + namespace

指令:
对资源进行配置
1
2
kubectl apply -f dashboard.yaml
kubectl delete -f dashboard.yaml
YAML配置文件管理对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
对象管理:
# 创建deployment资源
kubectl create -f nginx-deployment.yaml
# 查看deployment
kubectl get deploy
# 查看ReplicaSet
kubectl get rs
# 查看pods所有标签
kubectl get pods --show-labels
# 根据标签查看pods
kubectl get pods -l app=nginx
# 滚动更新镜像
kubectl set image deployment/nginx-deployment nginx=nginx:1.11
或者
kubectl edit deployment/nginx-deployment
或者
kubectl apply -f nginx-deployment.yaml
# 实时观察发布状态:
kubectl rollout status deployment/nginx-deployment
# 查看deployment历史修订版本
kubectl rollout history deployment/nginx-deployment
kubectl rollout history deployment/nginx-deployment --revision=3
# 回滚到以前版本
kubectl rollout undo deployment/nginx-deployment
kubectl rollout undo deployment/nginx-deployment --to-revision=3
# 扩容deployment的Pod副本数量
kubectl scale deployment nginx-deployment --replicas=10
# 设置启动扩容/缩容
kubectl autoscale deployment nginx-deployment --min=10 --max=15 --cpu-percent=80
(流量管理/配置服务在网关上的路由)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
apiVersion: networking.istio.io/v1beta1
kind: VirtualService # virtualservice(流量管理/配置服务在网关上的路由)
metadata:
name: admin-asset # 注册的服务名称
namespace: $NS
spec:
hosts:
- "*" # kubernetes下对应的service
gateways:
- istio-system/api-gateway # 选择网关
http:
- match: # 定义服务匹配模式
- uri: # 以uri的方式
prefix: "/admin/device" # prefix代表前缀,可以匹配到二级子目录上 (exact代表绝对路径,只能匹配定义的字段)
route:
- destination:
host: admin-asset
port:
number: 80
- match:
- uri:
prefix: "/admin.AssetService"
- uri:
prefix: "/admin.DeviceService"
- uri:
prefix: "/admin.SpaceService"
- uri:
prefix: "/admin.UsageService"
- uri:
prefix: "/admin.AccessService"
route: # 选择路由的服务
- destination:
host: admin-asset # 已注册istio服务
port:
number: 6443
retries:
attempts: 3
perTryTimeout: 60s
retryOn: unavailable,reset
corsPolicy:
allowOrigin:
- "*"
allowMethods:
- POST
- GET
- OPTIONS
- PUT
- DELETE
allowHeaders:
- grpc-timeout
- content-type
- keep-alive
- user-agent
- cache-control
- content-type
- content-transfer-encoding
- custom-header-1
- x-accept-content-transfer-encoding
- x-accept-response-streaming
- x-user-agent
- x-grpc-web
- x-auth-token
- x-org
maxAge: 24h
exposeHeaders:
- custom-header-1
- grpc-status
- grpc-message
- x-request-id
- x-err-code
allowCredentials: true

EnvoyFilter扩展Istio

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# https://www.envoyproxy.io/docs/envoy/v1.12.2/api-v2/config/filter/network/http_connection_manager/v2/http_connection_manager.proto.html
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
name: custom-istio-conn
namespace: istio-system # as defined in meshConfig resource.
spec:
configPatches:
- applyTo: NETWORK_FILTER
match:
context: GATEWAY
listener:
filterChain:
filter:
name: envoy.http_connection_manager
patch:
operation: MERGE
value:
typed_config:
"@type": "type.googleapis.com/envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager"
server_name: envoy
preserve_external_request_id: true