0%

需求:需要把关键字传入到pdf预览界面,并高亮展示。

这里使用的是:

1
2
var url = 'http://127.0.0.1:8080/file/test.txt'; //要预览文件的访问地址
window.open('http://127.0.0.1:8012/onlinePreview?url='+encodeURIComponent(base64Encode(url)));

修改代码:

1、修改 onlinePreview 接口

1
2
3
4
5
6
7
8
9
10
11
12
public class OnlinePreviewController {
@GetMapping( "/onlinePreview")
// 添加 keyword 接收高亮关键字
public String onlinePreview(String url, String keyword, Model model, HttpServletRequest req) {
// *************
model.addAttribute("file", fileAttribute);
// 高亮关键字返回给前端
model.addAttribute("keyword", keyword);
// *************
return filePreview.filePreviewHandle(fileUrl, model, fileAttribute);
}
}

2、前端接收参数,并且高亮

打开 server\src\main\resources\static\pdfjs\web\viewer.js 文件,

找到 getViewerConfiguration() 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function getViewerConfiguration() {
let errorWrapper = null;
errorWrapper = {
container: document.getElementById("errorWrapper"),
errorMessage: document.getElementById("errorMessage"),
closeButton: document.getElementById("errorClose"),
errorMoreInfo: document.getElementById("errorMoreInfo"),
moreInfoButton: document.getElementById("errorShowMore"),
lessInfoButton: document.getElementById("errorShowLess")
};
// 获取关键字
const queryString = document.location.search.slice(1); //new
const m = /(^|&)keyword=([^&]*)/.exec(queryString);//new
const keyword = m ? decodeURIComponent(m[2]) : "";//new
if(keyword){//new
// 讲关键字放入输入框中,当前 id 可以在 viewer.html 中找到
document.getElementById("findInput").value = keyword;//new
}//new
}

找到 PDFViewerApplication对象中的 _initializeViewerComponents方法,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
   if (!this.supportsIntegratedFind) {
this.findBar = new _pdf_find_bar.PDFFindBar(appConfig.findBar, eventBus, this.l10n);
// 添加打开查询框的操作
if (PDFViewerApplication.findBar.opened) {//new
PDFViewerApplication.findBar.open();//new
} else {//new
console.log('PDFViewerApplication.findBar.opened');//new
PDFViewerApplication.findBar.opened = false;//new
PDFViewerApplication.findBar.open();//new
}//new
// 执行高亮的操作
const highLightStr = appConfig.findBar.findField.value;//new
PDFViewerApplication.findController.executeCommand("find", {query:highLightStr, phraseSearch: false, caseSensitive: false, highlightAll: true, findPrevious: false,});//new
}

3、使前端修改生效

一个比较简单的方式,在 viewer.js 同目录下 viewer.js.map 中直接修改。

因为 viewer.js.map 中的代码为字符串,所以我们需要精确的找到我们修改的位置,把修改的内容复制进去。

本文地址: https://github.com/maxzhao-it/blog/post/d10ffa56/

依赖

1
2
3
4
5
<dependency>
<groupId>org.eclipse.paho</groupId>
<artifactId>org.eclipse.paho.client.mqttv3</artifactId>
<version>1.2.5</version>
</dependency>

代码

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
161
162
163
164
165
166
167
168
169
170
171

import org.eclipse.paho.client.mqttv3.*;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
import org.springframework.boot.SpringApplication;

public class MessagingApp {

public static void main(String[] args) {
String serviceUrl = "tcp://192.168.14.103:1883";
String userName = "test-mqtt:maxzhao";
String password = "maxzhao";
String clientId = "maxzhao_amdin";
// 创建对象
MessagingApp mqttClient = new MessagingApp();
// mqttClient类初始化
mqttClient.init(serviceUrl, userName, password, clientId);
// 订阅主题
mqttClient.subscribeTopic("/key1", 0);
// 发布消息
for (int i = 0; i < 50; i++) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// Do nothing
}
mqttClient.publishMessage("/key1", "消息 " + i, 1);
}
// 取消订阅
mqttClient.cleanTopic("/key");
SpringApplication.run(MessagingApp.class, args);
}

/**
* MQTT连接对象 对连接进行设置
*/
private MqttClient mqttClient;


/**
* 类初始化,建立mqttClient连接
*/
public void init(String serviceUrl, String userName, String password, String clientId) {
//初始化连接对象
/**
* mqtt客户端对象
*/
MqttConnectOptions mqttConnectOptions = new MqttConnectOptions();
//初始化mqttClient
if (mqttConnectOptions != null) {
// 设置是否清空session,这里如果设置为false表示服务器会保留客户端的连接记录,这里设置为true表示每次连接到服务器都以新的身份连接
mqttConnectOptions.setCleanSession(true);
//设置超时时间 单位为秒
mqttConnectOptions.setConnectionTimeout(30);
mqttConnectOptions.setUserName(userName);
mqttConnectOptions.setPassword(password.toCharArray());
//设置会话心跳时间 单位为秒 服务器会每隔1.5*20秒的时间向客户端发送个消息判断客户端是否在线,但这个方法并没有重连的机制
mqttConnectOptions.setKeepAliveInterval(45);
MemoryPersistence memoryPersistence = new MemoryPersistence();
if (mqttConnectOptions != null && clientId != null) {
try {
mqttClient = new MqttClient(serviceUrl, clientId, memoryPersistence);
} catch (MqttException e) {
e.printStackTrace();
}
} else {
System.out.println("mqttConnectOptions == null || clientId == null");
}
} else {
System.out.println("mqttConnectOptions == null");
}
System.out.println("MqttClient是否连接?" + mqttClient.isConnected());
if (mqttClient != null) {
//创建回调函数对象,用来接收已经订阅的消息
mqttClient.setCallback(new MqttCallback() {
@Override
public void connectionLost(Throwable throwable) {

}

@Override
public void messageArrived(String topic, MqttMessage message) throws Exception {
System.out.println("Client 接收消息主题 : " + topic);
System.out.println("Client 接收消息Qos : " + message.getQos());
System.out.println("Client 接收消息内容 : " + new String(message.getPayload()));
}

@Override
public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {

}
});
System.out.println("创建连接......");
try {
mqttClient.connect(mqttConnectOptions);
} catch (MqttException e) {
e.printStackTrace();
}
} else {
System.out.println("mqttClient对象为空,连接失败...");
}
System.out.println("mqttClient是否连接?" + mqttClient.isConnected());
}

