JoyLau's Blog

JoyLau 的技术学习与思考

  1. docker pull registry

  2. docker run -itd -v /data/registry:/var/lib/registry -p 5000:5000 –restart=always –privileged=true –name registry registry:latest
    参数说明
    -itd:在容器中打开一个伪终端进行交互操作,并在后台运行;
    -v:把宿主机的/data/registry目录绑定 到 容器/var/lib/registry目录(这个目录是registry容器中存放镜像文件的目录),来实现数据的持久化;
    -p:映射端口;访问宿主机的5000端口就访问到registry容器的服务了;
    –restart=always:这是重启的策略,假如这个容器异常退出会自动重启容器;
    –privileged=true 在CentOS7中的安全模块selinux把权限禁掉了,参数给容器加特权,不加上传镜像会报权限错误OSError: [Errno 13] Permission denied: ‘/tmp/registry/repositories/liibrary’)或者(Received unexpected HTTP status: 500 Internal Server Error)错误
    –name registry:创建容器命名为registry,你可以随便命名;
    registry:latest:这个是刚才pull下来的镜像;

  3. 测试是否成功: curl http://127.0.0.1:5000/v2/_catalog, 返回仓库的镜像列表

  4. 在中央仓库下载一个镜像: docker pull openjdk

  5. 更改这个镜像的标签: docker tag imageId domain:5000/openjdk 或者 docker tag imageName:tag domain:5000/openjdk

  6. 上传镜像到私服: docker push domain:5000/openjdk

报错: Get https://172.18.18.90:5000/v2/: http: server gave HTTP response to HTTPS client

解决: 需要https的方法才能上传,我们可以修改下daemon.json
vim /etc/docker/daemon.json
{
“insecure-registries”: [ “domain:5000”]
}

无网络搭建

  1. 在有网络的机器上 docker pull registry
  2. docker save registry > registry.tar 保存到个 tar 包
  3. 拷贝到服务器上, docker load -i registry.tar 导入镜像
  4. docker images 查看镜像
  5. 再继续上面的操作

docker 开启 tcp 端口

  • vim /usr/lib/systemd/system/docker.service

修改

1
ExecStart=/usr/bin/dockerd-current -H tcp://0.0.0.0:2375 -H unix://var/run/docker.sock \

重启即可,之后 idea 可输入 tcp://ip:2375 连接

允许跨域请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
version: 0.1
log:
fields:
service: registry
storage:
cache:
blobdescriptor: inmemory
filesystem:
rootdirectory: /var/lib/registry
http:
addr: :5000
headers:
X-Content-Type-Options: [nosniff]
Access-Control-Allow-Headers: ['Origin,Accept,Content-Type,Authorization']
Access-Control-Allow-Origin: ['*']
Access-Control-Allow-Methods: ['GET,POST,PUT,DELETE']
health:
storagedriver:
enabled: true
interval: 10s
threshold: 3

head 添加

1
2
3
Access-Control-Allow-Headers: ['Origin,Accept,Content-Type,Authorization']
Access-Control-Allow-Origin: ['*']
Access-Control-Allow-Methods: ['GET,POST,PUT,DELETE']

之后保存到本地,再挂载到容器的 /etc/docker/registry/config.yml 中

Harbor 搭建 Docker 私服

上述方式搭建的 docker 私服,属于比较简单使用的方法,只能在命令行上操作,很不方便,比如不能直接删除镜像,无法添加用户,设置私有仓库
Harbor 是一个图形化的私服管理界面,安装使用更易于操作

Harbor是一个用于存储和分发Docker镜像的企业级Registry服务器,通过添加一些企业必需的功能特性,例如安全、标识和管理等,扩展了开源Docker Distribution。作为一个企业级私有Registry服务器,Harbor提供了更好的性能和安全。提升用户使用Registry构建和运行环境传输镜像的效率。

  1. 下载离线包: https://github.com/goharbor/harbor/releases
  2. 解压
  3. 更改配置文件 docker-compose.yml 私服的仓库端口我们默认设置为 5000,但是 docker-compose.yml 文件中并没有配置,我们需要添加一个 ports 配置
