Galaxy

姚皓的技术博客-一杯咖啡,一首音乐,一台电脑,编程

0%

春节期间看完了极客时间的《说透中台》的课程,顺便也读了《企业IT架构转型之道-阿里巴巴中台战略思想与架构实战》一书。这篇从实际项目的角度来想象一下,如果让我来负责公司的中台,应该怎么做。
首先说下评价:这个课程符合我对ThoughtWorks的刻板印象:有点滥用理论术语,干货不多;问题题了不少,解决方案不落地。可能是都是给其他公司做的项目,有保密协议的缘故。能理解,但还是不推荐。阿里的那本中台书更加实在。

1. 中台概念整理

1.1 中台的目的

中台的目的就是企业能力复用。

1.2 中台的分类

中台主流分为两大类:业务中台/数据中台。业务中台产生数据,数据中台做数据的二次加工,并将结果再服务于业务中台。
也有“技术中台”的概念,可以理解为一些技术中间件的整合和封装,但我倾向于不将其认定为中台。
中台强调一个复用。如果根本没有系统从零开始建设,一上来就搞中台很容易会过度设计。

2. 中台的抓手

中台会面对所有业务线的需求。虽然中台有企业级的属性,但不代表建设中台的时候必须梳理企业的全业务线。中台的愿景是能力复用,那么最好有具体的新业务作为抓手。

阅读全文 »

上家公司虽然有这样那样的问题,但在能让我掌控的服务器资源自由度上,也不是随便在哪家公司就能有的。能随便申请个半打一打的4核8G的虚机来搞事情什么的。。。跳槽后就只有自己的Windows工作机了。Docker Desktop搞了半天也没法启用Kubernetes,这也是为什么之前的“Kubernetes实战”系列到7月就戛然而止的原因。
只靠Docker Desktop,平时开发的时候起个数据库或redis是足够用了,但像service mesh之类的就玩不了了。趁年前有空,搭了一套Minikube,把步骤顺便记录一下。原本想合并到之前kubeadm安装的那篇里,但可能会翻起来不方便,还是单独另开一篇吧。
后续“Kubernetes实战”系列都会基于minikube环境来搭建。

1. 软硬件条件

现在内存也不值钱了,插个16G足够玩了。
操作系统上,虽然Windows 10家庭版+VirtualBox/VMWare也可以,但从硬件利用率角度,还是用Windows 10企业版/专业版/教育版+Hyper-V比较好。
在控制面板->程序->启动或关闭Windows 功能 里面打开所有Hyper-V选项然后重启。
重启后运行systeminfo,看到如下内容,说明操作系统层面已经ok了:

1
Hyper-V 要求:     已检测到虚拟机监控程序。将不显示 Hyper-V 所需的功能。

Docker Desktop是否安装不影响,但在安装Minikube的过程中最好不要启动。在安装过程中报过一个create: precreate: no External vswitch nor Default Switch found的报错,不确定是不是相关。
顺带提一句,如果装了Docker Desktop,可以在Settings->Daemon->Registry mirrors里填写:https://dockerhub.azk8s.cnhttp://hub-mirror.c.163.comhttps://docker.mirrors.ustc.edu.cn
另外感谢这篇docker/kubernetes国内源/镜像源解决方式 - xinkun的博客 | Xinkun Blog的整理,我也复制一下备忘:

global proxy in China format example
dockerhub (docker.io) dockerhub.azk8s.cn dockerhub.azk8s.cn/<repo-name>/<image-name>:<version> dockerhub.azk8s.cn/microsoft/azure-cli:2.0.61 dockerhub.azk8s.cn/library/nginx:1.15
gcr.io gcr.azk8s.cn gcr.azk8s.cn/<repo-name>/<image-name>:<version> gcr.azk8s.cn/google_containers/hyperkube-amd64:v1.13.5
quay.io quay.azk8s.cn quay.azk8s.cn/<repo-name>/<image-name>:<version> quay.azk8s.cn/deis/go-dev:v1.10.0

2. 网络条件

以防万一请先关闭Windows防火墙。
因为你懂的那个原因,需要本地搞个SS的梯子。如果哪个步骤因为网络原因卡住了,可以切成代理再试一次。

