1、查询
(1)获取cloud solr server
Collapse source
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
|
/**
* 获取cloud solr server
* @author caibosi
* @created 2013-12-04
*/
public
enum
SolrServerManager {
CONTRACT_LIST(
"collection1"
),
REQUEST_AUDIT_LIST(
"collection2"
);
String serverName;
Logger logger = LoggerFactory.getLogger(SolrServerManager.
class
);
final
int
zkClientTimeout =
20000
;
// 心跳20秒
final
int
zkConnectTimeout =
10000
;
// 连接时间10秒
//如果采用的cloud server, 这个地址为zookeeper的地址,192.168.4.129:9983
final
String zkHost =
"ct.solrcloud.host"
;
CloudSolrServer cloudSolrServer =
null
;
private
SolrServerManager(String serverName){
this
.serverName = serverName;
try
{
cloudSolrServer =
new
CloudSolrServer(ConfigUtil.getValueByKey(
this
.zkHost));
logger.info(
"### get solr server"
);
}
catch
(MalformedURLException e) {
logger.error(
"connect solr server error"
,e);
}
cloudSolrServer.setDefaultCollection(
this
.serverName);
cloudSolrServer.setZkClientTimeout(
this
.zkClientTimeout);
cloudSolrServer.setZkConnectTimeout(
this
.zkConnectTimeout);
}
public
CloudSolrServer getSolrServer(){
return
cloudSolrServer;
}
}
|
(2)查询实例
Collapse source
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
|
/**
* 封装solr查询方法
* @author caibosi
* @created 2013-12-05
* @param query
* @return
* @throws SolrServerException
*/
public
SolrDocumentList getSolrDocumentList(SolrQuery query)
throws
SolrServerException {
CloudSolrServer server = SolrServerManager.CONTRACT_LIST.getSolrServer();
QueryResponse response = server.query(query);
SolrDocumentList docs = response.getResults();
logger.info(
"### query:"
+ decodeByUTF8(query.toString()));
logger.info(
"### solr response:"
+ToStringBuilder.reflectionToString(response));
return
docs;
}
// 将字符串 UTF-8 解码
public
String decodeByUTF8(String str) {
String target;
try
{
target = URLDecoder.decode(str,
"UTF-8"
);
}
catch
(Exception e) {
logger.error(
"解码出错!"
, e);
return
str;
}
return
target;
}
/**
* contract/list
* 根据合同编号 或 合同名称查询
*
* 抛出RuntimeException的话
* 会导致事务的回滚
* 影响到页面报错
*
* 因而抛出Exception
* http://xdy2008.iteye.com/blog/1973725
*
* @author caibosi
* @created 2013-12-05
* @param keyword
* @param page
* @return
*/
public
List<Contract> getContractListByKeyword(String keyword,Page page)
throws
Exception {
boolean
solrEnabled = ConfigUtil.getValue2BooleanByKey(
"solr.enabled"
);
if
(solrEnabled ==
false
)
throw
new
Exception(
"solr service disabled"
);
long
start = System.currentTimeMillis();
logger.info(
"### query contract list using solr, keyword :"
+keyword);
SolrQuery query =
new
SolrQuery();
query.setQuery(
"contractName:"
+keyword+
" OR contractNum:"
+keyword);
SolrDocumentList docs = getSolrDocumentList(query);
List<String> contractIds =
new
ArrayList<String>();
if
(CollectionUtils.isEmpty(docs) ==
false
){
for
(SolrDocument doc : docs){
String contractId = MapUtils.getString(doc,
"id"
);
if
(contractId!=
null
)
contractIds.add(contractId);
}
}
List<Contract> contractList =
new
ArrayList<Contract>();
if
(CollectionUtils.isEmpty(contractIds) ==
false
){
contractList = contractDao.findByIds(contractIds);
}
long
end = System.currentTimeMillis();
logger.info(
"### get by solr,cost:"
+(end-start));
return
contractList;
}
|
2、植入业务代码
植入方案:
(1)在原有的代码里头增加solr查询逻辑
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
|
/**
* @param contractListBean
* @param page
* @return
*/
@SolrCached
public
List<Contract> findContractListByContractListBean(ContractListBean contractListBean,
Page page) {
if
(StringUtil.isNotBlank(contractListBean.getKeyword())) {
// 有关键字,先等值匹配,再like
long
start = System.currentTimeMillis();
/**
* spring aop 不支持拦截方法内部再调同一类的其他方法
* 先写在这里测试下solr功能
*/
try
{
List<Contract> list = solrService.getContractListByKeyword(contractListBean.getKeyword(),page);
return
list;
}
catch
(Exception e) {
logger.error(
"get from solr by keyword error,get from db instead"
,e);
}
logger.info(
"### query key word from db :"
+contractListBean.getKeyword());
List<Contract> list = findContractListByContractListBeanInternal(contractListBean, page,
true
);
long
end = System.currentTimeMillis();
logger.info(
"### cost:"
+(end-start));
// if (list.size() == 0) {
// return findContractListByContractListBeanInternal(contractListBean, page, true);
// } else {
// return list;
// }
return
list;
}
else
{
// 没关键字,只查一次
return
findContractListByContractListBeanInternal(contractListBean, page,
true
);
}
}
|
Collapse source
优点:明了,直接,程序员看着直白,知道这里有经过solr的缓存
缺点:在编程的时候植入,业务逻辑与缓存逻辑混杂在一起
(2)利用aop,动态植入
原有的方法仅仅加一个注解
Collapse source
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
|
/**
* @param contractListBean
* @param page
* @return
*/
@SolrCached
(SolrCachedMethod.CONTRACT_LIST_KEYWORD)
public
List<Contract> findContractListByContractListBean(ContractListBean contractListBean,
Page page) {
if
(StringUtil.isNotBlank(contractListBean.getKeyword())) {
// 有关键字,先等值匹配,再like
long
start = System.currentTimeMillis();
List<Contract> list = contractService.findContractListByContractListBeanInternal(contractListBean, page,
true
);
long
end = System.currentTimeMillis();
logger.info(
"### cost:"
+(end-start));
// if (list.size() == 0) {
// return findContractListByContractListBeanInternal(contractListBean, page, true);
// } else {
// return list;
// }
return
list;
}
else
{
// 没关键字,只查一次
return
contractService.findContractListByContractListBeanInternal(contractListBean, page,
true
);
}
}
|
在拦截器里头处理
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
|
/**
* 拦截service相关方法
* 使用solr进行查询
* @author caibosi
* @created 2013-12-05
*/
@Aspect
@Component
public
class
SolrInterceptor {
private
final
Logger logger = LoggerFactory.getLogger(SolrInterceptor.
class
);
@Resource
private
SolrService solrService;
/**
* 拦截带有SolrCached的方法
* @param pjp
* @return
* @throws Throwable
*/
@Around
(
"@annotation(solrCached)"
)
public
Object getResultBySolr(ProceedingJoinPoint pjp,SolrCached solrCached)
throws
Throwable {
try
{
if
(solrCached.value() == SolrCachedMethod.CONTRACT_LIST_KEYWORD){
Object[] args = pjp.getArgs();
ContractListBean queryBean = (ContractListBean) args[
0
];
Page page = (Page) args[
1
];
if
(queryBean!=
null
&& queryBean.getKeyword()!=
null
){
return
solrService.getContractListByKeyword(queryBean.getKeyword(),page);
}
logger.info(
"### into findContractListByContractListBean"
);
}
}
catch
(Exception e){
logger.error(
"query solr error,query db instead"
,e);
}
return
pjp.proceed(pjp.getArgs());
}
}
|
Collapse source
优点:与业务逻辑分离开
缺点:程序员可能不知道此处会被拦截,进行solr查询
因而,采用注解的形式告知,但spring aop不支持同一类A方法内部调B方法中B方法的拦截,因而找到最细粒度的方法进行拦截可能优点困难,因而这个方法可能没办法拦截到。
解决方法:1、改造原来方法(移动其他类,但是改动大) 2、采用兼容模式,无法被拦截的,直接在代码里头写,可以被拦截的就在interceptor里头写、
3、同步索引
索引的同步与业务逻辑关联比较大,每个solr缓存的数据的触发条件都不大一样。
(1)增加
根据contract的add_time,对solr进行增量同步
方案1:采用delta import, 在配置文件里配置,同时使用crontab 定时执行该命令的url
方案2:在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
|
public
SolrImortResult importContractListByPage(Date start,Date end,Page page){
List<Contract> contractList = contractDao.findByAddTime(start,end,page);
if
(CollectionUtils.isEmpty(contractList)){
return
new
SolrImortResult(
new
Date(),
new
Date(),
0
);
}
else
{
logger.info(
"#### begin to update count:"
+contractList.size());
}
List<SolrInputDocument> inputDocs =
new
ArrayList<SolrInputDocument>();
SolrImortResult result =
new
SolrImortResult();
int
updateCount = -
1
;
for
(Contract ct : contractList){
SolrInputDocument doc =
new
SolrInputDocument();
doc.addField(
"contractId"
,ct.getContractId());
doc.addField(
"contractNum"
,ct.getContractNum());
doc.addField(
"contractName"
,ct.getContractName());
inputDocs.add(doc);
}
CloudSolrServer server = SolrServerManager.CONTRACT_LIST.getSolrServer();
try
{
server.connect();
server.add(inputDocs);
UpdateResponse response = server.commit();
if
(response!=
null
){
logger.info(
"### update to solr for page:"
+page.getPageNo()+
";response:"
+ToStringBuilder.reflectionToString(response));
updateCount = page.getPageSize();
}
}
catch
(Exception e) {
logger.error(
"update contract list to solr error"
,e);
updateCount = -
1
;
}
Date startTime = contractList.get(
0
).getAddTime();
Date endTime = contractList.get(contractList.size()-
1
).getAddTime();
result.setStartTime(startTime);
result.setEndTime(endTime);
result.setUpdateCount(updateCount);
return
result;
}
|
Collapse source
(2)更新
add方法添加字段,set方法更新
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
|
package
com.persia.solrj;
import
org.apache.solr.client.solrj.SolrServerException;
import
org.apache.solr.client.solrj.impl.CloudSolrServer;
import
org.apache.solr.common.SolrInputDocument;
import
org.slf4j.Logger;
import
org.slf4j.LoggerFactory;
import
org.springframework.stereotype.Service;
import
java.io.IOException;
import
java.util.*;
/**
* solr服务
* 获取solr server
* @author caibosi
* @created 2013-12-04
*/
@Service
public
class
SolrTestService {
private
static
final
Logger logger = LoggerFactory.getLogger(SolrTestService.
class
);
/**
* 这种有效果,删除再添加
* 但不知道与map方式的区别
* 如果是add全部字段,其实是没有区别滴
* 如果add部分字段,没有add的字段会丢失掉
* @throws java.io.IOException
* @throws org.apache.solr.client.solrj.SolrServerException
*/
public
void
updateByAdd()
throws
IOException, SolrServerException {
CloudSolrServer server = SolrServerManager.CONTRACT_SEARCH.getSolrServer();
server.connect();
SolrInputDocument doc =
new
SolrInputDocument();
doc.addField(
"contractId"
,
"74acd736-f84e-11e2-8dea-00222822153f"
);
doc.addField(
"contractName"
,
"测试update操作23"
);
doc.addField(
"contractNum"
,
"测试update操作33"
);
server.add(doc);
server.commit();
logger.info(
"### update success,contractId:{283a8436-481c-11e3-8dea-00222822153f}"
);
}
/**
* 下面这种纯属字面上的误会
* 实际上会更新整个文档
* 假设文档原来十几个字段
* 这种方式会替换为3个字段(如下)
* http://osdir.com/ml/solr-user.lucene.apache.org/2012-07/msg01065.html
*
* not setting internal flags for the
indexer to treat the SolrInputDocument differently based on if set or
add was called
*
* @throws java.io.IOException
* @throws org.apache.solr.client.solrj.SolrServerException
*/
public
void
updateBySet()
throws
IOException, SolrServerException {
CloudSolrServer server = SolrServerManager.CONTRACT_SEARCH.getSolrServer();
server.connect();
SolrInputDocument doc =
new
SolrInputDocument();
//如果id也用setfield的话,当文档不存在的时候,也会创建
doc.addField(
"contractId"
,
"74acd736-f84e-11e2-8dea-00222822153f"
);
doc.setField(
"contractNum"
,
"234254646664c1set"
);
doc.setField(
"contractName"
,
"测试update操作3set"
);
server.add(doc);
server.commit();
logger.info(
"### update success,contractId:{283a8436-481c-11e3-8dea-00222822153f}"
);
}
/**
|