/**
* 订阅主题
*
* @param topic topic
* @param qos 消息质量
*/
public void subscribeTopic(String topic, int qos) {
if (mqttClient != null && mqttClient.isConnected() && topic != null) {
try {
mqttClient.subscribe(topic, qos);
} catch (MqttException e) {
e.printStackTrace();
}
} else {
System.out.println("订阅失败 mqttClient 无法连接");
}
}

/**
* 取消订阅主题
*
* @param topic topic
*/
public void cleanTopic(String topic) {
if (mqttClient != null && mqttClient.isConnected()) {
try {
mqttClient.unsubscribe(topic);
} catch (MqttException e) {
e.printStackTrace();
}
} else {
System.out.println("取消订阅失败 mqttClient 无法连接");
}
}

/**
* 发布消息
*
* @param topic topic
* @param messageContent 消息内容
* @param qos 消息质量
*/
public void publishMessage(String topic, String messageContent, int qos) {
// 判断是否连接
if (mqttClient != null && mqttClient.isConnected()) {
System.out.println("发布消息.......");
System.out.println("发布消息人的clientId:" + mqttClient.getClientId());
MqttMessage mqttMessage = new MqttMessage();
mqttMessage.setQos(qos);
mqttMessage.setPayload(messageContent.getBytes());
MqttTopic mqttTopic = mqttClient.getTopic(topic);
if (mqttTopic != null) {
try {
MqttDeliveryToken publish = mqttTopic.publish(mqttMessage);
if (!publish.isComplete()) {
System.out.println("消息发布成功!");
}
} catch (MqttException e) {
e.printStackTrace();
}
} else {
System.out.println("mqttTopic == null");
}
} else {
System.out.println("mqttClient == null || mqttClient.isConnected() == false");
}
}
}

本文地址: https://github.com/maxzhao-it/blog/post/48712fed/

开启 mqtt 插件

1
~/rabbitmq-server/sbin/rabbitmq-plugins enable rabbitmq_mqtt

配置

1
vim ~/rabbitmq-server/etc/rabbitmq/rabbitmq.conf

写入配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
mqtt.listeners.tcp.1 = 1883
mqtt.listeners.tcp.2 = 1884
# 匿名用户的用户名密码
mqtt.default_user = guest
mqtt.default_pass = guest
# 不允许匿名用户访问
mqtt.allow_anonymous = false
mqtt.vhost = /
mqtt.exchange = amq.topic
# 默认24小时,可以配置 undefined
mqtt.subscription_ttl = 1800000
# 将被传递的未确认消息的最大数量
mqtt.prefetch = 10
# 新集群可用 quorum模式
mqtt.durable_queue_type = quorum
# 保留消息存储不会复制到集群其它节点
# 磁盘存储保留消息(最大2G)
# mqtt.retained_message_store = rabbit_mqtt_retained_msg_store_dets
# 内存存储保留消息
mqtt.retained_message_store = rabbit_mqtt_retained_msg_store_ets
# only used by DETS store
mqtt.retained_message_store_dets_sync_interval = 2000

创建用户和 vhost

1
2
3
4
5
6
7
8
9
10
11
# 创建 bhost
~/rabbitmq-server/sbin/rabbitmqctl add_vhost ,mqtt_vhost
# 修改guest的密码
~/rabbitmq-server/sbin/rabbitmqctl list_users
~/rabbitmq-server/sbin/rabbitmqctl change_password guest guest
# 创建其他管理员账号比如test/test:
~/rabbitmq-server/sbin/rabbitmqctl add_user maxzhao maxzhao
#
~/rabbitmq-server/sbin/rabbitmqctl set_user_tags maxzhao administrator
# /是 vhost的目录 Configure regexp Write regexp Read regexp
~/rabbitmq-server/sbin/rabbitmqctl set_permissions -p / maxzhao ".*" ".*" ".*"

客户端接入

1
2
username:mqtt_vhost:maxzhao
password:maxzhao

RabbitMQ 不支持 Qos2,会被降级到Qos1。

本文地址: https://github.com/maxzhao-it/blog/post/c4ae7068/

注意:erlang-24.x已经不支持 CentOS7

rpm 安装

安装 RabbitMQ需要安装Erlang

Erlanghttps://github.com/rabbitmq/erlang-rpm/releases
RabbitMQhttps://github.com/rabbitmq/rabbitmq-server/releases

RabbitMQErlang版本对应关系 https://www.rabbitmq.com/which-erlang.html

我这里下载了

root 授权

1
sudo echo 'rabbitmq ALL=(ALL) NOPASSWD:rpm' >> /etc/sudoers

开放防火墙

1
2
firewall-cmd --zone=public --add-port=45672/tcp --add-port=45673/tcp  --permanent
firewall-cmd --reload

普通用户安装erlang

1
2
3
4
5
6
7
8
sudo rpm -ivh openssl-libs-1.0.2k-25.el7_9.x86_64.rpm --force
# 安装环境
sudo rpm -ivh erlang-23.3.4.11-1.el7.x86_64.rpm
# 验证erlang 是否安装成功
erl -version
# 安装 RabbitMQ
tar -xf rabbitmq-server-generic-unix-3.9.21.tar.xz
mv rabbitmq_server-3.9.21 rabbitmq-server

