【NodeJS】Windows环境下如何做Node多版本管理?

InterviewCoder

# 【NodeJS】Windows 环境下如何做 Node 多版本管理?

# 前言

​ 在多个不同的前端项目中,总会遇到 nodeJs 需求版本不一致的情况,例如,项目 A 需要 node12 版本,项目 B 要求不能小于 node16 版本,此时就产生了冲突,因此我们需要使用一个 Node 版本控制来解决这一系列的问题。

# nvm-windows

​ 在 Windows 环境下,可以使用 nvm-windows 工具来管理多个 Node.js 版本。与 nvm 相似, nvm-windows 也是一个 Node.js 版本管理器,可以让你在同一台计算机上安装和管理多个 Node.js 版本。

以下是使用 nvm-windows 工具的步骤:

  1. 下载安装包

    1
    https://github.com/coreybutler/nvm-windows/releases/tag/1.1.11

首先需要下载并安装 nvm-windows 工具。可以从 GitHub 上下载最新版本的安装包。下载后,直接运行安装程序按照提示进行安装即可。

  1. 安装 Node.js 版本

安装完成后,在命令行中输入以下命令来安装 Node.js 的不同版本:

1
nvm install 14.17.5

这将安装 Node.js v14.17.5 版本。如果要安装其他版本,只需将 14.17.5 替换为所需的版本即可。安装完成后,可以使用以下命令查看已安装的 Node.js 版本:

1
nvm list
  1. 切换 Node.js 版本

要切换到已安装的特定版本,可以使用以下命令:

1
nvm use 14.17.5

这将使当前命令行窗口使用 Node.js v14.17.5 版本。如果要设置默认版本,请使用以下命令:

1
nvm alias default 14.17.5

这将将 Node.js v14.17.5 设置为默认版本。每次打开一个新的命令行窗口时,都会自动将其设置为默认版本。

  1. 其他命令

除了上述命令外, nvm-windows 还提供了其他有用的命令,例如:

  • nvm on :启用 nvm 环境变量。
  • nvm off :禁用 nvm 环境变量。
  • nvm uninstall 14.17.5 :卸载 Node.js v14.17.5 版本。

以上是在 Windows 环境下使用 nvm-windows 工具管理多个 Node.js 版本的基本步骤。

# 关于我

Brath 是一个热爱技术的 Java 程序猿,公众号「InterviewCoder」定期分享有趣有料的精品原创文章!

InterviewCoder

非常感谢各位人才能看到这里,原创不易,文章如果有帮助可以关注、点赞、分享或评论,这都是对我的莫大支持!

MySQL安装 starting the server失败的解决办法

InterviewCoder

# MySQL 安装 starting the server 失败的解决办法

​ 如果电脑是第一次安装 MySQL,一般不会出现这样的报错。如下图所示。starting the server 失败,通常是因为上次安装的该软件未清除干净。

在这里插入图片描述

完全卸载该软件的办法

第一步,进入… 控制面板 \ 程序 \ 程序和功能,卸载下图中的 MySQL 两个软件。

在这里插入图片描述
第二步,删除上次安装目录的 MySQL 残留文件,更不要忘了删除 ProgramData 下的 MySQL 文件夹,如下图所示。注意:这里的文件夹与上次安装目录里的残留文件不同,C:\ProgramData 一般默认是隐藏的。
在这里插入图片描述
第三步,Win+R 输入 regedit 运行,进入注册表编辑器,按下图路径,找到 MySQL,进而删除 MySQL 注册表信息。
在这里插入图片描述
第四步,做完前三步,打开服务,会发现 MySQL57 服务依然存在,如下图所示。这就是导致安装失败的重要原因,所以需要删除 MySQL57 服务。方法:以管理员的权限运行 dos 命令,然后输入 sc delete MySQL57 。

1
2
3
C:\Windows\system32>sc delete MySQL57
[SC] DeleteService 成功
12

在这里插入图片描述

现在就可以轻松的安装上 MySQL 软件了!

# 关于我

Brath 是一个热爱技术的 Java 程序猿,公众号「InterviewCoder」定期分享有趣有料的精品原创文章!

InterviewCoder

非常感谢各位人才能看到这里,原创不易,文章如果有帮助可以关注、点赞、分享或评论,这都是对我的莫大支持!

【Vue】el-upload限制只上传一张图片并隐藏右侧的上传区域

InterviewCoder

# 【Vue】el-upload 限制只上传一张图片并隐藏右侧的上传区域

使用 element UI 中的 el-upload 如何限制上传一张图片后隐藏右侧的上传区域

img