1
2
3
4
5
registry:
networks:
- harbor
ports:
- 5000:5000
  1. Harbor 默认使用的是 80 端口,不想使用的话可切换其他端口, 配置在 docker-compose.yml 的最下方
1
2
3
4
5
6
proxy:
image: goharbor/nginx-photon:v1.7.0
ports:
- 9339:80
- 443:443
- 4443:4443

此处需要注意的是,如果更改了其他端口,则需要在 common/templates/registry/config.yml 文件中更改一个配置 realm 加上端口,否则登录会出现错误

1
2
3
4
5
6
auth:
token:
issuer: harbor-token-issuer
realm: $public_url:9339/service/token
rootcertbundle: /etc/registry/root.crt
service: harbor-registry
  1. 修改配置文件 harbor.cfg
1
2
3
hostname = 34.0.7.183 ## 改为 IP 或者 域名,不要写错 localhost 或者 127.0.0.1
ui_url_protocol = http ## http 方式
harbor_admin_password = Hardor12345 ## admin 账号的默认登录密码
  1. ./prepare 完成配置

  2. ./install.sh 开始安装

  3. 打开浏览器

  4. 创建一个项目 joylau 注意这个名称很重要,名称对不上的话,会造成 image push 不成功,还有就是若果这个项目的是公开的话,则所有人都可以 pull ,但是 push 的话是需要登录的,登录的用户名和密码在该项目的成员下.默认的 admin 用户就可以

  5. 登录,退出命令 docker login 34.0.7.183:5000 ; docker logout 34.0.7.183:5000

  6. 之后的操作都是日常操作了

Docker Registry 添加认证

生成用户名密码

1
docker run --rm --entrypoint htpasswd registry -Bbn username password > ./htpasswd

假设将生成的文件放到 /registry/pwd/htpasswd

挂载用户名密码文件

-v /registry/pwd:/auth -e “REGISTRY_AUTH=htpasswd” -e “REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm” -e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd

至此,添加用户名密码完成,现在 pull 和 push 都需要登录

Docker login 密码存储

默认存储位置为: $HOME/.docker/config.json
很不安全, 使用 base64 解密即可看到用户名密码

将密码存储到钥匙串:

  1. 下载工具: https://github.com/docker/docker-credential-helpers/releases
  2. 将 docker-credential-osxkeychain 配置到 path 路径
  3. 配置 config.json
1
2
3
{
"credsStore": "osxkeychain"
}

说明

ownCloud 除了传统的部署方式,在如今 docker 大行其道的环境下,使用 docker 部署 ownCloud 才是最方便的

第一种 owncloud 镜像直接安装

直接部署 owncloud 镜像,该镜像地址: https://hub.docker.com/r/_/owncloud/

1
2
docker pull owncloud
docker run -d -p 80:80 owncloud

这种方式的需要你提前装好 MariaDb 数据库,在启动完成后打开页面会按照流程填写数据库的链接信息,之后就可以使用 ownCloud 了

第二种 分别安装

分别先后使用 docker 按照 redis,mariadb,ownCloud
安装 redis 的 mariadb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
docker volume create owncloud_redis

docker run -d \
--name redis \
-e REDIS_DATABASES=1 \
--volume owncloud_redis:/var/lib/redis \
webhippie/redis:latest

docker volume create owncloud_mysql
docker volume create owncloud_backup

docker run -d \
--name mariadb \
-e MARIADB_ROOT_PASSWORD=owncloud \
-e MARIADB_USERNAME=owncloud \
-e MARIADB_PASSWORD=owncloud \
-e MARIADB_DATABASE=owncloud \
--volume owncloud_mysql:/var/lib/mysql \
--volume owncloud_backup:/var/lib/backup \
webhippie/mariadb:latest

