早些年,传统数据中心主要是烟囱式架构,系统扩展性较差加上过去硬盘容量偏小、价格偏高,企业主要用存储保存关键数据。
“烟囱式”系统,来自维基百科的解释是:一种不能与其他系统进行有效协调工作的信息系统,又称为孤岛系统。
长久以来,这三种架构几乎统治了数据存储市场。所有行业用户的数据存储需求,都是在这三者中进行选择。
在新数据时代,传统数据中心向云计算、大数据、人工智能转变,越来越多的应用和企业在使用新的存储形态,“对象存储”这个词变得炙手可热。
对比对象存储与SAN和NAS的差别,不难看出对象存储的核心优势所在:对象存储直接提供API给应用使用,采用扁平化的结构管理所有桶(Bucket)和对象。每个桶和对象都有一个全局唯一的ID,根据ID可快速实现对象的查找和数据的访问。
对象存储支持基于策略的自动化管理机制,使得每个应用可根据业务需要动态地控制每个桶的数据冗余策略、数据访问权限控制及数据生命周期管理。正是这些优势使得对象存储具备极致的扩展和极易的数据管理,在新数据时代如鱼得水。
MinIO 是一款高性能、分布式的对象存储系统。基于Golang编写。它是一款软件产品, 可以100%的运行在标准硬件。即X86等低成本机器也能够很好的运行MinIO。
MinIO与传统的存储和其他的对象存储不同的是:它一开始就针对性能要求更高的私有云标准进行软件架构设计。因为MinIO一开始就只为对象存储而设计。所以他采用了更易用的方式进行设计,它能实现对象存储所需要的全部功能,在性能上也更加强劲,它不会为了更多的业务功能而妥协,失去MinIO的易用性、高效性。 这样的结果所带来的好处是:它能够更简单的实现局有弹性伸缩能力的原生对象存储服务。
MinIO在传统对象存储用例(例如辅助存储,灾难恢复和归档)方面表现出色。同时,它在机器学习、大数据、私有云、混合云等方面的存储技术上也独树一帜。当然,也不排除数据分析、高性能应用负载、原生云的支持。
在中国:阿里巴巴、腾讯、百度、中国联通、华为、中国移动等等9000多家企业也都在使用MinIO产品。
成长最快的对象存储系统
GiHub Stars 36.1k,Docker镜像拉取数量 1.03B,群组成员 20.4k,贡献者 879人。
主动、多节点复制是对象存储关键指标。
MinIO提供桶级粒度,并支持同步和近同步复制,这取决于体系结构的选择和数据的变化率。
数据是企业最关键的资产,因此必须在整个组织中方便、安全地提供数据,以便使其对每个人的价值最大化。因此,企业必须根据用户的需求采用一系列数据接口方法。
MinIO提供了一套标准接口,可以覆盖数据驱动企业中的每个角色,比如图形用户界面(GUI)、命令行界面(CLI)和应用程序编程接口(API)。
MinIO提供了高强度的加密算法并做了大量性能优化,几乎消除了存储加密操作相关性能的开销。
MinIO支持多种复杂的服务器端加密方案,以保护数据-无论其位于何处。
MinIO的方法可确保机密性,完整性和真实性,而性能开销却可以忽略不计。 使用AES-256-GCM,ChaCha20-Poly1305和AES-CBC支持服务器端和客户端加密。加密的对象使用AEAD服务器端加密进行了防篡改。此外,MinIO与所有常用的密钥管理解决方案(例如HashiCorp Vault)兼容并经过测试
MinIO使用密钥管理系统(KMS)支持SSE-S3。如果客户端请求SSE-S3,或启用了自动加密,则MinIO服务器会使用唯一的对象密钥对每个对象进行加密,该对象密钥受KMS管理的主密钥保护。由于开销极低,因此可以为每个应用程序和实例打开自动加密。
桶和对象的不可修改
保护数据不被删除(意外或故意)是涉及每个行业的关键合规组件。
MinIO支持一系列完整的功能,包括对象锁定、保留、合法保持、治理和遵从性。
MinIO的桶和对象不变性是由Cohasset Partners根据SEC规则17a-4(f)、FINRA规则4511和CFTC规则1.31进行Veeam认证和验证的。
支持多种部署环境:Kubernetes、Docker、Linux、macOS、Windows。
https://min.io/download#/linux
复制到 CentOS7 服务器 /opt/minio 路径下
mkdir /data/minioadduser miniochown minio:minio /data/miniochown minio:minio /data/minio/*chmod -R 777 /data/minio
chmod +x /opt/minio/miniomv minio /usr/local/bin
新建/usr/lib/systemd/system/minio.service
[Unit]Description=Minio ServerDocumentation=https://docs.minio.ioAfter=network.target[Service]User=minioGroup=minioPermissionsStartOnly=trueEnvironmentFile=-/etc/minio.confExecStartPre=/bin/bash -c "[ -n \"${MINIO_VOLUMES}\" ] || echo \"Variable MINIO_VOLUMES not set in /etc/minio.conf\""ExecStart=/usr/local/bin/minio server $MINIO_OPTS $MINIO_VOLUMESStandardOutput=journalStandardError=inherit# Specifies the maximum file descriptor number that can be opened by this processLimitNOFILE=65536# Disable timeout logic and wait until process is stoppedTimeoutStopSec=0# SIGTERM signal is used to stop MinioKillSignal=SIGTERMSendSIGKILL=noSuccessExitStatus=0[Install]WantedBy=multi-user.target
新建 /etc/minio.conf
MINIO_ACCESS_KEY=rootMINIO_SECRET_KEY=Viewhigh*132MINIO_VOLUMES="/data/minio/"MINIO_OPTS="--address :9000"
systemctl daemon-reloadsystemctl start minio
防火墙开放9000端口
firewall-cmd --zone=public --add-port=9000/tcp --permanentfirewall-cmd --reload
systemctl enable minio
浏览器访问:http://127.0.0.1:9000
账号/密码:root/Viewhigh*132。
打开MinIO官网,网址:https://www.minio.org.cn/ 点击版本归档,点击server/minio/release/windows-amd64/minio.exe开始下载。
在E:\Program Files\下,新建目录minio,在minio下新建data目录。将下载的minio.exe,拷贝到目录:E:\Program Files\minio\,如下图。
注意:安装目录根据实际需要新建。
打开cmd命令行,进入到minio.exe所在的目录。输入命令: minio.exe server E:\Program Files\minio\data
server后面的地址是文件上传之后的存储目录。
出现以下界面,说明minio启动成功。
打开浏览器,输入http://127.0.0.1:9000/login,打开以下页面,说明minio启动成功。
输入用户名和密码:minioadmin/minioadmin,点击登录。打开如下页面,说明minio成功安装,并正常运行。
目前minio用的还是默认的帐号密码,我们需要修改为自己需要的帐号和密码。关闭minio,按照以下步骤操作:
至此,密码修改完成。
如果要修改端口的话,可以用以下命令:
minio.exe --address :9000 server E:\Program Files\minio\data
在E:\Program Files\minio目录,新建run.bat文件,内容如下:
set MINIO_ACCESS_KEY=gov_cloudset MINIO_SECRET_KEY=viewhighminio.exe server E:\Program Files\minio\data
以后可以点击bat文件直接运行。
以maven配置为例,直接引用即可。
<dependency><groupId>io.minio</groupId><artifactId>minio</artifactId><version>8.2.0</version></dependency>
配置我们之前安装好的MinIO服务地址和登录用户。
################################################################### 文件存储##################################################################minio: url: http://10.10.231.68:9000 key: gov_cloud secret: viewhigh bucket: infra-storage
经过以上非常简单的两步操作,我们就将MinIO集成到我们的Java工程中了。
这里给大家提供一个简单的写入文件和读取文件的代码示例:
package com.vh.data.operation.report.report.storage.impl;import com.vh.data.operation.report.report.storage.FileStorage;import com.vh.data.operation.report.report.storage.exception.FileStorageException;import io.minio.*;import org.apache.commons.io.IOUtils;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Value;import org.springframework.stereotype.Component;import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.InputStream;import java.io.OutputStream;import java.util.HashMap;import java.util.Map;/** * Minio文件存储 */@Componentpublic class MinioFileStorage implements FileStorage { private Logger logger = LoggerFactory.getLogger(getClass()); private static final int DEFAULT_BUFFER_SIZE = 1024 * 1024; @Value("${minio.url}") private String minioUrl; @Value("${minio.key}") private String minioKey; @Value("${minio.secret}") private String minioSecret; @Value("${minio.bucket}") private String minioBucket; private MinioClient client; @Override public int writeFile(InputStream inputStream, String key, String contentType) { try (ByteArrayOutputStream bao = new ByteArrayOutputStream()) { MinioClient client = this.getClient(); // 检查存储桶是否已经存在 boolean isExist = client.bucketExists(BucketExistsArgs.builder().bucket(minioBucket).build()); if (!isExist) { // 创建存储桶。 client.makeBucket(MakeBucketArgs.builder().bucket(minioBucket).build()); } // 读取输入流到内存中 byte[] buff = new byte[DEFAULT_BUFFER_SIZE]; int bytesRead; while ((bytesRead = inputStream.read(buff)) != -1) { bao.write(buff, 0, bytesRead); } byte[] data = bao.toByteArray(); ByteArrayInputStream bin = new ByteArrayInputStream(data); // 使用putObject上传一个文件到存储桶中。 this.getClient().putObject(PutObjectArgs.builder() .bucket(minioBucket) .object(key) .stream(bin, bin.available(), 0L) .contentType(contentType) .build()); return data.length; } catch (Exception e) { throw new FileStorageException(e); } } @Override public void readFile(OutputStream outputStream, String key) { long startTime = System.currentTimeMillis(); logger.info("开始读取文件:" + key); // 客户端 MinioClient client = this.getClient(); // 文件流 try (InputStream is = client.getObject(GetObjectArgs.builder().bucket(minioBucket).object(key).build())) { IOUtils.copy(is, outputStream); } catch (Exception e) { logger.error("IO异常:" + e.getMessage(), e); throw new FileStorageException(e); } logger.info("结束读取文件:" + key + ",耗时:" + (System.currentTimeMillis() - startTime) / 1000); } private MinioClient getClient() { if (client != null) { return client; } return createClient(); } private synchronized MinioClient createClient() { if (client != null) { return client; } // 创建客户端 client = new MinioClient .Builder() .endpoint(minioUrl) .credentials(minioKey, minioSecret) .build(); return client; }}
除了在Java中使用以外,我们还可以非常方便的在MinIO后台页面进行文件管理。
在后台管理页面,可以创建删除桶。
可以创建、删除目录。
可以上传、下载、删除文件。
可以创建文件的直读分享链接。
MinIO是一个部署简单,性能强大,易于使用的对象存储项目。
希望这篇文章能够帮助到大家。
辞旧迎新,2022年过去,2023年到来。
2022年是坎坷的一年,托疫情的福,整体经济形势不好。
2022年的项目签约、回款都特别艰难,公司亏损严重,导致了年底大裁员,把整个研发团队都快裁没了,每个岗位基本就只留了一个人。
12月底请病假扣了不少工资,年终奖也泡汤了,一分没有,穷年石锤。
虽然人都裁了,但是项目其实完成的还不错,之前拖着没验收的几个项目,比如北京顺义区的、郑州市的基本都收尾回款了。
2023年轻装上阵,历史包袱小了很多。在售前团队的努力下,据说新商机池、准备签约的项目额度都很可观,开了个好头。
希望2023年能结出好的成果。
今年给公司做了不少技术培训。
有几个我已经在博客发布文章了,还有几个没发布。
借这个机会直接把PPT放出来吧,没有版权,可以随便使用或者转载,记得把PPT里我公司的LOGO删掉就行。
20220408_接口文档Knife4j培训
20220705_API接口单元测试培训
20220819_数据库版本变更管理培训
20221103_MinIO技术培训
20221103_Redis技术培训
今年还继续研究了数仓的产品设计,参考业界流行的标准,结合项目实践落地情况,自己设计了一套数据中台的产品原型,包括指标库、数据标准、元数据中心、数据建模等等功能模块。目前元数据中心已经研发完成准备上线,其他功能模块也在推进中,希望2023年能做完并在项目中落地使用。
自己的开源项目alice-code-creator代码生成器今年也在持续更新,主要添加了两个功能点。
新的开源项目在筹备中了,我准备做一个怀旧主机游戏站。
这几年一直在玩开源掌机,接触了很多linux改的模拟器系统和很多人发布的资源整合包,但是都不太合心意,整理的比较好的一般是外国站点,但是又没有中文。
国内整理的呢,一般都不全,不是缺预览视频就是缺描述字段啥的,找不到完美的整合包。
目前国内做的比较好的一个是天马前端(基于Pegasus Frontend开源项目),一个是simui(国内原创)。
鉴于外国站点没有中文,国内的资料又不全,所以我准备自己整理收集资料并制作一个怀旧游戏数据库开放给国内玩家使用。
计划支持功能如下:
目前已经申请了GitHub仓库,地址如下:
https://github.com/lxp135/alice-retro-game-db
感兴趣的朋友可以star一波。
我还画了一个LOGO,希望这个项目不要烂尾,哈哈。
由于工作比较忙,基本上家里的事我事甩手掌柜的,都是我爱人在忙前忙后,在这里感谢一波老婆大人。
闺女上小学了,可惜受疫情影响基本都是线上教学,希望2023年能恢复正常吧。
疫情在12月6日是个转择点,万万没想到持续了3年的封控政策能拐出U型弯。
阳了是真的难受,我基本上持续高烧了5天,体温一直在38°以上。
特别是第一天和第二天,忽冷忽热。晚上基本睡不着觉。
从第二周开始不发高烧,但是身体非常虚弱,遇到点冷空气或者稍微劳累就能感觉到明显的不舒服,然后开始持续咳嗦。
大概在发病一个月以后,所有症状消失,差不多能恢复到生病前的身体情况,但是也不能特别劳累。
没有测过序,根据网上的消息,自己判断可能是BF.7或者BF.5.2。
希望XBB能晚点传过来吧,给我们这第一批阳了的选手多一些恢复的时间。
希望2023年没有疫情,大家都能身体健康。
希望2023年经济恢复,大家都有钱赚。
开发环境、测试环境、生成环境同时存在多个数据库副本。
代码我们使用Git或者SVN能很好的管理版本,但是数据库呢?脚本呢?
不幸的是,我们做得并不好。许多项目仍然依赖于手动执行的SQL脚本。有时甚至没有(编写临时的SQL语句来解决问题)。
很快就会出现许多问题:
这台机器上的数据库处于什么状态?
某个SQL脚本是否已应用?
生产中的快速修复SQL是否在之后的测试环境中执行过?
如何创建一个新的数据库实例?
这些问题的答案往往是:我们不知道。
讲一个笑话:暴雪要重置60级经典版本魔兽世界,代码能找到、模型文件能找到、唯独当年版本的数据库找不到了。
目前做数据库变更管理的Java开源库,做得好的就是Flyway和Liquibase。
下面我们比较一下这两个库的特性:
在我的实际使用中发现,Flyway 存在几个严重问题:
在Liquibase中,以上三个问题均不存在。
Liquibase 是用于数据库重构、管理、记录变化与回滚的开源工具。 在写代码的时候,我们使用 Git 或 subversion 对代码进行版本控制,在数据库中,我们可以使用 liquibase 对数据库表进行版本控制。
持续维护
2006年开源至今发布了73个版本、1.6k 的 fork 人数、3.4k 的 star 人数、11323 次 Commit
丰富的特性
SQL脚本、没有限制、零依赖、约定优于配置、集群高可靠、启动时自动迁移、失败时阻止服务启动
丰富的数据库支持
Apache Derby、CockroachDB、Firebird、H2、HSQL、Informix、InterBase、MariaDB、MSSQL、MySQL、Oracle、PostgreSQL、SQLite、Sybase Anywhere、Sybase Enterprise。
Java无缝集成
Java 8/9/10/11/12/13 兼容SpringBoot 自动配置Maven、Gradle、Spring、Ant
引入依赖包:
<!-- https://mvnrepository.com/artifact/org.liquibase/liquibase-core --><dependency> <groupId>org.liquibase</groupId> <artifactId>liquibase-core</artifactId> <version>4.15.0</version></dependency>
如果你使用的数据库在默认支持范围,仅引用liquibase-core即可。如果是扩展支持的,需要根据数据库的不同引用扩展数据库jar包。
比如MongoDB:
<!-- https://mvnrepository.com/artifact/org.liquibase.ext/liquibase-mongodb --><dependency> <groupId>org.liquibase.ext</groupId> <artifactId>liquibase-mongodb</artifactId> <version>4.15.0</version></dependency>
1.使用 db.changelog-master.yaml 配置文件
如果我们没有在 Spring Boot 中指定更改日志文件的位置,则YAML 的默认路径是 db/changelog/db.changelog-master.yaml.
2.使用 springboot 注解 @Configuration
1.在resources/db/changelog下创建DDL和DML文件夹,将我们的数据库脚本放入其中。
2.编辑db.changelog-master.yaml文件,将SQL文件地址写入,Liquibase会在工程启动时,按照顺序执行。
SQL文件最上方,必须有两行注释。Liquibase会解析这两行注释。
--liquibase formatted sql--changeset <author name>:<a unique identifier for the SQL changeset>
第一行是固定写法,代表这个SQL文件是Liquibase使用。
第二行是指定作者和SQL文件的唯一编码,用冒号区隔。
推荐作者使用自己的邮箱,唯一编码直接定义为SQL文件的名称。
注:唯一编码不允许重复。
DDL和DML分开不同文件。
DDL例子:
DML例子:
将文件都配置好以后,启动工程,Liquibase会自行寻找全部的待执行SQL,并和当前配置的数据库链接中的库进行适配,将没有执行的SQL按照顺序执行。
如果SQL存在问题执行失败,则工程不会启动成功。
Liquibase会在数据库中创建两张表databasechangelog和databasechangeloglock。
我们可以看到databasechangelog中保存了我们脚本的执行记录,只有成功执行的脚本会记录到这个表中。
可以看到ID字段是我们在SQL文件写的注释第二行唯一编码,AUTHOR字段是注释第二行作者。
// 第一步,以宽度计算缩放比例,屏幕宽度/页面最外层元素宽度 let zoom = window.screen.width / document.getElementById("root").offsetWidth; // 第二步,设置页面body整体缩放比例,由于是以宽度计算的,所以现在x轴肯定是全屏 document.body.style.zoom = zoom; // 第三步,计算Y周的缩放比例,由于zoom参数不能拆分x、y轴,所以我们y轴也被缩放了。 // 屏幕高度/缩放比例/页面最外层元素高度 let scaleY = window.screen.height / zoom / document.getElementById("root").offsetHeight; // 第四步,设置页面body的y轴缩放比例 document.body.style.transform = "scaleY("+scaleY+")"; // 设置y周变换后body的起始位置 document.body.style.transformOrigin = "0 0"
]]>
一个完整的项目,无论是个人的还是公司的,自动化的单元测试是必不可少,否则以后任何的功能改动均可能造成灾难性影响。
假设你正在维护公司的一个项目,这个项目已经开发了几十个 API 接口,但是没有任何的单元测试。
现在你的 leader 让你去修改几个接口并实现一些新的功能,你接到需求后高效地完成了开发任务,然后手动测试了一遍改动的接口和新实现的功能,确保没有任何问题后,满心欢喜地提交了代码。代码上线后出了 BUG,分析原因发现原来是新的改动导致某个旧 API 接口出了问题,因为上线前只对改动的接口做了测试,所以未能发现这个问题。
你的 leader 批评了你,你因为事故记了过,年终绩效评价只能拿个C,奖金受到了影响。
但是如果我们有全面的单元测试,上述情况就有很大概率避免。只需要在代码发布前运行一遍单元测试,受影响的功能立即就会报错,这样就能在代码部署前发现问题,从而避免线上事故。
当然以上故事纯属虚构,说这么多只是希望大家在开发时养成良好的习惯,一是写优雅的代码,二是一定要测试自己写的代码。
现在假设我们有一支团队。
团队的现状:
没有API接口测试,后端研发只在接口研发完成时自己用Postman调用一遍本地测试,测试覆盖率极低。
在持续开发过程中,后期开发的接口可能与前期开发的接口复用同一段业务逻辑,改动逻辑代码后,前期开发的接口极大可能报错或失效。
上线一段时间后,由于业务、人员的变更,导致代码没人敢动,生怕影响了业务逻辑,不能持续重构与演进。
团队的资源:
团队采用前后端分离的开发方式,产品1人、UI设计师1人、前端2人、后端5人、测试1人。
需要一套高效的合作模式。
为了解决以上的问题,我们对API单元测试工具总结了六个需求点:
能用
能够通过页面配置,快速生成或批量导入我们开发好的接口定义和入参定义,最好能持续集成。
可扩展
接口入参可配置,可自定义Header、Cookie的信息,方便调试接口。
测试报告
支持自动化测试,方便的测试集配置方式,能出具测试报告,最好能持续集成。
能Mock数据
前端研发人员能根据接口定义,快速配置Mock Server进行并行开发。
持续集成
开发人员在合并代码时,自动触发接口测试,验证主流程不受影响。
个性化需求
支持前置与后置代码编写,支持不同团队个性化需求。
我们的需求,找到一款开源、广泛使用、能无缝对接Swagger规范(不用手工定义一个个接口),支持自动化测试流程、对前端支持Mock数据的工具。
经过对比,发现开源项目中,仅YAPI符合我们的要求,功能覆盖的最多,并且开源支持私有化部署。
本章节介绍YAPI的核心功能。
接口数据导入:
手工导入
在数据管理可快速导入其他格式的接口数据,方便快速添加接口。YApi 目前支持 postman, swagger, har 数据导入。
命令行导入
使用yapi-cli工具配置yapi-import.json文件,执行yapi import。
定时任务导入
定时任务仅支持Swagger规范数据自动同步。
三种合并模式:
接口运行功能,是用来测试真实接口的,类似『Postman』的功能。
点击运行 tab ,可进入到接口测试页面,首先安装『chrome crossRequest』扩展,才可正常使用此功能。
点击保存按钮可把当前接口保存到测试集,方便下次调试。
接口返回数据验证
接口的返回数据格式为json schema 在接口运行时会对接口返回数据和定义数据格式进行校验。
创建测试集合
使用 YApi 自动化测试,第一步需要做得是创建测试集合和导入接口,点击添加集合创建,创建完成后导入接口(同一个接口可以多次导入)。
编辑测试用例
编辑请求参数(Mock参数、变量参数)
编写断言脚本
调整测试集内接口执行顺序
运行自动化测试
在测试列表可以看到每个测试用例的 key,还有 开始测试、报告等功能
点击开始测试会按照 case 定义的参数从上往下一个一个进行测试,如果顺序有问题,可以拖动调整
测试完成之后,点击报告查看该次请求的结果
Mock 参数每次请求都会生成随机字符串。
YApi 提供了强大的变量参数功能,你可以在测试的时候使用前面接口的 参数 或 返回值 作为 后面接口的参数,即使接口之间存在依赖,也可以轻松 一键测试~
Tips: 参数只能是测试过程中排在前面的接口中的变量参数
格式:$.{key}.{params|body}.{path}
例如:现有两个接口,分别是“导航标题”和“文章列表”
文章列表接口需要传参数: 当前标题(id),而这个 id 需要通过 导航标题 的返回值获取,这时应在 文章列表 的参数输入框中根据前者的 key 找到对应 id。
其中 $.
是使用 动态变量 的标志,$.269.params
即表示 key 值为 269 用例的请求参数,$.269.body
即表示 key 值为 269 用例的返回值。
目前 yapi 中的query
、body
、header
和pathParam
的输入参数已经支持点击选择功能。
无需自己填写表达式,只需在弹窗中选择需要展示的表达式即可。
输入选项包括常量,mock数据,在测试集合中也支持变量选择。
具体用法:单击编辑按钮打开表达式生成器,点击需要的数据创建表达式,这里也可以实时查看表达式结果。
assert(value)
:判断 value 是否为 truth, 例如 assert(1) 通过, assert(0) 不通过,只要 value 不是 null, 0, false 等值验证通过assert.equal(actual, expected)
:判断 actual 是否等于 expected,例如 assert(1, 1)通过assert.notEqual(actual, expected)
:判断 actual 是否不等于 expectedassert.deepEqual(actual, expected)
:假设: actual = {a:1} 是一个对象,即便 expected = {a:1},如果使用 assert.equal 可能也是不相等的,因为在 js 引用的只是对象的一个指针,需要使用 assert.deepEqual 比较两个对象是否相等assert.notDeepEaual(actual, expected)
:深度比较两个对象是否不相等status
http 状态码
params
http request params, 合并了 query 和 body
body
返回 response body
header
返回 response header
records
记录的 http 请求信息,假设需要获取 key 为 555 的接口参数或者响应数据,可通过 records[555].params 或 records[555].body 获取
log
log(message) 函数,调试时使用,log 信息仅仅在断言失败后打印,失败断言前的信息
服务端自动化
开始测试功能是在浏览器跑自动化测试,他依赖于浏览器的使用环境。服务端自动化测试功能是在YApi服务端跑自动化测试,不需要依赖浏览器环境,只需要访问 YApi 提供的 url 链接就能跑自动化测试,非常的简单易用,而且可以集成到 jenkins。
运行
点击服务端测试,用户访问该 url 就可以获取当前测试用例的所有测试结果。
配置通用规则
配置通用规则能够使自动化测试,可以基于通用的规则去控制,无需手动一个一个维护case。
测试报告样例如下图所示:
定义Mock接口
无需单独定义任何Mock接口,YAPI会默认提供全部已导入接口的Mock数据查询接口。
定义Mock返回数据
mockjs
基于 json + 注释 定义 mock 数据
json-schema(默认方式)
开启 json-schema 功能后,根据 json-schema 定义的数据结构,生成随机数据。
使用Mock接口
查询mock接口返回示例如下图所示:
每一个后台服务,创建一个对应的YAPI项目,且工程名称保持一致。
项目配置中,主要配置两项:
项目名称要与工程名称一致。
接口基本路径,使用 /api/项目工程名称
初始化导入全部接口
配置定时任务,按10-30分钟执行一次
合并模式为智能合并
定时任务执行后,会有执行记录
环境域名必须配置
微服务项目一般配置为网关地址,普通项目直接配置工程地址:
网关权限验证
支持两种方式,header和cookie。
我本地测试,使用cookie中set关键字为ARCTIC_GATEWAY_SESSION,value取值格式为用户名@test,就可以通过网关权限认证。
大家可以根据自己的项目情况自行设置。
建议配置一定数量的测试集来保障测试效果。
每个测试项目,要创建一个涵盖大部分接口的通用测试集,其中配置如下两条必验规则:
检查HttpCode是否为200。
assert.equal(body.success, true)
每个功能模块本身的增删改查,为一个小的测试集。
除了httpcode为200、success为true以外,还要配置接口之间的断言。比如插入是否成功、修改是否成功、删除是否成功等。
按照业务流程的测试集(可选)
按照产品设计的业务流程配置测试集,比如正向主业务流程、分支业务流程、逆向业务流程等。
接口定义文档输出里程碑
前后端一起定义接口与入参
后端编写接口和相关注解形成Swagger标准文档
前端根据UE原型review接口定义与参数定义
是否存在错误或缺失的接口。
是否存在错误或缺失的参数。
联调前,后端编写API单元测试用例并自动化
全部接口httpcode必须为200,返回值success必须为true。
每个小功能模块,必须定义增删改查测试集并通过。
在团队实际使用过程中,执行力是关键因素,研发经理要保证前后端研发工程师确实认真配置了测试用例,在代码有变化时,测试用例需要一起变化。
这个习惯的养成很重要,初期很不容易,研发经理要进行督促。
在坚持一段时间,团队每个人都在单元测试中获益后,就可以成良好的循环,软件质量一定会上一个台阶。
API接口文档是伴随着前后端分离的开发模式而出现的,并且经历了几个重要演进。
在上古时期,程序员不分为前端和后端,统称Javaweb开发人员。没有存粹的前端开发人员。
技术框架以Struts和SpringMVC为。主页面渲染在后端处理。前端主要使用jQuery。
程序员仅在对外部提供跨系统调用的接口时会编写文档,一般以word或excel文档形式提供。
在这个时期,专业的前端人员开始出现,但是工作仅仅是讲UI设计图转化为HTML静态页面,并交付给后端开发人员。
页面渲染放到了前端技术栈,这个时期形成了各种模板引擎。
前后端的开发人员在开发前约定接口定义,形成文档。文档由后端研发手工维护。以wiki、cf等形式发布。
前端技术栈形成了React、Vue等重量级框架。
随着时代的发展,根据Java代码中的注解来生成文档,又分为离线文档和在线文档两种。
我们后续主要介绍根据注解生成在线文档这种形式。
knife4j是为Java MVC框架集成Swagger生成Api文档的增强解决方案。无缝兼容Swagger规范、和Knife4j增强注解(解决Swagger不支持的部分功能),界面比Swagger易于使用。可以与标准Swagger共存。
接口会根据分组进行显示,点击某一个接口名称后,右侧主界面会新增一个Tab页面显示这个接口的详细信息,其中包括:
Swagger Models界面可以看到工程中全部的实体类,方便统一查看与检索。每个实体类中会显示名称和类型。
每个接口都可以直接在界面中进行调试,通过自定义请求参数进行接口测试,并取得返回值帮助前端开发。而且支持权限验证,通过全局参数的设置,可以模拟Token或者cookie携带写入header中。
调试界面如图所示:
为了便于与其他系统进行集成,比如YAPI或Postman等调试工具导入接口,Knife4j提供了标准OpenAPI格式的接口定义。
访问你的工程路径+/v2/api-docs或/v3/api-docs(根据你引用的Knife4j版本决定)即可打开OpenAPI接口定义。
如下图所示:
Knife4j支持springboot,可直接在maven中配置。
<!-- Knife4j --><dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-spring-boot-starter</artifactId><version>3.0.3</version></dependency>
通过Spring注解的方式初始化Knife4j配置。
@Configuration@EnableSwagger2@EnableKnife4jpublic class SwaggerConfig { @Bean public Docket createRestApi() { return new Docket(DocumentationType.SWAGGER_2) // DocumentationType.SWAGGER_2 固定的,代表swagger2 .apiInfo(apiInfo()) // 用于生成API信息 .select() // select()函数返回一个ApiSelectorBuilder实例,用来控制接口被swagger做成文档 .apis(RequestHandlerSelectors.basePackage("com.viewhigh.pacific.index.controller")) // 用于指定扫描哪个包下的接口 .paths(PathSelectors.any())// 选择所有的API,如果你想只为部分API生成文档,可以配置这里 .build(); } /** * 用于定义API主界面的信息,比如可以声明所有的API的总标题、描述、版本 * @return ApiInfo 页面显示信息 */ private ApiInfo apiInfo() { return new ApiInfoBuilder() .title("XXX平台 - 系统服务API") // 可以用来自定义API的主标题 .description("系统服务") // 可以用来描述整体的API .termsOfServiceUrl("") // 用于定义服务的域名 .version("1.0") // 可以用来定义版本。 .build(); // }}
Knife4j不仅可以按照服务单独显示文档,也支持将多个服务的文档聚合显示,如下图所示:
本章节介绍Knife4j在编写时的主要内容。
我们在Controller层的类前要添加@Api注解,每个Controller前都必须添加。
Knife4j会根据改注解中的tags进行接口分组,相同名称的接口会分到同一组中进行展示。
如下图所示:
Knife4j会识别出Controller的每个方法为一个接口,所以为保证API接口文档的可读性,每个方法前均需要添加@ApiOperation注解。
还有一个注意的地方就是必须使用@PostMapping或者@GetMapping等注解指定数据类型,不能只使用@RequestMapping注解不指定类型,如果不指定类型,同一个接口会在Knife4j中被识别为多个接口,影响可读性。
如下图所示:
实体类的类前要添加@ApiModel注解。
每个字段前要添@ApiModelProperty注解,其中常用的参数有三个,value是字段名称、required是否必填项、hidden是否在API接口文档中显示。
为了更好的区分入参和出参,我们在入参时统一使用Dto,出参使用Vo。
除非字段一模一样,否则不使用默认生成的Entity作为入出参。这样可以保证生成的API文档没有冗余的字段,可读性高。
如下图所示:
总体来说,Knife4j是易于使用、界面美观、集成简单的API文档解决方案。
推荐开发方式是前后端分离的团队使用。
官网:https://doc.xiaominfo.com/knife4j/
文档:https://doc.xiaominfo.com/knife4j/documentation/
源码地址:https://gitee.com/xiaoym/knife4j
这样的
这样的
还有这样的。
那么他们是怎么做到的呢?
经过我的研究发现,那就是账号同名仓库,创建一个和你的账号相同名称的仓库,然后编辑readme.md文件,语法支持markdown,这个readme.md就会显示在你的个人主页中。
下图是我已经创建好的账号同名仓库:
仓库中readme.md代码:
<img align="right" src="https://github-readme-stats.vercel.app/api?username=lxp135&show_icons=true&theme=gruvbox_light&locale=cn" />### Hi there ?I'm liuxp, a fullstack developer.- ? Developer of [Alice CodeCreator](https://github.com/lxp135/alice-code-creator)- ? Working on <https://www.viewhigh.com> currently- ?? Have a daughter,a lovly little girl.- ? Mission: `Don't Repeat Yourself`,`Keep it Simple Stupid`,`Done is better than perfect`- ? 沈阳More details on [liuxp.me](https://liuxp.me/).
显示效果:
赶紧去把你的GitHub个人主页也装点一番吧~
]]>邀请函镇楼
很高兴回到我的母校,也很荣幸受崔玉副主任和战红梅老师的邀请跟咱们信息工程系的学弟学妹们分享我的学习经历和工作感悟,作为装备制造工程学校2006界毕业生,希望我今天的分享能给大家提供一些参考。
首先做个自我介绍,我是刘小平,今年35岁。是我校2006界毕业生。
目前于“望海康信”的“数据与咨询事业部”任职研发经理一职。
PPT上是我找工作用的简历,主要内容是我擅长的业务和技术技能。
求学经历
接下来跟大家介绍我的求学经历,小学和初中是按片划分的就读于非重点普通中小学。初中毕业前后我表哥向我推荐了装备学校的前身沈阳市机电学校,他就是我校机电专业毕业的,所以家长和我也没有太多考虑,直接选择了到机电学校上学。
入学的时候我的父母已经对我放弃不管了,由于自己喜欢计算机,所以就报了计算机专业。在就读的第一学期我们班就换了4个班主任,我所在的班级成为了谁都带不了的问题班级,当战红梅老师来到班级时我在班上直接问:“老师你能带我们多久啊”?战红梅老师这一带就是3年,一直到毕业,都是我的班主任。
当时的我认为学习生活是漫长而枯燥的,每天想方设法的琢磨怎么打发时间,但慢慢走上正轨后我发现我喜欢上学了,加上我确实喜欢计算机方面的东西,所以学的还行,都跟上了。再后来由于高考和就业的课程设置不同班级的同学经历了分流和重组,这个时候的我已经萌生了参加高考试一试的想法所以最终选择了升学班。
但是,我的学习成绩并不突出,学的好的科目和比较差的科目基本是一半一半,当时考试科目是抽签,从7-8门课程中抽3门课考试,高考之前谁都不知道考哪科,所以没办法都得学。在这里我想说的是,准备充分总是没错的,即便不能把所有科目学到最好,也要尽自己最大努力不留遗憾,因为机会就是给准备更充分的人的。
转眼间进入高三的最后冲刺阶段,战老师给我母亲打了一通电话,她说你家孩子学的还不错,有考大学的希望,我母亲听到这个消息的时候都不敢相信老师说的那个人是自己的儿子,后来她回忆说之前本来都已经放弃我了,但听到战老师对我的肯定激动异常.就这样在老师的鼓励和家长的督促下我倍加努力,在装备学校的学习生活告诉我一个受用至今的道理:哪怕身边的人都放弃了你,只要努力就不晚,为了做更好的自己吃苦受累也是值得的。
最后我这个初中时的后进生成功考入沈阳师范大学本科,而这是改变我命运的第一步。
我的工作经历
说到我的工作经历不得不提一下我去东软集团面试的故事。当时大四同学们都在找工作,我碰巧听到东软校园招聘的消息,但是东软并没有来我们学校招聘,几经周折我打听到了东软和东大每天通勤的班车线路,面试那天早早的我就去班车点等着啦,上车司机问我去哪,我说毕业生去面试的,他以为我是东大的学生没多说,我投了2元钱,就混上了车。到了东软后,我就和其他来应聘的学生一起去填表,填完交表的时候,有个招聘老师一看,你这个学校好像就你一个人呀,我说是,他们可能走散了。
其实东软根本就没去沈师招聘,但是当天有很多不同学校的毕业生,招聘的老师估计也不清楚,我就这样把表格递交上去了。再之后就是笔试,笔试分为逻辑行测和编程题两张卷纸,编程又分为c和Java方向,可以任选其一,由于编程我并不擅长,在面试时我动了个小心思,面试老师问我说:“你这个行测答的还可以,编程答的不好,正确率一半都不到?”“我在学校学的c,以后工作想做Java方向,所以就答Java的题目了。”其实如果答c可能结果更差……!就这样我怀着忐忑的心情好像把问题圆了过去。后来面试老师陆续问了能不能出差、能不能加班等问题,耍了小聪明的我没有退路了,必须回答“能”,而且态度很诚恳,给面试官的印象还不错,很幸运我通过了面试,签了三方就业协议。
其实我今天想告诉大家的是面试时扎实的专业知识,真实诚恳的态度,机智而准确的语言表达能力都决定你成功就业的重要因素,如果让我再来一次我会在上学的时候更有针对性的提高自己的短板。
我在东软集团工作时的团队
接下来我再讲一个在东软集团实习的故事。职入东软后,单位并没有直接安排我进入工作岗位,而是给安排了始业教育培训。始业教育培训包括职场礼仪、书面沟通和最重要的编程培训。培训班30人左右,都是同一届的毕业生,里面很多985和211学校的学生,他们都非常聪明学习能力也很强,课后作业和测验我基本一直排在后几名,学了大概两个月左右,我仅能勉强跟上学习进度,仅仅是没掉队。从始业教育培训班倒数几名,到进入团队工作,我发现我基本什么也做不了,白天工作上遇到很多解决不了的问题,晚上只能请老员工帮我写代码,可是我看到一整天的工作量基本上老员工半小时到一小时就能干完了,虚心求教吧,他们下班后,我再独自学习老员工写的代码1-2个小时,天天晚上10点出公司11点到家,那个阶段整个人瘦了10多斤。就这样坚持了半年,才上手胜任。
时至今日那段印象深刻的经历一直是我不断学习奋勇向前的动力,总之就是永远走出舒适区,持续学习持续进步才能成为更好的自己。
我在京东工作时的一些照片
目前和我一起入职东软的很多同届毕业生很多转行了,和我一样坚持下来的不多。现在的同事基本全是985、211大学的学生,还有研究生,我带过的新人也不乏东北大学、北京邮电等名牌学校计算机专业的毕业生,能够成为他们的导师,相信战老师也会为我感到骄傲和自豪。
持之以恒、勤能补拙,话虽老道理不老。人笨不怕,别人学一小时就会,我学两小时三小时甚至一整天没有问题的。记忆力不好不用怕,别人看一遍记住的,我看两遍三遍十遍一样记住。实在篇章太长,我把它记到手机记事本里,脑子中记个索引总行了吧。起点低不用怕,人生是一场长跑,不用在乎一城一池一时的得失,持续的坚持与努力,终归会有得到回报的一天。
我个人不建议任何人不上大学选择就业,其实大学是人生中一段美好旅程,因为:
我们从当前现状和未来发展趋势两个方面来进行评价,首先看当前现状:
按国家统计局2019年数据显示,计算机、科研、金融分列第一二三名,年收入分别是16万、13万和13万。其他大量行业年收入连10万都没有。
再看未来发展趋势:
软件行业增速,近年虽然有所降低,但是依然维持在12-13%。保持在两倍GDP增速。
从业人员数量还在增长,即便是20年受疫情影响,依然逆势增长3.1%。
所以不用犹豫了,闭眼睛选计算机相关专业就行。
这是我抓取前程无忧的招聘数据,制作的图表:
工作机会数量基本上可以决定你找到好工作的难度。
北上广深作为传统一线城市牢牢占据前四名。
武汉、杭州、成都、南京、西安、重庆作为新一线城市紧随其后。
我在左侧图中标记出了沈阳的工作机会,只搜到了一万两千,上海搜到了十三万左右。
我们又知道沈阳的常驻人口是800万,上海是2400万,人口差距3倍。工作机会差距10倍,你就可以想象在不同地区找工作的难度了。
一线城市好处多多
如果故土难离,那么沈阳作为副省级城市,软件行业目前依然有一千亿的产值。浑南高新区有很多软件园,还是有很多工作机会的。
硕士博士以上 1%
本科 24%
专科 52%
中专/高中及以下 23%
通过小学算术题计算一下,如果你没有上大学,你只能选择23%的工作机会。
那么上了大专之后,在全部工作机会中,你可以选择其中的75%,差距已经很大了。
那么上了本科之后,你可以选择99%的工作机会,几乎就是全部了。
最好读完大学再就业,至少也是读完大专。
第一选城市,能选大城市就选大城市,最好北上广深。
第二选行业,能选计算机软硬件、科研、金融最好。实在不行,也要按照统计局的行业平均工资从上往下选。
我之前讲的很多资料可以到我的个人博客liuxp.me
查找下载。
之后如果有问题可以通过我的邮箱contact@liuxp.me
写邮件联系我。
我的演讲就到这里,谢谢大家。
YApi是国内开源的一款面向开发、产品、测试人员提供更优雅的接口管理服务。可以帮助开发者轻松创建、发布、维护 API。
支持私有化部署、开源免费、无缝兼容swagger.json格式标准。
下面我就介绍一下在CentOS中私有化部署的操作流程,由于官方文档存在问题,我这里会着重指出一些注意事项。
注意:YApi支持nodejs版本为 7.6 - 12,14以上启动会报错。
yum install nodejsyum install npm
npm install -g nn 12.13.0
注意:YApi支持的MongoDB版本要大于2.6
yum install -y mongodb-org
vi mongodb.conf# 设置数据文件的存放目录,改成你自己的数据盘dbpath = /usr/local/mongodb/mongodb3.2.4/bin/data/test/db# 设置日志文件的存放目录及其日志文件名,改成你自己的数据盘logpath = /usr/local/mongodb/mongodb3.2.4/bin/data/test/logs/mongodb.log# 设置端口号(默认的端口号是 27017)port = 27017# 设置为以守护进程的方式运行,即在后台运行fork = true# nohttpinterface = truenohttpinterface = true按英文状态下的“:wq”保存并退出;
1)启动systemctl start mongod2)重启systemctl restart mongod3)关闭systemctl stop mongod
yum -y install git
npm install -g yapi-cli --registry https://registry.npm.taobao.org
yapi server
YApi-CLI默认访问端口是9090,如果9090已被占用,可以手动修改启动端口。正常来说不用修改。
- 找到yapi-cli的路径
/usr/lib/node_modules/yapi-cli/src/commands
- 编辑server.js
vi server.js
- 修改监听端口
修改 app.listen(9090)
注意:部署版本不要选择1.10.1和1.10.2,这两个版本会调用阿里内网地址,部署会失败。必须选择1.9.3以下版本。
安装完成后,我们可以敲命令启动YApi后台程序了。
node server/app.js //启动服务器后,请访问 127.0.0.1:{config.json配置的端口},初次运行会有个编译的过程,请耐心等候
运行成功后,我们通过浏览器访问YApi,如下图所示:
但是通过命令启动的YAPI服务,一关掉终端就停止运行了,所以我们需要使用PM2来将YAPI作为后台服务运行。
npm install -g pm2
pm2 start opt/yapi/my-yapi/vendors/server/app.js
至此,YAPI的整个部署流程我们就走完了,如果在部署的过程中,有什么问题可以在我的博客留言,我会尽量帮助大家解决。
找到并修改config.json配置文件中邮件发送配置内容
修改完成后,重启YApi即可。
利用WinRAR和NW.js将WEB应用程序打包为一个可在Windows系统直接运行的EXE可执行文件。
接下来,介绍详细的打包步骤
这里的website1文件夹就是我们的html页面,如图所示:
这里我们需要配置程序的入口地址,将website1文件夹下page1_0.html文件作为入口,如图所示:
这里再多解释一下其他几个参数的含义:
删除程序文件和package.json配置文件,拖拽打包好的package.nw文件到nw.exe上运行,看程序是否正确运行,如下图所示:
开始->运行->输入cmd回车,打开dos窗口。
进入当前所在打包目录。
使用copy /b nw.exe+package.nw app.exe命令,将nw.exe与package.nw文件合并重命名为app.exe。
删除原nw.exe与package.nw文件。
运行app.exe查看程序是否正常运行。
以上是NW.js的打包流程,这里我们可以看到,依然有很多程序文件暴露在外,我们的最终要打包为一个exe可执行文件,所以还要用WinRAR进行二次打包。
选中全部文件,鼠标右键->添加到压缩文件…
配置自解压参数,把这个√点上:
进入高级TAB页,打开自解压选型配置:
模式配置页,解包到临时文件夹√点上,安静模式选择全部隐藏:
更新配置页,选择解压并替换文件、覆盖所有文件:
文本和图标配置页,可以选择程序图标(会显示在Windows系统中),这步可跳过:
设置配置页,自解压后自动运行的程序名称 app.exe --disable-pinch
注意: --disable-pinch是chrome内核启动参数,含义为禁用触控手势,该参数仅在你的WEB程序是触摸屏程序时配置即可,其他情况无需配置。
以上全部配置完成后,点击确定,压缩成功。
设置程序的名称,可以是中文。
到这步就完成了,你就获得了一个可以在Windows系统运行的web应用程序,现在就可以将这个文件发送给用户使用了。
]]>2021年是一个奋斗的年份,除了应届毕业刚工作那会以外,2021可以说是最忙也是压力最大的一年了。
2021年3月转正,也是我来到望海的第一个完整年份。
在这一年中,我经历了从入职时的架构师定位到研发经理的转变。
在一个小规模的部门中,并不需要两个架构师的存在,在技术方向的探索和选型中,有一个架构师指明方向就行了,其他人负责查缺补漏即可。
多个有领导力的架构师同时存在是会存在一定程度的内耗的,所以说,架构师也并不是越多越好。
在业务方面也是接触到了数字卫健的全部历史项目,比如最重要的上海申康项目,深挖坑广埋人的郑州和北京顺义项目等等。对数字卫健现有的全部业务进行了深入了解。
包括监管平台、财务大屏、预算编制、支出控制等不同业务。
甚至参与了智慧运营平台的初期建设工作,设计并实现了第一版的指标库,虽然没实际使用,哈哈哈,算了这段删除。
在参与申康项目时,我梳理了全部申康一期二期的表结构,并编写了excel文档,补全了所有表与字段的中文含义。现在这个excel基本上做过申康项目的研发和实习生人手一份。
在参与海南和郑州项目,特别是郑州项目时,我梳理了财务、成本、人资、耗材、绩效等全部监管报表的查询逻辑和对应的数据库表和字段,并形成了excel文档。
且在只有部分交接的情况下,理顺了郑州现场屎山一般的部署环境。
在后期参与北京顺义项目时,深入理解学习了工作流引擎的,重写了流转记录功能、添加了新的流程变量,接手了老刘的遗产。
了解很少一部分HERP的技术框架和业务逻辑,能帮项目经理查一些库表,解决一些简单的问题,最重要的是,做一下北京研发和项目经理之间的润滑剂吧,相当于一个翻译,能把两边说的话都翻译成对面能听明白的方式。
还梳理了HERP的发版流程。
单体补丁的发布流程。
在技术方面,接触了部门全部技术栈,包括不限于springboot、springcloud、react(能改BUG、有例子情况下能开发新需求)、各版本fineReport(不同版本差异,二次开发集成认证)、viewhighBI(拆解了公司BI设计思路和数据结构)、UniEAP(主要靠老刘支持哈哈哈)、cas(在做郑州项目时,做到cas无源码二次开发集成卫宁统一认证)、甚至HERP(现在能做到读懂,可以在项目经理做数据时,告诉他哪个页面的数据是从哪个表来的)
在解决各种现场问题时,有时候迫于时间压力,必须另辟蹊径。
比如在做申康合院时,有几十张数据库表中数据的处理,外加几十个帆软页面的改造。而时间紧迫,就那么一两天。
为了不加班,这任务必须得动脑干,不能傻干了。
数据处理我首先排除了项目经理提的手动处理选项,因为太多表了,人的精力不够且容易出错。
我决定写了一个存储过程来执行数据处理,这一个存储过程要适配几十张不同的表,并识别出维度和事实字段,非常不好写,但是我就喜欢写这样的程序,写完很有成就感。这一个存储过程我写了两天,最终完成一劳永逸。甚至前几天又有合院问题时又用上了,值了。
在帆软页面改造时,一个一个找依然不能满足速度需求。
通过分析帆软的源文件我发现,帆软源文件的存储格式是xml,可以直接使用IDEA打开且不会乱码。
那么我就决定直接使用高级的语法匹配,整个进行工程级别的文字替换即可了,我花了半天时间找到所有需要修改的地址和特征并编写替换规则,就搞定了几十个帆软页面。
最后花了半天时间测试,没有任何问题,这样整个合院的需求3天就搞定了。
再举一个郑州的例子,郑州是阿里统一建设,阿里要求所有系统集成卫宁的统一认证。
我们在郑州部署的系统全是东拼西凑的,用了三种不同的数据库,4个帆软版本还不一致,里面有不同时期的基础平台,甚至存在UniEAP……
如果按照标准的接入方式集成卫宁统一认证,那么改造的工作量非常的巨大,且卫宁只提供一个入口跳转地址到我们的系统。
经过讨论与慎重思考,我决定将我们的cas与卫宁统一认证做集成,这样的好处1可以保留我们的cas登录入口,2我们9个子系统不用每个都改造了。
但是做起来很难,首先,网上没什么资料可以参考,都是各种系统集成cas,而不是cas去集成别人。
其次,我们还没有郑州cas的源码,郑州是一个老版本的cas,且之前有过二开,网上的cas源码不能直接用。
那么,我用IDEA直接打开郑州cas整个目录作为工程路径,然后手动指定lib包,利用IDEA自带的反编译功能直接阅读cas的源码。
参照卫宁的集成文档,用了一天半时间复写了多个配置文件与class类,实现了卫宁统一认证与cas的集成。
要说今年最遗憾的就是顺义项目,年中的时候我没有下狠心接手重构,导致现在积重难返,产品化也失败了,只能按照项目做,而且质量一般般。
顺义预算,其实还真完全是我部门主导研发的产品,并不是二开。
我总结的经验的教训:
以上三点,我认为归根结底是人的问题,再说明白一点就是上一任研发经理、产品经理共同导致的问题,初期不能识别需求正确与否,做的和甲方想要的南辕北辙。中期不能管理代码质量,自己不写代码不做代码review,下属写的代码稀烂也不管导致质量奇差,一天天跟下属你好我好大家好。后期又不舍得那点奖金,认为没有重构的必要,完完全全的失职。
用正确的人在正确的位置上,才能如臂使指,丝滑顺畅。
不要抱有不切实际的幻想,当一件事情有可能往最坏的结果发生时,它就一定发生。
文章什么的都可以无缝迁移,在solo中选择导出为markdown格式,然后在Halo中导入即可。文章内容、发表时间等等数据都可以带过来,非常方便。
]]>#header { width: 1395px}#top .top-wrap { width: 1395px}#main { width: 1355px;}#footer { width: 1315px;}.about { width: 1395px}#m-wrap { width: 1355px;}#main-container { width: 1355px; margin: 0 auto;}#main-container .main-left { width: 350px; height: 400px; border: 1px solid #e3e3e3; overflow: auto;}#main-container .main-right { width: 992px; border: 1px solid #e3e3e3; position: relative;}.splitbar{ left:352px !important;}.table_container_main { width: 992px !important;}#navbar { width: 1355px;}#site { width: 1355px;}#site .site-center { width: 1349px;}
自定义CSS样式我已经写好,按照如上图配置即可。
统计局默认显示宽度为955,我增加了400到1355,改后效果如下:
]]>切换回Win10经典右键菜单,执行以下命令
reg add "HKCU\Software\Classes\CLSID\{86ca1aa0-34aa-4e8b-a509-50c905bae2a2}\InprocServer32" /f /ve
恢复到Win11新版右键菜单
reg delete "HKCU\Software\Classes\CLSID\{86ca1aa0-34aa-4e8b-a509-50c905bae2a2}" /f
2.重启电脑
]]>这是我个人使用了好久的代码生成工具,从去年开始准备开源,调整代码结构并编写使用文档,终于正式发布1.0了。
AliceCodeCreator是一个基于数据库表结构,使用Velocity模板引擎来构建输出的代码生成器。
该项目的文档在https://github.com/lxp135/alice-code-creator-docs中,欢迎Star。
该项目的源码在https://github.com/lxp135/alice-code-creator中,欢迎Star。
该项目首页https://creator.liuxp.me
]]>最后,祝大家工作效率高高的,
955
从不加班。
2.访问https://fastly.net.ipaddress.com/github.global.ssl.fastly.net
3.修改本地host文件 C:\Windows\System32\drivers\etc
PS:若还是不能访问,打开CMD运行如下命令 ipconfig /flushdns 再试一下。
]]>有认识我的朋友可能知道我过去一两年内采集了很多数据,包括不限于招聘、人口信息、气象信息、彩票开奖信息、企业工商信息等等方向,我一直在思考能用这些数据做点什么有意义的事情。
最近我决定把这些数据通过可视化的方式展现出来,今天发布的就是第一个项目《中国就业形势实时数据可视化》,点击名称可以进入页面,建议使用PC端浏览器。
下面具体介绍一下各个图形的含义
上面的职位数量和企业数量代表目前后台采集的数据数量,本数据实时更新。
地图部分显示了38个主要城市,圆圈大小使用平均工资计算,计算公式为
平均工资-3000)/系数
这样圆圈的大小区别会更加明显。
鼠标悬浮到城市上,可以显示出该城市的平均工资、中位数工资和工作机会数量(也就是采集的样本数量),工作机会越多代表该城市的招聘需求绝对值越大。
工作机会行业TOP10排行
可以看到高新产业比如电子技术/半导体/集成电路、计算机软件、制药/生物工程、互联网/电子商务占据了半壁江山。说明我国的产业升级还是很成功的,高新产业提供了大量就业岗位。
另外就是房地产、教育和服务业还是主要支柱,依旧提供了数量众多的岗位。
工作机会城市TOP10排行
可以看到传统一线城市北上广深依旧位列前四名不可动摇,众多企业聚集在这四个城市。但是我们也要看到武汉、杭州和成都这三座新一线城市正在奋起直追,这三座城市中,除杭州房价已经很高外,武汉和成都的房价还在每平米1万-2万之间,考虑到传统一线城市突破天际的房价,我认为武汉和成都不失为一个打工者目前通过奋斗还能立足的好选择。并且武汉和成都的房价我认为在未来有很大空间,值得考虑。
学历要求
我们可以看到需要大学学历的岗位在统计分布中占据统治地位(大学本科占23.6%,大学专科占52.41),由于这些年大学的疯狂扩招,大学学历从比较优势变成及格线。目前只有985和211院校才有一定优势了。
工作经验要求
工作经验分布的比较均衡,从应届毕业生至油腻的中年人,都有相应的岗位提供,说明我们的就业市场是比较充分的,就业环境是比较健康的。
工资城市TOP10排行
平均工资这里出现了与传统认知不符合的情况,首先是你找不到广州这座城市,作为传统一线城市广州的平均工资已经跌落到10名开外了。其次你发现了一些偏远城市的平均工资水平其实很高,比如说拉萨的平均工资能够达到8340之多。其实也很好理解,拉萨的样本数只有155个,代表只有155条招聘信息,并且这些招聘信息大多是政府和企事业单位发布的,这就说明当地的工作机会还是不够多,经济活动并不活跃。高工资主要还是靠高原补贴等因素影响,并不建议选择。
工资行业TOP10排行
这里基本被3个高新行业垄断,房地产、金融、互联网(软件)。考虑到房地产已经是夕阳产业,所以没什么好犹豫的,直接选择去金融行业干研发(软件工程师)就对了。
企业性质分布
民营公司占74.75%,绝对多数。从这里就能看出,提供巨量就业岗位的还是广大中小微企业。这就不难理解今年疫情中,国家给中小微企业的各种政策倾斜和税收减免,毕竟要保就业。
]]>后记:前端代码开源,后续会放到GitHub上,项目地址为https://github.com/lxp135/graphs 可以点一波star了。后台代码就不开源了,大家需要数据的话,可以直接从前端代码中调用的接口地址获取,我并没有做权限控制,由于服务器能力有限,请适度查询。如果有需要的话,后续我可以提供接口文档。
本项目数据来源于互联网各大招聘网站。
定向采集,指我们已经有了明确的采集目标与采集规则,比如我们要采集一个新闻站点,那么首先取得新闻列表中全部详情页面的链接地址,再通过链接请求详情页面的内容,这就是一个标准的定向采集爬虫。
定向采集在大数据分析、商业情报、网络安全等领域有着广泛的应用。
在 Java 中,可以使用 Jsoup、HttpClient、Selenium 等工具来实现定向采集。
我们要了解到,web网页其实是一种超文本标记语言,它通常存储为文本文档,我们的目的就是把这个文本文档下载到本地。
在网页内容中,可能会存在一些图片或者是文件等资源,这些内容如果不同时采集到本地,那么我们保存的内容就是不完全的,所以我们还有能够下载图片与文件。图片与文件通常存储为二进制的流,我们的目的就是把这个流读取到本地并创建为二进制文件。
HttpClient 是一个强大的 HTTP 客户端库,支持发送 HTTP 请求、处理 HTTP 响应等多种功能。在采集网页内容时,可以使用 HttpClient 发送 HTTP 请求并获取响应,然后通过解析响应数据来获取需要的信息。
以下是使用 HttpClient 进行网页采集的示例代码:
import org.apache.http.client.HttpClient;import org.apache.http.client.methods.HttpGet;import org.apache.http.impl.client.HttpClientBuilder;import org.apache.http.util.EntityUtils;import java.io.IOException;public class WebCrawler { public static void main(String[] args) { HttpClient client = HttpClientBuilder.create().build(); HttpGet request = new HttpGet("https://www.example.com/"); try { // 发送 HTTP 请求并获取响应 HttpResponse response = client.execute(request); // 处理响应数据 String content = EntityUtils.toString(response.getEntity()); System.out.println("Content: " + content); } catch (IOException e) { e.printStackTrace(); } }}
在上面的示例代码中,我们使用 HttpClient 发送 HTTP 请求并获取响应,然后通过 EntityUtils 工具类将响应数据转换为字符串,并输出字符串内容。
Selenium是一个用于测试web应用的工具。Selenium测试直接运行在浏览器中,就像真正的用户在操作一样。支持的浏览器包括IE(7, 8, 9, 10, 11),Mozilla Firefox,Safari,Google Chrome,Opera等。我们可以通过Java调用Selenium执行逻辑,模拟人工操作。
我们为什么要用Selenium呢? 由于互联网应用越来越复杂,新上线的很多网站已经不是静态站点了,有很多单页应用,所谓单页应用就是整个站点仅有一个网页,网站上的所有内容通过http请求动态加载,这样的网站通过上面下载网页的形式已经不能正常读取了,所以我们借由Selenium访问并进行采集。
下面是一个用Selenium进行采集任务的代码示例:
import org.openqa.selenium.WebDriver;import org.openqa.selenium.chrome.ChromeDriver;import org.openqa.selenium.By;import org.openqa.selenium.WebElement;import java.util.List;public class WebCrawler { public static void main(String[] args) { // 设置浏览器驱动路径 System.setProperty("webdriver.chrome.driver", "path/to/chromedriver"); // 创建 Chrome 浏览器实例 WebDriver driver = new ChromeDriver(); // 打开网页 driver.get("https://www.example.com/"); // 获取需要的元素 List<WebElement> links = driver.findElements(By.tagName("a")); for (WebElement link : links) { System.out.println("Link: " + link.getAttribute("href")); } // 关闭浏览器实例 driver.quit(); }}
在上面的示例代码中,我们使用 Selenium 创建 Chrome 浏览器实例,并打开指定的网页,然后通过定位链接元素的方式获取所有的链接,并输出链接的地址。
除了上述工具之外,还有一些其他的 Java 网络爬虫框架,例如 WebMagic、Crawler4j 等,可以帮助我们更方便地实现定向采集功能。
以上全部工具,我们可以根据实际情况任意组合使用,最终目的就是帮助我们顺利采集到数据即可。
]]>送走亥猪,喜迎子鼠。十二年又是一个轮回,在新的一年里,祝大家万事如意、心想事成!
2019年,是我在东软熙康稳定工作的一年,组织了五条业务线的研发工作,按照敏捷的要求制作迭代表格并每天开早会,从无任何迭代延期并能够保证交付质量。
其中在带领门诊项目研发时,面临时间紧(交付截止日期固定,研发测试上线仅不到1个月的时间)、任务重(从零开始,新UE。需要实现视频会诊、影像、高拍仪、短信、排班等等多种功能。)、人员拼凑(项目组人员从各个部门借调,彼此不熟悉,对微服务架构也不熟悉)的情况下,通过详细的前期规划、UE设计、系统设计和数据库设计避免了返工,通过前期加班抢进度保证了效率,通过封闭开发保证了沟通顺畅,通过早会与线上代码review保证了质量,最终甚至提前完成。这个项目使我的管理能力得到了提高。
2019年度绩效评价得到了A。
本年度我还参加了公司组织的高潜员工培训计划,据说公司采购课程价格不菲,其中“当责”、“沟通”等课程使我受益匪浅,我还把我学过的课程画了一张思维导图。
图比较大,点击下载高潜培训思维导图。
注:建议下载到电脑上浏览,手机看的话图太小了,字看不清。
还有毕业证书,哈哈,晒一下:
技术博客在2019年重新开张了,并且从wordpress更换为b3-solo。写的文章还是太少,争取在2020年做到周更,至少也得是月更,并把Java爬虫系列教程写完。
新技术探索方面(指对我自己来说),我今年开坑了一个小程序《肉蛋菜价查查看》,目前已上线,大家在微信小程序中直接搜索就可以看到,用的是uni-app跨平台研发,uni-app是5+plus的升级换代版本,我这些年从5+plus一直用到uni-app,是做一些小型项目(APP、小程序)的首选框架,官网是https://uniapp.dcloud.io/,大家感兴趣可以看看。
今年还研究了three.js。目前能在web端读取并展示3D模型了,还可以进行一些简单的互动,这方面在国内目前应用的我感觉还比较少,只有一些网上的3D展馆,而且很多做的并不好看。
其实可以把threejs引入到web网站中落地,比如做一个3D版本的博客,菜单、导航、文章内容、其他信息等通过3D形式展现出来,应该是一种非常新颖的形式,不知道2020年能不能实现,先给自己挖个坑,哈哈。
今年还做了微信服务号的开发,仔细的研究了微信内置的X5浏览器,实现了点播与直播的播放器、语音识别、文件上传、拍照与相册调用、微信支付等大量功能,积累了丰富的代码量。
Java方面,今年继续研究SpringCloud,在gateway基础上,实现了一套API接口鉴权系统,可以自动读取工程中全部接口并持久化到数据库中,并根据类形成接口组。在后台配置appKey与appSecret。通过 appKey -> 角色 -> 接口的形式进行关联。鉴权时,客户端请求将相关鉴权信息放入到header中,gateway拦截并进行鉴权判断是否通过,对业务代码透明。
明年如果还在东软熙康工作的话,我准备研究一下医学影像存档与通讯还有PACS工具。给我们大熙康提供点高端组件,省着总集成别人家的,还不给人钱,一天天求爷爷告奶奶似的。
还有空的话,我准备写一下熙康这么多年来远程医疗的架构演进图,梳理一下技术架构。
生活方面今年基本乏善可陈了,也没有出去旅游,没什么大的改变,除了研究技术以外,基本就是天天宅在家里打游戏看动画片而已? 。
闺女一天天健康成长,就是特长班有点贵。
目前学习了舞蹈、英语、美术、逻辑思维、口才,这一年费用得大几万……
不过也值了,还参加了2020辽宁少儿春晚朵朵童星的录制,春节期间播出~
来几张闺女美图:
舞蹈考级证书:
]]>希望我的闺女顺利成长,无病无灾。父母和老婆都健健康康,开开心心。
本章主要讲解爬虫需要用到的 Java 常识和一些类库等,方面初学者或者使用其他语言的同学快速上手,如果您本身会使用 Java 编程语言,那么本章可以略过不看。
本教程选择 Java 编程语言环境,而没有使用 python。目前网上存在大量的 python 爬虫教程,而 Java 教程寥寥无几,这并不代表 Java 做爬虫不行,Java 语言拥有严谨的语法结构和海量的类库,我认为 Java 在开发爬虫方面拥有后发优势。
准备开发环境
从Oracle官网下载 JDK 并安装。
本教程选择 Java SE 8 版本 jdk,您也可以自行选择更高版本,理论上都是向下兼容的。
开发工具我选择了 IDEA 而不是 Eclipse,从IntelJ官网下载并安装。
IDEA 比 Eclipse 好用太多,基本属于用过就回不去的感觉。但是 IDEA 是收费软件,建议如果有能力还是购买正版支持吧,学生凭借学生证可以申请正版授权免费使用,嫌麻烦还可以使用社区版(免费),或者自行破解,我在这里就不提供破解方法了。
结构化数据存储,我们选用MySQL,版本选择5.7。之所以不选择更高版本,是因为目前绝大多数云服务器提供的版本最高就是5.7,为了保持统一,所以我们也选择5.7。
从MySQL官网下载MySQL并安装。
为了方便测试和查看数据,我们还需要一个图形化的数据库编辑器。
这里选择Navicat for MySQL做为我们的图形化工具。
基础这节,仅列一下需要了解的知识点。如果您不具备Java基础,请自行买实体书或寻找网络教程学习。
<!-- 一个Java工具包 http://www.hutool.cn/ --> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>4.5.1</version> </dependency>
<!-- google图片压缩 --> <dependency> <groupId>net.coobird</groupId> <artifactId>thumbnailator</artifactId> <version>0.4.8</version> </dependency>
]]>