主要代码块:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
监听事件
watch: {
onChangeImgUrl: {
handler(newName) {
var aa = document.querySelector('.el-upload--picture-card')
if (newName) {
aa.style.display = 'none'
} else {
setTimeout(() => {
aa.style.display = 'inline-block'
}, 1100)
}
}
}

原理:

因为只上传一张图片,监听图片的 url 即可。听过监听图片的 url 存在与否,通过获取 dom 元素设置不同的样式。

以下完整代码实现如下效果:

img

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

父组件setBank.vue
<template>
<div class="setBank">
<el-button type="primary" @click="addInfo">入驻</el-button>
<el-dialog :visible.sync="checkDialogVisible" title="入驻">
<Upload />
</el-dialog>
</div>
</template>

<script>
import Upload from './upload.vue'
export default {
components: {
Upload
},
data() {
return {
checkDialogVisible: false
}
},

methods: {
// 入驻
addInfo() {
this.checkDialogVisible = true
}
}
}
</script>
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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106

子组件 upload.vue
<template>
<div class="con_form">
<el-form ref="addForm" :model="addForm" class="demo-comCreditForm" label-width="200px">
<el-form-item label="企业名称:" prop="name"> <el-input v-model="addForm.name" placeholder="转贷机构名称" /> </el-form-item>
<el-form-item label="统一社会信用代码:" prop="code"> <el-input v-model="addForm.code" placeholder="请输入统一社会信用代码" /> </el-form-item>
<div class="upload_icon">
<el-form-item label="上传:" prop="addForm">
<el-upload
:auto-upload="false"
:limit="limitCount"
:on-remove="handleRemove"
:on-change="onChange"
:on-success="handleSuccess"
:file-list="fileList"
:data="uploadData"
:before-upload="beforeAvatarUpload"
action="#"
class="avatar-uploader"
list-type="picture-card"
accept="image/jpg,image/jpeg,image/png">
<img v-if="url" :src="url" class="el-upload-list__item-thumbnail"></img>
<i v-else slot="default" class="el-icon-plus" />
</el-upload>
<div class="el-upload__tip">jpg.jpeg、png格式,大小5M以内</div>
</el-form-item>
</div>
</el-form>
</div>

</template>

<script>
export default {
data() {
return {
limitCount: 1,
url: '',
onChangeImgUrl: '',
uploadData: {
name: 'testFile'
},
fileList: [],
// 申请入驻银行 表单
addForm: {
name: '',
code: '' // 社会信用代码
}
}
},
watch: {
onChangeImgUrl: {
handler(newName) {
var aa = document.querySelector('.el-upload--picture-card')
if (newName) {
aa.style.display = 'none'
} else {
setTimeout(() => {
aa.style.display = 'inline-block'
}, 1100)
}
}
}
},
methods: {
onChange(file, fileList) {
this.onChangeImgUrl = file.url
},
handleRemove(file, fileList) {
this.onChangeImgUrl = ''
},
handleSuccess(file, fileList) {
this.$set(this.myForm, 'netTgThumbnail', fileList.response.bean.result.fileUrlPath)
},
// 限制图片大小
beforeAvatarUpload(file) {
const isLt5M = file.size / 1024 / 1024 < 5
if (!isLt5M) {
this.$message.error('上传头像图片大小不能超过 2MB!')
}
return isLt5M
}
}
}
</script>

<style scoped lang="scss">
/deep/.upload_icon .el-form-item__content {
position: relative;
height: 190px;
}
.avatar-uploader {
width: 145px;
height: 145px;
position: absolute;
left: 0;
top: 0;
}
.con_form .upload_icon .el-form-item__content .el-upload__tip {
position: absolute;
left: 0;
bottom: 0;
color: red;
}
</style>

# 关于我

Brath 是一个热爱技术的 Java 程序猿,公众号「InterviewCoder」定期分享有趣有料的精品原创文章!

InterviewCoder

非常感谢各位人才能看到这里,原创不易,文章如果有帮助可以关注、点赞、分享或评论,这都是对我的莫大支持!

【Java】Integer的缓存机制

InterviewCoder

# Integer 的缓存机制

# 文章目录

# 一 现象

在引入 Integer 的缓存机制前,可以先判断一下以下几种情况

1
2
3
4
5
6
7
8
9
10
11
12
   # 一:自动装箱
Integer s1 = 2;
Integer s2 = 2;
System.out.println(s1 == s2);
# 答案为true

# 二:
Integer s1 = new Integer(2);
Integer s2 = new Integer(2);
System.out.println(s1 == s2);
# 答案为false

情况二很好理解,虽然传值相同,但是 Integer 是包装类,不同对象的引用地址是不一样的,而 “==” 比的是引用地址,那为什么情况一中会得到结果 true
这里就不得不提到 Integer 的缓存机制了

# 二 Integer 的缓存机制

# 2.1 自动装箱等效于 valueOf

1
2
3
4
#以上代码的情况一的自动装箱等效于Integer中的ValueOf()方法,如下:
Integer s1 = Integer.valueOf(2);
Integer s2 = Integer.valueOf(2);
System.out.println(s1 == s2);

# 2.2 valueOf

以下是 valueOf () 方法的具体实现

1
2
3
4
5
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}

可以看见,其实现方式和 IntegerCache 有关

# 2.3 IntegerCache

IntegerCache 是 Interger 的一个静态内部类,实现如下:

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
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];

static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;

cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);

// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}

可以看到,默认情况下,该静态内部类会直接缓存 - 127 到 128 的 Integer 对象,因此,在 valueOf () 方法中,如果值在 - 127-128 之间,都会直接返回缓存中的该对象而不会重新生成对象,引用地址当然相同了。
而且 java5 引入时,该范围还是固定的,而在 java6 之后,Integer 的缓存中还可以通过 Integer.IntegerCache.high 来设置最大值。由于这个缓存机制,程序中第一次使用 Integer 的时候还需要一定的时间家长该缓存类

# 三 为什么要有缓存机制

# 3.1 原因

因为我们常见的基本数据类型中,使用包装类包装数值时会创建大量对象,如果没有缓存的话,会有大量的包装类被创建,占用内存,降低效率。选择最常用的数值范围设置缓存机制,就可以优化这一现象

# 3.2 其他包装对象的缓存

既然缓存机制的原因我们知道了,那除了 Integer 之外,其他包装类有这种缓存机制吗?肯定是有的

  • ByteCache:缓存 Byte 对象
  • ShortChche:缓存 Short 对象
  • LongChche:缓存 Long 对象
  • CharacterChche:缓存 Character 对象
    Byte,Short,Long 的缓存范围都是 - 128-127,Character 的缓存范围是 0-127,除了 Integer,其他的缓存范围都是固定的

# 关于我

Brath 是一个热爱技术的 Java 程序猿,公众号「InterviewCoder」定期分享有趣有料的精品原创文章!

InterviewCoder

非常感谢各位人才能看到这里,原创不易,文章如果有帮助可以关注、点赞、分享或评论,这都是对我的莫大支持!

RocketMQ

InterviewCoder

# RocketMQ

# 应用场景

  • 异步解藕
  • 削峰填谷
  • 消息分发

# 环境搭建

  1. 上传 rocketmq-all-4.4.0-bin-release.zip 到家目录

  2. 使用解压命令进行解压

    1
    unzip /usr/local/rocketmq-all-4.4.0-bin-release.zip
  3. 软件重命名

    1
    mv /usr/local/rocketmq-all-4.4.0-bin-release/ /usr/local/rocketmq-4.4/
  4. 修改启动参数配置

    JAVA_OPT=”${JAVA_OPT} -server -Xms1g -Xmx1g -Xmn1g

    两个文件

    1
    2
    3
    vi /usr/local/rocketmq-4.4/bin/runbroker.sh

    vi /usr/local/rocketmq-4.4/bin/runserver.sh
  5. 启动名字服务和代理服务

    1
    2
    3
    4
    5
    nohup sh /usr/local/rocketmq-4.4/bin/mqnamesrv &

    # -n localhost:9876 指定名称服务的地址, 类似于zk的地址

    nohup sh /usr/local/rocketmq-4.4/bin/mqbroker -n localhost:9876 -c /usr/local/rocketmq-4.4/conf/broker.conf &
  6. 检验是否启动正常

    使用 java 的内置命令: jps 可以看到 BrokerStartup 和 NamesrvStartup 进程

    使用 Linux 命令 **: netstat-ntlp 可以看到 9876 的端口和 10911 的端口 **

    使用 ps-ef |grep java

    查看启动日志:

    tail -100f ~/logs/rocketmqlogs/namesrv.log

    tail -100f ~/logs/rocketmqlogs/broker.log

  7. 关闭 RocketMQ

    1
    2
    3
    4
    5
    6
    7
    # 1.关闭NameServer

    sh /usr/local/rocketmq-4.4/bin/mqshutdown namesrv

    # 2.关闭Broker

    sh /usr/local/rocketmq-4.4/bin/mqshutdown broker