阅读全文 »

1. 微服务的公共API模块

微服务之间调用进程会出现DTO实体类的重复定义。比如服务A的接口返回User实体,服务B接收的时候,也需要定义一个同样的User实体。
在引入了Feign后,就有了一个避免项目间重复定义实体类的简单方案:我们可以在服务A开发的时候专门抽出来一个API模块。

API公共模块

这个API模块可以包含接口方法定义,URI以及和对外实体类定义(DTO),可以认为是A和B之间互通的约定。
一个最简单的API模块代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
@Data
@AllArgsConstructor
@NoArgsConstructor
public class DemoDto implements Serializable {
private String text;
}

@RequestMapping("/demo")
public interface DemoApiService {
@GetMapping("/hello")
DemoDto hello();
}

服务A的Controller负责对接口定义进行实现:

1
2
3
4
5
6
7
@RestController
public class DemoProducerController implements DemoApiService {
@Override
public DemoDto hello() {
return new DemoDto("hello");
}
}

服务A项目将API模块发布到Maven私服上。服务B项目只需要对API模块添加依赖:

阅读全文 »

微服务的开发模式下,联调和服务注册一旦涉及多个环境(开发/SIT/UAT),就会变得有些复杂。本文总结一下我们在此问题上尝试过的几个workaround,以及最终推荐的方案。

1. 背景

以下描述的案例中,将我们所拥有的服务精简为三个:

  • um:用户微服务
  • ent:企业微服务
  • bi:BI微服务
    ent会调用um;bi会调用ent和um。
    网络环境分成办公网段和开发环境网段。办公网段可以访问开发环境网段,但开发环境网段无法访问办公网段。
    三个微服务都被打包成镜像,以单副本Pod的形式部署在K8S云的开发环境节点上。
    服务注册使用Nacos,网关路由使用的是Zuul。

部署环境

2. 单环境内部请求流程

如果只考虑SIT环境,整个服务注册+请求的处理流程可以简单描述如下:

  1. um-sit服务(um的sit环境,下同)启动,将自己的service ip注册到Nacos服务端
  2. ent-sit服务启动,将自己的service ip注册到Nacos服务端
  3. 前端web对http://域名/api/ent-sit 的某个接口发起请求
  4. 通过K8S Ingress的域名映射,找到了Zuul应用
  5. Zuul向Nacos查询ent-sit的地址,得到ip:172.0.0.2。这个是ent-sit的service内部ip
  6. Zuul将请求转给ent-sit的service,Pod里的ent-sit容器中的应用接收到请求,开始处理
  7. ent-sit容器在处理过程中需要解析token,于是向Zuul请求um-sit
  8. Zuul向Nacos查询um-sit的地址,得到ip:172.0.0.1。这个是um-sit的service内部ip
  9. Zuul将请求转给um-sit。um处理完token,返回用户信息
  10. ent-sit处理结束,将结果返回给Zuul
  11. Zuul将结果转给前端web,流程结束
阅读全文 »

从Oracle或MySQL切换到PostgreSQL(以下简称pgsql)后,多少有些不一样的地方需要适应。这里就将和开发相关的一些区别挂一漏万地列举一下。

1. Schema模式

和Oracle与MySQL一样,pgsql中也有TableSpace(表空间),用于定义用来存放表示数据库对象的文件的位置。
但在Schema(模式)的定义上,三者有很大的差别。
对于MySQL,模式与数据库同义。甚至可以用CREATE SCHEMA来创建数据库,效果和CREATE DATABASE一样。
对于Oracle,schema与数据库用户密切相关:

A schema is a collection of logical structures of data, or schema objects. A schema is owned by a database user and has the same name as that user. Each user owns a single schema.

而pgsql中,层次结果如下:
PostgreSQL Hierachy

从图中可以看到,schema是database与table中间的一层。可以理解为命名空间类似的概念。当新创建一个数据库时,pgsql会默认创建一个public schema。如果没有指定的话,就是以public schema来操作各种数据对象。 例如:CREATE TABLE products ( ... ) 等同于 CREATE TABLE public.products ( ... )
schema不能互相嵌套。同一个schema下不能有重复的对象名字,但在不同schema下可以重复。
schema与database的差别在于schema不是严格分离的:一个用户可以访问他所连接的数据库中的任意模式中的对象。