接着我们配置一些 ownCloud web 服务的环境变量,并在启动容器时使用这些变量

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
export OWNCLOUD_VERSION=10.0
export OWNCLOUD_DOMAIN=localhost
export ADMIN_USERNAME=admin
export ADMIN_PASSWORD=admin
export HTTP_PORT=80

docker volume create owncloud_files

docker run -d \
--name owncloud \
--link mariadb:db \
--link redis:redis \
-p ${HTTP_PORT}:8080 \
-e OWNCLOUD_DOMAIN=${OWNCLOUD_DOMAIN} \
-e OWNCLOUD_DB_TYPE=mysql \
-e OWNCLOUD_DB_NAME=owncloud \
-e OWNCLOUD_DB_USERNAME=owncloud \
-e OWNCLOUD_DB_PASSWORD=owncloud \
-e OWNCLOUD_DB_HOST=db \
-e OWNCLOUD_ADMIN_USERNAME=${ADMIN_USERNAME} \
-e OWNCLOUD_ADMIN_PASSWORD=${ADMIN_PASSWORD} \
-e OWNCLOUD_REDIS_ENABLED=true \
-e OWNCLOUD_REDIS_HOST=redis \
--volume owncloud_files:/mnt/data \
owncloud/server:${OWNCLOUD_VERSION}

之后稍等片刻,打开网页即可

第三种 docker-compose 部署

首先保证 docker-compose 的版本在 1.12.0+

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 创建一个新的目录
mkdir owncloud-docker-server

cd owncloud-docker-server

# 下载 docker-compose.yml 文件
wget https://raw.githubusercontent.com/owncloud-docker/server/master/docker-compose.yml

# 配置环境变量文件
cat << EOF > .env
OWNCLOUD_VERSION=10.0
OWNCLOUD_DOMAIN=localhost
ADMIN_USERNAME=admin
ADMIN_PASSWORD=admin
HTTP_PORT=80
HTTPS_PORT=443
EOF

# 构建并启动容器
docker-compose up -d

当上面的流程都完成时,通过运行 docker-compose ps 检查所有容器是否已成功启动
还可以使用 docker-compose logs --follow owncloud 来查看日志
docker-compose stop 停止容器
docker-compose down 停止和删除容器