# 编写 sh 脚本文件

  • 启动 (startRocketMQ.sh)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    # !/bin/bash

    echo '------------------rocketmq-nameServer-starter-------------------------'
    nohup sh /usr/local/rocketmq-4.4/bin/mqnamesrv &
    echo '------------------rocketmq-nameServer-started-------------------------'

    echo '------------------rocketmq-brokerServer-starter-----------------------'
    nohup sh /usr/local/rocketmq-4.4/bin/mqbroker -n localhost:9876 -c /usr/local/rocketmq-4.4/conf/broker.conf &
    echo '------------------rocketmq-brokerServer-started-----------------------'
  • 关闭 (stutdownRocketMQ.sh)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    # !/bin/bash

    echo '------------------rocketmq-nameServer-shutdown-------------------------'
    sh /usr/local/rocketmq-4.4/bin/mqshutdown namesrv
    echo '------------------rocketmq-nameServer-shutdowned-------------------------'

    echo '------------------rocketmq-brokerServer-shutdown-----------------------'
    sh /usr/local/rocketmq-4.4/bin/mqshutdown broker
    echo '------------------rocketmq-brokerServer-shutdowned-----------------------'

# 监控平台

使用 jar

1
nohup  java -jar  rocketmq-console-ng-1.0.1.jar  &

# SpringBoot 集成

# 依赖

1
2
3
4
5
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-spring-boot-starter</artifactId>
<version>2.0.3</version>
</dependency>

# 配置

  • 生产者

    1
    2
    rocketmq.name-server=127.0.0.1:9876
    rocketmq.producer.group=my-group
  • 消费者

    1
    rocketmq.name-server=127.0.0.1:9876

# 编码

  • 生产者

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    @RestController
    public class HelloController {
    @Autowired
    private RocketMQTemplate rocketMQTemplate;
    @RequestMapping("01-hello")
    public String sendMsg(String message,String age) throws Exception{
    //发送消息
    SendResult sendResult = rocketMQTemplate.syncSend("01-boot:", message);

    System.out.println(sendResult.getMsgId());
    System.out.println(sendResult.getSendStatus());
    return "success";
    }
    }
  • 消费者

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    @Component
    @RocketMQMessageListener(
    topic = "01-boot",
    consumerGroup = "wolfcode-consumer"
    )
    public class HelloConsumer implements RocketMQListener<MessageExt> {
    @Override
    public void onMessage(MessageExt messageExt) {
    System.out.println("消费消息"+messageExt);
    }
    }

# 发送消息方式 (生产者)

# 发送类型
  • 同步消息

    1
    2
    3
    SendResult sendResult = rocketMQTemplate.syncSend("020-boot", msg);
    System.out.println(sendResult.getMsgId());
    System.out.println(sendResult.getSendStatus());
  • 异步消息

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    rocketMQTemplate.asyncSend("020-boot", msg, new SendCallback() {
    @Override
    public void onSuccess(SendResult sendResult) {
    System.out.println(sendResult.getMsgId());
    System.out.println(sendResult.getSendStatus());
    }

    @Override
    public void onException(Throwable throwable) {
    System.out.println(throwable);
    }
    });
  • 一次性消息

    1
    rocketMQTemplate.sendOneWay("020-boot", msg);
# 发送时间

默认立即发送

  • 延时发送

    1
    2
    // 参数1:主题 2:消息 3:rocket发送最大允许时间 4:延时级别(18级)
    SendResult sendResult = rocketMQTemplate.syncSend("020-boot", MessageBuilder.withPayload(msg).build(),100000,3);

# 消费模式 (消费者)

以组为单位 默认为集群模式

  • 集群模式 (每组只有一个可以收到)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    @Component
    @RocketMQMessageListener(
    topic = "020-boot",
    messageModel = MessageModel.CLUSTERING,
    consumerGroup = "wolfcode-consumer"
    )
    public class MqListenner implements RocketMQListener<String> {

    @Override
    public void onMessage(String s) {
    System.out.println("今天上映:"+s);
    }
    }
  • 广播模式 (每组的所有消费者都可以收到)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    @Component
    @RocketMQMessageListener(
    topic = "020-boot",
    messageModel = MessageModel.BROADCASTING,
    consumerGroup = "wolfcode-consumer"
    )
    public class MqListenner implements RocketMQListener<String> {

    @Override
    public void onMessage(String s) {
    System.out.println("今天上映:"+s);
    }
    }

# 消息过滤

# Tag 标签模式

在发送的消息 Topic:Tag 中间使用冒号隔开

  • 生产者

    1
    2
    3
    4
    5
    @RequestMapping("/sendTagMsg")
    public String sendTagMsg(String msg) {
    rocketMQTemplate.convertAndSend("020-boot:TagB",msg);
    return "success";
    }
  • 消费者

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    @Component
    @RocketMQMessageListener(
    topic = "020-boot",
    selectorType = SelectorType.TAG,
    //接收TagB或TagA
    secretKey = "TagB || TagA",
    consumerGroup = "wolfcode-consumer"
    )
    public class MqListenner implements RocketMQListener<String> {
    @Override
    public void onMessage(String s) {
    System.out.println("今天上映:"+s);
    }
    }
# SQL92 过滤