对于数据库管理人员来说,还需要了解一下授权相关的差别,但在本文中就略过了。更多可以参考这篇:PostgreSQL · 特性分析 · 逻辑结构和权限体系

1.1 Schema与开发相关

阅读全文 »

搜了一下中文技术博客上似乎没有相关的文章,就简要翻译一下。

需求

假设公司内部有非常多Maven项目,需要deploy到一个内部maven私有仓库中。
如果希望maven deploy命令可以成功执行,一般需要在pom.xml中添加:

1
2
3
4
5
6
<distributionManagement>
<repository>
<id>nexus-site</id>
<url>http://central_nexus/server</url>
</repository>
</distributionManagement>

但需要deploy的项目很多的情况下,我们肯定不希望在每个项目的pom文件中都重复添加这个配置。

方案一

为所有项目增加一个公共的parent pom项目。那么只需要在这个项目的pom文件中添加:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>your.company</groupId>
<artifactId>company-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>pom</packaging>

<distributionManagement>
<repository>
<id>nexus-site</id>
<url>http://central_nexus/server</url>
</repository>
</distributionManagement>

</project>
阅读全文 »

本篇主要针对的是Jenkins服务器处在局域网中,无法连上互联网的情况下如何做自动化部署。
本文对网络的架设是:有内部的git服务器,yum私有仓库,nexus Repository OSS私有仓库和npm私有仓库。如果这些条件都没有,可能你们还是本地开发机上打包稍微快一些。

1. 安装

1.1 安装必要依赖

Jenkins的必要依赖是JDK。后续自动化部署的必要依赖是git,Maven和Node.js。
具体的安装步骤就不详述了。rpm安装(针对JDK)或私有YUM仓库安装都可以。
例如私有YUM仓库中openjdk,直接运行yum install java-1.8.0-openjdk.x86_64 即可。

Maven私有仓库配置
Maven打包的时候默认会从公网的仓库拉取依赖的第三方库。我们需要将其改为指向私有仓库。
首先可以通过如下两条命令之一获得配置文件地址:

1
2
mvn --version
mvn -e -X

假设settings.xml文件的位置在/etc/maven/路径下。
编辑该文件内容:

1
vi /etc/maven/settings.xml
阅读全文 »

起因

最近在整理代码规范,按照之前oracle的习惯,定了以下的字段长度设定规范:

  • 名称字段:varchar(200)
  • 较长的名称字段/简介字段:varchar(500)
  • 特别长的描述字段: varchar(2000)
  • 超过2000中文字的字段:text
    为什么是200长度,而不是100或300,也是拍脑袋想的,类似DND里的房规。
    但在被问起为什么不设置为经常见到的varchar(255)时,一时回答不上来。趁这个机会,把字段长度这块的知识汇总梳理一下。

为什么会经常被设置为varchar(255)

MySQL 4.1版本之前,varchar的最大长度是255 byte字节(也有一说是5.0.3版本之前)。查了下这个版本发布都是2004年的事情了。惯性真恐怖,我可不相信还有多少系统是从2004年升级过来的。

varchar(50)和varchar(255)有性能上的差别么?

对于INNODB,varchar(50)varchar(255)这两者在存放方式上完全一样:1-2 byte保存长度,实际的字符串存放在另外的位置,每个字符1 byte到4 byte不定(视编码和实际存储的字符而定)。所以将一个字段从varchar(50)长度改成varchar(100)长度不会导致表的重建。但如果把长度从varchar(50)改成varchar(256)就不一样了,表示长度会需要用到2 byte或更多。