配置启动

安装web管理界面

1
2
3
~/rabbitmq-server/sbin/rabbitmq-plugins enable rabbitmq_management
# 查看所有的插件
~/rabbitmq-server/sbin/rabbitmq-plugins list

配置

1
2
3
cp rabbitmq.conf.example ~/rabbitmq-server/etc/rabbitmq/rabbitmq.conf
sed -i 's?# listeners.tcp.default = 5672?listeners.tcp.default = 45672?' ~/rabbitmq-server/etc/rabbitmq/rabbitmq.conf
sed -i 's?# management.tcp.port = 15672?management.tcp.port = 45673?' ~/rabbitmq-server/etc/rabbitmq/rabbitmq.conf

启动

1
2
3
4
# 后台启动
~/rabbitmq-server/sbin/rabbitmq-server -detached
# 停机
~/rabbitmq-server/sbin/rabbitmqctl shutdown

参考

其它管理

用户管理

1
2
3
4
5
6
7
8
9
10
11
12
# 修改guest的密码
~/rabbitmq-server/sbin/rabbitmqctl list_users
~/rabbitmq-server/sbin/rabbitmqctl change_password guest guest

# 创建其他管理员账号比如test/test:
~/rabbitmq-server/sbin/rabbitmqctl add_user maxzhao maxzhao
#
~/rabbitmq-server/sbin/rabbitmqctl set_user_tags maxzhao administrator
# /是 vhost的目录 Configure regexp Write regexp Read regexp
~/rabbitmq-server/sbin/rabbitmqctl set_permissions -p / maxzhao ".*" ".*" ".*"
# Sets user topic permissions for an exchange,默认使用 AMQP default 的exchange
# sudo rabbitmqctl set_topic_permissions

添加vhost

1
2
3
4
5
6
7
8
9
10
11
# 查看帮助
~/rabbitmq-server/sbin/rabbitmqctl --help
# 查看创建 vhost 的帮助
~/rabbitmq-server/sbin/rabbitmqctl add_vhost --help
# 创建
~/rabbitmq-server/sbin/rabbitmqctl add_vhost maxzhao_vhost
# 查看
~/rabbitmq-server/sbin/rabbitmqctl list_vhosts
# 赋权 注意 /maxzhao_vhost 与 maxzhao_vhost 不一样
~/rabbitmq-server/sbin/rabbitmqctl set_permissions -p /maxzhao_vhost maxzhao ".*" ".*" ".*"

删除 vhost

1
2
~/rabbitmq-server/sbin/rabbitmqctl add_vhost maxzhaoTest
~/rabbitmq-server/sbin/rabbitmqctl delete_vhost maxzhaoTest

日志目录

1
~/rabbitmq-server/var/log/rabbitmq

错误:address (cannot connect to host/port)

1
echo 'NODENAME=rabbitmq@192.168.14.103' >> ~/rabbitmq-server/etc/rabbitmq/rabbitmq-env.conf

主要配置

~/rabbitmq-server/etc/rabbitmq/rabbitmq.conf

1
2
listeners.tcp.default = 45672
management.tcp.port = 45673

~/rabbitmq-server/etc/rabbitmq/rabbitmq-env.conf

1
NODENAME=rabbitmq@localhost

用户 tags 一般授权 monitoring

用户角色tags可分为五类:

先启用:management plugin

  • 超级管理员(administrator) 管理all。
  • 监控者(monitoring) 查看rabbitmq节点的相关信息(进程数,内存使用情况,磁盘使用情况等)。
  • 策略制定者(policymaker) 对policy进行管理。但无法查看节点的相关信息(上图红框标识的部分)。
  • 普通管理者(management) 仅可登陆管理控制台,无法看到节点信息,也无法对策略进行管理。
  • none 无法登陆管理控制台,生产者和消费者。

本文地址: https://github.com/maxzhao-it/blog/post/21590923/

2022-01-16:升级 filebeat-8.6.0

安装

下载

1
2
3
wget https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-8.6.0-linux-x86_64.tar.gz 
tar -zxf filebeat-8.6.0-linux-x86_64.tar.gz
mv filebeat-8.6.0-linux-x86_64 filebeat

启动 nginx 模块

1
/home/nginx/filebeat/filebeat modules enable nginx -c /home/nginx/filebeat/filebeat.yml

修改 nginx.yml 文件

1
2
3
4
5
6
7
8
9
10
11
12
cat > /home/nginx/filebeat/modules.d/nginx.yml <<EOF
- module: nginx
# Access logs
access:
enabled: true
var.paths: ["/home/nginx/nginx/logs/access.log*"]
# Error logs
error:
enabled: true
var.paths: ["/home/nginx/nginx/logs/error.log*"]
EOF
cat /home/nginx/filebeat/modules.d/nginx.yml

参考地址

命令行中指定

1
-M "nginx.access.var.paths=[/home/nginx/nginx/logs/access.log*]" -M "nginx.error.var.paths=[/home/nginx/nginx/logs/error.log*]"

输出到 ES