版本更新

  1. 进入 .yaml 或 .env 目录
  2. 将 ownCloud 设置维护模式, docker-compose exec server occ maintenance:mode --on
  3. 停止容器, docker-compose down
  4. 修改. env 文件的版本号,手动或者 sed -i 's/^OWNCLOUD_VERSION=.*$/OWNCLOUD_VERSION=<newVersion>/' /compose/*/.env
  5. 重新构建并启动, docker-compose up -d

指定挂载目录

  1. owncloud-server : /mnt/data

注意挂载本地目录时,要设置递归文件夹的可读权限 chmod -R 777 ./owncloud/*

配置说明
OWNCLOUD_VERSION: ownCloud 版本
OWNCLOUD_DOMAIN: ownCloud 可访问的域
ADMIN_USERNAME: 管理员用户名
ADMIN_PASSWORD: 管理员密码
HTTP_PORT: 使用的端口
HTTPS_PORT: SSL使用的端口

总结来说,推荐使用第三种方式来部署.

docker-compose 文件备份

docker-compose.yml:

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
version: '2.1'

volumes:
files:
driver: local
mysql:
driver: local
backup:
driver: local
redis:
driver: local

services:
owncloud:
image: owncloud/server:${OWNCLOUD_VERSION}
restart: always
container_name: owncloud-server
ports:
- ${HTTP_PORT}:8080
depends_on:
- db
- redis
environment:
- OWNCLOUD_DOMAIN=${OWNCLOUD_DOMAIN}
- OWNCLOUD_DB_TYPE=mysql
- OWNCLOUD_DB_NAME=owncloud
- OWNCLOUD_DB_USERNAME=owncloud
- OWNCLOUD_DB_PASSWORD=owncloud
- OWNCLOUD_DB_HOST=db
- OWNCLOUD_ADMIN_USERNAME=${ADMIN_USERNAME}
- OWNCLOUD_ADMIN_PASSWORD=${ADMIN_PASSWORD}
- OWNCLOUD_MYSQL_UTF8MB4=true
- OWNCLOUD_REDIS_ENABLED=true
- OWNCLOUD_REDIS_HOST=redis
healthcheck:
test: ["CMD", "/usr/bin/healthcheck"]
interval: 30s
timeout: 10s
retries: 5
volumes:
- /home/liufa/owncloud-data:/mnt/data

db:
image: webhippie/mariadb:latest
restart: always
container_name: owncloud-mysql
environment:
- MARIADB_ROOT_PASSWORD=owncloud
- MARIADB_USERNAME=owncloud
- MARIADB_PASSWORD=owncloud
- MARIADB_DATABASE=owncloud
- MARIADB_MAX_ALLOWED_PACKET=128M
- MARIADB_INNODB_LOG_FILE_SIZE=64M
healthcheck:
test: ["CMD", "/usr/bin/healthcheck"]
interval: 30s
timeout: 10s
retries: 5
volumes:
- /home/liufa/owncloud-mysql:/var/lib/mysql
- /home/liufa/owncloud-mysql-backup:/var/lib/backup

redis:
image: webhippie/redis:latest
container_name: owncloud-redis
restart: always
environment:
- REDIS_DATABASES=1
healthcheck:
test: ["CMD", "/usr/bin/healthcheck"]
interval: 30s
timeout: 10s
retries: 5
volumes:
- /home/liufa/owncloud-redis:/var/lib/redis

.env:

1
2
3
4
5
6
OWNCLOUD_VERSION=10.0
OWNCLOUD_DOMAIN=0.0.0.0
ADMIN_USERNAME=admin
ADMIN_PASSWORD=
HTTP_PORT=1194
HTTPS_PORT=443

nginx 反向代理时的配置

注意配置 请求头 和 限制上传文件的大小

1
2
3
4
5
6
7
8
9
10
11
12
server {
listen 80;
#listen [::]:80 default_server;
server_name cloud.joylau.cn;
location / {
# proxy_pass http://JoyCloud;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $http_host;
proxy_pass http://127.0.0.1:1194;
client_max_body_size 10000m;
}
}

背景

我们经常能看到在各大电商网站搜索关键字的时候,底下下拉框会补全你要搜索的商品,或者类似的商品,有时候甚至连错别字也能纠正过来,其实ElasticSearch也能实现这样的功能

创建索引

首先,能够被自动补全的需要设置索引类型为”completion”,其次,还可以设置自动提示为中文分词

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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
{
"settings": {
"analysis": {
"analyzer": {
"ik": {
"tokenizer": "ik_max_word"
},
"ngram_analyzer": {
"tokenizer": "ngram_tokenizer"
}
},
"tokenizer": {
"ngram_tokenizer": {
"type": "ngram",
"min_gram": 1,
"max_gram": 30,
"token_chars": [
"letter",
"digit"
]
}
}
}
},
"mappings": {
"knowledge_info": {
"properties": {
"infoId": {
"type": "string"
},
"infoTitle": {
"type": "string",
"search_analyzer": "ik_max_word",
"analyzer": "ik_max_word",
"fields": {
"suggest": {
"max_input_length": 30,
"preserve_position_increments": false,
"type": "completion",
"preserve_separators": false,
"analyzer": "ik_max_word"
},
"wordCloud": {
"type": "string",
"analyzer": "ik_smart",
"fielddata":"true"
}
}
},
"infoKeywords": {
"type": "string",
"search_analyzer": "ik_max_word",
"analyzer": "ik_max_word",
"fields": {
"suggest": {
"max_input_length": 30,
"preserve_position_increments": false,
"type": "completion",
"preserve_separators": false,
"analyzer": "ik_max_word"
},
"wordCloud": {
"type": "string",
"analyzer": "ik_smart",
"fielddata":"true"
}
}
},
"infoSummary": {
"type": "string",
"search_analyzer": "ik_max_word",
"analyzer": "ik_max_word",
"fields": {
"suggest": {
"max_input_length": 30,
"preserve_position_increments": false,
"type": "completion",
"preserve_separators": false,
"analyzer": "ik_max_word"
},
"wordCloud": {
"type": "string",
"analyzer": "ik_smart",
"fielddata":"true"
}
}
},
"infoContent": {
"type": "text",
"search_analyzer": "ik_max_word",
"analyzer": "ik_max_word"
},
"propertyAuthor": {
"type": "string",
"search_analyzer": "ik_max_word",
"analyzer": "ik_max_word"
},
"propertyIssueUnit": {
"type": "string",
"search_analyzer": "ik_max_word",
"analyzer": "ik_max_word"
},
"propertyStandardCode": {
"type": "string",
"search_analyzer": "ik_max_word",
"analyzer": "ik_max_word"
},
"propertyLiteratureCategory": {
"type": "string",
"search_analyzer": "ik_max_word",
"analyzer": "ik_max_word"
},
"propertyLcCode": {
"type": "string",
"search_analyzer": "ik_max_word",
"analyzer": "ik_max_word"
},
"propertyLiteratureCode": {
"type": "string",
"search_analyzer": "ik_max_word",
"analyzer": "ik_max_word"
},
"data": {
"type": "text"
},
"attachment.content": {
"type": "text",
"search_analyzer": "ik_max_word",
"analyzer": "ik_max_word"
},
"auditState": {
"type": "string"
},
"infoType": {
"type": "string"
},
"infoFileUrl": {
"type": "string"
},
"infoFileName": {
"type": "string",
"search_analyzer": "ik_max_word",
"analyzer": "ik_max_word",
"fields": {
"suggest": {
"max_input_length": 60,
"preserve_position_increments": false,
"type": "completion",
"preserve_separators": false,
"analyzer": "ik_max_word"
}
}
},
"createTime": {
"type": "string"
}
}
}
}
}

其中 elasticsearch 需要安装中文分词 ik 插件和附件处理插件 ingest-attachment

Java API 调用

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
/**
* 自动完成提示
* @param search search
* @return MessageBody
*/
public MessageBody autoCompleteKnowledgeInfo(KnowledgeSearch search) {
//设置搜索建议
CompletionSuggestionBuilder infoTitleSuggestion = new CompletionSuggestionBuilder("infoTitle.suggest")
.text(search.getQuery())
.size(6);
CompletionSuggestionBuilder infoKeywordsSuggestion = new CompletionSuggestionBuilder("infoKeywords.suggest")
.text(search.getQuery())
.size(6);
CompletionSuggestionBuilder infoSummarySuggestion = new CompletionSuggestionBuilder("infoSummary.suggest")
.text(search.getQuery())
.size(6);
CompletionSuggestionBuilder infoFileNameSuggestion = new CompletionSuggestionBuilder("infoFileName.suggest")
.text(search.getQuery())
.size(6);
SuggestBuilder suggestBuilder = new SuggestBuilder()
.addSuggestion("标题", infoTitleSuggestion)
.addSuggestion("关键字", infoKeywordsSuggestion)
.addSuggestion("摘要", infoSummarySuggestion)
.addSuggestion("附件",infoFileNameSuggestion);
SearchRequestBuilder searchRequest = client.prepareSearch(ES_KNOWLEDGE_INDEX)
.setFetchSource(false)
.suggest(suggestBuilder);
List<JSONObject> list = new ArrayList<>();

//查询结果
SearchResponse searchResponse = searchRequest.get();

/*没查到结果*/
if (searchResponse.getSuggest() == null) {
return MessageBody.success(list);
}
searchResponse.getSuggest().forEach(entries -> {
String name = entries.getName();
for (Suggest.Suggestion.Entry<? extends Suggest.Suggestion.Entry.Option> entry : entries) {
for (Suggest.Suggestion.Entry.Option option : entry.getOptions()) {
JSONObject object = new JSONObject();
object.put("name",name);
object.put("text",option.getText().string());
list.add(object);
}
}
});
return MessageBody.success(list);
}