既然255长度以下对INNODB都一样,而且我们平时基本上也不太会使用到MYISAM,那么是不是为了省心,我们就可以把255长度以下的字段的类型都设置成varchar(255)了呢?
非也。
因为内存表介意。
虽然我们不会明文创建内存表,但所有的中间结果都会被数据库引擎存放在内存表。我们可以通过EXPLAIN或者SHOW STATUS可以查看MYSQL是否使用了内存表用来帮助完成某个操作。
而内存表会按照固定长度来保存。以utf-8编码为例,对于varchar(255),每一行所占用的内存就是长度的2 byte + 3 * 255 byte。对于100条数据,光一个varchar字段就占约1GB内存。如果我们该用varchar(50),就可以剩下来约80%的内存空间。
除此之外,255长度也可能会对索引造成坑。MySQL在5.6版本及之前的最大长度是767 byte。但MySQL 5.5版本后开始支持4个byte的字符集utf8mb4(沙雕表情用到的字符太多,长度不够用)。255 * 4 > 767,所以索引就放不下varchar(255)长度的字段了。虽然MySQL在5.7版本后将限制改成了3072 byte,但如果是多字段的联合索引还是有可能会超过这个限制。

阅读全文 »

有部分老Web系统只有在IE下才能正常打开。其中有一部分是即使polyfill也没法搞定的兼容性原因,另一部分就是因为使用到了ActiveX。后者中我接触到的就有金格控件和泛微OA的。
对于新开发的Portal系统,没有余力为了迁就IE,对每个功能还额外做兼容性测试。于是剩下的方案就是在单点登录跳转到相应的页面的时候,指定使用IE打开。
其实这个功能并不罕见。比如腾讯的网站上经常有点击图标打开QQ,而淘宝网页上也有很多点击打开阿里旺旺。从原理上,这是利用到了Windows自定义协议URI Scheme。

URI Scheme

自定义协议从本质上就是修改注册表。官方资料可以参考这篇Registering an Application to a URI Scheme (Windows) | Microsoft Docs
官方给了一个范例,注册一个alert://的协议,点击后打开自定义的alert.exe。

1
2
3
4
5
6
7
8
9
10
HKEY_CLASSES_ROOT
alert
(Default) = "URL:Alert Protocol"
URL Protocol = ""
DefaultIcon
(Default) = "alert.exe,1"
shell
open
command
(Default) = "C:\Program Files\Alert\alert.exe" "%1"

方案1

能看出这是一种比较通用的方案。能打开自定义的alert.exe,自然也能打开IE。所以只要将以下内容保存为test.reg,点击运行后就能将注册表项导入:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Windows Registry Editor Version 5.00  

[HKEY_CLASSES_ROOT\openIE]
@="URL:OpenIE Protocol"
"URL Protocol"=""

[HKEY_CLASSES_ROOT\openIE\DefaultIcon]
@="iexplore.exe,1"

[HKEY_CLASSES_ROOT\openIE\shell]

[HKEY_CLASSES_ROOT\openIE\shell\open]

[HKEY_CLASSES_ROOT\openIE\shell\open\command]
@="cmd /c set m=%1 & call set m=%%m:openIE:=%% & call \"C:\\Program Files\\Internet Explorer\\iexplore.exe\" %%m%% & exit"

与微软官方范例的差别在于将协议改为了openIE://(这个不重要),以及最后的命令改为了一串很长的:

阅读全文 »

我们在上一篇已经将Dockerfile精简为了:

1
2
FROM nginx:alpine
COPY ./dist /usr/share/nginx/html

但相信你也发现了,内容中还是对web服务器有着强依赖。当我们想换成其他版本的Nginx镜像,或换成其他Web服务器,就必须修改源代码中的Dockerfile,重新制作镜像。
这种情况并非不可能。典型的场景之一:我们镜像所依赖的Nginx或tomcat版本出现了某个安全事故,而该问题可以通过将web服务器或web容器版本升级到最新版本解决。
所以我们希望能在Dockerfile中将Nginx的痕迹彻底抹除,只在Kubernetes的YAML中指定web服务器。

一个很自然的想法就是:我们使用一个默认的web服务器镜像。在使用该镜像的容器启动之前,将静态网站的文件拷贝到相应目录,就像在前一篇从ConfigMap获取配置文件一样。
这就是initContainer的作用。

1. initContainer

我们首先将镜像改为alpine,并修改COPY的路径:

1
2
FROM alpine:latest
COPY ./dist /html

重新编译为latest版本(这是为了强制每次重新拉取镜像):

阅读全文 »