注意:在使用 SQL 过滤的时候,需要配置参数 enablePropertyFilter=true

  • 生产者

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    //Sql92过滤
    @RequestMapping("/sendSQLMsg")
    public String sendSQLMsg(int age,String msg) {
    Map<String,Object> map=new HashMap<>();
    //用户自定义属性
    map.put("age", age);
    map.put("name", "hesj");
    //也可以设置系统属性
    map.put(MessageConst.PROPERTY_KEYS,age);
    template.convertAndSend("02-RocketMQ-Top7",msg,map);
    return "success";
    }
  • 消费者

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    @Component
    @RocketMQMessageListener(
    topic = "02-RocketMQ-Top7",
    messageModel = MessageModel.CLUSTERING,
    selectorType = SelectorType.SQL92,
    selectorExpression = "age > 16",
    consumerGroup= "wolfcode-consumer7"
    )
    public class MqListiner7 implements RocketMQListener<String> {
    @Override
    public void onMessage(String msg) {
    System.out.println("消费消息SQl92"+msg);
    }
    }

# 关于我

Brath 是一个热爱技术的 Java 程序猿,公众号「InterviewCoder」定期分享有趣有料的精品原创文章!

InterviewCoder

非常感谢各位人才能看到这里,原创不易,文章如果有帮助可以关注、点赞、分享或评论,这都是对我的莫大支持!

微服务拆分细则

InterviewCoder

​ 在微服务的设计过程中,微服务设计有多大,微服务粒度的把控,一直是设计人员需要考虑和设计的难点。

因为服务粒度设计过大,不能得到微服务架构带来的便利,例如:更加敏态的开发,更频繁的版本发布,由于服务功能划分的小,可以根据实际的业务场景,选择更加合适的技术进行代码重构等等。

​ 但同时我们也要注意,不是服务越” 微 “越好,因为服务的过度拆分会使架构的设计复杂度大大提升,同时也会大大提升运维和测试的复杂度等。

​ 所以对服务拆分粒度的把控,对设计人员来讲就至关重要了,甚至对项目的成败有非常重要的影响。

这篇文档提供了一些主要的微服务拆分原则,供您参考,来帮助您进行更加合理粒度的微服务设计。

# 微服务的拆分原则 - 通用

编号 原则 说明
原则 1 基于业务分析拆分 基于 TOGAF, ADA 等
原则 2 基于 DDD 领域驱动设计中的子域设计拆分 基于领域驱动设计
原则 3 根据动作和用例拆分 比如支付
原则 4 根据名词或者资源拆 比如账号
原则 5 架构稳定 拆分的结构稳定,不会经常修改
原则 6 服务是可测试的 集成测试要可定义,测试可回溯
原则 7 单一原则 一个服务做一个业务, 自己治理自己的数据库
原则 8 开闭原则 面向对象理论, 对扩展开放, 对修改关闭
原则 9 高内聚 强一致,强依赖关系的放在一起, 减少分布式事务
原则 10 低耦合 服务间互相独立
原则 11 足够小的团队可维护,最大两个 pizza team 6-10 人一个 pizza team
原则 12 团队自治,自己的服务的开发和发布要跟别的团队尽可能小的协调

# 微服务的拆分原则 - 技术侧重点

编号 原则 说明
原则 1 潜在风险 服务的风险性
原则 2 资源性能计算性能硬盘性能内存容量网络带宽 机器的性能决定了方案的部分选择
原则 3 安全 安全要求是否很高,安全的策略
原则 4 高并发瞬时并发持续并发 并发的种类, 持续的时间
原则 5 数据库数据量大小读操作写操作数据类型 数据的类型, 读写的多少,数据量

# 关于我

Brath 是一个热爱技术的 Java 程序猿,公众号「InterviewCoder」定期分享有趣有料的精品原创文章!

InterviewCoder

非常感谢各位人才能看到这里,原创不易,文章如果有帮助可以关注、点赞、分享或评论,这都是对我的莫大支持!

阿里云OSS的CDN加速和OSS传输加速的区别

InterviewCoder

# 概述

本文主要介绍阿里云对象存储 OSS 的传输加速功能和 CDN 加速 OSS 资源的区别,以便您根据实际业务进行选择。

# 详细信息

阿里云对象存储 OSS 以海量、安全、低成本、高可靠等特点已经成为用户存储静态资源和文件的首要选择,实际使用中面向全球各地用户访问 OSS 资源时,访问速度会受到客户端网络、OSS 的下行带宽、Bucket 地域、访问链路长等限制出现访问慢的情况。以下主要介绍 CDN 加速 OSS 和 OSS 传输加速的加速方式:

# 实现原理

具体实现加速的原理如下:

  • CDN 加速 OSS:是建立并覆盖在承载网之上,由遍布全球的边缘节点服务器群组成的分布式网络。阿里云 CDN 能分担源站压力,避免网络拥塞,确保在不同区域、不同场景下加速网站内容的分发,提高资源访问速度。由 CDN 全球广泛分布的边缘节点缓存 OSS 存储的静态数据,从而实现客户端从边缘节点直接获取数据的方式来实现访问的加速。
  • OSS 传输加速:利用全球分布的云机房,将全球各地用户对您存储空间(Bucket)的访问,经过智能路由解析至就近的接入点,使用优化后的网络及协议,为云存储互联网的上传、下载提供端到端的加速方案。

# 资源加速场景介绍

OSS 传输加速是针对 OSS 的链路加速,使用 OSS 传输加速后支持 OSS 提供的任意特性。CDN 通过全球边缘节点缓存 OSS 资源,加速同时可降低带宽成本。OSS 传输加速和 CDN 加速完全是两个不同的产品,且应对的场景不同,详情请参见 CDN 应用场景传输加速场景

  • 如果您的业务是第三方数据源加速,推荐您使用 CDN 加速。
  • 如果您的 OSS 资源需要进行多次下载的操作,并且不要求数据强一致性,推荐您使用 CDN 加速。
  • 如果您的 OSS 资源需要加速下载,并且访问量少,推荐您使用 OSS 传输加速。
  • 如果您的 OSS 资源需要进行多次下载的操作,并且要求数据强一致性,推荐您使用 OSS 传输加速。
  • 如果您的业务存储的是动态资源,且数据更新频繁,推荐您使用 OSS 传输加速。
  • 如果您的业务存储的是静态资源,且更新少,推荐您使用 CDN 加速。

# CDN 加速和 OSS 传输加速的对比

CDN 加速和 OSS 传输加速的使用场景不同,其优缺点对比如下:

加速方式 实现方法 应用场景 优点 缺点
CDN 加速 OSS 通过全球分布的边缘节点缓存数据来实现加速。 网站或应用中小文件大文件的下载视音频点播 CDN 边缘节点全球分布,数量多。CDN 节点提供的服务带宽量大。 对于访问量大的资源,命中率高,访问量小的资源命中率低,节点未缓存的情况下,还是需要回源访问,回源依赖实时的公网回源链路。CDN 静态资源的访问,对于上传、删除等动态请求加速效果不明显。
OSS 传输加速 实现的是客户端到 OSS 服务端之间链路优化来实现的加速功能,实际每次资源的请求还是从 OSS 来进行获取。 远距离数据传输加速 GB、TB 级大文件上传和下载非静态、非热点数据下载加速 OSS 存储节点全球主要区域分布。远距离以及大文件的上传和下载加速。 所有的访问都是回源到 OSS 访问,占用 OSS 的服务带宽。同一区域大量用户集中访问资源的情况下,效果没有 CDN 加速效果好。只能使用 HTTPS 方式访问。

#

# 关于我

Brath 是一个热爱技术的 Java 程序猿,公众号「InterviewCoder」定期分享有趣有料的精品原创文章!

InterviewCoder

非常感谢各位人才能看到这里,原创不易,文章如果有帮助可以关注、点赞、分享或评论,这都是对我的莫大支持!

阿里巴巴Java开发手册中的DO、DTO、BO、AO、VO、POJO定义

InterviewCoder

分层领域模型规约:

DO( Data Object):与数据库表结构一一对应,通过 DAO 层向上传输数据源对象。
DTO( Data Transfer Object):数据传输对象,Service 或 Manager 向外传输的对象。
BO( Business Object):业务对象。 由 Service 层输出的封装业务逻辑的对象。
AO( Application Object):应用对象。 在 Web 层与 Service 层之间抽象的复用对象模型,极为贴近展示层,复用度不高。
VO( View Object):显示层对象,通常是 Web 向模板渲染引擎层传输的对象。
POJO( Plain Ordinary Java Object):在本手册中, POJO 专指只有 setter/getter/toString 的简单类,包括 DO/DTO/BO/VO 等。
Query:数据查询对象,各层接收上层的查询请求。 注意超过 2 个参数的查询封装,禁止使用 Map 类来传输。
领域模型命名规约:

数据对象:xxxDO,xxx 即为数据表名。
数据传输对象:xxxDTO,xxx 为业务领域相关的名称。
展示对象:xxxVO,xxx 一般为网页名称。
POJO 是 DO/DTO/BO/VO 的统称,禁止命名成 xxxPOJO。

# 关于我

Brath 是一个热爱技术的 Java 程序猿,公众号「InterviewCoder」定期分享有趣有料的精品原创文章!

InterviewCoder

非常感谢各位人才能看到这里,原创不易,文章如果有帮助可以关注、点赞、分享或评论,这都是对我的莫大支持!

Linux下使用war包部署Jenkins

InterviewCoder

# Linux 下使用 war 包部署 Jenkins

# 前言

jenkins 可以多种方式安装,可以 docker,也可以直接下载 war 包,然后 java -jar 方式启动。

1
2
3
4
5
6
7
8
9
10
1.创建安装目录
mkdir -p /jenkins
2.切换目录
cd /jenkins
3.下载war包
wget -O /jenkins/jenkins.war http://mirrors.jenkins.io/war-stable/latest/jenkins.war
4.启动
BUILD_ID=dontKillMe nohup java -DJENKINS_HOME=/jenkins (如果有历史home可以指定其他home:/root/.jenkins) -Xms1046m -Xmx2000m -jar jenkins.war --httpPort=9444 >>jenkinsLog.log 2>&1 &
5.访问
http://{your ip}:9444

# 备注:

  1. –httpPort 自定义端口
  2. jenkins 默认工作目录:/root/.jenkins
  3. nohup 后台启动
  4. ‘>>log’ 以追加的方式记录日志
  5. 2>&1 2: 标准异常输出 1: 标准输出 ,2>&1 两种输出都记录到 log 文件中
  6. & 后台启动方式
  7. BUILD_ID=dontKillMe 防止误杀包

# 关于我

Brath 是一个热爱技术的 Java 程序猿,公众号「InterviewCoder」定期分享有趣有料的精品原创文章!

InterviewCoder

非常感谢各位人才能看到这里,原创不易,文章如果有帮助可以关注、点赞、分享或评论,这都是对我的莫大支持!

Flutter下加载本地资源GIF,怎么做到每次进入页面都会出现动画效果?

InterviewCoder

1. 问题:Flutter 加载 GIF 之后,只有第一次进入页面才会执行动画,接下来都不会执行了
2. 原因:因为 Flutter 的图片缓存机制,在第一次加载图片后,会将图片缓存下来,所以再次访问,你看见的还是上次的已经执行完毕的动画
3. 解决:在 dispose 中把 imageCache 用 clear 方法清理掉

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
String asset = "images/401.gif";
Widget img = Image.asset("images/401.gif",key: UniqueKey(),);

@override
void initState() {
super.initState();
//初始化
toast();
}

@override
void dispose() {
super.dispose();
//清理缓存
imageCache!.clear();
}

void toast() {
DialogUtils.showErrorMessage("401",
gravity: ToastGravity.CENTER, toastLength: Toast.LENGTH_SHORT);

Future.delayed(Duration(seconds: 3), () {
DialogUtils.showMessage("您没有权限访问......",
gravity: ToastGravity.CENTER, toastLength: Toast.LENGTH_LONG);
});
}

# 关于我

Brath 是一个热爱技术的 Java 程序猿,公众号「InterviewCoder」定期分享有趣有料的精品原创文章!

InterviewCoder

非常感谢各位人才能看到这里,原创不易,文章如果有帮助可以关注、点赞、分享或评论,这都是对我的莫大支持!

【VsCode】VsCode中的HTML嵌套注释、多行注释、多级注释,提高生产力

InterviewCoder

# 【VsCode】VsCode 中的 HTML 嵌套注释、多行注释、多级注释,提高生产力

# HTML 嵌套注释插件:HTML-Comment

# 概述

相信我,这是市面上最好用的 HTML 注释插件,因为这是我体验过很多主流插件后才开发出的工具。

