一直有将前台改写成vue的打算,最近刚把项目样子搭出来,同学们可以来看下支持下点点star,未来计划去整合~
仓库地址
在线演示
yum install java-1.8.0-openjdk.x86_64
java -version
vi /etc/profile
#set java environment
JAVA_HOME=/usr/lib/jvm/jre-1.8.0-openjdk
PATH=$PATH:$JAVA_HOME/bin
CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
export JAVA_HOME CLASSPATH PATH
- 卸载系统之前的docker
sudo yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine
- 设置存储库
sudo yum install -y yum-utils
sudo yum-config-manager \
--add-repo \
https://download.docker.com/linux/centos/docker-ce.repo
- 安装DOCKER引擎
sudo yum install docker-ce docker-ce-cli containerd.io
- 启动Docker.
sudo systemctl start docker
- 配置镜像加速
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://ozz4irqv.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
- 开机自启
sudo docker update redis --restart=always
- 拉取 mysql镜像
sudo docker pull mysql:8.0
- 启动mysql容器
# --name指定容器名字 -v目录挂载(左主右从) -p指定端口映射 -e设置mysql参数 -d后台运行
sudo docker run --name mysql -v /usr/local/mysql/data:/var/lib/mysql -v /usr/local/mysql:/etc/mysql/conf.d -v /usr/local/mysql/log:/var/log/mysql -e MYSQL_ROOT_PASSWORD=root -p 3306:3306 -d mysql:8.0
- 进入mysql容器
docker exec -it a4435a23e7a470297fded7fbdeb1a06045530e1631517585f490c680e4039891 bin/bash
- 开机自启
sudo docker update mysql --restart=always
- 拉取redis镜像到本地
docker pull redis
- 修改需要自定义的配置(docker-redis默认没有配置文件,自己在宿主机建立后挂载映射)
位置在 /usr/local/redis/redis.conf
#开启远程权限
bind 0.0.0.0
#开启aof持久化
appendonly yes
- 启动redis服务运行容器
docker run --name redis -v /usr/local/redis/data:/data -v /usr/local/redis/redis.conf:/usr/local/etc/redis/redis.conf -p 6379:6379 -d redis redis-server /usr/local/etc/redis/redis.conf
- 连接 redis
docker exec -it redis redis-cli
# 终极版!
docker run -p 80:80 --name nginx \
-v /usr/local/nginx/html:/usr/share/nginx/html \
-v /usr/local/nginx/conf/nginx.conf/:/etc/nginx/nginx.conf \
-v /usr/local/nginx/logs:/var/log/nginx \
-d nginx
docker run -p 80:80 --name nginx \
-v /Users/june/AServerMiddleware/nginx-docker/html:/usr/share/nginx/html \
-v /Users/june/AServerMiddleware/nginx-docker/conf/nginx.conf:/etc/nginx/nginx.conf \
-v /Users/june/AServerMiddleware/nginx-docker/conf/default.conf:/etc/nginx/conf.d/default.conf \
-v /Users/june/AServerMiddleware/nginx-docker/logs:/var/log/nginx -d nginx
Docker系列之RabbitMQ安装部署教程 - 云+社区 - 腾讯云 (tencent.com)
docker run -d --name rabbitmq -p 5671:5671 -p 5672:5672 -p 4369:4369 -p 25672:25672 -p 15671:15671 -p 15672:15672 -e RABBITMQ_DEFAULT_USER=june -e RABBITMQ_DEFAULT_PASS=L200107208017./@ rabbitmq:management
# 4369 25672 Erlang发现&集群端口
# 5671 5672 AMQP 端口
# 15672 web管理后台端口
# 61613 61614 STOMP协议端口
# 1883 8883 MQTT 协议端口
https://www.jianshu.com/p/3d3e17bc629f
SQL位置
# 1.创建本地配置文件
mkdir -p /home/nacos/logs/ #新建logs目录
mkdir -p /home/nacos/init.d/
vim /home/nacos/init.d/custom.properties #修改配置文件
# 2.添加如下内容
server.contextPath=/nacos
server.servlet.contextPath=/nacos
server.port=8848
spring.datasource.platform=mysql
db.num=1
db.url.0=jdbc:mysql://sh-cdb-0ej7ogfe.sql.tencentcdb.com:58887/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
db.user=root
db.password=L200107208017@
nacos.cmdb.dumpTaskInterval=3600
nacos.cmdb.eventTaskInterval=10
nacos.cmdb.labelTaskInterval=300
nacos.cmdb.loadDataAtStart=false
management.metrics.export.elastic.enabled=false
management.metrics.export.influx.enabled=false
server.tomcat.accesslog.enabled=true
server.tomcat.accesslog.pattern=%h %l %u %t "%r" %s %b %D %{
User-Agent}i
nacos.security.ignore.urls=/,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-fe/public/**,/v1/auth/login,/v1/console/health/**,/v1/cs/**,/v1/ns/**,/v1/cmdb/**,/actuator/**,/v1/console/server/**
nacos.naming.distro.taskDispatchThreadCount=1
nacos.naming.distro.taskDispatchPeriod=200
nacos.naming.distro.batchSyncKeyCount=1000
nacos.naming.distro.initDataRatio=0.9
nacos.naming.distro.syncRetryDelay=5000
nacos.naming.data.warmup=true
nacos.naming.expireInstance=true
# 3.启动容器
docker run \
--name nacos -d \
-p 8848:8848 \
--privileged=true \
--restart=always \
-e JVM_XMS=256m \
-e JVM_XMX=256m \
-e MODE=standalone \
-e PREFER_HOST_MODE=hostname \
-v /home/nacos/logs:/home/nacos/logs \
-v /home/nacos/init.d/custom.properties:/home/nacos/init.d/custom.properties \
nacos/nacos-server:1.4.2
Linux直接部署
# 注意,不要同时在开发机器部署微服务,云服务器部署sentinel,因为sentinel也是需要访问本机的!
java -Dserver.port=8858 -Dcsp.sentinel.dashboard.server=localhost:8858 -Dproject.name=sentinel-dashboard -Dsentinel.dashboard.auth.username=sentinel-qs -Dsentinel.dashboard.auth.password=L200107208017@ -jar sentinel-dashboard-1.8.1.jar
本机部署
nohup java -server -Xms64m -Xmx256m -Dserver.port=8858 -Dcsp.sentinel.dashboard.server=localhost:8858 -Dproject.name=sentinel-dashboard -jar /Users/june/AServerMiddleware/sentinel/sentinel-dashboard-1.8.1.jar
zipkin 使用外部 MySQL 持久化存储 - 简书 (jianshu.com)
docker run -d -p 9411:9411 openzipkin/zipkin:latest
-e JAVA_OPTS=-Xmx128m \
docker run \
--name zipkin-server -d \
--restart=always \
-p 9411:9411 \
-e MYSQL_USER=root \
-e MYSQL_PASS=L200107208017@ \
-e MYSQL_HOST=sh-cdb-0ej7ogfe.sql.tencentcdb.com \
-e STORAGE_TYPE=mysql \
-e MYSQL_DB=zipkin \
-e MYSQL_TCP_PORT=58887 \
openzipkin/zipkin
git config --global user.name "June"
git config --global user.email "[email protected]"
ssh-keygen -t rsa -C "[email protected]"
# 查看生成的密钥内容
cat ~/.ssh/id_rsa.pub
# 复制密钥内容到 gitee,以后该机器就推送内容不用输入密码
# 测试
ssh -T [email protected]
# gitignore 中添加以下内容
**/mvnw
**/mvnw.cmd
**/.mvn
**/target
.idea
**/.gitignore
**/README.md
- 官网下载 node.js 附带有 npm
- 配置 npm 镜像
npm config set registry http://registry.npm.taobao.org/
利用这个模块给每一个业务模块生成代码,以 product 模块为例
SpringCloud Alibaba - Nacos (服务发现/注册)
SpringCloud Alibaba - Nacos (动态配置管理)
SpringCloud Alibaba - Seata (分布式事务解决方案)
SpringCloud Alibaba - Sentinel (限流、降级、熔断等)
SpringCloud - Ribbon (负载均衡)
SpringCloud - Feign 远程调用服务
SpringCloud - Gateway (API网关)
SpringCloud - Sleuth (调用链监控)
这块配不好,踩坑少不了
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-alibaba-dependenciesartifactId>
<version>2.1.0.RELEASEversion>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.3.1.RELEASEversion>
<relativePath/>
parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>Hoxton.SR6version>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
dependency>
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
@EnableDiscoveryClient
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-openfeignartifactId>
dependency>
@EnableFeignClients(basePackages = "org.june.member.feign")
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-alibaba-nacos-configartifactId>
dependency>
bootstrap.properties
指定配置中心位置以及自己的服务名称spring.application.name=mall-coupon
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
服务名.properties
配置文件,这个文件可以动态读取@RefreshScope
,使用 @Value("${name.age}")
(在成员变量处)方式获取注:
命名空间的说明
# 追加在 bootstrap.properties 中
spring.cloud.nacos.config.namespace=3ce35e9e-4e10-44df-b1a4-8fd753c3e4ea
配置中心的说明
# 追加在 bootstrap.properties 中
spring.cloud.nacos.config.group=ddd
类似于上面的命名空间,都是用来隔离文件的
spring.application.name=mall-coupon
# 服务注册/发现
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
# 配置中心 这两项可以只配 第二个
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
spring.cloud.nacos.config.namespace=2863031e-ae6a-4bff-903e-48d27201e1ab
spring.cloud.nacos.config.ext-config[0].data-id=datasource.yml
spring.cloud.nacos.config.ext-config[0].group=dev
spring.cloud.nacos.config.ext-config[0].refresh=true
spring.cloud.nacos.config.ext-config[1].data-id=mybatis.yml
spring.cloud.nacos.config.ext-config[1].group=dev
spring.cloud.nacos.config.ext-config[1].refresh=true
spring.cloud.nacos.config.ext-config[2].data-id=others.yml
spring.cloud.nacos.config.ext-config[2].group=dev
spring.cloud.nacos.config.ext-config[2].refresh=true
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-gatewayartifactId>
dependency>
注:SpringBoot 2.3.1.RELEASE
对应 SpringCloud Hoxton.SR6
不会报错
Spring Cloud Gateway 2.1.0 中文官网文档 - 云+社区 - 腾讯云 (tencent.com)
ECMAScript 是浏览器脚本语言的规范,JavaScript 是该规范的具体实现。以下示例以 JavaScript 为例
let arr = [1,2,3]
// ↓
let [a,b,c] = arr
/
const person = {
name: "June",
age: 21
}
// ↓
const {
name:var1,age:var2} = person;
console.log(var1,var2)
let str = "helloworld"
str.startsWith()
str.endsWith()
str.includes()
let var1 = "June"
let var2 = "March"
let ss = `${
var1}
this is a test
${
var2}`
console.log(ss)
function test1(a,b){
...
}
test(var1) // 只传一个
///
function test2(...vars){
...
}
test(var1,var2) // 传多个
var print = function(obj){
console.log(obj)
}
// ↓
var print = obj => console.log(obj)
/ 对象解构
var person = {
name:"jack",
age:21
}
var hello = ({
name}) => console.log("hello," + name)
var person = {
name:"jack",
age:21
}
Object.keys(person) -> ["name","age"]
Object.values(person) -> ["jack",21]
Object.entries(person) -> [Array(2),Array(2),Array(2)]
// 追加
const target = {
a:1}
const source1 = {
b:2}
const source2 = {
c:3}
Object.assign(target, source1, source2)
target -> {
a:1, b:2, c:3}
// 对象简写1
const age = 21
const name = "张三"
const person = {
age,name}
// 对象简写2
let person = {
name: "jack",
eat: function(food){
console.log(this.name + "在吃" + food)
},
eat2: food => console.log(person.name + "在吃" + food)
}
/// 拷贝对象(深拷贝)
let p1 = {
name: "Any", age:15}
let p2 = {
...p1}
/// 对象合并(会覆盖)
let age = {
age:15}
let name = {
name: "Amy"}
let person = {
...age,...name}
let arr = ['1', '2', '3', '4']
arr.map(item)=>{
return item*2
} // => [2,4,6,8]
// ↓
arr = arr.map(item=>item*2)
let result = arr.reduce((a,b)=>{
return a+b
},100) // => 110
/ js1.js
var name = "jack"
var age = 21
function add(a,b){
return a + b
}
export{
name,age,add}
// js2.js
import {
name,age,add} from "./js1.js"
add(1,2)
文本值绑定
{
{ name }}
属性值绑定,一般用于 href、class、style
<a v-bind:href:"link">gogogoa>
你好
双向绑定,不同于以上两者
事件绑定
遍历
v-if 条件为 true,元素才会被渲染;v-show 条件为true,元素才会显示
前者是注释掉了相关代码,后者把样式改变了
{
{totalPrice}}
sudo npm install webpack -g
npm install -g @vue/cli-init
sudo npm install --global vue-cli
# 再创建项目文件夹
vue init webpack vue-demo
cd vue-demo
npm run dev
MySQL的库
在Index中,可以定义一个或多个类型,类似于MySQL中的表;每一种类型的数据放在一起;
保存在某个索引下,某种类型的一个数据,文档是JSON格式的,Document就像是MySQL中某个Table里面的内容
# Docker !8版本需要额外配置东西
docker pull elasticsearch:7.17.0
docker pull kibana:7.17.0
# 创建
mkdir -p /Users/june/AServerMiddleware/elsatic-docker/plugins
mkdir -p /Users/june/AServerMiddleware/elsatic-docker/config
mkdir -p /Users/june/AServerMiddleware/elsatic-docker/data
#
echo "http.host: 0.0.0.0" >/Users/june/AServerMiddleware/elsatic-docker/config/elasticsearch.yml
#
chmod -R 777 /Users/june/AServerMiddleware/elsatic-docker
# 启动Elastic search
# 9200是用户交互端口 9300是集群心跳端口
# -e指定是单阶段运行
# -e指定占用的内存大小,生产时可以设置32G
sudo docker run --name elasticsearch -p 9200:9200 -p 9300:9300 \
-e "discovery.type=single-node" \
-e ES_JAVA_OPTS="-Xms64m -Xmx512m" \
-v /Users/june/AServerMiddleware/elsatic-docker/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml \
-v /Users/june/AServerMiddleware/elsatic-docker/data:/usr/share/elasticsearch/data \
-v /Users/june/AServerMiddleware/elsatic-docker/plugins:/usr/share/elasticsearch/plugins \
-d elasticsearch:7.17.0
sudo docker run --name kibana -e ELASTICSEARCH_HOSTS=http://124.222.22.217:9200 -p 5601:5601 -d kibana:7.17.0
# 配置中文
docker cp 源 目的 # 容器写法 容器ID:路径 主机直接写路径
docker exec -it ID /bin/bash
kibana.yml 中添加 i18n.locale: "zh-CN"
GET /_cat/nodes 查看所有节点
GET /_cat/health 查看es健康状况
GET /_cat/master 查看主节点
GET /_cat/indices 查看所有索引 show databases
注:http://124.222.22.217:9200/customer/external/1
其中的 ‘1’ 指定了id,PUT 请求必须携带id;而 POST 可以不指定 id,不指定id,会自动生成id,指定id会对其进行修改(不存在则新增)
GET customer/external/1
精确根据ID查找
GET customer/_search
查询所有
GET customer/_search 条件查询
{
"query":{"match_all":{}},
"sort":[
{"account_number":"asc"}
],
"from":10,
"size":10
}
http://124.222.22.217:9200/customer/external/1?if_seq_no=0&if_primary_term=1 # 修改配合并发使用
POST携带JSON(带上doc) http://124.222.22.217:9200/customer/external/1/_update # 会检查前后更新内容是否一致,其余方式如PUT、POST(不带_update)都不会对比内容
DELETE http://124.222.22.217:9200/customer/external/1
POST custmoer/external/_bulk
{"index":{"_id":1}}
{"name":"Jone"}
{"index":{"_id":"2"}}
{"name":"Jane"}
# 回车必要
ES支持两种基本方式减缩:
# 样例
GET bank/_search?q=*&sort=account_number:asc
-------
GET bank/_search
{
"query":{
"match_all":{
}
},
"sort":[{
"account_number":"asc"
},{
"balance":"desc"
}
]
}
请求体语法格式
{
QUERY_NAME{
ARGUMENT:VALUE,
ARGUMENT:VALUE
}
}
# 一级参数
query 指定查询操作
sort 指定排序字段
from 分页操作
size 分页操作
_source 指定查询字段
# 二级参数
query:match { key:value } 非字符串值模糊查询(按相关度-score排序);数字则精确匹配
query:match_phrase 类似于前者,但不会对字符串进行分词,而是当做一条短语进行匹配
query:multi_match 分词 + 多字段匹配
query:bool 构造复杂查询 must must_not should(可以提高得分)
query:filter 不计算相关性得分,直接过滤
query:term term是代表完全匹配,即不进行分词器分析,文档中必须包含整个搜索的词汇;全文检索字段用 match ,其他 text 字段用term
query:aggregations 字段聚合处理
指定索引下的属性类型
那么如何修改?
elastic已经不推荐使用 type
安装ik分词器
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
public Map<String, List<Catalog2Vo>> getCatalogJson(){
String catalogJSON = redis.opsForValue().get("catalogJson");
if(StringUtils.isEmpty(catalogJSON)){
Map<String, List<Catalog2Vo>> catalogJsonFromDB = getCatalogJsonFromDB();
redis.opsForValue().set("catalogJson",
JSON.toJSONString(catalogJsonFromDB));
}
Map<String, List<Catalog2Vo>> list = JSON.parseObject(catalogJSON,
new TypeReference<Map<String, List<Catalog2Vo>>>(){
});
return list;
}
该段代码存在分布式锁的问题
依托于redis的 set catalog_lock lockId [ex seconds][px millseconds] nx
命令实现
public Map<String, List<Catalog2Vo>> getCatalogJson() {
// double check
String catalogJSON = redis.opsForValue().get("catalogJson");
if (StringUtils.isEmpty(catalogJSON)) {
// 分布式加锁 ↓
String lockId = UUID.randomUUID().toString();
// set catalog_lock lockId [ex seconds][px millseconds] nx
if (Boolean.TRUE.equals(redis.opsForValue().
setIfAbsent("catalog_lock", lockId, 300L, TimeUnit.SECONDS))) {
// log.error("redis成功加锁!!!");
// 分布式加锁 ↑
//业务执行开始//
try {
Map<String, List<Catalog2Vo>> catalogJsonFromDB = getCatalogJsonFromDB();
redis.opsForValue().set("catalogJson",
JSON.toJSONString(catalogJsonFromDB),
1, TimeUnit.DAYS);
} finally {
//业务执行结束,勿忘删除//
// 防止业务执行时间过长,导致删除操作实际上删除的是别人的锁
// 但是这两步骤并不是原子操作,获取值进行比较的时候可能锁已经过期
// 所以需要采用 lua 脚本来保证原子性
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
redis.execute(new DefaultRedisScript<>(script, Long.class), Collections.singletonList("lock"), lockId);
// log.error("redis成功删锁!!!");
}
} else {
// log.error("等待锁!!");
try {
// 防止空转
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
return getCatalogJson();
}
}
return JSON.parseObject(catalogJSON,
new TypeReference<Map<String, List<Catalog2Vo>>>() {
});
}
<dependency>
<groupId>org.redissongroupId>
<artifactId>redissonartifactId>
<version>3.16.8version>
dependency>
@Configuration
public class RedissonConfig {
@Bean
RedissonClient redisson(){
Config config = new Config();
config.useSingleServer()
.setAddress("redis://127.0.0.1:6379")
.setPassword("L200107208017@./");
return Redisson.create(config);
}
}
加锁示例
计数器,正计时
计数器,倒计时
/**
* 普通锁实现
*/
public Map<String, List<Catalog2Vo>> getCatalogJson() {
// 双重检查
String catalogJson = redis.opsForValue().get("catalogJson");
Map<String, List<Catalog2Vo>> catalogJsonFromDB;
if (StringUtils.isEmpty(catalogJson)) {
// lock
RLock catalogLock = redisson.getLock("catalogJsonLock");
catalogLock.lock();
try {
// 双重检查
catalogJson = redis.opsForValue().get("catalogJson");
if (StringUtils.isNotEmpty(catalogJson)) {
return JSON.parseObject(catalogJson,
new TypeReference<Map<String, List<Catalog2Vo>>>() {
});
}
catalogJsonFromDB = getCatalogJsonFromDB();
} finally {
catalogLock.unlock();
}
return catalogJsonFromDB;
}else{
return JSON.parseObject(catalogJson,
new TypeReference<Map<String, List<Catalog2Vo>>>() {
});
}
}
/**
* 读写锁实现
*/
public Map<String, List<Catalog2Vo>> getCatalogJson() {
RReadWriteLock readWriteLock