代码摘取自项目中的部分, 另外前端还可以配合自动完成的插件,最终来实现效果.

背景

有时我们希望查询 固定条件下的全部数据
ES 默认的策略是返回10条数据
虽然可以 setSize()
但是默认上限是 10 万还是 100 万条数据,这不够优雅,一般不这么干

TransportClient 方法

1
2
3
4
5
6
7
8
9
10
TimeValue keepAlive = TimeValue.timeValueMinutes(30);
SearchRequestBuilder searchRequest = client.prepareSearch(ES_KNOWLEDGE_INDEX)
.setScroll(keepAlive)
.setSize(10000);
SearchResponse searchResponse = searchRequest.get();
do {
//处理的业务 saveIds(searchResponse);
searchResponse = client.prepareSearchScroll(searchResponse.getScrollId()).setScroll(keepAlive).execute()
.actionGet();
} while (searchResponse.getHits().getHits().length != 0);

背景

因项目需求,需要一个自动提示的功能,想到之前有 jquery 的 jQuery-Autocomplete 插件,于是就直接拿来用了,
直接在github 上找到了一个 starts 最多的项目 jQuery-Autocomplete
看了下插件的 API 可配置项很多,有一个 appendTo 配置,是我想要的,于是就决定使用这个差价

直接把 插件下载下来 放到项目中去,直接 $(…).autocomplete is not a function
……

