网页右边,向下滑有目录索引,可以根据标题跳转到你想看的内容 |
---|
如果右边没有就找找左边 |
整个案例的源码(可作为日后开发脚手架):https://download.csdn.net/download/grd_java/21094886 |
---|
Solr讲解:https://blog.csdn.net/grd_java/article/details/119476059 |
---|
Dubbo讲解:https://blog.csdn.net/grd_java/article/details/119322297 |
---|
RabbitMQ讲解:https://blog.csdn.net/grd_java/article/details/119696892 |
---|
启动solr、RabbitMQ、Zookeeper
一、创建项目,搭建环境
1. 创建父工程,引入依赖 |
---|
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.10.RELEASE</version>
</parent>
<dependencyManagement>
<dependencies>
<!--Spring boot不带web依赖,不会提供端口,因为一些-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.5.3</version>
</dependency>
<!--Spirng boot带web,会自动提供端口-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.5.3</version>
</dependency>
<!--thymeleaf 模板依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
<version>2.5.3</version>
</dependency>
<!--spring boot data 封装了solrJ 的jar包-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-solr</artifactId>
<version>2.1.10.RELEASE</version>
</dependency>
<!--amqp RabbitMQ-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
<version>2.1.10.RELEASE</version>
</dependency>
<!--Dubbo整合-->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>2.7.3</version>
</dependency>
<!--Zookeeper整合-->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>4.2.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>4.2.0</version>
</dependency>
<!--myBatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.19</version>
</dependency>
<!--apace commons工具-->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.11.0</version>
</dependency>
</dependencies>
</dependencyManagement>
2. 创建pojo模块存放实体类,创建与solr和数据库对应的实体类(必须继承序列化接口) |
---|
3. 创建mapper模块,处理所有数据库相关操作 |
---|
- 依赖,从父工程依赖中复制过来,然后引入自己的实体类子模块
- mapper接口
- xml文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.yzpnb.mapper.ProductMapper">
<insert id="insertProduct" parameterType="com.yzpnb.bean.Product">
insert into t_product(name,price) value(#{name},#{price});
</insert>
</mapper>
- 配置文件
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.47.1:3306/dubbo_demo?serverTimezone=CST&useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true #?后面参数表示时区,非常重要
username: root
password: 123456
# MyBatis
mybatis:
# 搜索指定包别名
type-handlers-package: com.yzpnb.bean
# 配置mapper的扫描,找到所有的mapper.xml映射文件
mapperLocations: classpath:mybatis/*Mapper.xml
4. 创建api模块,存放Dubbo远程调用接口 |
---|
- 引入依赖(引入自己pojo和mapper子模块即可)
- 远程调用接口
5. 创建provider子模块,生产所有远程调用接口 |
---|
- 依赖(需要api子模块,自动继承api中依赖)
<dependencies>
<dependency>
<groupId>com.yzpnb</groupId>
<artifactId>api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!--Spring boot不带web依赖,不会提供端口,因为一些-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!--Dubbo整合-->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
</dependency>
<!--Zookeeper整合-->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
</dependency>
</dependencies>
- 配置文件
dubbo:
application:
name: dubbo-provider # 在dubbo注册中心的名字
registry:
address: zookeeper://192.168.10.105:2181 # zookeeper地址,用zookeeper协议
protocol:
port: 20884 #dubbo端口,可以不写,和Dubbo注册中心取到联系
spring:
profiles:
active: mybatis # 加载其它配置文件,加载的是 application-*.yaml,我们只要指定*所代表的即可
- 启动类
- 远程api接口实现类
二、商品新增
1. 创建子模块product,用来处理所有和product相关的服务 |
---|
- 依赖,需要调用远程api接口,其它就是必须的spring-web,dubbo,zookeeper
- service
- controller
import com.yzpnb.bean.Product;
import com.yzpnb.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/product")
public class ProductController {
@Autowired
private ProductService productService;
//http://localhost:8080/product/insertProduct/huawei/6999
@GetMapping("insertProduct/{name}/{price}")
public int findAllDept(@PathVariable("name") String name,@PathVariable("price") Double price){
Product product = new Product();
product.setPrice(price);
product.setName(name);
return productService.insertProduct(product);
}
}
三、Solr同步
1. 创建子模块solr-pojo,存放solr需要使用的实体类 |
---|
- 引入依赖
- 创建实体类
2. 创建子模块search,用于操作solr |
---|
- 引入依赖(amqp rabbitmq的包不需要引入)
- 启动类
- 配置文件
- service
import com.yzpnb.bean.search_pojo.SearchProduct;
import com.yzpnb.service.SearchService;
import org.apache.solr.client.solrj.response.UpdateResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.solr.core.SolrTemplate;
import org.springframework.stereotype.Service;
@Service
public class SearchServiceImpl implements SearchService {
//相当于SolrJ的Solr客户端,这里Spirng data帮我们封装了
@Autowired
private SolrTemplate solrTemplate;
/**
* 插入数据到solr
* @param searchProduct
* @return
*/
@Override
public boolean insert(SearchProduct searchProduct) {
//这里我们执行添加修改操作时,需要指定对哪个核心做操作,这里操作testcore核心
UpdateResponse response = solrTemplate.saveBean("testcore", searchProduct);
solrTemplate.commit("testcore");//提交也需要指定提交到哪个核心
if(response.getStatus() == 0){
return true;
}
return false;
}
}
- 编写配置类,定义SolrTemplate的Bean实例,否则会报错,找不到SolrTemplate
import org.apache.solr.client.solrj.SolrClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.solr.core.SolrTemplate;
@Configuration
public class SolrConfig {
@Autowired
SolrClient solrClient;
@Bean
public SolrTemplate getSolrTemplate(){
return new SolrTemplate(solrClient);
}
}
- 先编写一个controller测试一下业务是否正常
import com.yzpnb.bean.search_pojo.SearchProduct;
import com.yzpnb.service.SearchService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/product-search")
public class SearchController {
@Autowired
private SearchService searchService;
@GetMapping("insertSearchProduct/{name}/{price}")
public boolean insert(@PathVariable("name") String name,@PathVariable("price") Double price){
SearchProduct searchProduct = new SearchProduct();
searchProduct.setName(name);
searchProduct.setPrice(price);
return searchService.insert(searchProduct);
}
}
- 运行测试(测试完,就可以将controller删除了,他和mapper子模块一样,只是一个用来远程调用使用的接口)
3. 同步solr |
---|
- api子模块中引入search子模块(方便我们远程调用)
- 重写Provider 调用接口
import com.yzpnb.bean.Product;
import com.yzpnb.bean.search_pojo.SearchProduct;
import com.yzpnb.dubbo.service.ProductDubboService;
import com.yzpnb.mapper.ProductMapper;
import com.yzpnb.service.SearchService;
import org.apache.dubbo.config.annotation.Service;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
@Service
public class ProductDubboServiceImpl implements ProductDubboService {
@Autowired
private ProductMapper productMapper;
@Autowired
private SearchService searchService;
@Override
public int insertProduct(Product product) {
int i = productMapper.insertProduct(product);
if(i == 1){
SearchProduct searchProduct = new SearchProduct();
BeanUtils.copyProperties(product,searchProduct);//把product的属性复制给searchProduct
boolean b = searchService.insert(searchProduct);
if(b == true){
return 1;//同步插入成功
}
}
//否则插入失败,
//删除刚刚插入到数据库的数据,这里省略这些步骤,id我们也自增的,实际项目都是需要自己生成id,记录,这一步,通过记录的id,删除
//为了简单直接返回0
return 0;
}
}
- 启动provider和product,测试
四、通过RabbitMQ 实现异步同步solr
- 前面的代码有一个问题,solr的同步都写到了一起,这样做,每次存储,都会浪费同步的时间
- 通过RabbitMQ就可以异步完成这件事,数据存储到数据库,不用关心同步问题,让RabbitMQ帮忙干这件事即可
首先,因为RabbitMQ规定,如果发送消息是一个对象,那么必须序列化实体类对象 |
---|
public static final long serialVersionUID=1l;
其次,因为Solr子模块search,我们刚刚删除了controller和启动类,需要相应更改 |
---|
- 因为没有启动类,所以需要依赖其它模块的服务器进行加载,那么首先需要修改配置文件的名字,方便之后加载
1. 创建子模块rabbitmq,存放RabbitMQ的所有发送操作(发送消息到队列) |
---|
- 依赖
- 配置文件(因为这同样是一个不需要启动的模块,需要借助其它模块加载配置文件)
- 配置类,配置队列实例Bean
- 发送类,提供发送消息到队列的方法
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class Sender {
@Autowired
private AmqpTemplate amqpTemplate;
public void send(Object obj){
amqpTemplate.convertAndSend("MyQueue",obj);
System.out.println("RabbitMQ-----------A obj send to MyQueue");
}
}
2. 创建子模块receive,存放所有接收队列消息的操作 |
---|
- 依赖
- 配置文件
- 启动类
- 接收类
import com.yzpnb.bean.search_pojo.SearchProduct;
import com.yzpnb.service.SearchService;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
@Component//启动项目,自动注入这个类
public class Receive {
@Autowired
private SearchService searchService;
@RabbitListener(queues = "MyQueue")//标注此方法为接收MyQueue队列消息的方法
public void solr(Object obj){
System.out.println("RabbitMQ solr方法 收到 MyQueue的消息---------");
Message msg = (Message) obj;
ByteArrayInputStream bIS = null;
ObjectInputStream oIS = null;
try{
bIS = new ByteArrayInputStream(msg.getBody());
oIS = new ObjectInputStream(bIS);
Object o = oIS.readObject();
SearchProduct searchProduct = (SearchProduct) o;
boolean b = searchService.insert(searchProduct);
if(b == true){//同步成功结束代码执行
return;
}else{
//同步失败,执行逻辑,数据库内容删除,这里省略
}
}catch (Exception e){
e.printStackTrace();
}finally {
try {
bIS.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
oIS.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
- 重写provider的远程调用,同步不需要它来同步了
运行测试 |
---|
- 启动provider
- 启动receive
- 启动product