很多人在使用 html 的注释嵌套使用时都会发现并不能达到我们想到的效果,正常情况以下注释会报错:

正常注释效果

原生的方法是不支持这种嵌套注释的,只能使用变种方法进行多级注释,方便起见做了个注释插件,使用注释插件注释效果如下:

插件效果注释

# 使用方法(放开注释同下)

Ctrl+Shift+/

目前改插件只支持 vscode,后续会开发 WebStorm 版本

# 修改按键快捷方式

打开 vscode,找到左上角文件 -> 首选项 -> 键盘快捷方式,输入 HTML-Comment,可以查看并修改按键。

在这里插入图片描述

# 支持列表

适用于 htm、html、asp、cfm、jsx、md、njk、php、svelte、svg、tsx、twig、vue、xml、xsl 等格式文件中任意 <!-- <tag></tag> --> 风格的代码块。

目前该插件只支持 vscode,后续会开发 WebStorm 版本

# 关于我

Brath 是一个热爱技术的 Java 程序猿,公众号「InterviewCoder」定期分享有趣有料的精品原创文章!

InterviewCoder

非常感谢各位人才能看到这里,原创不易,文章如果有帮助可以关注、点赞、分享或评论,这都是对我的莫大支持!

redis主从集群-哨兵模式搭建文档

InterviewCoder

准备工作

  • # 三台服务器

服务器 ip 角色
192.168.20.1
192.168.20.2
192.168.20.3
  1. # docker 安装 redis

docker pull redis

  1. # 运行

分别从三台服务器运行 redis 镜像,注意映射不同外端口

1
docker run -p 6380:6379 --name redis -v /mydata/redis/data/redis.conf:/etc/redis/redis.conf  -v /mydata/redis/data:/data -d redis redis-server /etc/redis/redis.conf --appendonly yes --daemonize no --masterauth #如果主节点设置了密码,请输入主服务密码

docker exec -it redis_master /bin/bash

  1. # 查看角色

# 主:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
127.0.0.1:6379> auth #你的密码
OK
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:0
master_failover_state:no-failover
master_replid:f28e9097e4c8cd3f67292181be12955909afd88e
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
127.0.0.1:6379>

# 从 01:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:0
master_failover_state:no-failover
master_replid:48a7e3866afc4b6784ef49353b57fbc979ee2935
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
127.0.0.1:6379>

# 从 02:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:0
master_failover_state:no-failover
master_replid:de30809041f22f2dd6abc9cb34536f26df97e647
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
127.0.0.1:6379>
  1. # 在两台从服务器上执行命令 replicaof 192.168.20.1 6379

1
2
3
127.0.0.1:6379> replicaof 192.168.20.1 6379
OK
127.0.0.1:6379>
  1. # 查看主服务信息

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
127.0.0.1:6379> info replication
# Replication
role:master

###从服务器信息
connected_slaves:2
slave0:ip=172.17.0.3,port=6379,state=online,offset=1456,lag=1
slave1:ip=172.17.0.4,port=6379,state=online,offset=1456,lag=0
###从服务器信息

master_failover_state:no-failover
master_replid:9b8c2ef4539809505fa5bc1dd779c4500298011d
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:1456
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:1456
127.0.0.1:6379>


127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:0
master_failover_state:no-failover
master_replid:f28e9097e4c8cd3f67292181be12955909afd88e
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
127.0.0.1:6379>

  1. # 测试:

主服务器上:

1
2
3
127.0.0.1:6379> set name zs
OK
127.0.0.1:6379>

# 从服务器也可以查看到信息

1
2
3
4
5
127.0.0.1:6379> replicaof 192.168.20.1 6379
OK
127.0.0.1:6379> get name
"zs"
127.0.0.1:6379>

# 异常统计:

可能遇到的 BUG:MASTER aborted replication with an error: NOAUTH Authentication required.

场景:在配置主从后发现两个从节点的 info replication 中出现:master_link_status:down

原因:大部分原因是因为主节点配置了密码

解决:在从节点的配置文件中加入 masterauth 你的主节点密码 即可

# 关于我

Brath 是一个热爱技术的 Java 程序猿,公众号「InterviewCoder」定期分享有趣有料的精品原创文章!

InterviewCoder

非常感谢各位人才能看到这里,原创不易,文章如果有帮助可以关注、点赞、分享或评论,这都是对我的莫大支持!

Docker安装Jenkins

InterviewCoder

# Docker 安装 Jenkins

# 一、前言

# 1、领头羊

1
作为领先的开源自动化服务器,Jenkins 提供了数百个插件来支持构建、部署和自动化任何项目。

# 2、特点

  • 持续集成和持续交付:作为可扩展的自动化服务器,Jenkins 可以用作简单的 CI 服务器或变成任何项目的持续交付中心。
  • 简易安装:Jenkins 是一个独立的基于 Java 的程序,可以开箱即用,包含适用于 Windows、Linux、macOS 和其他类 Unix 操作系统的软件包。
  • 易于配置:Jenkins 可以通过其 Web 界面轻松设置和配置,其中包括即时错误检查和内置帮助。
  • 插件:凭借更新中心的数百个插件,Jenkins 与持续集成和持续交付工具链中的几乎所有工具集成。
  • 可扩展:Jenkins 可以通过其插件架构进行扩展,为 Jenkins 可以做的事情提供几乎无限的可能性。
  • 分散式:Jenkins 可以轻松地在多台机器上分配工作,帮助更快地跨多个平台推动构建、测试和部署。

# 二、Docker 安装 Jenkins

# 1、docker search jenkins 查询镜像