项目中我写的只是其中的一个模块,页面的代码是纯 html 页面写的,然后通过 panel 引入 html 代码片段
很奇怪,为什么插件无法加载

于是就就把官方的demo跑了一下,没有问题

又怀疑是 jQuery 版本的问题,
官方的demo jQuery 版本是 1.8.2,项目使用的是1.11.1,
于是又在官方的 demo 下替换jQuery的版本
发现使用没有问题

又怀疑是插件的版本过高,于是再 GitHub 的 release 上找了个2014年发布的1.2.2的版本,这已经是能找到的最低版本了
发现还是不行

这就奇怪了,我之前也引入过其他的插件,正常使用都没有问题,偏偏使用这个有问题
于是想着插件的引入方式有问题,打开一看,jQuery插件的引入方式都是大同小异的
本人前端不擅长,也不知道怎么改…..

于是又在 GitHub上找了其他的插件,有的能用,但是没有我想要的功能….

一直这么来来回回的测试,已经晚上 10 点了…..
从吃完晚饭一直研究到现在还是没有解决
心里好气啊!!!!!
空调一关,直接回家了!!!!

解决

今天早上来又差了点资料,找到了个不太靠谱,但又想尝试了下的方法
TypeError: $(…).autocomplete is not a function

试一下吧,没想到真的可以

发一张对比图

query-Load-Plugins

背景

这一段时间 GitHub 在国内的访问又出问题,代码提交不上去,需要在 Git 上走代理了

Git 使用 ss 代理配置

  1. 需要全局 git 都走代理
1
2
git config --global http.proxy 'socks5://127.0.0.1:1080'
git config --global https.proxy 'socks5://127.0.0.1:1080'

取消

1
2
git config --global --unset http.proxy
git config --global --unset https.proxy

但是有时候我们并不需要所有的 git 仓库都走代理,可以去掉上述的命令中的 --global,然后到你需要走代理的那个 git 仓库下执行命令,或者添加配置:

  1. 单独配置 git 走代理
    在 .git => config 文件中加入配置
1
2
3
4
[https]
proxy = socks5://127.0.0.1:1080
[http]
proxy = socks5://127.0.0.1:1080

其实,也就是上述命令执行后添加的配置.配置后就可以愉快的 clone push 了.

Ubuntu 使用全局代理

Windows 和 MacOS 下的 ss 全局代理很方便,点击切换下就可以了,而 Ubuntu 下需要多点操作:

  1. 启动 shadowsocks-qt5,并连接上
  2. 生成 pac 文件,如果有现成的 pac 文件,直接进入第四步
  3. 生成 pac 文件