1
2
cp /home/nginx/filebeat/filebeat.yml /home/nginx/filebeat/filebeat.yml-bak
vim /home/nginx/filebeat/filebeat.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
filebeat.inputs:
- type: filestream
id: my-filestream-id
enabled: false
paths:
- /var/log/*.log

filebeat.config.modules:
path: ${path.config}/modules.d/*.yml
reload.enabled: false
reload.period: 60s
setup.template.settings:
index.number_of_shards: 1
#index.number_of_replicas: 1
setup.template.name: "nginx-log"
setup.template.pattern: "nginx-log-*"
setup.template.overwrite: false
setup.kibana:
host: "192.168.14.123:45601"
protocol: "http"
username: "maxzhao"
password: "maxzhao."
output.elasticsearch:
hosts: ["192.168.14.123:49200"]
protocol: "http"
index: "nginx-log-%{+yyyy.MM.dd}"
username: "maxzhao"
password: "maxzhao."
# 这里一定要注释掉
processors:
- add_host_metadata:
when.not.contains.tags: forwarded
- add_cloud_metadata: ~
- add_docker_metadata: ~
- add_kubernetes_metadata: ~
#migration.6_to_7.enabled: true
# 关闭索引自动管理
# setup.ilm.enabled: false

启动

1
2
3
4
5
6
7
8
9
cd /home/nginx/filebeat
# 测试配置文件
./filebeat test config -c filebeat.yml -e
# 创建 index 和 Dashboard
./filebeat -c filebeat.yml -e
./filebeat setup -c filebeat.yml --dashboards
# 启动
nohup /home/nginx/filebeat/filebeat -c /home/nginx/filebeat/filebeat.yml -e >> /home/nginx/filebeat/filebeat.log 2>&1 &
tail -f /home/nginx/filebeat/filebeat.log

启停脚本

1
2
3
echo "nohup /home/nginx/filebeat/filebeat -c /home/nginx/filebeat/filebeat.yml -e >> /home/nginx/filebeat/filebeat.log 2>&1 &" > /home/nginx/filebeat-start.sh
echo "ps -ef|grep '/filebeat' | grep -v grep |awk '{print \$2}' |xargs -I {} kill '{}'" > /home/nginx/filebeat-shutdown.sh
chmod +x filebeat-*.sh

nginx 配置

1
2
3
4
5
6
7
http {
#日志格式
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"'
'$request_time $upstream_response_time $upstream_addr $upstream_status';
}

filebeat 配置

filebeat/module/nginx/access/ingest/pipeline.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
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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
description: Pipeline for parsing Nginx access logs. Requires the geoip and user_agent
plugins.
processors:
- set:
field: event.ingested
value: '{{_ingest.timestamp}}'
- rename:
field: message
target_field: event.original
- grok:
field: event.original
patterns:
- (%{NGINX_HOST} )?"?(?:%{NGINX_ADDRESS_LIST:nginx.access.remote_ip_list}|%{NOTSPACE:source.address})
- (-|%{DATA:user.name}) \[%{HTTPDATE:nginx.access.time}\] "%{DATA:nginx.access.info}"
%{NUMBER:http.response.status_code:long} %{NUMBER:http.response.body.bytes:long}
"(-|%{DATA:http.request.referrer})" (-|%{NUMBER:nginx.access.request_time:double}) (-|%{NUMBER:nginx.access.response_time:double}) %{DATA:nginx.access.upstream_addr} (-|%{NUMBER:nginx.access.upstream_status_code:long}) "(-|%{DATA:user_agent.original})"
pattern_definitions:
NGINX_HOST: (?:%{IP:destination.ip}|%{NGINX_NOTSEPARATOR:destination.domain})(:%{NUMBER:destination.port})?
NGINX_NOTSEPARATOR: "[^\t ,:]+"
NGINX_ADDRESS_LIST: (?:%{IP}|%{WORD})("?,?\s*(?:%{IP}|%{WORD}))*
ignore_missing: true
- grok:
field: nginx.access.info
patterns:
- '%{WORD:http.request.method} %{DATA:_tmp.url_orig} HTTP/%{NUMBER:http.version}'
- ""
ignore_missing: true
- uri_parts:
field: _tmp.url_orig
ignore_failure: true
- set:
field: url.domain
value: "{{destination.domain}}"
if: ctx.url?.domain == null && ctx.destination?.domain != null
- remove:
field:
- nginx.access.info
- _tmp.url_orig
ignore_missing: true
- split:
field: nginx.access.remote_ip_list
separator: '"?,?\s+'
ignore_missing: true
- split:
field: nginx.access.origin
separator: '"?,?\s+'
ignore_missing: true
- set:
field: source.address
if: ctx.source?.address == null
value: ""
- script:
if: ctx.nginx?.access?.remote_ip_list != null && ctx.nginx.access.remote_ip_list.length > 0
lang: painless
source: >-
boolean isPrivate(def dot, def ip) {
try {
StringTokenizer tok = new StringTokenizer(ip, dot);
int firstByte = Integer.parseInt(tok.nextToken());
int secondByte = Integer.parseInt(tok.nextToken());
if (firstByte == 10) {
return true;
}
if (firstByte == 192 && secondByte == 168) {
return true;
}
if (firstByte == 172 && secondByte >= 16 && secondByte <= 31) {
return true;
}
if (firstByte == 127) {
return true;
}
return false;
}
catch (Exception e) {
return false;
}
}
try {
ctx.source.address = null;
if (ctx.nginx.access.remote_ip_list == null) {
return;
}
def found = false;
for (def item : ctx.nginx.access.remote_ip_list) {
if (!isPrivate(params.dot, item)) {
ctx.source.address = item;
found = true;
break;
}
}
if (!found) {
ctx.source.address = ctx.nginx.access.remote_ip_list[0];
}
}
catch (Exception e) {
ctx.source.address = null;
}
params:
dot: .
- remove:
field: source.address
if: ctx.source.address == null
- grok:
field: source.address
patterns:
- ^%{IP:source.ip}$
ignore_failure: true
- set:
copy_from: '@timestamp'
field: event.created
- date:
field: nginx.access.time
target_field: '@timestamp'
formats:
- dd/MMM/yyyy:H:m:s Z
on_failure:
- append:
field: error.message
value: '{{ _ingest.on_failure_message }}'
- remove:
field: nginx.access.time
- user_agent:
field: user_agent.original
ignore_missing: true
- geoip:
field: source.ip
target_field: source.geo
ignore_missing: true
- geoip:
database_file: GeoLite2-ASN.mmdb
field: source.ip
target_field: source.as
properties:
- asn
- organization_name
ignore_missing: true
- rename:
field: source.as.asn
target_field: source.as.number
ignore_missing: true
- rename:
field: source.as.organization_name
target_field: source.as.organization.name
ignore_missing: true
- set:
field: event.kind
value: event
- append:
field: event.category
value: web
- append:
field: event.type
value: access
- set:
field: event.outcome
value: success
if: "ctx?.http?.response?.status_code != null && ctx.http.response.status_code < 400"
- set:
field: event.outcome
value: failure
if: "ctx?.http?.response?.status_code != null && ctx.http.response.status_code >= 400"
- append:
field: related.ip
value: "{{source.ip}}"
if: "ctx?.source?.ip != null"
- append:
field: related.ip
value: "{{destination.ip}}"
if: "ctx?.destination?.ip != null"
- append:
field: related.user
value: "{{user.name}}"
if: "ctx?.user?.name != null"
- script:
lang: painless
description: This script processor iterates over the whole document to remove fields with null values.
source: |
void handleMap(Map map) {
for (def x : map.values()) {
if (x instanceof Map) {
handleMap(x);
} else if (x instanceof List) {
handleList(x);
}
}
map.values().removeIf(v -> v == null);
}
void handleList(List list) {
for (def x : list) {
if (x instanceof Map) {
handleMap(x);
} else if (x instanceof List) {
handleList(x);
}
}
}
handleMap(ctx);
on_failure:
- set:
field: error.message
value: '{{ _ingest.on_failure_message }}'

filebeat/fields.yml ,在nginx 的结构下添加

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- name: request_time
type: double
description: >
The request_time.
- name: response_time
type: double
description: >
The upstream_response_time.
- name: upstream_addr
type: keyword
description: >
The upstream_addr.
- name: upstream_status_code
type: long
description: >
The upstream_status.

本文地址: https://github.com/maxzhao-it/blog/post/e0916ba3/

使用 Nginx 负载 前后端:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
server {
listen 10001;
server_name 0.0.0.0;
location /seed-auth {
proxy_pass http://authServer;
# session 地址
proxy_cookie_path /seed-auth /;
}
location / {
root /home/maxzhao/seed-auth/frontend/;
index index.html;
}
}
upstream authServer {
server 192.168.14.118:20001;
server 192.168.14.117:20001;
}

本文地址: https://github.com/maxzhao-it/blog/post/793975d1/

Nacos config 原文地址

Github经常抽搐,记录下来。

以下内容与Nacos config 原文地址内容一样——更新于 2021-04-19

Spring Cloud Alibaba Nacos Config

Nacos 提供用于存储配置和其他元数据的 key/value 存储,为分布式系统中的外部化配置提供服务器端和客户端支持。使用 Spring Cloud Alibaba Nacos Config,您可以在 Nacos Server 集中管理你 Spring Cloud 应用的外部属性配置。

Spring Cloud Alibaba Nacos Config 是 Config Server 和 Client 的替代方案,客户端和服务器上的概念与 Spring Environment 和 PropertySource 有着一致的抽象,在特殊的 bootstrap 阶段,配置被加载到 Spring 环境中。当应用程序通过部署管道从开发到测试再到生产时,您可以管理这些环境之间的配置,并确保应用程序具有迁移时需要运行的所有内容。

快速开始

Nacos 服务端初始化

1、启动Nacos Server。启动方式可见 Nacos 官网

2、启动好Nacos之后,在Nacos添加如下的配置:

1
2
3
4
5
6
7
8
Data ID:    nacos-config.properties

Group : DEFAULT_GROUP

配置格式: Properties

配置内容: user.name=nacos-config-properties
user.age=90
Note 注意dataid是以 properties(默认的文件扩展名方式)为扩展名。
客户端使用方式

如果要在您的项目中使用 Nacos 来实现应用的外部化配置,使用 group ID 为 com.alibaba.cloud 和 artifact ID 为 spring-cloud-starter-alibaba-nacos-config 的 starter。

1
2
3
4
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>

现在就可以创建一个标准的 Spring Boot 应用。

1
2
3
4
5
6
7
8
9
10
@SpringBootApplication
public class ProviderApplication {

public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = SpringApplication.run(ProviderApplication.class, args);
String userName = applicationContext.getEnvironment().getProperty("user.name");
String userAge = applicationContext.getEnvironment().getProperty("user.age");
System.err.println("user name :"+userName+"; age: "+userAge);
}
}

在运行此 Example 之前, 必须使用 bootstrap.properties 配置文件来配置Nacos Server 地址,例如:

bootstrap.properties

1
2
spring.application.name=nacos-config
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
Note 注意当你使用域名的方式来访问 Nacos 时,spring.cloud.nacos.config.server-addr 配置的方式为 域名:port。 例如 Nacos 的域名为abc.com.nacos,监听的端口为 80,则 spring.cloud.nacos.config.server-addr=abc.com.nacos:80。 注意 80 端口不能省略。

启动这个 Example,可以看到如下输出结果:

1
2
3
4
2018-11-02 14:24:51.638  INFO 32700 --- [main] c.a.demo.provider.ProviderApplication    : Started ProviderApplication in 14.645 seconds (JVM running for 15.139)
user name :nacos-config-properties; age: 90
2018-11-02 14:24:51.688 INFO 32700 --- [-127.0.0.1:8848] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@a8c5e74: startup date [Fri Nov 02 14:24:51 CST 2018]; root of context hierarchy
2018-11

基于 dataid 为 yaml 的文件扩展名配置方式

spring-cloud-starter-alibaba-nacos-config 对于 yaml 格式也是完美支持的。这个时候只需要完成以下两步:

1、在应用的 bootstrap.properties 配置文件中显示的声明 dataid 文件扩展名。如下所示

bootstrap.properties

1
spring.cloud.nacos.config.file-extension=yaml

2、在 Nacos 的控制台新增一个dataid为yaml为扩展名的配置,如下所示:

1
2
3
4
5
6
7
8
Data ID:        nacos-config.yaml

Group : DEFAULT_GROUP

配置格式: YAML

配置内容: user.name: nacos-config-yaml
user.age: 68

这两步完成后,重启测试程序,可以看到如下输出结果。

1
2
3
2018-11-02 14:59:00.484  INFO 32928 --- [main] c.a.demo.provider.ProviderApplication:Started ProviderApplication in 14.183 seconds (JVM running for 14.671)
user name :nacos-config-yaml; age: 68
2018-11-02 14:59:00.529 INFO 32928 --- [-127.0.0.1:8848] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@265a478e: startup date [Fri Nov 02 14:59:00 CST 2018]; root of context hierarchy

支持配置的动态更新

spring-cloud-starter-alibaba-nacos-config 也支持配置的动态更新,启动 Spring Boot 应用测试的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@SpringBootApplication
public class ProviderApplication {

public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = SpringApplication.run(ProviderApplication.class, args);
while(true) {
//当动态配置刷新时,会更新到 Enviroment中,因此这里每隔一秒中从Enviroment中获取配置
String userName = applicationContext.getEnvironment().getProperty("user.name");
String userAge = applicationContext.getEnvironment().getProperty("user.age");
System.err.println("user name :" + userName + "; age: " + userAge);
TimeUnit.SECONDS.sleep(1);
}
}
}

如下所示,当变更user.name时,应用程序中能够获取到最新的值:

1
2
3
4
5
6
7
8
9
user name :nacos-config-yaml; age: 68
user name :nacos-config-yaml; age: 68
user name :nacos-config-yaml; age: 68
2018-11-02 15:04:25.069 INFO 32957 --- [-127.0.0.1:8848] o.s.boot.SpringApplication : Started application in 0.144 seconds (JVM running for 71.752)
2018-11-02 15:04:25.070 INFO 32957 --- [-127.0.0.1:8848] s.c.a.AnnotationConfigApplicationContext : Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@10c89124: startup date [Fri Nov 02 15:04:25 CST 2018]; parent: org.springframework.context.annotation.AnnotationConfigApplicationContext@6520af7
2018-11-02 15:04:25.071 INFO 32957 --- [-127.0.0.1:8848] s.c.a.AnnotationConfigApplicationContext : Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@6520af7: startup date [Fri Nov 02 15:04:24 CST 2018]; root of context hierarchy
//从 Enviroment 中 读取到更改后的值
user name :nacos-config-yaml-update; age: 68
user name :nacos-config-yaml-update; age: 68
Note 你可以通过配置 spring.cloud.nacos.config.refresh.enabled=false 来关闭动态刷新

可支持profile粒度的配置

spring-cloud-starter-alibaba-nacos-config 在加载配置的时候,不仅仅加载了以 dataid 为 ${spring.application.name}.${file-extension:properties} 为前缀的基础配置,还加载了dataid为 ${spring.application.name}-${profile}.${file-extension:properties} 的基础配置。在日常开发中如果遇到多套环境下的不同配置,可以通过Spring 提供的 ${spring.profiles.active} 这个配置项来配置。

1
spring.profiles.active=develop
Note ${spring.profiles.active} 当通过配置文件来指定时必须放在 bootstrap.properties 文件中。

Nacos 上新增一个dataid为:nacos-config-develop.yaml的基础配置,如下所示:

1
2
3
4
5
6
7
Data ID:        nacos-config-develop.yaml

Group : DEFAULT_GROUP

配置格式: YAML

配置内容: current.env: develop-env

启动 Spring Boot 应用测试的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@SpringBootApplication
public class ProviderApplication {

public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = SpringApplication.run(ProviderApplication.class, args);
while(true) {
String userName = applicationContext.getEnvironment().getProperty("user.name");
String userAge = applicationContext.getEnvironment().getProperty("user.age");
//获取当前部署的环境
String currentEnv = applicationContext.getEnvironment().getProperty("current.env");
System.err.println("in "+currentEnv+" enviroment; "+"user name :" + userName + "; age: " + userAge);
TimeUnit.SECONDS.sleep(1);
}
}
}

启动后,可见控制台的输出结果:

1
2
in develop-env enviroment; user name :nacos-config-yaml-update; age: 68
2018-11-02 15:34:25.013 INFO 33014 --- [ Thread-11] ConfigServletWebServerApplicationContext : Closing org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@6f1c29b7: startup date [Fri Nov 02 15:33:57 CST 2018]; parent: org.springframework.context.annotation.AnnotationConfigApplicationContext@63355449

如果需要切换到生产环境,只需要更改 ${spring.profiles.active} 参数配置即可。如下所示:

1
spring.profiles.active=product

同时生产环境上 Nacos 需要添加对应 dataid 的基础配置。例如,在生成环境下的 Naocs 添加了dataid为:nacos-config-product.yaml的配置:

1
2
3
4
5
6
7
Data ID:        nacos-config-product.yaml

Group : DEFAULT_GROUP

配置格式: YAML

配置内容: current.env: product-env

启动测试程序,输出结果如下:

1
2
in product-env enviroment; user name :nacos-config-yaml-update; age: 68
2018-11-02 15:42:14.628 INFO 33024 --- [Thread-11] ConfigServletWebServerApplicationContext : Closing org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@6aa8e115: startup date [Fri Nov 02 15:42:03 CST 2018]; parent: org.springframework.context.annotation.AnnotationConfigApplicationContext@19bb07ed
Note 此案例中我们通过 spring.profiles.active=<profilename> 的方式写死在配置文件中,而在真正的项目实施过程中这个变量的值是需要不同环境而有不同的值。这个时候通常的做法是通过 -Dspring.profiles.active=<profile> 参数指定其配置来达到环境间灵活的切换。

支持自定义 namespace 的配置

首先看一下 Nacos 的 Namespace 的概念, Nacos 概念

用于进行租户粒度的配置隔离。不同的命名空间下,可以存在相同的 Group 或 Data ID 的配置。Namespace 的常用场景之一是不同环境的配置的区分隔离,例如开发测试环境和生产环境的资源(如配置、服务)隔离等。

在没有明确指定 ${spring.cloud.nacos.config.namespace} 配置的情况下, 默认使用的是 Nacos 上 Public 这个namespae。如果需要使用自定义的命名空间,可以通过以下配置来实现:

1
spring.cloud.nacos.config.namespace=b3404bc0-d7dc-4855-b519-570ed34b62d7
Note 该配置必须放在 bootstrap.properties 文件中。此外 spring.cloud.nacos.config.namespace 的值是 namespace 对应的 id,id 值可以在 Nacos 的控制台获取。并且在添加配置时注意不要选择其他的 namespae,否则将会导致读取不到正确的配置。

支持自定义 Group 的配置

在没有明确指定 ${spring.cloud.nacos.config.group} 配置的情况下, 默认使用的是 DEFAULT_GROUP 。如果需要自定义自己的 Group,可以通过以下配置来实现:

1
spring.cloud.nacos.config.group=DEVELOP_GROUP
Note 该配置必须放在 bootstrap.properties 文件中。并且在添加配置时 Group 的值一定要和 spring.cloud.nacos.config.group 的配置值一致。

支持自定义扩展的 Data Id 配置

Spring Cloud Alibaba Nacos Config 从 0.2.1 版本后,可支持自定义 Data Id 的配置。关于这部分详细的设计可参考 这里。 一个完整的配置案例如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
spring.application.name=opensource-service-provider
spring.cloud.nacos.config.server-addr=127.0.0.1:8848

# config external configuration
# 1、Data Id 在默认的组 DEFAULT_GROUP,不支持配置的动态刷新
spring.cloud.nacos.config.extension-configs[0].data-id=ext-config-common01.properties

# 2、Data Id 不在默认的组,不支持动态刷新
spring.cloud.nacos.config.extension-configs[1].data-id=ext-config-common02.properties
spring.cloud.nacos.config.extension-configs[1].group=GLOBALE_GROUP

# 3、Data Id 既不在默认的组,也支持动态刷新
spring.cloud.nacos.config.extension-configs[2].data-id=ext-config-common03.properties
spring.cloud.nacos.config.extension-configs[2].group=REFRESH_GROUP
spring.cloud.nacos.config.extension-configs[2].refresh=true

可以看到:

  • 通过 spring.cloud.nacos.config.extension-configs[n].data-id 的配置方式来支持多个 Data Id 的配置。
  • 通过 spring.cloud.nacos.config.extension-configs[n].group 的配置方式自定义 Data Id 所在的组,不明确配置的话,默认是 DEFAULT_GROUP。
  • 通过 spring.cloud.nacos.config.extension-configs[n].refresh 的配置方式来控制该 Data Id 在配置变更时,是否支持应用中可动态刷新, 感知到最新的配置值。默认是不支持的。
Note 多个 Data Id 同时配置时,他的优先级关系是 spring.cloud.nacos.config.extension-configs[n].data-id 其中 n 的值越大,优先级越高。
Note spring.cloud.nacos.config.extension-configs[n].data-id 的值必须带文件扩展名,文件扩展名既可支持 properties,又可以支持 yaml/yml。 此时 spring.cloud.nacos.config.file-extension 的配置对自定义扩展配置的 Data Id 文件扩展名没有影响。

通过自定义扩展的 Data Id 配置,既可以解决多个应用间配置共享的问题,又可以支持一个应用有多个配置文件。

为了更加清晰的在多个应用间配置共享的 Data Id ,你可以通过以下的方式来配置:

1
2
3
4
5
6
7
8
# 配置支持共享的 Data Id
spring.cloud.nacos.config.shared-configs[0].data-id=common.yaml

# 配置 Data Id 所在分组,缺省默认 DEFAULT_GROUP
spring.cloud.nacos.config.shared-configs[0].group=GROUP_APP1

# 配置Data Id 在配置变更时,是否动态刷新,缺省默认 false
spring.cloud.nacos.config.shared-configs[0].refresh=true

可以看到:

  • 通过 spring.cloud.nacos.config.shared-configs[n].data-id 来支持多个共享 Data Id 的配置。
  • 通过 spring.cloud.nacos.config.shared-configs[n].group 来配置自定义 Data Id 所在的组,不明确配置的话,默认是 DEFAULT_GROUP。
  • 通过 spring.cloud.nacos.config.shared-configs[n].refresh 来控制该Data Id在配置变更时,是否支持应用中动态刷新,默认false。

配置的优先级

Spring Cloud Alibaba Nacos Config 目前提供了三种配置能力从 Nacos 拉取相关的配置。

  • A: 通过 spring.cloud.nacos.config.shared-configs[n].data-id 支持多个共享 Data Id 的配置
  • B: 通过 spring.cloud.nacos.config.extension-configs[n].data-id 的方式支持多个扩展 Data Id 的配置
  • C: 通过内部相关规则(应用名、应用名+ Profile )自动生成相关的 Data Id 配置

当三种方式共同使用时,他们的一个优先级关系是:A < B < C

完全关闭配置

通过设置 spring.cloud.nacos.config.enabled = false 来完全关闭 Spring Cloud Nacos Config

本文地址: https://github.com/maxzhao-it/blog/post/15474/

直接上代码:

【1】引入jar

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<dependencies>
<dependency>
<groupId>org.apache.solr</groupId>
<artifactId>solr-solrj</artifactId>
<version>4.10.4</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.1</version>
</dependency>
</dependencies>

【测试代码】
javabean:

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
public class Car implements Serializable {
//注意:这里的字符串id字段要在scheme.xml中有配置体现
@Field("id")
private String id;
@Field("name")
private String name;
@Field("price")
private double price;
@Field("url")
private String url;

public Car(){}

public Car(String id, String name, double price,String url){
this.id = id;
this.name = name;
this.price = price;
this.url = url;
}

//getter setter方法
@Override
public String toString() {
return "Car{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", price=" + price +
", url='" + url + '\'' +
'}';
}

testSolr.java

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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
/**
* Author:lvfang
*
* Created by mis on 2017/5/24.
*/
public class SolrTest {

public final String baseURL = "http://192.168.22.128:8081/solr";

public HttpSolrServer server=null;

/**
* 创建SolrServer对象
* 该对象有两个可以使用,都是线程安全的 1、CommonsHttpSolrServer:启动web服务器使用的,通过http请求的 2、
* EmbeddedSolrServer:内嵌式的,导入solr的jar包就可以使用了 3、solr
* 4.0之后好像添加了不少东西,其中CommonsHttpSolrServer这个类改名为HttpSolrClient
*
*/
@Before
public void init() throws Exception{
//new HttpSolrClient.Builder(BASE_URL).build()
server=new HttpSolrServer(baseURL);
}

/**
* 基于索引名添加
* @throws Exception
*/
@Test
public void testAdd() throws Exception{
Car car1 = new Car("Audi000A4001","奥迪A4L",295000,"/images/001.jpg");
//Car car1 = new Car("BMW0000B5001","宝马5系",436800,"/images/002.jpg");
SolrInputDocument doc1 = new SolrInputDocument();
doc1.addField("id",car1.getId());
doc1.addField("name",car1.getName());
doc1.addField("price",car1.getPrice());
doc1.addField("url",car1.getUrl());
// 这里也可以传递集合 List<SolrInputDocument>
server.add(doc1);
UpdateResponse rspcommit = server.commit();
// 操作完成 0
rspcommit.getStatus();
System.out.println("操作完成!!!");
}

/**
* 基于bean添加(注意,这里bean的属性要添加solr的注解@Field
* @throws Exception
*/
@Test
public void testAddBean() throws Exception{
Car car1 = new Car("BMW0000B5001","宝马5系",436800,"/images/002.jpg");
server.addBean(car1);
// 添加集合 List<Car>
// server.addBeans(list);
server.commit();
System.out.println("操作完成!!!");
}

/**
* Document结果转换bean
* @throws Exception
*/
@Test
public void change() throws Exception {
SolrDocument doc1 = new SolrDocument();
doc1.addField("id","Siju0032SJ0C8001");
doc1.addField("name","世爵C8");
doc1.addField("price",1370000);
doc1.addField("url","/images/005.jps");

Car car = server.getBinder().getBean(Car.class,doc1);
System.out.println(car);
System.out.println("操作完成!!!");
}

/**
* 删除
* @throws Exception
*/
@Test
public void testDel() throws Exception{
//server.deleteById("1");
server.deleteByQuery("*:*");
server.commit();
System.out.println("操作完成!!!");
}

/**
* 修改(可以理解为重新添加,)
* @throws Exception
*/
@Test
public void testUpdate() throws Exception {
SolrInputDocument doc1 = new SolrInputDocument();
doc1.addField("id","1");
doc1.addField("title","lisi");
server.add(doc1);

server.commit();
System.out.println("操作完成!!!");
}

/**
* 查询
* @throws Exception
*/
@Test
public void testQuery() throws Exception {
SolrQuery query = new SolrQuery("name:*");
//SolrQuery query = new SolrQuery("*:*");
query.setStart(0);//起始页
query.setRows(3);//每页显示数量

QueryResponse rsp = server.query( query );
SolrDocumentList results = rsp.getResults();
System.out.println("总记录数为:" + results.getNumFound());//查询总条数

for(SolrDocument document : results){
System.out.println(document.get("id") + " " + document.get("name") + " " + document.get("price"));
}
}


/**
* 多条件查询
* @throws Exception
*/
@Test
public void testQueryMulti() throws Exception {
ModifiableSolrParams params = new ModifiableSolrParams();

//组织查询条件
params.set("q","*:*");
params.set("start",0);
params.set("rows",10);
params.set("sort","price desc");

QueryResponse response = server.query(params);
SolrDocumentList list = response.getResults();

for(SolrDocument doc : list){
System.out.println(server.getBinder().getBean(Car.class,doc));
}
}

/**
*
* @throws Exception
*/
@Test
public void testQueryCase() throws Exception{
SolrQuery params = new SolrQuery();

//AND OR NOT条件
// params.set("q","name:35系 AND price:152880.0");
// params.set("q","name:5系 OR price:152880.0");
// params.set("q","name:5系 NOT price:152880.0");

//To 条件 min <= price <= max
// params.set("q","price:[130000 TO 160000]");

//To 条件 min < price < max
// params.set("q","price:{130000 TO 160000}");
// params.addFacetQuery("name:宝马");

//显示设置
// params.setHighlight(true);
// params.addHighlightField("name");
// params.setHighlightSimplePre("<font color = 'red'>");
// params.setHighlightSimplePost("</font>");
// params.setHighlightSnippets(1);
// params.setHighlightFragsize(100);


// params.set("start",0);
// params.set("rows",10);
params.set("sort","price desc");
QueryResponse response = server.query(params);
SolrDocumentList list = response.getResults();

for(SolrDocument doc : list){
System.out.println(server.getBinder().getBean(Car.class,doc));
}
}

@After
public void destroy(){
server.shutdown();
server = null;
}
}

参考JAVA对Solr的增删查改

本文地址: https://github.com/maxzhao-it/blog/post/3276/

java.lang.IllegalStateException: Unable to read meta-data for class

1
java.lang.IllegalStateException: Unable to read meta-data for class

一般是由 resources\META-INF\spring.factories文件引起的,检查当前文件。

本文地址: https://github.com/maxzhao-it/blog/post/38204/