# 1.1、正常查询结果

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
[root@localhost ~]# docker search jenkins
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
jenkins DEPRECATED; use "jenkins/jenkins:lts" instead 5504 [OK]
jenkins/jenkins The leading open source automation server 3087
jenkins/jnlp-slave a Jenkins agent which can connect to Jenkins… 150 [OK]
jenkins/inbound-agent 65
bitnami/jenkins Bitnami Docker Image for Jenkins 53 [OK]
jenkins/slave base image for a Jenkins Agent, which includ… 48 [OK]
jenkins/agent 39
jenkins/ssh-slave A Jenkins slave using SSH to establish conne… 38 [OK]
jenkins/ssh-agent Docker image for Jenkins agents connected ov… 24
jenkins/jnlp-agent-docker 8
jenkins/jnlp-agent-maven A JNLP-based agent with Maven 3 built in 7
jenkins/pct Plugin Compat Tester 5 [OK]
jenkins/jenkins-experimental Experimental images of Jenkins. These images… 3 [OK]
jenkins/jnlp-agent-python A JNLP-based agent with Python built in 3
jenkins/jnlp-agent-alpine 2
jenkins/jnlp-agent-node 1
rancher/jenkins-jenkins 1
jenkins/ath Jenkins Acceptance Test Harness 1 [OK]
jenkins/core-changelog-generator Tool for generating Jenkins core changelogs 1
jenkins/jenkinsfile-runner Jenkinsfile Runner packages 1
jenkins/core-pr-tester Docker image for testing pull-requests sent … 1
jenkins/jnlp-agent-ruby 1
jenkins/remoting-kafka-agent Remoting Kafka Agent 1 [OK]
rancher/jenkins-jnlp-slave 0
rancher/jenkins-slave Jenkins Build Slave 0 [OK]
[root@localhost ~]# ^C
[root@localhost ~]#

# 1.2、可能异常情况,这个异常解决方法为下面第 2 点

img

# 2、上面报这个 ERROR 解决方法

# 2.1、更新时间同步即可:ntpdate cn.pool.ntp.org

# 2.2、如果提示不存在 ntpdate 命令需要先安装该命令:yum install ntpdate

# 2.3、date 中国时间

img

# 3、****docker pull jenkinsci/blueocean**** 拉取 Jenkins 镜像

img

# 4、docker images 查看本地镜像

img

# 5、CentOS7 安装 JDK 安装 已有 JDK 可以跳过

# 5.1、可以下载 linux 版本 tar.gz 压缩包到本地不用解压

img

# 5.2、****cd /usr, mkdir java**** 进入 usr 创建 java 文件夹

# 5.3、****cd java* 进入 java 文件夹,用 * rz**** 将 linux 版的 jdk 压缩包上传到这里

img

# 5.4、(将 JDK 移到 java,mv jdk-8u301-linux-x64.tar.gz/usr/java) 移动文件命令

# 5.5、*tar* *-**zxvf* jdk-8u301-linux-x64.tar.gz,解压会有 jdk1.8.0_301 出现

img

# 5.6、****vi /etc/profile**** 配置 linux 系统 JDK 环境变量

1) 配置内容

1
2
export JAVA_HOME=/usr/java/jdk1.8.0_301
export JRE_HOME=${JAVA_HOME}/jre

# 5.7、****source /etc/profile**** 使配置生效

# 5.8、*sudo yum install glibc.i686*,可能报错解决方案、否则会会报找不到

# 5.9、****java -version**** 测试,出现如下即为成功

img

# 6、CentOS7 安装 Maven

# 6.1、*cd /usr/local*

# 6.2、****rz* 上传,*tar -zxvf apache-maven-3.6.1-bin.tar.gz**** 解压

img

# 6.3、*vi /etc/profile*

# 6.4、****source /etc/profile**** 刷新环境变量

# 6.5、****mvn -v**** 查看版本

1
2
3
4
5
6
7
[root@localhost local]# mvn -v
Apache Maven 3.6.3 (cecedd343002696d0abb50b32b541b8a6ba2883f)
Maven home: /usr/local/apache-maven-3.6.3
Java version: 1.8.0_301, vendor: Oracle Corporation, runtime: /usr/java/jdk1.8.0_301/jre
Default locale: en_US, platform encoding: UTF-8
OS name: "linux", version: "3.10.0-1127.el7.x86_64", arch: "amd64", family: "unix"
[root@localhost local]#

到此 JDK、Maven 环境准备完成

# 7、* 启动容器,并 ** 挂载上面配置的环境 *

1
2
3
4
5
6
7
8
9
docker run \
-u root \
-d \
--restart=always \
-p 8001:8080 \
-p 50000:50000 \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /var/jenkins_home:/var/jenkins_home \
jenkinsci/blueocean

img

# 9、访问 Jenkins,提示输入密码

img

# 10、初次可以选择推荐的

img

# 11、 等待安装

img

# 12、如下访问 Jenkins 成功啦

img

到此就结束 Docker 安装 Jenkins 啦,后面的章节将介绍如何配置 jenkins,敬请期待!

# 关于我

Brath 是一个热爱技术的 Java 程序猿,公众号「InterviewCoder」定期分享有趣有料的精品原创文章!

InterviewCoder

非常感谢各位人才能看到这里,原创不易,文章如果有帮助可以关注、点赞、分享或评论,这都是对我的莫大支持!

【Docker】Docker下安装Canal并整合SpringBoot

InterviewCoder

# Canal 是一个同步增量数据的一个工具

# 目录

# 概念

canal 是阿里巴巴旗下的一款开源项目,纯 Java 开发。基于数据库增量日志解析,提供增量数据订阅 & 消费,目前主要支持了 MySQL(也支持 mariaDB)

# Mysql 开启 binlog

在部署 Canal 之前,需要先安装 Mysql。

我用的是 5.7.27 的 mysql

# 是否开启 binlog

输入以下命令,查看是否开启 binlog
OFF 则表示未开启 binlog

1
show variables like 'log_bin';

在这里插入图片描述

# 开启 binlog 日志

修改 mysql 的配置文件,在 [mysqld] 下添加以下内容

1
2
3
4
5
6
# server_id不重复即可,不要和canal的slaveId重复
server_id=1
# 开启binlog
log_bin = mysql-bin
# 选择row模式
binlog_format = ROW

修改完毕,重启 mysql
查看是否开启
在这里插入图片描述

# 创建授权用户

创建授权用户 canal 用于 cannal 服务监听 mysql 的 binlog

1
2
3
4
5
6
# 新建用户 用户名:canal  密码:canal 
CREATE USER canal IDENTIFIED by 'canal';
# 授权
GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'canal'@'%';
# 刷新MySQL的系统权限相关表
FLUSH PRIVILEGES;

# 部署 Canal

如果没有部署过 docker,看我之前写的 Linux 部署 Docker

# 拉取镜像

1
docker pull canal/canal-server:latest

# 挂载 properties 配置文件

先进行第一次运行,拷贝 properties 配置文件

1
docker run -p 11111:11111 --name canal -d canal/canal-server:latest