安装 pip

1
2
$ sudo pip install genpac
$ pip install -U genpac ## 安装或更新

创建 user-rules.txt 文件

1
2
3
mkdir vpnPAC
cd vpnPAC
touch user-rules.txt

生成 autoproxy.pac 文件

1
genpac --format=pac --pac-proxy="SOCKS5 127.0.0.1:1080" --output="autoproxy2.pac" --gfwlist-url="https://raw.githubusercontent.com/gfwlist/gfwlist/master/gfwlist.txt" --user-rule-from="user-rules.txt"

github 上的 gfwlist.txt 文件可能读取不到,多试几次

  1. 配置使用

配置使用

背景

昨天和别人吃饭谈起了家里宽带的问题,办了多少兆的宽带,网速能有多少等云云,对方是个小白,和他说了半天,但是有些深层次的原理我也弄不清楚,后来我上网科普了一下,现在整理如下

什么是宽带速率?

宽带速率是指技术上所能达到的理论最高信息传送比特率,一般是上传和下载的速度,速率越高,上传和下载的越快。用户申请的宽带业务速率指技术上所能达到的最大理论速率值。但用户上网时还受到用户电脑软硬件的配置、所浏览网站的地址、终端网站带宽等情况的影响。因此,用户上网时的速率通常低于理论速率值。

理论上,2M,即2Mb/s,宽带理论速率是 256KB/S。实际速率大约为103–200KB/S。(其原因是受用户计算机性能、网络设备质量、资源使用情况、网络高峰期、网站服务能力、线路衰耗、信号衰减等多因素的影响而造成的)。4M,即4Mb/s宽带理论速率是 512KB/S 实际速率大约为200—440KB/S。

计算方法

在计算机科学中,bit是表示信息的最小单位,叫做二进制位;一般用0和1表示。Byte叫做字节,由8个位(8bit)组成一个字节(1Byte),用于表示计算机中的一个字符。bit与Byte之间可以进行换算,其换算关系为:1Byte=8bit(或简写为:1B=8b);在实际应用中一般用简称,即1bit简写为1b(注意是小写英文字母b),1Byte简写为1B(注意是大写英文字母B)。

在计算机网络或者是网络运营商中,一般,宽带速率的单位用bps(或b/s)表示;bps表示比特每秒即表示每秒钟传输多少位信息,是bit per second的缩写。在实际所说的1M带宽的意思是1Mbps(是兆比特每秒Mbps不是兆字节每秒MBps)。

换算公式:1B=8b 1B/s=8b/s(或1Bps=8bps)

规范提示:实际书写规范中B应表示Byte(字节),b应表示bit(比特),但在平时的实际书写中有的把bit和Byte都混写为b ,如把Mb/s和MB/s都混写为Mb/s,导致人们在实际计算中因单位的混淆而出错。

实例: 在我们实际上网应用中,下载软件时常常看到诸如下载速度显示为128KBps(KB/s),103KB/s等等宽带速率大小字样,因为ISP提供的线路带宽使用的单位是比特,而一般下载软件显示的是字节(1字节=8比特),所以要通过换算,才能得实际值。然而我们可以按照换算公式换算一下:

1Mb/s = 1024Kb/s = 128×8(Kb/s) = 128KB/s

即 1Mb/s = 128KB/s

宽带速率对照表

常见宽带 理论最高速率(Mbps) 理论最高速率(KB/S) 常见下载速率(供参考)
1M 1 Mbps 128 KB/S 77~128 KB/S
2M 2 Mbps 256 KB/S 154~256 KB/S
3M 3 Mbps 384 KB/S 231~384 KB/S
4M 4 Mbps 512 KB/S 307~512 KB/S
6M 6 Mbps 620 KB/S 462~620 KB/S
8M 8 Mbps 1024 KB/S 614~1024 KB/S
10M 10 Mbps 1280 KB/S 768~1280 KB/S
12M 12 Mbps 1536 KB/S 922~1536 KB/S
20M 20 Mbps 2560 KB/S 1536~2560 KB/S
30M 30 Mbps 3840 KB/S 2560~3840 KB/S
50M 50 Mbps 6400 KB/S 3840~6400 KB/S
100M 100 Mbps 12800 KB/S 7680~12800 KB/S