拷贝运行后的容器中配置文件,用来文件挂载

1
2
3
4
5
6
# 创建canal宿主机挂载目录
mkdir -p /opt/canal/conf
# 查看docker运行情况,复制容器id
docker ps
# 拷贝配置文件
docker cp 容器id:/home/admin/canal-server/conf/example/instance.properties /opt/canal/conf/

移除当前容器

1
2
docker stop canal
docker rm canal

修改配置文件
在这里插入图片描述

# 创建容器

运行新的容器,同时挂载修改后的配置文件

1
docker run -p 11111:11111 --name canal -v /opt/canal/conf/instance.properties:/home/admin/canal-server/conf/example/instance.properties -d canal/canal-server:latest

开放端口

1
firewall-cmd --zone=public --add-port=11111/tcp --permanent && firewall-cmd --reload

# 关于我

Brath 是一个热爱技术的 Java 程序猿,公众号「InterviewCoder」定期分享有趣有料的精品原创文章!

InterviewCoder

非常感谢各位人才能看到这里,原创不易,文章如果有帮助可以关注、点赞、分享或评论,这都是对我的莫大支持!

Knife4J接口文档

InterviewCoder

Swagger2 及 knife4j 使用

最近项目中用到了 Swagger2 和 knife4j 作为接口文档。所以自己简单搭建了一套环境学习下,总体体验下来,这个框架很方便也很简单易用。

Swagger2 和 Swagger-ui
springFox 官方推荐的是 Swagger2 和 Swagger-ui 配套使用

maven 依赖


io.springfox
springfox-swagger2
2.9.2



io.springfox
springfox-swagger-ui
2.9.2

增加配置文件
@Configuration
@EnableSwagger2
public class Swagger2 {

@Bean
public Docket controllerApi() {
    return new Docket(DocumentationType.SWAGGER_2)
            .apiInfo(new ApiInfoBuilder()
                    .title("文档说明--API接口文档")
                    .description("包括保存、查询等")
                    .version("版本号:1.0")
                    .build())
            .select()
            .apis(RequestHandlerSelectors.basePackage("com.example.myswagger.controller"))
            .paths(PathSelectors.any()) // 如果适配所有api,可以改为PathSelectors.any()
            .build();
}

API 接口增加 swagger 注解
@Controller
@RequestMapping
@Api (tags = “接口服务”)
public class HelloController {

@ApiOperation("根目录")
@GetMapping("/")
@ResponseBody
public  String hello(){
    System.out.println("23123");
    return "hello";
}


@ApiOperation("保存用户信息")
@ApiImplicitParams({
        @ApiImplicitParam(name = "name", value = "名字", required = true, paramType = "path"),
        @ApiImplicitParam(name = "age", dataType = "int", value = "年龄", required = true, paramType = "query")
})
@PostMapping("/save")
@ResponseBody
public Boolean save(
        @RequestParam("name") String name,
        @RequestParam("age") Integer age
) {
    return true;
}

效果展示
启动项目访问

http://localhost:8080/swagger-ui.html

测试接口:

项目地址
https://gitee.com/LylYorick/myswagger

swagger 注解详解
@Api:用在请求的类上,表示对类的说明
tags=“说明该类的作用,可以在 UI 界面上看到的注解”
value=“该参数没什么意义,在 UI 界面上也看到,所以不需要配置”

@ApiOperation:用在请求的方法上,说明方法的用途、作用
value=“说明方法的用途、作用”
notes=“方法的备注说明”

@ApiImplicitParams:用在请求的方法上,表示一组参数说明
@ApiImplicitParam:用在 @ApiImplicitParams 注解中,指定一个请求参数的各个方面
name:参数名
value:参数的汉字说明、解释
required:参数是否必须传
paramType:参数放在哪个地方
・header --> 请求参数的获取:@RequestHeader
・query --> 请求参数的获取:@RequestParam
・path(用于 restful 接口)–> 请求参数的获取:@PathVariable
・body(不常用)
・form(不常用)
dataType:参数类型,默认 String,其它值 dataType=“Integer”
defaultValue:参数的默认值

@ApiResponses:用在请求的方法上,表示一组响应
@ApiResponse:用在 @ApiResponses 中,一般用于表达一个错误的响应信息
code:数字,例如 400
message:信息,例如 "请求参数没填好"
response:抛出异常的类

@ApiModel:用于响应类上,表示一个返回响应数据的信息
(这种一般用在 post 创建的时候,使用 @RequestBody 这样的场景,
请求参数无法使用 @ApiImplicitParam 注解进行描述的时候)
@ApiModelProperty:用在属性上,描述响应类的属性
Swagger2 和 knife4j
虽然 Swagger-ui 很好,但是国人还是开发了一个 knife4j 的的 swaggerui,更加好看和方便使用

maven 依赖

1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>2.0.8</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.10.5</version>
</dependency>

增加配置类
@Configuration
@EnableSwagger2WebMvc
public class Knife4jConfiguration {

@Bean(value = "defaultApi2")
public Docket defaultApi2() {
    Docket docket=new Docket(DocumentationType.SWAGGER_2)
            .apiInfo(new ApiInfoBuilder()
                    //.title("swagger-bootstrap-ui-demo RESTful APIs")
                    .description("# swagger-bootstrap-ui-demo RESTful APIs")
                    .termsOfServiceUrl("http://www.xx.com/")
                    .contact("xx@qq.com")
                    .version("1.0")
                    .build())
            //分组名称
            .groupName("2.X版本")
            .select()
            //这里指定Controller扫描包路径
            .apis(RequestHandlerSelectors.basePackage("com.example.myswaggerknife4j.controller"))
            .paths(PathSelectors.any())
            .build();
    return docket;
}

API 接口增加 swagger 注解
@Controller
public class HelloController {

@PostMapping("/save")
@ResponseBody
public Boolean save(
        @RequestParam("name") String name,
        @RequestParam("age") Integer age
) {
    return true;
}

效果展示
访问 http://localhost:8080/doc.html

项目地址
https://gitee.com/LylYorick/my-swagger-knife4j

# 关于我

Brath 是一个热爱技术的 Java 程序猿,公众号「InterviewCoder」定期分享有趣有料的精品原创文章!

InterviewCoder

非常感谢各位人才能看到这里,原创不易,文章如果有帮助可以关注、点赞、分享或评论,这都是对我的莫大支持!