背景

最近维护了一个比较老的项目,操作数据库直接用的 Spring 的 JdbcTemplate,有很多地方我们传入的参数都是不确定的
简单的还好,复杂的 sql 语句在代码里用字符串拼接起来简直不能忍,
又不想对原来的项目有什么大的改动,就想这能不能在现在的基础上优化一下
还好有 NamedParameterJdbcTemplate

解释

具名参数: SQL 按名称(以冒号开头)而不是按位置进行指定. 具名参数更易于维护, 也提升了可读性. 具名参数由框架类在运行时用占位符取代
具名参数只在 NamedParameterJdbcTemplate 中得到支持。NamedParameterJdbcTemplate可以使用全部jdbcTemplate方法

初始化

  1. 该类位于 org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate
  2. 有2个构造方法,参数分别是 DataSource 和 JdbcOperations
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* Create a new NamedParameterJdbcTemplate for the given {@link DataSource}.
* <p>Creates a classic Spring {@link org.springframework.jdbc.core.JdbcTemplate} and wraps it.
* @param dataSource the JDBC DataSource to access
*/
public NamedParameterJdbcTemplate(DataSource dataSource) {
Assert.notNull(dataSource, "DataSource must not be null");
this.classicJdbcTemplate = new JdbcTemplate(dataSource);
}

/**
* Create a new NamedParameterJdbcTemplate for the given classic
* Spring {@link org.springframework.jdbc.core.JdbcTemplate}.
* @param classicJdbcTemplate the classic Spring JdbcTemplate to wrap
*/
public NamedParameterJdbcTemplate(JdbcOperations classicJdbcTemplate) {
Assert.notNull(classicJdbcTemplate, "JdbcTemplate must not be null");
this.classicJdbcTemplate = classicJdbcTemplate;
}
  1. 实例化 bean 只要将 dataSource 或者 JdbcTemplate 传入到构造参数即可
1
2
3
4
<bean id="namedParameterJdbcTemplate"
class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
<constructor-arg ref="dataSource"/>
</bean>

使用

  1. 注入 namedParameterJdbcTemplate

参数的传入

namedParameterJdbcTemplate 参数传入有 2 中方法:

  1. Map<String, ?> paramMap 我们熟知的 map

  2. SqlParameterSource paramSource
    该接口默认的实现有三个类:

    MapSqlParameterSource 实现非常简单,只是封装了java.util.Map;
    当 Map<String, ?> paramMap 用吧 或者 new MapSqlParameterSource(paramMap) 参数为 map

    BeanPropertySqlParameterSource 封装了一个JavaBean对象,通过JavaBean对象属性来决定命名参数的值。
    new BeanPropertySqlParameterSource(dto) new 出一个 BeanPropertySqlParameterSource 对象,构造方法传入实体类即可,绝大部分情况下我们都使用这种方式

    EmptySqlParameterSource 一个空的SqlParameterSource ,常用来占位使用
    没用过

数据返回

  1. 返回 Map

  2. 返回 RowMapper 包装好的实体类,该类有2中实现
    SingleColumnRowMapper ,sql结果为一个单列的数据,如List , List,String,Integer等

    BeanPropertyRowMapper, sql结果匹配到对象 List< XxxVO> , XxxVO

示例

1
2
3
4
5
6
7
KnowledgeInfo info = new KnowledgeInfo();
info.setAuditState("1");
List<KnowledgeInfo> infos = namedParameterJdbcTemplate.query(
sql,
new BeanPropertySqlParameterSource(info),
new BeanPropertyRowMapper<>(KnowledgeInfo.class)
);

注意: sql 语句中的参数使用 :参数名 进行占位

0%