py-20-JTWEB02
作者:互联网
京淘2
目录:
day15:微服务dubbo,zookeeper
day16:消息队列rabbitmq
day17:Solr
day18:容器docker
day19:手机端android
day20:培优讲义
day15:微服务dubbo,zookeeper
2.课程安排
- 微服务 dubbo,zookeeper
- 消息队列 rabbitmq
- Solr
- 容器 docker
- 手机端 android
3.rpc
Dubbo 是微服务 , 服务是 service-
需求分析(2019-2-21)
- 不容易定位 bug
- 不容易扩展
- 不能单独发布
- 容易定位 bug
- 容易扩展
- 能单独发布
2.分析
3.设计
-
Rpc server
- Maven quick start
- cartService
- cartServiceImpl
- providerMain
2. Rpc client
- maven quick start
- cartService
- CartController.invoke 测试
- Invoke 中联网 测试
4.实现
5.Rpc总结
4.Zookeeper
-
安装虚拟机
- 解压 \ 培优课前资料 \ 京淘课前资料下发 -2\3-jt 初始化虚拟机 \ dsCentOS-empty.zip
- 启动 vmware
- 双击解压后 vmx 文件, vmware 就会加载虚拟机
- 设置静态 ip
2.安装zookeeper
-
装java1.8
- 使用 fz 访问 /usr/local/src
- 创建文件夹 java
- 上传 java1.8
- 在 xshell 中解压 java1.8
5.使用fz访问/etc/profile修改java目录
6.在xshell中执行source /etc/profile让环境变量生效
7.在xshell中执行java -version
2.装zookeeper
- 从 ftp/source 中下载 zookeeper
- 解压 /usr/local/src/zookeeper.tar.gz
- Xshell 中在 /usr/local/src/zookeeper 文件中执行 mkdir data log
cd zookeeper-3.4.8
[root@localhost zookeeper-3.4.8]# ls
bin ivysettings.xml recipes
[root@localhost zookeeper-3.4.8]# pwd
/usr/local/src/zookeeper-3.4.8
[root@localhost zookeeper-3.4.8]# mkdir data log
4.修改配置文件
文件改成 zoo.cfg 12 行修改 dataDir, 增加 dataLogDirdataDir=/usr/local/src/zookeeper-3.4.8/data
dataLogDir=/usr/local/src/zookeeper-3.4.8/log
传到/usr/local/src/zookeeper-3.4.8/conf
5.启动 zookeeper 服务器[root@localhost zookeeper-3.4.8]# cd bin
[root@localhost bin]# ls
zkCli.sh zkEnv.sh zkServer.sh
[root@localhost bin]# sh zkServer.sh start
Starting zookeeper ... STARTED
6.启动
zookeeper
客户端
Redis-server redis-cli
zkServer.sh zkCli.sh
进入zookeeper客户端
[root@localhost bin]# sh zkCli.sh
列出注册的服务
[zk: localhost:2181(CONNECTED) 0] ls /
[zookeeper]
退出
[zk: localhost:2181(CONNECTED) 1] quit
5.Dubbo案例
-
分析
2.设计
-
dubboInterface
2.dubboProvider1
- 创建 maven
- 依赖 dubboInterface
- 依赖 dubbo,zookeeper
- 实现类 CartServiceImpl
- applicationContext-provider.xml
- 启动 spring 框架
- 执行 zkCli.sh
[zk: localhost:2181(CONNECTED) 0] ls /
[zookeeper]
[zk: localhost:2181(CONNECTED) 1] ls /
[dubbo, zookeeper]
查看 dubbo服务
[zk: localhost:2181(CONNECTED) 2] ls /dubbo
[com.tedu.service.CartService]
查看cartService有多少个提供者
[zk: localhost:2181(CONNECTED) 3] ls /dubbo/com.tedu.service.CartService/providers
3.Dubbo后台管理,监控
不同 jdk 用不同的 root.war- 查看 windows 的 jdk 版本号
- 把对应的 root.war 拷贝到 tomat/webapps
- 双击 /bin/startup.bat 启动 tomcat,tomcat 会把 root.war 解压到 root 中
- 修改 root/web-inf/dubbo.properties
dubbo.registry.address=zookeeper://192.168.216.202:2181
dubbo.admin.root.password=root
dubbo.admin.guest.password=guest
5.重启
tomcat
6.浏览器访问
http://localhost:8090
4.dubboProvider2
- 拷贝 dubboprovider1 项目
- 修改 pom.xml
<groupId>com.tedu</groupId>
<artifactId>dubboProvider2</artifactId>
3.修改
cartServiceImpl
public String findCartByUserId(Long userId) {
return "提供者2 返回"+userId;
}
4.修改
applicationContext-provider.xml
<!-- 1. 设置应用名称-->
<dubbo:application name="provider2-of-cart"/>
<!-- 2.配置zookeeper地址 -->
<dubbo:registry address="zookeeper://192.168.216.202:2181">
</dubbo:registry>
<!-- 3.配置服务的端口号 -->
<dubbo:protocol port="20889" name="dubbo">
</dubbo:protocol>
<!-- 4.配置实现类的类名 -->
<bean class="com.tedu.dubboProvider.CartServiceImpl" id="cartService">
</bean>
<!-- 5.配置接口名,开放服务 -->
<dubbo:service interface="com.tedu.service.CartService" ref="cartService">
</dubbo:service>
5. 启动服务器
public static void main(String[] args) {
//spring框架
ClassPathXmlApplicationContext context=new
ClassPathXmlApplicationContext("applicationContext-provider.xml");
System.out.println("start provider 2");
context.start();
while(true)
{}
}
6.在dubbo后台管理中能看到提供者2
5.dubboConsumer
-
quick start
-
依赖dubboInterface
-
依赖dubbo,zookeeper
-
配置文件applicationContext-consumer.xml
-
启动spring框架,调服务
3.实现
5.问题
跨进程和跨域
一台电脑上Tomcat访问mysql是跨进程
一台电脑上访问另一台电脑的进程
跨域
A网站用js(ajax)访问B网站的数据(json,xml)
A网站能访问B网站的js,image
<script src=baidu/jquery.js>
<img scr=baidu/1.png
day16:消息队列rabbitmq
-
下载资料
2.复习
Dubbo 是个微服务框架,以前叫 soa SOA 面向服务的架构 ( Service-oriented architecture )是一个组件模型,它将应用程序的不同功能单元(称为服务)通过这些服务之间 定义良好的接口 和契约联系起来。 接口是采用中立的方式进行定义的 ,它应该独立于实现服务的硬件平台、操作系统和编程语言。这使得构建在各种各样的系统中的服务可以以一种统一和通用的方式进行交互。 把代码分开,用 servlet,httpclient,webservice,dubbo Zookeeper 服务注册中心 服务有服务器,也叫服务提供者 服务有客户端,也叫服务消费者3.重构京淘
Jt-web 是服务消费者 Com.jt.web.cartService Jt-cart 是服务提供者 Com.jt.cart.CartService 以前 重构4.重构购物车
-
需求
2.分析
3.设计
-
导入改后的京淘
- 查看 maven 位置
- Eclipse à file à switch workspace à other
- 设置 maven 位置
文件寻贴主得
-
上传 \京淘项目每日课前资料\Day07\redis-3.2.8.tar.gz到/usr/local/src
-
tar -xzvf redis-3.2.8.tar.gz
-
cd redis-3.2.8
-
make install
-
上传 ftp/code/day16/cluster到/usr/local/src/ redis-3.2.8
-
cd cluster
-
sh start.sh
-
ps -ef | grep redis
-
在redis的根目录下执行
./src/redis-trib.rb create --replicas 2 192.168.216.202:7000 192.168.216.202:7001 192.168.216.202:7002 192.168.216.202:7003 192.168.216.202:7004 192.168.216.202:7005 192.168.216.202:7006 192.168.216.202:7007 192.168.216.202:7008
./src/redis-trib.rb create --replicas 2 192.168.228.132:7000 192.168.228.132:7001 192.168.228.132:7002 192.168.228.132:7003 192.168.228.132:7004 192.168.228.132:7005 192.168.228.132:7006 192.168.228.132:7007 192.168.228.132:7008
-
查看集群状态
3.运行京淘
-
关闭验证
2.改redis地址
3.启switch hosts
4.启nginx
5.每个项目 run as àmaven install
6.通过maven运行tomcat
4.Jt-parent添加依赖
5.创建jt-interface
6.修改jt-cart
3.实现
4.购物车消费者
-
Jt-web添加jt-interface,dubbo依赖
-
删除pop.Cart,CartService,CartServiceImpl
-
Jt-web/CartController中的com.jt.web.pojo.Cart改成com.jt.cart.pojo.Cart,service的包也要改
-
Jt-web/OrderController也改
-
拷贝昨天配置文件
<!-- 1,应用名称 -->
<dubbo:application name="consumer-of-cart"/>
<!-- 2,zookeeper -->
<dubbo:registry address="zookeeper://192.168.216.202:2181"></dubbo:registry>
<!-- 3,配置那些接口由dubbo来执行 check=false 网站启动时,不检查有没有提供者-->
<dubbo:reference timeout="90000" check="false" interface="com.jt.cart.service.CartService" id="cartService"></dubbo:reference>
6.Jt-web run as maven install
7.deubug运行
8.zkCli.sh看到消费者
9.dubbo后台管理看到消费者
10.在浏览器www.jt.com/cart/show.html,用debug跟踪程序
6.购物车添加商品,修改数量
在Jt-cart中cartServiceImpl三个方法加断点
在Jt-web中cartController三个方法中加断点
5.重构订单
-
Jt-interface
-
创建com.jt.order.pojo
-
创建com.jt.order.service
-
从jt-order/pojo/拷贝实体类过来
-
从jt-order/service拷贝OrderService
5.Run as àmaven install
2.Jt-order
-
依赖jt-interface
-
依赖dubbo,zookeeper
-
删pojo中实体类
-
删service/OrderService
-
删除controller/OrderController
-
删除Service/OrderServiceImpl中的@Service
-
从jt-cart中拷贝配置文件applicationContext-provider.xml
-
改应用名,不改zookeeper地址,改端口号,改bean,改对外公开的接口名
-
Run as àmaven install
-
启动jt-order
-
sh zkCli.sh
12.dubbo后台管理看到提供者
3.Jt-web
-
删除service.OrderService
-
删除service.OrderServiceImpl
-
OrderController中把com.jt.web.service.OrderService改成com.jt.order.service.OrderService
-
删除pojo三个实体类Order,OrderItem,OrderShipping.
-
orderController 中import com.jt.web.pojo.Order改成com.jt.order.pojo.Order
-
修改applicationContext-sumber.xml增加
<dubbo:reference timeout="90000" check="false"
interface="com.jt.order.service.OrderService"
id="orderService"></dubbo:reference>
7.Run as àmaven install
8.sh zkCli.sh查看消费者有没有注册成功
9.dubbo后台管理查看消费者
10.删除所有断点
11.访问网站,提交订单
12.Jt-web/orderController中两个方法加断点
13.Jt-order/OrderServiceImpl两个方法加断点
6.比较软件
-
在资源管理器,选中001,右键“选 为左侧对比窗口”
2.选中005 右键“与001 比较”
7.消息队列
-
消息队列作用
-
使用场景
-
应用解耦
场景说明:用户下单后,订单系统需要通知库存系统。传统的做法是,订单系统调用库存系统的接口。如下图
传统模式的缺点:
-
假如库存系统无法访问,则订单减库存将失败,从而导致订单失败
-
订单系统与库存系统耦合高
如何解决以上问题呢?引入应用消息队列后的方案,如下图:
-
订单系统:用户下单后,订单系统完成持久化处理,将消息写入消息队列,返回用户订单下单成功
-
库存系统:订阅下单的消息,获取下单信息,库存系统根据下单信息,进行库存操作
-
假如:在下单时库存系统不能正常使用。也不影响正常下单,因为下单后,订单系统写入消息队列就不再关心其他的后续操作了。实现订单系统与库存系统的应用解耦
流量削锋
流量削锋是消息队列中的常用场景,一般在秒杀或团抢活动中使用广泛
应用场景:秒杀活动,一般会因为流量过大,导致流量暴增,应用挂掉。为解决这个问题,一般需要在应用前端加入消息队列。
-
用户的请求,服务器接收后,首先写入消息队列。假如消息队列长度超过最大数量,则直接抛弃用户请求或跳转到错误页面
-
秒杀业务根据消息队列中的请求信息,再做后续处理
-
通过消息队列缓解服务器压力.
异步处理
场景说明:用户注册后,需要发注册邮件和注册短信。传统的做法有两种 1.串行的方式;2.并行方式
(1)串行方式:将注册信息写入数据库成功后,发送注册邮件,再发送注册短信。以上三个任务全部完成后,返回给客户端
(2)并行方式:将注册信息写入数据库成功后,发送注册邮件的同时,发送注册短信。以上三个任务完成后,返回给客户端。与串行的差别是,并行的方式可以提高处理的时间
假设三个业务节点每个使用50毫秒钟,不考虑网络等其他开销,则串行方式的时间是150毫秒,并行的时间可能是100毫秒。
如何解决这个问题呢?
引入消息队列,将不是必须的业务逻辑,异步处理。改造后的架构如下:
按照以上约定,用户的响应时间相当于是注册信息写入数据库的时间,也就是50毫秒。注册邮件,发送短信写入消息队列后,直接返回,因此写入消息队列的速度很快,基本可以忽略,因此用户的响应时间可能是50毫秒。
2.什么是消息队列
arrayList,queue
MQ全称为Message Queue, 消息队列(MQ)是一种应用程序对应用程序的通信方法。应用程序通过读写出入队列的消息来通信,而无需专用连接来链接它们。
消息队列,是一端写入数据到队列中,一端从队列中获取消息,并且通信通过RPC调用,可以传递java对象,方便操作
3.案例
4.缺点
增加了复杂度
可靠性降低
5.消息队列产品
6.rabbmitMq介绍
是电信爱立信公司,为银行业务负责研发的消息队列.
1. 基于erlang语言开发具有高可用高并发的优点,适合集群服务器。
2. 健壮、稳定、易用、跨平台、支持多种语言、文档齐全。
3. 有消息确认机制和持久化机制,可靠性高。
4. 开源
2.Rabbitmq安装
-
使用fz在/usr/local/src创建rabbitmq
-
Fz上传rabbitmq到/usr/local/src/rabbitmq
-
Xshell进入/usr/local/src/rabbitmq
-
执行命令是rpm -ivh rabbitmq-server-3.6.1-1.noarch.rpm
[root@localhost rabbitmq]# cd /usr/lib/rabbitmq/lib/rabbitmq_server-3.6.1/plugins
[root@localhost plugins]# ls
amqp_client-3.6.1.ez rabbitmq_mqtt-3.6.1.ez
cowboy-1.0.3.ez rabbitmq_recent_history_exchange-1.2.1.ez
cowlib-1.0.1.ez rabbitmq_sharding-0.1.0.ez
mochiweb-2.13.0.ez rabbitmq_shovel-3.6.1.ez
rabbit_common-3.6.1.ez rabbitmq_shovel_management-3.6.1.ez
rabbitmq_amqp1_0-3.6.1.ez rabbitmq_stomp-3.6.1.ez
rabbitmq_auth_backend_ldap-3.6.1.ez rabbitmq_tracing-3.6.1.ez
rabbitmq_auth_mechanism_ssl-3.6.1.ez rabbitmq_web_dispatch-3.6.1.ez
rabbitmq_consistent_hash_exchange-3.6.1.ez rabbitmq_web_stomp-3.6.1.ez
rabbitmq_event_exchange-3.6.1.ez rabbitmq_web_stomp_examples-3.6.1.ez
rabbitmq_federation-3.6.1.ez ranch-1.2.1.ez
rabbitmq_federation_management-3.6.1.ez README
rabbitmq_management-3.6.1.ez sockjs-0.3.4.ez
rabbitmq_management_agent-3.6.1.ez webmachine-1.10.3.ez
rabbitmq_management_visualiser-3.6.1.ez
[root@localhost plugins]# cd /usr/sbin
[root@localhost sbin]# ls
5.安装后台管理系统
[root@localhost rabbitmq]# cd /usr/sbin
[root@localhost sbin]# ls ra*
rabbitmqctl rabbitmq-plugins rabbitmq-server raid-check
[root@localhost sbin]# vim rabbitmq-plugins
Man查看命令的帮助文档
[root@localhost sbin]# man ls
[root@localhost sbin]# man rabbitmq-plugins
[root@localhost sbin]# rabbitmq-plugins enable rabbitmq_management
The following plugins have been enabled:
mochiweb
webmachine
rabbitmq_web_dispatch
amqp_client
rabbitmq_management_agent
rabbitmq_management
6.启动rabbitmq
[root@localhost sbin]# ls ra*
rabbitmqctl rabbitmq-plugins rabbitmq-server raid-check
[root@localhost sbin]# service rabbitmq-server start
Starting rabbitmq-server: SUCCESS
rabbitmq-server.
[root@localhost sbin]# pwd
/usr/sbin
7.在虚拟机中打开浏览器访问localhost:15672,用户名和密码是guest
8.在windows浏览器中输入http://192.168.216.202:15672,登录失败
9.修改配置文件允许guest在别的电脑登录后台管理系统
把文件rabbitmq.config.example下载到windows中
Windows中把rabbitmq.config.example改成rabbitmq.config
Windows中修改rabbitmq.config
删除64行前面的%%和后面的,
把文件传到/etc/rabbitmq/
10.重启rabbitmq
[root@localhost sbin]# pwd
/usr/sbin
[root@localhost sbin]# ls ra*
rabbitmqctl rabbitmq-plugins rabbitmq-server raid-check
[root@localhost sbin]# service rabbitmq-server restart
Restarting rabbitmq-server: SUCCESS
rabbitmq-server.
11.在windows中登录
3.Rabbitmq后台管理
7.课前资料
密码:3gxq
今天晚上在家里下
明天早上共享,互相拷贝。
9.总结
10.问题
解决方法
-
拷贝别人的maven 文件夹
-
Maven update,
3.重新配置maven
a.解压maven安装包
b.Conf/settings.xml配置mirror,文件夹
c.Eclipse关联
d.Maven的服务器改成aliyun
day20:培优讲义
-
课程安排
- 微服务D ubbo,zookeeper ,京淘中使用
- 消息队列R abbitmq ,京淘中使用
- 搜索服务器 Solr ,京淘中使用
- 容器D ocker ,京淘中使用
- 手机端商城开发 android
2.Dubbo
1.微服务好处
-
单体架构缺点:
2.分布式优点
- 拆分成多个项目如cart,sso,order,web容易维护,好定位bug
- 可扩展,订单部署 3 台服务器,web访问集群nginx,nginx再访问3台订单服务器
- 单独发布
3.分布式两种实现
- httpClient 跨域
2.RPC实现
RPC(Remote Procedure Call)—远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据。RPC使得开发包括网络分布式多程序在内的应用程序更加容易。 RPC采用客户机/服务器模式。请求程序就是一个客户机,而服务提供程序就是一个服务器。首先,客户机调用进程发送一个调用信息到服务进程,然后等待应答信息。在服务器端,进程保持睡眠状态直到调用信息到达为止。当一个调用信息到达,服务器获得数据,计算结果,发送答复信息,然后等待下一个调用信息,最后,客户端调用进程接收答复信息,获得进程结果,然后调用执行继续进行。 总结:RPC调用的规则可以传输java对象.底层实现时将数据转化流,并且该流经过加密处理.并且rpc内部使用UTF-8编码格式 要求:传输的java对象必须序列化3.Rpc实现
4.需求
一台服务器处理能力有限,把service放在另一台服务器上 单机存在的问题 加order服务器 容易定位bug,单独发布 什么是服务 服务提供者,服务消费者5.设计
6.实现
-
RPCProvider maven project
- cartServcie
- cartServiceImpl
- ProviderMain
public class ProviderMain {
public static void main(String[] args) {
try {
System.out.println("start server 20881");
ServerSocket serverSocket = new ServerSocket(20881);
while (true) {
ObjectInputStream objectInputStream = null;
ObjectOutputStream objectOutputStream = null;
try {
Socket socket = serverSocket.accept();
// 1,接收数据
InputStream inputStream = socket.getInputStream();
objectInputStream=new ObjectInputStream(inputStream);
String interfaceName = objectInputStream.readUTF();
String methodName = objectInputStream.readUTF();
Class[] parameterTypes = (Class[]) objectInputStream.readObject();
Object[] argments = (Object[]) objectInputStream.readObject();
// 2,执行方法
// 根据interfaceName找到实现类
// map(interfaceName,impl);
Class implClass = CartServiceImpl.class;
Object implObject = implClass.newInstance();
Method method = implClass.getMethod(methodName, parameterTypes);
Object result = method.invoke(implObject, argments);
// 3,返回数据
OutputStream outputStream = socket.getOutputStream();
objectOutputStream = new ObjectOutputStream(outputStream);
objectOutputStream.writeObject(result);
} catch (Exception e) {
e.printStackTrace();
} finally {
objectInputStream.close();
objectOutputStream.close();
}
}
} catch (Exception e) {
// TODO: handle exception
}
}
}
2.RPCConsumer
-
拷贝cartService
-
ConsumerMain
先复习代理Proxy
再联网
public class CartController {
public static void main(String[] args) {
try {
Object object = getObject(CartService.class);
CartService cartService = (CartService) object;
cartService.findCartByUserId(91L);
} catch (Exception e) {
e.printStackTrace();
}
}
static class MyInvocationHandler implements InvocationHandler {
String interfaceName;
public MyInvocationHandler(String interfaceName) {
super();
this.interfaceName = interfaceName;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
ObjectOutputStream objectOutputStream = null;
ObjectInputStream objectInputStream = null;
try {
// 1,得方法名,参数数据
String methodName = method.getName();
Class[] parameterTypes = method.getParameterTypes();
// 2,发送数据
Socket socket = new Socket("127.0.0.1", 20881);
OutputStream outputStream = socket.getOutputStream();
objectOutputStream = new ObjectOutputStream(outputStream);
objectOutputStream.writeUTF(interfaceName);
objectOutputStream.writeUTF(methodName);
objectOutputStream.writeObject(parameterTypes);
objectOutputStream.writeObject(args);
// 3,接收数据
InputStream inputStream = socket.getInputStream();
objectInputStream = new ObjectInputStream(inputStream);
Object result = objectInputStream.readObject();
System.out.println("收到远程方法执行结果" + result);
return result;
} catch (Exception e) {
e.printStackTrace();
} finally {
objectOutputStream.close();
objectInputStream.close();
}
return null;
}
}
public static Object getObject(final Class interfaceInfo) {
// 1.将本地的接口调用转换成JDK的动态代理,在动态代理中实现接口的远程调用
String interfaceName = interfaceInfo.getName();
ClassLoader classLoader = interfaceInfo.getClassLoader();
Class[] interfaces = { interfaceInfo };
MyInvocationHandler myInvocationHandler = new MyInvocationHandler(interfaceName);
Object object = Proxy.newProxyInstance(classLoader, interfaces, myInvocationHandler);
return object;
}
}
先运行成功
再断点调试
3.RPCInterface
为什么要创建rpcInterface
7.总结
RPC框架dubbo封装了客户端代理,联网通讯。
RPC是远程调用一个方法。是跨进程调用
RPC与httpClient对比
RPC与servlet对比
用http协议进行远程调用的缺点
4.Dubbo学习
8.微服务的治理方案
客户端直连服务提供者,无法实现集群。
业务逻辑说明:
-
当服务的提供者启动时,会将服务的名称:IP:端口会写入注册中心.
-
注册中心内部会维护服务列表,某个服务提供者关机了,服务还能正常进行
-
当消费者需要访问服务时,需要先访问注册中心获取服务列表信息.之后将服务列表保存到本地缓存中.方便后续的访问.在客户端内部有负载均衡的算法,筛选出一台服务器,之后进行访问.
-
如果后台服务器出现宕机现象.这时注册中心通过心跳检测的方式判断服务器是否宕机.如果服务器宕机则会将该服务器信息从服务列表中删除.之后将新的服务列表发送消费者(客户端)进行更新.
Zookeeper总结
5.Zookeeper
9.Zookeeper介绍
ZooKeeper是一个分布式应用程序协调服务。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。
当客户端发起请求时,zookeeper返回正确的服务器地址.
10.Zookeeper下载
6.Zookeeper安装
11.克隆虚拟机或加载新的虚拟机
\京淘课前资料下发-2\3-jt初始化虚拟机\
Centos6.5
查看windows vmnet8的ip,为虚拟机设置静态ip
关闭防火墙
service iptables status
service iptables stop
永久关闭防火墙
chkconfig iptables off
12.安装xshell
\京淘课前资料下发-2\8-Xshell
使用filezilla连接虚拟机上传文件,下载文件,编辑文件
京淘课前资料下发-2\7-Fz
执行rz,可上传文件,执行sz可下载文件
13.上传java
使用fz进入/usr/local/src
创建java文件夹
上传jdk1.8
14.安装JDK
-
查看上传的文件
tar -xzvf jdk-8u51-linux-x64.tar.gz
2.使用fz直接修改/etc/profile,配置环境变量
source /etc/profile 使配置生效
执行 java -version
15.上传安装文件
\京淘项目每日课前资料\Day15-dubbo实现购物车\zookeeper安装包
解压目录:
tar -xzvf zookeeper-3.4.8.tar.gz
16.修改配置文件
1.在zookeeper-3.4.8目录下创建文件夹data和log
mkdir data log
2.修改fz使用editplus打开cfg
3.用fz打开 zookeeper3.4.8/conf/zoo_sample.cfg
4.另存为到windows d:\zoo.cfg,文件编码为utf-8
5.用pwd命令查看data路径为/usr/local/src/zookeeper-3.4.8/data
6.修改zoo.cfg文件
7.fz上传zoo.cfg到zookeeper3.4.8/conf中
17.启动zk
sh zkServer.sh start
sh zkServer.sh stop
./zkServer.sh start
18.Zk客户端的使用
sh zkCli.sh -server 192.168.216.200:2181
ls /
quit
7.Dubbo介绍
19.概念说明
Dubbo是 阿里巴巴公司开源的一个高性能优秀的服务框架,使得应用可通过高性能的RPC实现服务的输出和输入功能,可以和 框架无缝集成。
20.Dubbo流程图
组件介绍:
提供者:将接口具体实现的.
消费者:调用接口方法的
注册中心:负责管理服务,负责协调调度.
8.Dubbo监控系统
\京淘项目每日课前资料\Day15-dubbo实现购物车\dubbo控制台
21.检测JDK
说明:Dubbo开发时使用的版本为1.7.所以监控默认使用的1.7版本
如果jdk为1.8则需要使用1.8的监控系统
22.部署tomcat
拷贝tomcat-dubbo
拷贝root.war到webapps
启动tomcat
Root.war会自动解压
23.修改dubbo配置文件
1.说明:将dubbo的ROOT.war进行修改.改为自己的IP地址.修改后记得保存.
2.关闭tomcat,重启tomcat
24.效果展现
用户名是root 密码是root
改成中文,查看提供者,无提供者。
启动问题:
-
检测zk启动是否正常
-
检测防火墙是否关闭
-
检测测试类是否正常
-
检测ROOT.war中zk地址是否正确
-
如果tomcat闪退,证明系统环境变量中添加tomcat,删除环境变量
9.Dubbo入门案例
25.项目关系
26.创建cartInterface
<groupId>com.jt.1803</groupId>
<artifactId>cartInterface</artifactId>
-
创建包
2.创建接口
public interface CartService {
public String findCartByUserId(Long userId);
}
3.发布jar
Run as àmaven install
27.创建 provider
-
创建maven quickstart
-
依赖cartInterface
-
添加dubbo,zkclient依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.8.4</version>
</dependency>
<dependency>
<groupId>com.github.sgroschupf</groupId>
<artifactId>zkclient</artifactId>
<version>0.1</version>
</dependency>
Dubbo依赖spring等框架
4.写服务实现接口,字符串是第1个提供者
public class CartServiceImpl implements CartService {
public String findCartByUserId(Long userId) {
return "第1个提供者 cart userId=" + userId;
}
}
5. 在applicationContext-provider.xml中注册服务
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://code.alibabatech.com/schema/dubbo
http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<!-- 1. 设置应用名称-->
<dubbo:application name="provider-of-cart"/>
<!-- 2.配置zookeeper地址 -->
<dubbo:registry address="zookeeper://192.168.228.132:2181" timeout="100000">
</dubbo:registry>
<!-- 3.配置服务的端口号 -->
<dubbo:protocol port="20888" name="dubbo">
</dubbo:protocol>
<!-- 4.配置实现类的类名 -->
<bean class="com.tedu.dubboProvider.CartServiceImpl" id="cartService">
</bean>
<!-- 5.配置接口名,开放服务 -->
<dubbo:service interface="com.tedu.service.CartService" ref="cartService">
</dubbo:service>
</beans>
6.添加约束文件
从dubbo.jar中meta-inf中提取dubbo.xsd
Windowàpreferenceàxmlàxml catalog
需要将配置文件 编译 关闭配置文件之后重新打开.
7.main启动项目
public class provider1Main {
public static void main(String[] args) throws Exception {
ClassPathXmlApplicationContext context=
new ClassPathXmlApplicationContext("applicationContext-provider.xml");
context.start();
System.out.println("提供者1启动");
System.in.read();
System.out.println("提供者1退出");
}
}
8.zkCli查看服务
./zkCli.sh -server 192.168.216.200:2181
ls /
ls /dubbo
ls /dubbo/com.jt.cart.service.CartService/providers
quit
9.dubbo监控中能查到服务,选中服务查看详细信息
10.console中退出,监控中看不到服务
11.applicationContext-provider.xml放在包中,会出错
12.源码跟踪dubbo启动流程
com.alibaba.dubbo.config.AbstractInterfaceConfig.
protected List<URL> loadRegistries(boolean provider)
28.创建第二个提供者
-
拷贝cartProvider
-
修改cartProvider2的pom.xml
-
修改端口号
-
修改cartServiceImpl改成server2
public String findCartByUserId(Long userId) {
return "server2 返回"+userId;
}
5.修改provider2Main
public class provider2Main {
public static void main(String[] args) throws Exception {
ClassPathXmlApplicationContext context=
new ClassPathXmlApplicationContext("applicationContext-provider.xml");
context.start();
System.out.println("提供者2启动");
System.in.read();
System.out.println("提供者2退出");
}
}
6.启动服务提供者2,向zookeeper注册服务
7.Dubbo 监控中看到服务
8.集群
29.创建consumer
-
创建类型为 quickstart的项目cartConsumer
-
依赖cartInterface
-
依赖dubbo,zkClinet
-
配置applicationContext-consumer.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://code.alibabatech.com/schema/dubbo
http://code.alibabatech.com/schema/dubbo/dubbo.xsd ">
<!--1,设置应用名-->
<dubbo:application name="consumer-of-cart" />
<!--2,设置注册中心地址 -->
<dubbo:registry address="zookeeper://192.168.216.200:2181" />
<!--3,得到远程服务代理对象,可以像使用本地bean一样使用cartService -->
<dubbo:reference timeout="50000" id="cartService" interface="com.jt.cart.service.CartService" />
</beans>
5.启动消费者
如果没有启动提供者,先启消费者会报错,因为得不到代理对象
public class CartController {
public static void main(String[] args) throws Exception {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext-consumer.xml");
CartService cartService = (CartService) context.getBean("cartService");
while (true) {
String string = cartService.findCartByUserId(18L);
System.out.println("服务消费者收到的结果:" + string);
Thread.currentThread().sleep(1000);
}
}
}
两个服务提供者都会被调用,随机分配
6.倍权 server1 16倍
7.禁用
8.启用
9.关闭zookeeper
sh zkServer.sh stop
Dubbo客户端有缓存。
10.关闭provider1
11.关闭provider2
1.面试问题
说明:当zk如果宕机后,消费者能否正确消息?????
关zk,provider1,provider2
答案:可以
因为zk会动态的向客户端更新服务列表信息.当zk宕机后,由于之前已经同步了zk的服务列表信息,所以客户端可以按照自己已经缓存的清单进行访问.如果在这个期间服务端程序发现宕机现象,那么则访问故障机时由于不能通信,则等待超时时间,则访问下一台服务器.
如果这时,所有的服务端程序都宕机,则整个服务陷入瘫痪.
2.与httpclient对比
Dubbo支持集群,故障转移
增加tomcat要改nginx.conf
增加dubbo不用改zookeeper
3.Dubbo缺点
1、性能,分布式系统是跨进程,跨网络的,性能很受网络延迟和带宽的影响。
2、可靠性:由于高度依赖网络状况,任何一次远程调用都可能失败。
3、运维成本:一个系统拆成了多个服务,每个服务都得配置,部署,监控,日志处理
单体的优点
1、易于开发:开发的方式简单,方便运行也容易调试。
2、易于部署。
1.SOA思想
30.SOA介绍
SOA面向服务的架构(Service-oriented architecture)是一个组件模型,它将应用程序的不同功能单元(称为服务)通过这些服务之间定义良好的接口和契约联系起来。接口是采用中立的方式进行定义的,它应该独立于实现服务的硬件平台、操作系统和编程语言。这使得构建在各种各样的系统中的服务可以以一种统一和通用的方式进行交互。
3.部署京淘项目
课上在新京淘上练习,课下在前面做的京淘上练习
31.以管理员身份运行Switchhosts
\培优课前资料\京淘课前资料下发-2\5-switchHost
127.0.0.1 image.jt.com
127.0.0.1 manage.jt.com
127.0.0.1 www.jt.com
127.0.0.1 sso.jt.com
127.0.0.1 cart.jt.com
127.0.0.1 order.jt.com
127.0.0.1 solr.jt.com
32.安装linux redis集群
以前装过redis,再装客户端连不上。
-
上传 \京淘项目每日课前资料\Day07\redis-3.2.8.tar.gz到/usr/local/src
-
tar -xzvf redis-3.2.8.tar.gz
-
cd redis-3.2.8
-
make install
-
上传 亿发,移动端课前资料\redisCluster\集群配置文件\cluster到/usr/local/src/ redis-3.2.8
-
cd cluster
-
sh start.sh
-
ps -ef | grep redis
-
在redis的根目录下执行
./src/redis-trib.rb create --replicas 2 192.168.216.200:7000 192.168.216.200:7001 192.168.216.200:7002 192.168.216.200:7003 192.168.216.200:7004 192.168.216.200:7005 192.168.216.200:7006 192.168.216.200:7007 192.168.216.200:7008
-
查看集群状态
quit 退出redis-cli
33.Nginx
\京淘项目每日课前资料\Day04\nginx\nginx-1.9.0.zip
Conf/nginx.conf添加下面内容
#gzip on;
server {
listen 80;
server_name manage.jt.com;
location / {
proxy_pass http://localhost:8091;
}
}
server {
listen 80;
server_name www.jt.com;
location / {
proxy_pass http://localhost:8092;
}
}
server {
listen 80;
server_name sso.jt.com;
location / {
proxy_pass http://127.0.0.1:8093;
}
}
server {
listen 80;
server_name cart.jt.com;
location / {
proxy_pass http://127.0.0.1:8094;
}
}
server {
listen 80;
server_name order.jt.com;
location / {
proxy_pass http://127.0.0.1:8095;
}
}
#图片服务器
server {
listen 80;
server_name image.jt.com;
location / {
root D:\jt-upload;
}
}
34.创建jtdb
-
文件—》新建查询编辑器
-
拷贝jtdb.sql中的所有内容到查询窗口中,选中所有内容,执行
-
刷新对象浏览器
-
Tb-order_item表添加CREATED, UPDATED,因为用到映射,列名区分大小写。
35.导入项目,修改redis 服务器ip地址
关闭xml验证
-
导入parent,common,manager,sso,cart,order,web
如果报错,eclipseàprojectàclean
2.Sso,jt-web中修改redis服务器ip地址
36.每个项目执行run as àmaven install 发布到仓库中
37.启动manager,sso,cart,order,jt-web
38.断点调试
先添加商品到购物车中,不要提交订单,提交订单后,购物车中没商品,再点我的购物车报nullpoint,跟不到jt-cart
Jt-web中下面两个方法加断点
com.jt.web.controller.CartController.findCartByUserId(Model)
com.jt.web.service.CartServiceImpl.findCartListByUserId(Long)
Jt-cart中下面两个方法加断点
com.jt.cart.controller.CartController.findCartByUserId(Long)
com.jt.cart.service.CartServiceImpl.findCartListByUserId(Long)
4.京淘购物车重构
2.项目关系
强调interface项目的作用(service,pojo),与消费者,提供者的关系。
3.修改jt-parent
拷贝pom.xml中的依赖内容,不能拷贝整个pom.xml
4.创建jt-interface
39.创建项目
说明:创建一个quick start
40.添加继承和依赖
1.继承jt-parent
2.依赖common
3.拷贝dubbo依赖
Parent中已经有了,还要再添加依赖,不添加会出错
41.拷贝pojo,service
创建com.jt.cart.pojo包
从jt-cart中移动Cart.java到com.jt.cart.pojo包
创建com.jt.cart.service包,
从jt-cart中移动cartService.java到jt-interface/com.jt.cart.service
5.修改jt-cart
42.添加dubbo依赖
Parent中已经有了,还要再添加依赖,不添加会出错
Jt-parent,jt-interface,jt-cart添加的dubbo的依赖都不一样,因为dubbo要依赖很多包。
43.编辑提供者配置文件
从上一个项目拷贝applicationContext-provider.xml放在main/resources/sping,修改applicationName,端口号,className,id
<!-- 提供方应用信息,用于计算依赖关系 -->
<dubbo:application name="jt-cart" />
<!-- 使用multicast广播注册中心暴露服务地址 -->
<dubbo:registry address="zookeeper://192.168.126.170:2181"/>
<!-- 用dubbo协议在20880端口暴露服务 -->
<dubbo:protocol name="dubbo" port="20880" />
<!-- 声明需要暴露的服务接口 -->
<dubbo:service interface="com.jt.dubbo.service.DubboCartService" ref="cartService" />
<!-- 具体的实现bean -->
<bean id="cartService" class="com.jt.cart.service.CartServiceImpl" />
44.CartServiceImpl注释掉@Service
45.修改CartController
注释findCartByUserId
46.Web.xml中加载dubbo配置文件
47.启动测试
-
cartController中所有代码注释掉
-
Jt-parent,jt-common,jt-inteface,jt-cart三个项目Run as àmaven install
-
启动jt-cart
-
dubbo监控上查看服务
5. Zk客户端上查看服务
./zkCli.sh -server 192.168.216.200:2181
ls /
ls /dubbo
ls /dubbo/com.jt.cart.service.CartService/providers
ls /dubbo/com.jt.cart.service.CartService/ consumers
quit
6.修改jt-web中购物车查询功能
48.断点复习 jt-web cartController. findCartByUserId
49.添加依赖
依赖jt-interface
在pom.xml中添加dubbo依赖,从jt-cart中拷贝
删除jt-web 中的pojo.Cart, cartService ,cartServcieImpl
修改Jt-web中的cartController,orderController中的import
50.拷贝配置文件
<dubbo:application name="jt-web"/>
<dubbo:registry address="zookeeper://192.168.126.170:2181"/>
<!--
check=false 表示启动时不检查是否有提供者, 只有调用时才检查
id 应该和提供者给定的接口id一致
interface 表示生产者的路径
-->
<dubbo:reference check="false" id="cartService" interface="com.jt.cart.service.CartService" timeout="5000"/>
51.查看cartController
52.启动
Dubbo监控中能看到消费者
53.测试
直接输入网址
http://www.jt.com/cart/show.html
断点时间长了会报错,consumer中加timeout
54.页面效果
7.购物车新增,数量修改
55.查看jt-web/CartController
56.查看jt-cart/CartServiceImpl
加断点,先分析添加商品,再分析修改数量
5.Dubbo重构订单
8.修改jt-interface,创建 com.jt.order.pojo,com.jt.order.service
Jt-interface中创建order子包,在order中再创建order.pojo,order.service
9.修改jt-order项目
57.Jt-order依赖jt-interface
58.Jt-order添加dubbo依赖
从jt-cart中拷贝依赖
59.拷贝jt-order/service,pojo到 jt-inteface
拷贝jt-order/Order,OrderItem,shipping到jt-interface/com.jt.order.pojo中,删除jt-order中的实体类
拷贝jt-order/orderService到jt-interface/com.jt.order.service,不要拷贝orderServiceImpl,删除jt-order中的orderService,
60.修改jt-order/orderServiceImpl
注释掉@service
Jt-order中的OrderServiceImpl要重新导入Order
61.修改提供者配置文件
拷贝配置文件
修改应用名,端口号,classname,id,接口名
62.发布服务
Jt-interface 增加了Order要run as maven install
Jt-order run as maven install
Debug
Dubbo监控中查看提供者
10.编辑jt-web/orderController
63.删除pojo和service
删除pojo/order,order-item.shipping
删除server/orderService,orderServiceImpl
64.修改 jt-web/orderController
删除import order,orderService,重新import
65.修改applicationContext-consumers.xml
增加
<dubbo:reference check="false" id="orderService" interface="com.jt.order.service.OrderService" timeout="500000"/>
66.订单业务实现
断点跟踪
6.关于Dubbo使用
11.关于名称的定义
说明:定义Dubbo的名称,应该根据接口的名称进行定义.
不同的接口使用不同的应用名称.一个接口的多个实现类,使用相同的应用名称.否则会报警??????
7.消息队列
12.消息队列作用
67.消息队列使用的三种场景介绍
-
应用解耦
场景说明:用户下单后,订单系统需要通知库存系统。传统的做法是,订单系统调用库存系统的接口。如下图
传统模式的缺点:
-
假如库存系统无法访问,则订单减库存将失败,从而导致订单失败
-
订单系统与库存系统耦合高
如何解决以上问题呢?引入应用消息队列后的方案,如下图:
-
订单系统:用户下单后,订单系统完成持久化处理,将消息写入消息队列,返回用户订单下单成功
-
库存系统:订阅下单的消息,获取下单信息,库存系统根据下单信息,进行库存操作
-
假如:在下单时库存系统不能正常使用。也不影响正常下单,因为下单后,订单系统写入消息队列就不再关心其他的后续操作了。实现订单系统与库存系统的应用解耦
2.流量削锋
流量削锋是消息队列中的常用场景,一般在秒杀或团抢活动中使用广泛
应用场景:秒杀活动,一般会因为流量过大,导致流量暴增,应用挂掉。为解决这个问题,一般需要在应用前端加入消息队列。
-
用户的请求,服务器接收后,首先写入消息队列。假如消息队列长度超过最大数量,则直接抛弃用户请求或跳转到错误页面
-
秒杀业务根据消息队列中的请求信息,再做后续处理
-
通过消息队列缓解服务器压力.
3.异步处理
场景说明:用户注册后,需要发注册邮件和注册短信。传统的做法有两种 1.串行的方式;2.并行方式
(1)串行方式:将注册信息写入数据库成功后,发送注册邮件,再发送注册短信。以上三个任务全部完成后,返回给客户端
(2)并行方式:将注册信息写入数据库成功后,发送注册邮件的同时,发送注册短信。以上三个任务完成后,返回给客户端。与串行的差别是,并行的方式可以提高处理的时间
假设三个业务节点每个使用50毫秒钟,不考虑网络等其他开销,则串行方式的时间是150毫秒,并行的时间可能是100毫秒。
如何解决这个问题呢?
引入消息队列,将不是必须的业务逻辑,异步处理。改造后的架构如下:
按照以上约定,用户的响应时间相当于是注册信息写入数据库的时间,也就是50毫秒。注册邮件,发送短信写入消息队列后,直接返回,因此写入消息队列的速度很快,基本可以忽略,因此用户的响应时间可能是50毫秒。
68.消息队列
MQ全称为Message Queue, 消息队列(MQ)是一种应用程序对应用程序的通信方法。应用程序通过读写出入队列的消息来通信,而无需专用连接来链接它们。
消息队列,是一端写入数据到队列中,一端从队列中获取消息,并且通信通过RPC调用,可以传递java对象,方便操作.
69.案例
70.缺点
增加了复杂度
可靠性降低
71.消息队列的产品
72.RabbitMQ介绍
是电信爱立信公司,为银行业务负责研发的消息队列.
1. 基于erlang语言开发具有高可用高并发的优点,适合集群服务器。
2. 健壮、稳定、易用、跨平台、支持多种语言、文档齐全。
3. 有消息确认机制和持久化机制,可靠性高。
4. 开源
13.RabbitMQ安装
73.上传文件
使用fz在/usr/local/src下创建文件夹rabbitmq
上传\京淘项目每日课前资料\Day17-rabbitMQ\ rabbitmq-server-3.6.1-1.noarch.rpm
到/usr/local/src/rabbitmq
74.安装rabbitMQ
rpm -ivh rabbitmq-server-3.6.1-1.noarch.rpm
75.安装rabbitMQ web后台管理
cd /usr/sbin 能看到安装后产生的目录,fz中看不到
ll ra*
vim rabbitmq-plugins
man ls
man rabbitmq-plugins
rabbitmq_shovel是消息的搬运插件
rabbitmq_management是管理插件
rabbitmq-plugins enable rabbitmq_shovel rabbitmq_management
执行命令:
rabbitmq-plugins enable rabbitmq_managementnt
[root@localhost sbin]# rabbitmq-plugins enable rabbitmq_management
The following plugins have been enabled:
mochiweb
webmachine
rabbitmq_web_dispatch
amqp_client
rabbitmq_management_agent
rabbitmq_management
Applying plugin configuration to rabbit@localhost... failed.
* Could not contact node rabbit@localhost.
Changes will take effect at broker restart.
* Options: --online - fail if broker cannot be contacted.
--offline - do not try to contact broker.
76.启动rabbitMQ
service rabbitmq-server start 启动
service rabbitmq-server stop 停止
service rabbitmq-server restart 重启
77.端口介绍
-
rabbitMQ监控系统的端口:15672
-
rabbitMQ服务调用端口:5672
78.本机登录
在虚拟机中用localhost能访问,能登录
用户名和密码:guest
在windows上能看到登录界面,但不能登录。
79.修改配置文件允许guest在任何电脑登录
/usr/share/doc 放的是共享数据,如文档
/etc/放的是配置文件
将文件复制到指定目录下:
cp /usr/share/doc/rabbitmq-server-3.6.1/rabbitmq.config.example /etc/rabbitmq/rabbitmq.config
使用fz编辑文件/etc/rabbitmq/rabbitmq.config
处于安全的考虑,guest这个默认的用户只能通过http://localhost:15672 来登录,其他的IP无法直接使用这个账号。
修改文件64行
1.将%%去掉
2.将,号去掉
修改为:
service rabbitmq-server restart
永久开启和关闭防火墙
开启:chkconfig iptables on;(重启后生效)
关闭:chkconfig iptables off;(重启后生效)
14.消息队列监控介绍
Virtualhost exchange queue channel之间的关系
virtualHost:设置权限
Exchanges: 交换机 作用:将消息发往不同的队列中
Queues:表示消息队列 存储数据的位置是内存.
80.新建用户
Admin中
序号 | 角色名称 | 说明 |
1. | 超级管理员(administrator) | 可登陆管理控制台,可查看所有的信息,并且可以对用户,策略(policy)进行操作。 |
2. | 监控者(monitoring) | 可登陆管理控制台,同时可以查看rabbingmq节点的相关信息(进程数,内存使用情况,磁?????????????? |
3. | 策略制定者(policymaker) | 可登陆管理控制台,同时可以对policy进行管理。但无法查看节点的相关信息 |
4. | 普通管理者(management) | 仅可登陆管理控制台,无法看到节点信息,也无法对策略进行管理。 |
5. | 其他 | 无法登录管理控制台,通常就是普通的生产者和消费者。 |
81.新增虚拟主机
创建多个虚拟主机,再创建/game
一个mysql可以创建多个库
82.选中virtual host 单击超连接
设置后
8.消息队列模式
15.新建maven quickstart
16.简单模式
simple
83.需求
P:消息的生产者
红色部分:代表队列
C:代表消费者 从队列中获取消息后执行.
工作原理:
当客户端(生产者)将消息写入消息队列中时,消息队列中信息的数量加1.
消费者实时监听消息队列,当消息队列中有消息时,则获取消息,之后执行业务逻辑.同时消息队列的数量减一
84.导入jar包
<!-- 消息队列 -->
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>3.5.1</version>
</dependency>
85.定义生产者
发送数据的过程与jdbc连接相似,共5步
public class Test_1_simple_provider {
@Test
public void provider() throws Exception
{
//1,建立连接
ConnectionFactory factory=new ConnectionFactory();
factory.setHost("192.168.216.200");
factory.setPort(5672);
factory.setUsername("jtadmin");
factory.setPassword("jtadmin");
factory.setVirtualHost("/jt");
Connection connection=factory.newConnection();
//2,建立通道
Channel channel=connection.createChannel();
//3,定义队列
//durable true 持久化,重启服务器后,数据还有
//exclusive true,只能通过当前连接消费 false
//autoDelete true 队列中消息处理完后,自动删除队列
//arguments 参数
channel.queueDeclare("order", true, false, false, null);
//4,发送消息,routingKey必须与queue一致
String msg="msg1";
channel.basicPublish("", "order", null, msg.getBytes());
//5,关闭
channel.close();
connection.close();
System.out.println("发送数据成功");
}
}
发消息,在后台overview,queue,查看队列,消息数。Default exchange
发送两个消息
Test_1_simple_p2_connection.java
//5,关闭
//channel.close();
//connection.close();
System.out.println("发送数据成功");
while(true)
{
}
加while()查看channel,连接
去掉while,客户端就断开了,后台不显示channel,连接
86.定义消费者
接收数据的过程与jdbc查询相似,共5步
public class Test_1_simple_consumer {
@Test
public void consumer() throws Exception
{
//1,建立连接
ConnectionFactory factory=new ConnectionFactory();
factory.setHost("192.168.216.200");
factory.setPort(5672);
factory.setUsername("jtadmin");
factory.setPassword("jtadmin");
factory.setVirtualHost("/jt");
Connection connection=factory.newConnection();
//2,建立通道
Channel channel=connection.createChannel();
//3,定义队列
//durable true 持久化,重启服务器后,数据还有
//exclusive true,只能通过当前连接消费 false
//autoDelete true 队列中消息处理完后,自动删除队列
//arguments 参数
channel.queueDeclare("order", true, false, false, null);
//4,创建消费者
QueueingConsumer consumer=new QueueingConsumer(channel);
//autoAck:自动回复消息
channel.basicConsume("order", true, consumer);
//5,取消息
while(true)
{
Delivery delivery=consumer.nextDelivery();
byte[] data=delivery.getBody();
String mString=new String(data);
System.out.println("消费者取到:"+mString);
}
}
}
查看监控中消息被处理了
工作中,消息提供者和消息消费者运行在不同服务器上
Delivery的处理有点像resultSet和迭代器
87. 存在的问题
消费者处理态少,队列中消息积累很多
17.工作模式
work
88.需求
说明:
由一个生产者负责消息写入队列,但是如果有一个消费者负责消费,可能会造成消息的积压.所以准备多个消费者共同消费一个队列中的消息.
89.定义消费者1
拷贝简单模式的消费者代码,修改一下。
public class Test_2_work_consumer1 {
@Test
public void consumer() throws Exception
{
//1,建立连接
ConnectionFactory factory=new ConnectionFactory();
factory.setHost("192.168.216.200");
factory.setPort(5672);
factory.setUsername("jtadmin");
factory.setPassword("jtadmin");
factory.setVirtualHost("/jt");
Connection connection=factory.newConnection();
//2,建立通道
Channel channel=connection.createChannel();
//3,定义队列
//durable true 持久化,重启服务器后,数据还有
//exclusive true,只能通过当前连接消费 false
//autoDelete true 队列中消息处理完后,自动删除队列
//arguments 参数
channel.queueDeclare("orderQueue", true, false, false, null);
//4,创建消费者
QueueingConsumer consumer=new QueueingConsumer(channel);
//autoAck:自动回复消息
channel.basicConsume("orderQueue", true, consumer);
//5,取消息
System.out.println("消费者1启动");
while(true)
{
Delivery delivery=consumer.nextDelivery();
byte[] data=delivery.getBody();
String mString=new String(data);
System.out.println("消费者1取到:"+mString);
}
}
}
拷贝消费者2
public class Test_2_work_consumer2 {
@Test
public void consumer() throws Exception
{
//1,建立连接
ConnectionFactory factory=new ConnectionFactory();
factory.setHost("192.168.216.200");
factory.setPort(5672);
factory.setUsername("jtadmin");
factory.setPassword("jtadmin");
factory.setVirtualHost("/jt");
Connection connection=factory.newConnection();
//2,建立通道
Channel channel=connection.createChannel();
//3,定义队列
//durable true 持久化,重启服务器后,数据还有
//exclusive true,只能通过当前连接消费 false
//autoDelete true 队列中消息处理完后,自动删除队列
//arguments 参数
channel.queueDeclare("orderQueue", true, false, false, null);
//4,创建消费者
QueueingConsumer consumer=new QueueingConsumer(channel);
//autoAck:自动回复消息
channel.basicConsume("orderQueue", true, consumer);
//5,取消息
System.out.println("消费者2启动");
while(true)
{
Delivery delivery=consumer.nextDelivery();
byte[] data=delivery.getBody();
String mString=new String(data);
System.out.println("消费者2取到:"+mString);
}
}
}
90.定义生产者
拷贝简单模式代码加 while()
public class Test_2_work_provider {
@Test
public void provider() throws Exception {
// 1,建立连接
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("192.168.216.200");
factory.setPort(5672);
factory.setUsername("jtadmin");
factory.setPassword("jtadmin");
factory.setVirtualHost("/jt");
Connection connection = factory.newConnection();
// 2,建立通道
Channel channel = connection.createChannel();
// 3,定义队列
// durable true 持久化,重启服务器后,数据还有
// exclusive true,只能通过当前连接消费 false
// autoDelete true 队列中消息处理完后,自动删除队列
// arguments 参数
channel.queueDeclare("orderQueue", true, false, false, null);
// 4,发送消息
for (int i = 3; i < 10; i++) {
String msg = "msg" + i;
channel.basicPublish("", "orderQueue", null, msg.getBytes());
}
// 5,关闭
channel.close();
connection.close();
System.out.println("发送数据成功");
}
}
运行三次,后台显示有有三个消息
先启动消费者1,再启消费者2,消息全被第一个消费者处理了。
两个消费都启动后,再生产2个消息,分别被处理
18.发布订阅模式
91.需求
X: 代表交换机 如果有消息则在发布订阅模式中,将消息发送到连接交换机的队列中
特点:如果生产者发送消息,那么订阅的全部消费者都会执行消息.
后期添加的消费者得不到以前的消息。需要先启动消费者
92.生产者
public class Test_3_publish_p {
// 定义生产者
@Test
public void provider() throws IOException {
System.out.println("开始发布消息");
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("192.168.216.200");
factory.setPort(5672);
factory.setUsername("jtadmin");
factory.setPassword("jtadmin");
factory.setVirtualHost("/jt");
Connection connection = factory.newConnection();
// 定义通道
Channel channel = connection.createChannel();
// 定义交换机名称
String exchange_name = "E1";
// fanout是定义发布订阅模式 direct是 路由模式 topic是主题模式
channel.exchangeDeclare(exchange_name, "fanout");
String msg = "order1" ;
channel.basicPublish(exchange_name, "", null, msg.getBytes());
channel.close();
connection.close();
}
}
93.消费者1
public class Test_3_publish_c1 {
@Test
public void consumer1() throws Exception{
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("192.168.216.200");
factory.setPort(5672);
factory.setUsername("jtadmin");
factory.setPassword("jtadmin");
factory.setVirtualHost("/jt");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
String exchange_name = "E1";
//定义交换机模式
channel.exchangeDeclare(exchange_name, "fanout");
String queue_name = UUID.randomUUID().toString();
System.out.println("队列名称"+queue_name);
//定义队列
channel.queueDeclare(queue_name, true, false, false, null);
//将队列和交换机绑定 key:表示接收数据标识
channel.queueBind(queue_name, exchange_name, "");
//定义消费数量
channel.basicQos(1);
//定义消费者
QueueingConsumer consumer = new QueueingConsumer(channel);
//将消费者和队列绑定,并且需要手动返回
channel.basicConsume(queue_name, false, consumer);
System.out.println("消费者1");
while(true){
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String msg = new String(delivery.getBody());
System.out.println(msg+"入库");
//false表示一个一个返回
channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
}
}
}
先启生产者,再启消费者收不到前面的消息
后台查看交换机。有时看不到信息
一个消息被多个订阅者处理
94.消费者2
@Test
public void consumer1() throws Exception{
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("192.168.216.200");
factory.setPort(5672);
factory.setUsername("jtadmin");
factory.setPassword("jtadmin");
factory.setVirtualHost("/jt");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
String exchange_name = "E1";
//定义交换机模式
channel.exchangeDeclare(exchange_name, "fanout");
String queue_name = UUID.randomUUID().toString();
System.out.println("队列名称"+queue_name);
//定义队列
channel.queueDeclare(queue_name, true, false, false, null);
//将队列和交换机绑定 key:表示接收数据标识
channel.queueBind(queue_name, exchange_name, "");
//定义消费数量
channel.basicQos(1);
//定义消费者
QueueingConsumer consumer = new QueueingConsumer(channel);
//将消费者和队列绑定,并且需要手动返回
channel.basicConsume(queue_name, false, consumer);
System.out.println("消费者2");
while(true){
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String msg = new String(delivery.getBody());
System.out.println(msg+"给用户发短信");
//false表示一个一个返回
channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
}
}
}
95.案例
案例:微信公众号,订阅不同公众号,收到的消息不一样。
学生报名后,班主任要处理消息,项目经理也要处理消息
96.总结
订阅与工作模式区别是,订阅模式每个消费者得到的消息是一样的,工作模式每个消费者得到消息是不一样的。
解决高并发用工作模式
同一个消息,多个模块要处理用订阅
订阅像广播
19.路由模式
学生留级消息只发给班主任,准备座位,不要发消息给项目经理。
97.需求
说明:路由模式是发布订阅模式的升级,通过定义不同的路由key使得程序将消息发送到不同的队列中.
98.定义生产者
public class Test_4_routing_provider {
// 定义生产者
@Test
public void provider() throws IOException {
System.out.println("开始发布消息");
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("192.168.216.200");
factory.setPort(5672);
factory.setUsername("jtadmin");
factory.setPassword("jtadmin");
factory.setVirtualHost("/jt");
Connection connection = factory.newConnection();
// 定义通道
Channel channel = connection.createChannel();
// 定义交换机名称
String exchange_name = "E2";
// fanout是定义发布订阅模式 direct是 路由模式 topic是主题模式
channel.exchangeDeclare(exchange_name, "direct");
String msg = "order6" ;
//channel.basicPublish(exchange_name, "domestic", null, msg.getBytes());
channel.basicPublish(exchange_name, "agent", null, msg.getBytes());
channel.close();
connection.close();
}
}
99.消费者1
public class Test_4_routing_consumer1 {
@Test
public void consumer1() throws Exception{
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("192.168.216.200");
factory.setPort(5672);
factory.setUsername("jtadmin");
factory.setPassword("jtadmin");
factory.setVirtualHost("/jt");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
String exchange_name = "E2";
//定义交换机模式
channel.exchangeDeclare(exchange_name, "direct");
String queue_name = UUID.randomUUID().toString();
System.out.println("队列名称"+queue_name);
//定义队列
channel.queueDeclare(queue_name, true, false, false, null);
//将队列和交换机绑定 key:表示接收数据标识
channel.queueBind(queue_name, exchange_name, "domestic");
//定义消费数量
channel.basicQos(1);
//定义消费者
QueueingConsumer consumer = new QueueingConsumer(channel);
//将消费者和队列绑定,并且需要手动返回
channel.basicConsume(queue_name, false, consumer);
System.out.println("消费者1 国内库");
while(true){
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String msg = new String(delivery.getBody());
System.out.println(msg+"入国内库");
//false表示一个一个返回
channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
}
}
}
100.消费者2
拷贝消费1,修改routingkey
public class Test_4_routing_consumer2 {
@Test
public void consumer1() throws Exception{
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("192.168.216.200");
factory.setPort(5672);
factory.setUsername("jtadmin");
factory.setPassword("jtadmin");
factory.setVirtualHost("/jt");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
String exchange_name = "E2";
//定义交换机模式
channel.exchangeDeclare(exchange_name, "direct");
String queue_name = UUID.randomUUID().toString();
System.out.println("队列名称"+queue_name);
//定义队列
channel.queueDeclare(queue_name, true, false, false, null);
//将队列和交换机绑定 key:表示接收数据标识
channel.queueBind(queue_name, exchange_name, "agent");
//定义消费数量
channel.basicQos(1);
//定义消费者
QueueingConsumer consumer = new QueueingConsumer(channel);
//将消费者和队列绑定,并且需要手动返回
channel.basicConsume(queue_name, false, consumer);
System.out.println("消费者2代购库");
while(true){
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String msg = new String(delivery.getBody());
System.out.println(msg+"入代购库");
//false表示一个一个返回
channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
}
}
}
先启动提供者发消息,再启动消费者,收不到以前的消息
启动消费者后,再用提供者发布消息,消费者能收到消息。
总结:消费者的队列只放有某个 routingkey的消息
相当于实现了select routingkey=’agent’功能.
订阅模式:每个消费者收到的消息都一样
路由模式:每个消费者收到的消息不一样
20.主题模式
101.需求
说明:
可以通过路由key将消息发送到一类相同的key中 使用通配符实现
符号说明:
#号:表示任意字符(任意个.)
*号:任意单个字符或者词组(单个.)
102.生产者
public class Test_5_topic_provider {
// 定义生产者
@Test
public void provider() throws IOException {
System.out.println("开始发布消息");
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("192.168.216.200");
factory.setPort(5672);
factory.setUsername("jtadmin");
factory.setPassword("jtadmin");
factory.setVirtualHost("/jt");
Connection connection = factory.newConnection();
// 定义通道
Channel channel = connection.createChannel();
// 定义交换机名称
String exchange_name = "E3";
// fanout是定义发布订阅模式 direct是 路由模式 topic是主题模式
channel.exchangeDeclare(exchange_name, "topic");
String msg = "msg10" ;
//channel.basicPublish(exchange_name, "domestic.cart", null, msg.getBytes());
//channel.basicPublish(exchange_name, "domestic.order", null, msg.getBytes());
//channel.basicPublish(exchange_name, "agent.cart", null, msg.getBytes());
channel.basicPublish(exchange_name, "agent.order", null, msg.getBytes());
channel.close();
connection.close();
}
}
103.消费者1
public class Test_5_topic_consumer1 {
@Test
public void consumer1() throws Exception{
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("192.168.216.200");
factory.setPort(5672);
factory.setUsername("jtadmin");
factory.setPassword("jtadmin");
factory.setVirtualHost("/jt");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
String exchange_name = "E3";
//定义交换机模式
channel.exchangeDeclare(exchange_name, "topic");
String queue_name = UUID.randomUUID().toString();
System.out.println("队列名称"+queue_name);
//定义队列
channel.queueDeclare(queue_name, true, false, false, null);
//将队列和交换机绑定 key:表示接收数据标识
channel.queueBind(queue_name, exchange_name, "domestic.*");
//定义消费数量
channel.basicQos(1);
//定义消费者
QueueingConsumer consumer = new QueueingConsumer(channel);
//将消费者和队列绑定,并且需要手动返回
channel.basicConsume(queue_name, false, consumer);
System.out.println("消费者1 国内库");
while(true){
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String msg = new String(delivery.getBody());
System.out.println(msg+"入国内库");
//false表示一个一个返回
channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
}
}
}
104.消费者2
public class Test_5_topic_consumer2 {
@Test
public void consumer1() throws Exception{
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("192.168.216.200");
factory.setPort(5672);
factory.setUsername("jtadmin");
factory.setPassword("jtadmin");
factory.setVirtualHost("/jt");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
String exchange_name = "E3";
//定义交换机模式
channel.exchangeDeclare(exchange_name, "topic");
String queue_name = UUID.randomUUID().toString();
System.out.println("队列名称"+queue_name);
//定义队列
channel.queueDeclare(queue_name, true, false, false, null);
//将队列和交换机绑定 key:表示接收数据标识
channel.queueBind(queue_name, exchange_name, "agent.*");
//定义消费数量
channel.basicQos(1);
//定义消费者
QueueingConsumer consumer = new QueueingConsumer(channel);
//将消费者和队列绑定,并且需要手动返回
channel.basicConsume(queue_name, false, consumer);
System.out.println("消费者2代购库");
while(true){
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String msg = new String(delivery.getBody());
System.out.println(msg+"入代购库");
//false表示一个一个返回
channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
}
}
}
105.总结
9.京淘整合rabbitMQ
21.整合说明
选那个模式?
消息很多,应该选路由模式
22.提供消息
106.添加依赖
依赖在jt-parent中已经添加过了
<!-- 消息队列 -->
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>3.5.1</version>
</dependency>
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId>
<version>1.4.0.RELEASE</version>
</dependency>
107.业务设计
说明:由于订单业务逻辑相对复杂.同时入库3张表.所以使用消息队列实现订单入库.
角色划分:
生产者:jt-order
消费者:jt-rabbitmq
正常有两个项目,为了减少代码,我们用一个项目
108.添加配置文件
Jt-order/main/resources/properties/rabbitmq.properties
rabbit.ip=192.168.216.200
rabbit.port=5672
rabbit.username=jtadmin
rabbit.password=jtadmin
rabbit.vhost=/jt
交给spring容器管理
/resources/spring/applicationContext.xml 中增加一个value
<list>
<value>classpath:/property/jdbc.properties</value>
<value>classpath:/property/redis.properties</value>
<value>classpath:/property/rabbitmq.properties</value>
</list>
109.编辑生产者配置文件
applicationContext-rabbitmq-send.xml
用xml实现建立连接,配置交换机
<!-- 异步的线程池,线程池的最在数不能设定太小,不然<rabbit:listener/>/@RabbitListener太多的话,会出现发无法正常消费问题 -->
<task:executor id="taskExecutor" pool-size="4-256" queue-capacity="128" />
<!-- 定义RabbitMQ的连接工厂 -->
<rabbit:connection-factory id="connectionFactory"
host="${rabbit.ip}" port="${rabbit.port}" username="${rabbit.username}" password="${rabbit.password}"
virtual-host="${rabbit.vhost}"
publisher-confirms="true"
publisher-returns="true"
channel-cache-size="5"
executor="taskExecutor"/>
<!-- 定义Rabbit模板,指定连接工厂以及定义exchange -->
<rabbit:template id="amqpTemplate" connection-factory="connectionFactory"
exchange="orderExchange" />
<!-- MQ的管理,包括队列、交换器等 -->
<rabbit:admin connection-factory="connectionFactory" />
<!-- 定义交换器,自动声明交换机 ,durable持久化 -->
<rabbit:direct-exchange name="orderExchange" auto-declare="true" durable="true">
</rabbit:direct-exchange>
110.发送消息
同步变异步,只负责发消息
Jt-order/orderServiceImpl是消息提供者
将orderServiceImpl类另存为OrderMsgConsumer
OrderMsgConsumer是消息消费者
@Autowired
RabbitTemplate rabbitTemplate;
//通过消息队列实现订单入库
@Override
public String saveOrder(Order order) {
String orderId = order.getUserId() + "" +System.currentTimeMillis();
order.setOrderId(orderId);
String routingKey = "saveorder"; //定义路由key
rabbitTemplate.convertAndSend(routingKey, order);
return orderId;
}
111.查询数据库失败
原因是数据刚写入消息队列,还没有写入数据库
112.Jt-web中修改js
修改order.js,查找success.html 5777行
if(result.status == 200){
window.alert("订单提交成功")
location.href = "/order/success.html?id="+result.data;
}
修改order-cart.jsp 加version=1,让js 立刻生效
23.消费消息
113.修改消费者实现类OrderMsgConsumer
不实现接口,删除@override
saveOrder返回void
去掉@service,删除别的方法,
public class OrderMsgConsumer {
@Autowired
private OrderMapper orderMapper;
@Autowired
private OrderItemMapper orderItemMapper;
@Autowired
private OrderShippingMapper orderShippingMapper;
public void saveOrder(Order order) {
//拼接orderId号
//String orderId = order.getUserId() + "" + System.currentTimeMillis();
String orderId = order.getOrderId();
Date date = new Date();
//入库订单
order.setOrderId(orderId);
order.setStatus(1);
order.setCreated(date);
order.setUpdated(date);
orderMapper.insert(order);
System.out.println("订单入库成功!!!!!");
//获取订单物流信息
OrderShipping orderShipping = order.getOrderShipping();
orderShipping.setOrderId(orderId);
orderShipping.setCreated(date);
orderShipping.setUpdated(date);
orderShippingMapper.insert(orderShipping);
System.out.println("订单物流信息入库成功!!");
//实现订单商品入库
List<OrderItem> orderItemList = order.getOrderItems();
for (OrderItem orderItem : orderItemList) {
orderItem.setOrderId(orderId);
orderItem.setCreated(date);
orderItem.setUpdated(date);
orderItemMapper.insert(orderItem);
}
System.out.println("订单入数据库成功!!!");
//return orderId;
}
}
114.定义消费者配置文件
applicationContext-rabbitmq-consumer.xml
<!-- 异步的线程池,线程池的最在数不能设定太小,不然<rabbit:listener/>/@RabbitListener太多的话,会出现发无法正常消费问题 -->
<task:executor id="taskExecutor" pool-size="4-256" queue-capacity="128" />
<!-- 定义RabbitMQ的连接工厂 -->
<rabbit:connection-factory id="connectionFactory"
host="${rabbit.ip}" port="${rabbit.port}" username="${rabbit.username}" password="${rabbit.password}"
virtual-host="${rabbit.vhost}"
publisher-confirms="true"
publisher-returns="true"
channel-cache-size="5"
executor="taskExecutor"/>
<!-- MQ的管理,包括队列、交换器等 -->
<rabbit:admin connection-factory="connectionFactory" />
<!-- 定义消息队列 -->
<rabbit:queue name="orderQueue" auto-declare="true"/>
<!-- 定义交换机,并且完成队列和交换机的绑定 -->
<rabbit:direct-exchange name="orderExchange" auto-declare="true">
<rabbit:bindings>
<!-- 前台系统只接收商品更新的消息,key路由key -->
<rabbit:binding queue="orderQueue" key="saveorder"/>
</rabbit:bindings>
</rabbit:direct-exchange>
<!-- 定义监听 -->
<rabbit:listener-container connection-factory="connectionFactory">
<!-- 监听一个队列,当队列中有消息,就会自动触发类.方法,传递消息就作为方法的参数,根据方法声明的参数强转 -->
<rabbit:listener ref="rabbitMQService" method="saveOrder" queue-names="orderQueue"/>
</rabbit:listener-container>
<bean id="rabbitMQService" class="com.jt.order.service.RabbitMQService"></bean>
115.关于RabbitMQ返回值问题
说明:由于使用消息队列目的是为了减轻后台服务器压力.采用异步的操作.导致数据返回过早查询数据失败.
解决:
-
订单入库时定时跳转(5秒)
-
订单入库成功后,给用户弹框提示
-
如果遇到高并发的问题.提示友好页面让用户稍后查询(15-30分钟)
10.全文检索
24.检索方式
116.检索说明
传统方式:
根据关键字,匹配所有的文章效率是最低的.
创建索引的方式:
对于查询提供了索引的机制,可以非常快速的定位文章的位置.
117.倒排索引
查字典
倒排索引源于实际应用中需要根据属性的值来查找记录。这种索引表中的每一项都包括一个属性值和具有该属性值的各记录的地址。由于不是由记录来确定属性值,而是由属性值来确定记录的位置,因而称为倒排索引(inverted index)。带有倒排索引的文件我们称为倒排索引文件,简称倒排文件(inverted file)。
搜索案例 京东搜索?小姐姐,纸
25.Lucene
118.Lucene说明
Lucene是apache软件基金会4 jakarta项目组的一个子项目,是一个开放源代码的全文检索引擎工具包,但它不是一个完整的全文检索引擎,而是一个全文检索引擎的架构,提供了完整的查询引擎和索引引擎,部分文本分析引擎(英文与德文两种西方语言)。Lucene的目的是为软件开发人员提供一个简单易用的工具包,以方便的在目标系统中实现全文检索的功能,或者是以此为基础建立起完整的全文检索引擎。Lucene是一套用于全文检索和搜寻的开源程式库,由Apache软件基金会支持和提供。Lucene提供了一个简单却强大的应用程式接口,能够做全文索引和搜寻。在Java开发环境里Lucene是一个成熟的免费开源工具。就其本身而言,Lucene是当前以及最近几年最受欢迎的免费Java信息检索程序库。人们经常提到信息检索程序库,虽然与搜索引擎有关,但不应该将信息检索程序库与搜索引擎相混淆。 [1]
119.导入测试jar包
<!-- 全文检索lucene -->
<!-- lucene 和新功能包 -->
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-core</artifactId>
<version>4.10.2</version>
</dependency>
<!-- -->
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-analyzers-common</artifactId>
<version>4.10.2</version>
</dependency>
<!-- lucene 自带智能中文中文分词器 -->
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-analyzers-smartcn</artifactId>
<version>4.10.2</version>
</dependency>
<!-- lucene查询扩展转化器 -->
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-queryparser</artifactId>
<version>4.10.2</version>
</dependency>
<!-- IK分词器 -->
<dependency>
<groupId>org.wltea.analyzer</groupId>
<artifactId>ik-analyzer</artifactId>
<version>2012FF_u1</version>
</dependency>
<dependency>
<groupId>org.apache.solr</groupId>
<artifactId>solr-solrj</artifactId>
<version>5.2.1</version>
</dependency>
<!-- 爬虫jsoup -->
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.9.1</version>
</dependency>
120.创建索引
lucene/LuceneTest
//先为文章生成索引文件. 之后通过索引文件检索文章的位置
@Test
public void createIndex() throws IOException{
//1、创建索引的文件夹
Directory d = FSDirectory.open(new File("./index"));
//2、定义分词器 可以根据中文语义 进行分词
外国人能理解中文的博大精深吗?国人写的分词器
Analyzer analyzer = new IKAnalyzer();
//创建索引的配置文件 Analyzer analyzer 分词器
IndexWriterConfig conf =
new IndexWriterConfig(Version.LUCENE_4_10_2, analyzer);
//创建索引的输出对象
/**
* Directory d 索引输出的位置
* IndexWriterConfig 索引输出的配置文件 定义版本号 定义分词器
*/
IndexWriter indexWriter = new IndexWriter(d, conf);
//准备文章 id title sellPoint price
Document 相当于行 field相当于列
Document document = new Document();
document.add(new LongField("id",7299782,Store.YES));
document.add(new StringField("title", "诺基亚 NOKIA X6 6GB+64GB 星空黑 全网通 双卡双待 移动联通电信4G手机",Store.YES));
document.add(new TextField("sellPoint", "限时限量秒杀直降100,三期白条免息,晒单赢好礼~8.8诺基亚品牌日来袭,爆品X6秒杀直?100>>"?, Store.YES));
document.add(new LongField("price", 1599, Store.YES));
indexWriter.addDocument(document);
//关闭输出流
indexWriter.close();
}
运行代码后,在项目根目录下生成index文件夹。
121.查看索引
说明:根据文章创建索引后,根据工具可以查看索引的创建情况.
查看关键词
查看记录数
直接查询
再添加一个记录 bmw
再查询sellPoint:love
添加中文数据,能看关键词,记录,查询不了。
没有对比就没有杀害。使用standardanalyzer做对比
Textfield才会分词,stringfield 不会分词
Title先用stringfiled只有一个词组,改成textfiled就有多个词组了。
122.根据索引实现检索
创建新的类,先查询love,再查询小米
//根据索引文件实现文章的查询
/**
* 1.定义索引文件的位置
* 2.创建查询的对象属性和属性值
* 3.获取查询的Document信息
* 4.获取document属性的值.
* @throws IOException
*/
@Test
public void seracher() throws IOException{
Directory directory = FSDirectory.open(new File("./index"));
//创建检索文件
IndexSearcher indexSearcher = new IndexSearcher(IndexReader.open(directory));
//创建查询条件 Term表示分组
Query query = new TermQuery(new Term("sellPoint", "秒杀"));
//查询 results 表示最顶端的20条记录数
TopDocs docs = indexSearcher.search(query, 20);
//访问总数
System.out.println("数据访问的总数:"+docs.totalHits);
//获取文章的得分
for ( ScoreDoc scoreDoc : docs.scoreDocs) {
System.out.println("获取文章的得分:"+scoreDoc.score); //分数越高 越靠前
int index = scoreDoc.doc; //获取索引值
//获取文章内容
Document document = indexSearcher.doc(index);
//输出文章内容
System.out.println("标题:"+document.get("title"));
System.out.println("卖点:"+document.get("sellPoint"));
System.out.println("价格:"+document.get("price"));
System.out.println("id为:"+document.get("id"));
}
directory.close();
}
26.Solr介绍
123.Lucene中存在问题
说明:
可以为商品记录生成索引文件.但是需要每条信息都进行遍历.并且将item对象转化为Document对象之后才能创建索引.
缺点:
-
效率太低
-
如果商品修改了,则需要同步索引文件.
-
如果商品新增了,则需要重新遍历重新索引.
-
重新生成索引文件的时间较长.
27.Solr介绍
124.说明
Solr是一个独立的企业级搜索应用服务器,它对外提供类似于Web-service的API接口。用户可以通过http请求,向搜索引擎服务器提交一定格式的XML文件,生成索引;也可以通过Http Get操作提出查找请求,并得到XML格式的返回结果.
基于Lucene的全文搜索服务器。同时对其进行了扩展,提供了比Lucene更为丰富的查询语言,同时实现了可配置、可扩展并对查询性能进行了优化,并且提供了一个完善的功能管理界面,是一款非常优秀的全文搜索引擎。
特点:
-
solr可以根据数据库表自动生成索引文件.
-
Solr可以动态的定期自动更新索引(对更新的数据进行索引的修改)
125.同类型产品
网址:Elasticsearch:官方分布式搜索和分析引擎 | Elastic
28.Solr安装
126.安装JDK
使用jdk1.7,有下面的警告
WARNING: Java version 1.7.0_51 has known bugs with Lucene and requires the -XX:-UseSuperWord flag. Please consider upgrading your JVM.
使用1.7 安装solr后,再换成1.8会报“”
source /etc/profile
127.上传安装包
tar -xzvf solr-5.2.1.tgz
启动solr
./solr start
./solr stop
./solr restart
128.Solr管理界面
说明:solr搭建时必须严格按照路径的要求实现.
Add core 添加jt 会报下面错误
129.添加solrconfig.xml配置文件
-
在指定的目录下新建jt jt/conf jt/data
cd /usr/local/src/solr-5.2.1/server/solr
mkdir jt jt/conf jt/data
2.添加配置文件
修改\京淘项目每日课前资料\Day18-solr\solrLinux安装包\02-所需配置文件\jt\conf
<requestHandler name="/dataimport" class="org.apache.solr.handler.dataimport.DataImportHandler">
<lst name="defaults">
<str name="config">dih-config.xml</str>
</lst>
</requestHandler>
3.编辑DIH配置文件这里的ip必须是 VMnet8IP地址
4.编辑schema.xml
作用:用于将item对象封装为document对象.
4·把conf文件夹中文件上传到/usr/local/src/solr-5.2.1/server/solr/jt/conf
5.为IK分词器添加索引词
进入指定路径下:
/usr/local/src/solr-5.2.1/server/solr-webapp/webapp/WEB-INF
mkdir classes
从\京淘项目每日课前资料\Day18-solr\solrLinux安装包\02-所需配置文件\classes拷贝
6.添加jar包文件
京淘项目每日课前资料\Day18-solr\solrLinux安装包\02-所需配置文件\solr配置jar包
将solr文件导入lib文件夹下
7.重启solr
130.导入数据创建索引
-
Mysql上授权,关闭防火墙,
grant [权限] on [数据库名].[表名] to ['用户名']@['web服务器的ip地址'] identified by ['密码'];
grant all on *.* to 'root'@'%' identified by 'root';
2.创建jt项目
执行导入数据
选中jt core dataimport àfull importà选中item execute
Refresh status
q:中输入关键字,进行查询
问题说明:
-
检查IP地址
-
关闭windos防火墙
-
开放mysql对外访问权限.
安装\培优课前资料\课前资料\telnet-0.17-47.el6_3.1.x86_64.rpm
rpm -ivh telnet-0.17-47.el6_3.1.x86_64.rpm
11.实现全文检索
29.需求
30.分析
31.设计
32.Jt-parent添加solrj依赖
33.修改jt-interface
创建com.jt.search.pojo包Item.java类
//对应solr中的数据,solr中的数据来之mysql的tb_item
public class Item extends BasePojo {
//id,title,price,sellPoint,image
//@Field设置属性id的值对应solr中数据的id field
@Field("id")
private Long id;
@Field("title")
private String title;
@Field("sellPoint")
private String sellPoint;
//controller把list<item>放到request中,jsp 中用jstl,foreach search.jsp中item.images
@Field("image")
private String images;
@Field("price")
private Long price;
public String[] getImages() {
//1.png,2.png
return images.split(",");
}
//get/set方法
添加com.jt.search.service包 SearchService接口
public interface SearchService {
public List<Item> findItemByKey(String key);
}
34.创建全文检索服务器
131.创建项目jt-search
132.添加继承和依赖
-
添加继承jt-paraent
2.Pom.xml中添加dubbo依赖
<!-- dubbo,zookeeper相关依赖 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.7</version>
</dependency>
<dependency>
<groupId>com.github.abel533</groupId>
<artifactId>mapper</artifactId>
<version>2.3.2</version>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
<version>3.0.7.Final</version>
</dependency>
3.添加依赖jt-common
4.依赖jt-interface
133.添加tomcat插件
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<port>8099</port>
<path>/</path>
</configuration>
</plugin>
</plugins>
</build>
134.从jt-cat拷贝到jt-search/src/main/resources/spring/下
删除里面所有配置,粘贴一下内容
<!--配置包扫描
如果是单个项目需要定位到具体的service
如果是多个项目,需要都进行包扫描 所以写的范围较大
-->
<context:component-scan base-package="com.jt"/>
<!--引入外部配置文件 由于后期可能会引入多个配置文件 所以采用list的形式 -->
<bean id="propertyPlaceholder" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:/property/solr.properties</value>
</list>
</property>
</bean>
135.修改web.xml文件
<web-app>
<display-name>Archetype Created Web Application</display-name>
<!--定义POST乱码解决 -->
<filter>
<filter-name>characterEncoding</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<!--定义初始化参数 -->
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<!--扫描全部以applicationContext开通的配置文件 -->
<param-value>classpath:/spring/applicationContext*.xml</param-value>
</init-param>
<!--让servlet第一个加载 -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
创建包com.jt.search.service添加SearchServiceImpl类实现SearchService
public class SearchServiceImpl implements SearchService{
@Override
public List<Item> findItemByKey(String key) {
return null;
}
}
添加提供者配置文件
从jt-cart中拷贝applicationContext-provider.xml。
修改应用名,端口号,类名,id,接口名。
<!-- 1. 设置应用名称-->
<dubbo:application name="provider-of-jtsearch"/>
<!-- 2.配置zookeeper地址 -->
<dubbo:registry timeout="90000" address="zookeeper://192.168.216.202:2181">
</dubbo:registry>
<!-- 3.配置服务的端口号 -->
<dubbo:protocol port="20896" name="dubbo">
</dubbo:protocol>
<!-- 4.配置实现类的类名 -->
<bean class="com.jt.search.service.SearchServiceImpl" id="searchServiceImpl">
</bean>
<!-- 5.配置接口名,开放服务 -->
<dubbo:service timeout="90000" interface="com.jt.search.service.SearchService" ref="searchServiceImpl">
</dubbo:service>
测试服务提供者是否注册成功
把jt-parent,jt-interface,jt-search三个项目打包
启动jt-search项目。
启动redis,zk,rabbitmq
cd /usr/local/src/redis-3.2.8/cluster/
sh start.sh
cd /usr/local/src/zookeeper-3.4.8/bin
sh zkServer.sh start
cd /usr/sbin
service rabbitmq-server start
运行jt-search
查看zk,进入zk的bin目录
sh zkCli.sh
ls /
ls /dubbo
ls /dubbo/com.jt.search.service.SearchService/providers
1.Jt-search中Spring整合solrJ
-
添加solr. Properties
在\src\main\resources下创建property文件夹
\src\main\resources\property下创建solr.properties
把下面的内容拷贝到solr.properties,修改ip地址。
SOLR.URL=http://192.168.126.172:8983/solr/jt
2.修改applicationContext.xml
加载solr.properties,把原先的注释删除。
<bean id="propertyPlaceholder" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:/property/solr.properties</value>
</list>
</property>
</bean>
3.添加solr配置文件
拷贝applicationContext.xml另存为applicationContext-solrj.xml,得到xmlns。删除里面的的组件扫描和加载properties标签,添加下面的bean标签。
<bean id="httpSolrServer" class="org.apache.solr.client.solrj.impl.HttpSolrServer">
<constructor-arg index="0" value="${SOLR.URL}"/>
<!-- 设置响应解析器,solrj没有提供json解析器,所以通常用xml解析器 -->
<property name="parser">
<bean class="org.apache.solr.client.solrj.impl.XMLResponseParser"/>
</property>
<!-- 设置重试次数,推荐设置为1 -->
<property name="maxRetries" value="1"/>
<!-- 建立连接的最长时间 ,单位是:毫秒-->
<property name="connectionTimeout" value="500"/>
</bean>
4.实现检索
public class SearchServiceImpl implements SearchService {
@Autowired
private HttpSolrServer httpSolrServer;
//实现全文检索
@Override
public List<Item> findItemByKey(String key) {
List<Item> itemList = null;
try {
SolrQuery query = new SolrQuery(key);
query.setStart(0); //暂时写死 0条记录开始
query.setRows(20); //每页显示多少条记录
QueryResponse response = httpSolrServer.query(query);
//将document数据 为item对象赋值
itemList = response.getBeans(Item.class);
} catch (Exception e) {
e.printStackTrace();
}
return itemList;
}
}
-
编辑消费者的配置文件
在applicationContext-consumer.xml中添加下面标签。
<dubbo:reference interface="com.jt.search.service.SearchService"
id="searchService" check="false" timeout="50000">
</dubbo:reference>
2.编写Searchcontroller
@Controller
public class SearchController {
@Autowired
SearchService searchService;
// q:查询的关键字
@RequestMapping("/search")
public String search(String q,Model model) throws Exception{
//通过地址栏访问search.html?q=手机
//地址栏访问是get请求,
//get请求中文乱码需要程序处理
//post请求中文乱码是由web.xml中的filte处理
byte[] data=q.getBytes("ISO-8859-1");
//数据发给dubbo提供者
//dubbo中消费者发数据给提供者用的编码是utf-8
String key=new String(data, "UTF-8");
//通过调用接口的抽象方法去调用微服务的提供者
List<Item> itemList=
searchService.findItemByKey(key);
model.addAttribute("itemList", itemList);
//转发到search.jsp
return "search";
}
}
3.发布程序
发布jt-web
启动manager,cart,order,sso,jt-web
4.效果展现
1.直接在浏览器地址栏中输入下面的url
http://www.jt.com/search.html?q=电脑
2.通过网页访问,在输入框中输入关键词进行搜索
12.Docker
VMware 锁定文件失败 开启模块 diskearly 的操作失败 未能启动虚拟机”
那也可以直接找到你的虚拟机所在目录(不是VMware的安装目录),找到虚拟机系统文件目录(下图所示),进入到文件夹下。删除以.lck为后缀名的文件。之后重启虚拟机,即可正常运行了。
13.Docker容器技术
37.需求场景
-
11.11,6.18电商狂欢节,特点是超高并发(处理方式:A.系统优化,nginx+tomcat、redis、rabbitMQ、读写分离、mycat主从复制、lucene+solr、dubbo。B.硬件优化,增加服务器,砸钱)。
-
斗鱼、熊猫、虎牙、全名、印客。。。直播,特点是高并发,数据流
这些案例有个特点,阶段性高并发,并不是持续,当软件层面优化做到极致之后只能增加硬件来支撑,但是成本不容易控制。一台刀片机2-15w。
小公司,租云服务器,大公司,出租云服务器。
一台云服务器不一定是一整台刀片机,可能是一小部分。
问题:
-
一台服务器如何部署多个系统,如何拆分资源?
-
如何快速部署成百上千太服务器?
解决办法:
虚拟机?Vmware?Vbox?--资源占用率太大。人工部署慢。
38.Docker简介
Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。容器是完全使用沙箱机制,相互之间不会有任何接口。
39.Docker与虚拟机对比
每个虚拟器需要安装一个操作系统
虚拟机:资源全部隔离,cpu、内存、磁盘相互独立,一台宿主机中可以安装的虚拟机数量是一定的,有限的,移植性不强(文件很大,拷贝传输困难)。
Docker:部分共享部分隔离,共享cpu,内存,磁盘甚至操作系统,极为轻量,可以有更多的个体对外提供服务。移植性强,共享仓库,移植时只需传输脚本文件。(类似maven,拿到pom文件就可以拿到所有的jar包)
由于以上提到的docker的特点,运行时资源共享宿主机,相当于在宿主机上边安装了一些应用,之后理想状态下享用宿主机的所有资源,所以性能与宿主机一样高,而虚拟机将资源完全划分出来并封闭不进行共享,所以他只能享用到自己分到的部分资源,也就是分多少用多少,并且还要被操作系统等资源占用很大一部分。
40.Docker五大要素
-
沙箱:隔离,将应用之间的必要资源隔离开,防止互相影响,应用之间互相独立。
-
镜像:模板,centos+tomcat+mysql+redis,镜像可以拆分,传输用dockerfile
-
容器:镜像的实例,镜像只读,容器可写,容器中可以保存应用产生的零时文件
-
数据卷:挂载到容器上,用于保存必要数据。比如容器中数据库的数据,但是不建议用,因为重启容器需要挂载同步数据,效率较慢,解决方式:数据库拆分
-
仓库:与maven仓库概念一致,应用都会保存在仓库中,创建容器是根据镜像的规定进行拉取,可以共享。
41.Docker的版本及要求
Docker分为docker-ce(社区版-开源)和docker-ee(企业级-收费)
Docker基于linux3.8及以上版本64bit内核开发,所以在使用docker前需要确认linux内核版本,一般centos7以上。
查看linux版本命令:
uname -a
42.Docker的安装
培优课前资料\京淘课前资料下发-2\Day19-Docker\ centos7-empty.zip
双击vmx文件
43.修改静态IP
在windows上查看vmnet8的ip
vim /etc/sysconfig/network-scripts/ifcfg-ens33
修改ip地址与vmnet8在同一个网关,网关,dns
执行下面的命令重启网络服务,让ip地址生效.
service network restart
44.关闭防火墙
用xshell连接不上,因为有防火墙
systemctl stop firewalld.service #关闭防火墙服务
systemctl disable firewalld.service #禁止防火墙开启启动
firewall-cmd --state #检查防火墙状态
147.验证docker是否安装好
执行docker version 命令查看docker的版本,虚拟机中的docker已经装好了,1.7.3到1.7.1中的安装命令不要执行。
148.卸载老版本的 docker 及其相关依赖
sudo yum remove docker docker-common container-selinux docker-selinux docker-engine
149.安装 yum-utils,它提供了 yum-config-manager,可用来管理yum源
sudo yum install -y yum-utils
150.添加yum源
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
151.更新索引
sudo yum makecache fast
152.安装 docker-ce
sudo yum install docker-ce
45.启动 docker
//查看docker是否启动
systemctl status docker
Active: inactive (dead)
//启动docker
systemctl start docker
Active: active (running)
service docker start 启动
service docker stop 停止
service docker restart 重启
153.验证是否安装成功
docker info
docker images
//查看容器
docker ps -a
46.docker命令
命令 | 用法 |
yum -y install docker-ce | 下载最新版的docker |
service docker start | 启动Docker服务 |
service docker stop | 停止Docker服务 |
service docker restart | 重新启动Docker服务 |
docker version | 查看Docker的版本号 |
docker pull 镜像地址:版本 | 从镜像仓库中下载 |
docker save a2a69ca5184a > jt-centOS6.tar | 根据镜像id导出镜像 |
docker save -o redis-3.2.8.tar redis:3.2.8 | 根据镜像名称导出镜像 |
docker load -i docker-centos-6.5.tar | 指定jar包导入镜像文件 |
docker rmi a2a69ca5184a | 根据Id号删除镜像文件 |
docker rmi -f a2a69ca5184a | 强制删除镜像文件 删除镜像前需要先关闭容器 |
docker images | 查询所有镜像文件 |
docker inspect index.alauda.cn/tutum/centos:6.5 | 查看镜像文件细节信息 |
docker tag 旧镜像名称和端口 redis-ali:0.0.1 | 修改镜像的名称 |
docker build -t 镜像名称:版本号 | 根据dockerfile来创建镜像文件 |
docker run -d --name 容器名 镜像名:版本号 | 根据镜像名称启动容器 |
docker run -d --name 容器名(自定) 镜像id号 | 根据镜像id启动容器 |
docker run -d -p 虚拟机端口:镜像端口 --name 容器名 镜像名:版本号 | 启动容器,并指定暴露端口 |
docker ps | 查看活动的docker容器进程 |
Docker ps -a/-all | 查看全部的容器 |
docker exec -it 容器id bash | 进入指定的容器 |
docker stop 容器Id号 | 停止指定容器 |
docker start 容器Id号 | 启动创建好的容器 |
docker stop $(docker ps -q) & docker rm $(docker ps -aq) | 关闭和删除所有的容器 |
docker rm 容器Id | 删除指定的容器 |
47.Redis案例
本地导入镜像
由于网络限制,课堂使用导入镜像方式
进入/usr/local/src创建redis文件夹上传redis-3.2.8.tar包执行
docker load -i redis-3.2.8.tar
154.查看镜像
docker images
155.创建容器可能出现的错误
解决方法如下
-
重启dockerDaemon
sudo systemctl daemon-reload
2.重启docker
sudo systemctl restart docker
156.创建容器
docker run -d --name redis7000 -p 7000:6379 redis:3.2.8
参数说明:
-d,则containter将会运行在后台模式(Detached mode)
--name 实例名称
-p 对外程序访问端口7000,宿主机映射的redis端口6379
最后的redis为镜像的名称
157.单个节点测试
创建maven quick start项目,项目名jt-redis
继承jt-parent,pom.xml中把junit版本改成4.12.
创建测试类Jedis Test
@Test //完成单实例链接
public void jedis(){
Jedis jedis = new Jedis("192.168.163.30", 7000);
//jedis.auth("123456");
jedis.set("name", "tony"); //调用redis命令set
String s = jedis.get("name");
System.out.println(s);
jedis.close();
}
158.查看服务器上的数据
[root@localhost redis]# docker exec -it redis7000 /bin/bash
root@bb43e1308c91:/data# redis-cli
127.0.0.1:6379> keys*
(error) ERR unknown command 'keys*'
127.0.0.1:6379> keys *
1) "name"
127.0.0.1:6379> get name
"tony"
127.0.0.1:6379> exit
root@bb43e1308c91:/data# exit
exit
[root@localhost redis]#
159.创建多个容器
docker run -d --name redis7001 -p 7001:6379 redis:3.2.8
docker run -d --name redis7002 -p 7002:6379 redis:3.2.8
160.查看容器
docker ps -a
161.测试分片
//redis测试分片
@Test
public void test02(){
//2.创建分片的连接池
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxTotal(500);
poolConfig.setMaxIdle(20);
//3.准备redis的分片
List<JedisShardInfo> shards = new ArrayList<JedisShardInfo>();
shards.add(new JedisShardInfo("192.168.65.110", 7000));
shards.add(new JedisShardInfo("192.168.65.110", 7001));
shards.add(new JedisShardInfo("192.168.65.110", 7002));
//1.创建分片的对象
ShardedJedisPool jedisPool =
new ShardedJedisPool(poolConfig, shards);
//获取jedis对象
ShardedJedis shardedJedis = jedisPool.getResource();
//5.redis的存取值操作
for (int i = 0; i < 9; i++) {
shardedJedis.set("n"+i,"我是分片操作"+i);
}
}
-
复习
周日没来的同学,拷贝别人的centos7,双击.vmx文件加载
1.Solr
Solr原理
1.分词 java/编程/思想
2.倒排索引
Mysql 是从字符串”java编程思想”中找编程
倒排索引是根据关键词找字符串
Lucene是早期的框架
Solr是基于lucene,iKAnalyzer开发,功能加大,可以通过界面导入数据,可以处理word,pdf,excel文档。可以通过solrQuery来查询数据
在java中访问solr服务器,使用solrJ框架
2.Docker
根据镜像创建多个容器,每个容器是独立的操作系统,能设置ip地址,是个轻量的虚拟机
服务器超过100台,用docker,装出的容器的环境是一样的,装的速度快。
2.Docker
3.创建网络
1.查看ip地址
//启动docker
[root@localhost ~]# systemctl start docker
//重启
[root@localhost ~]# service docker restart
//查看ip地址
[root@localhost ~]# ifconfig
docker0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.17.0.1 netmask 255.255.0.0 broadcast 172.17.255.255
inet6 fe80::42:4eff:fe72:14fd prefixlen 64 scopeid 0x20<link>
ether 02:42:4e:72:14:fd txqueuelen 0 (Ethernet)
RX packets 27 bytes 1092 (1.0 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 39 bytes 2753 (2.6 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.228.133 netmask 255.255.255.0 broadcast 192.168.228.255
inet6 fe80::3ccc:4370:379d:488d prefixlen 64 scopeid 0x20<link>
ether 00:0c:29:45:a1:d9 txqueuelen 1000 (Ethernet)
RX packets 136766 bytes 198630173 (189.4 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 7505 bytes 1052096 (1.0 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 1 (Local Loopback)
RX packets 456 bytes 44848 (43.7 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 456 bytes 44848 (43.7 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
veth4a62e05: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet6 fe80::7c3d:b4ff:fef8:9d8e prefixlen 64 scopeid 0x20<link>
ether 7e:3d:b4:f8:9d:8e txqueuelen 0 (Ethernet)
RX packets 14 bytes 767 (767.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 34 bytes 2464 (2.4 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
veth509b5e2: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet6 fe80::90e3:dff:fed5:bce3 prefixlen 64 scopeid 0x20<link>
ether 92:e3:0d:d5:bc:e3 txqueuelen 0 (Ethernet)
RX packets 5 bytes 263 (263.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 16 bytes 1115 (1.0 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
vethbef83b1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet6 fe80::3425:30ff:fe69:faa6 prefixlen 64 scopeid 0x20<link>
ether 36:25:30:69:fa:a6 txqueuelen 0 (Ethernet)
RX packets 8 bytes 440 (440.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 19 bytes 1418 (1.3 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
2. 创建网络
执行docker network create --subnet=172.18.0.0/24 dockernet创建一个网络
[root@localhost ~]# docker network create --subnet=172.18.0.0/24 dockernet
6012c4592d74e5760a77916e4028f0a43545121b934791ebbbd007f614ec1149
[root@localhost ~]# ifconfig
br-6012c4592d74: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
inet 172.18.0.1 netmask 255.255.255.0 broadcast 172.18.0.255
ether 02:42:b1:30:0e:f3 txqueuelen 0 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
docker0: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
inet 172.17.0.1 netmask 255.255.0.0 broadcast 172.17.255.255
inet6 fe80::42:4eff:fe72:14fd prefixlen 64 scopeid 0x20<link>
ether 02:42:4e:72:14:fd txqueuelen 0 (Ethernet)
RX packets 27 bytes 1092 (1.0 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 39 bytes 2753 (2.6 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
4.安装mysql容器
3.上传文件
上传“\培优课前资料\亿发,移动端课前资料\dockerImage\”中的mysql-image.tar到/usr/local/src文件夹中。
4.加载mysql镜像
[root@localhost ~]# cd /usr/local/src
[root@localhost src]# ls
mysql-image.tar redis
//加载镜像
[root@localhost src]# docker load -i mysql-image.tar
//查看镜像,刚加载的镜像的repository,tag为none
[root@localhost src]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
<none> <none> 44a8e1a5c0b2 16 months ago 407MB
//设置镜像的名称
[root@localhost src]# docker tag 44a8e1a5c0b2 docker.io/mysql:5.7
//再次查看镜像
[root@localhost src]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
mysql 5.7 44a8e1a5c0b2 16 months ago 407MB
5.创建mysql容器
//启动mysql docker容器,设置root密码,设置默认编码,指定端口映射。
[root@localhost src]# docker run --net dockernet --ip 172.18.0.11 --name=mysql-container -it -d -p 3306:3306 -e MYSQL_ROOT_PASSWORD=root mysql:5.7 --character-set-server=utf8
[root@localhost src]# docker ps -a
//查看mysql的ip地址
[root@localhost src]# docker inspect mysql-container
[
"Networks": {
"dockernet": {
"IPAMConfig": {
"IPv4Address": "172.18.0.11"
},
]
6.导入数据库
在虚拟机中执行ifconfig,ens33的ip是虚拟机的ip
在windows启动sqlyog客户端,导入数据,Ip地址是虚拟机的ip
关闭以前的连接
连接成功后,只显示4个数据库
执行“\培优课前资料\京淘项目每日课前资料\Day19-Docker\EasyMall”文件夹下的easymall.sql
该文件寻贴主得
刷新后能看到easymall数据库
7.如查连不上mysql,报10060或10061,解决办法如下
先关机poweroff,再开机,xshell重新打开连接
systemctl stop firewalld.service // 关闭防火墙服务
systemctl disable firewalld.service // 禁止防火墙开启启动
firewalld-cmd --state //查看防火墙状态
systemctl start docker //启动docker
docker start mysql-container //启动mysql
5.安装tomcat容器
8.服务器结构图
9.上传镜像
上传“\培优课前资料\亿发,移动端课前资料\dockerImage\”中的tomcat7-jre8-image.tar到/usr/local/src文件夹中。
10.安装镜像
[root@localhost ~]# cd /usr/local/src
[root@localhost src]# ls
mysql-image.tar redis tomcat7-jre8-image.tar
[root@localhost src]# docker load -i tomcat7-jre8-image.tar
[root@localhost src]# docker images
[root@localhost src]# docker tag 510e45f8f9f7 docker.io/tomcat:7.0
[root@localhost src]# docker images
11.创建容器
docker run -d -p 8080:8080 --net dockernet --ip 172.18.0.12 --name easyMall-tomcat tomcat:7.0
docker ps –a
windows浏览器中访问http://虚拟机ip:8080,能看到tomcat首页。
6.部署easymall
12.修改数据库链接地址
将“\培优课前资料\京淘项目每日课前资料\Day19-Docker\EasyMall”文件夹中的ROOT.war改成ROOT.rar,用winrar直接打开。
将ROOT文件中的\WEB-INF\classes\c3p0-config.xml文件修改,修改数据库ip地址为mysql容器的ip地址172.18.0.11。保存修改,将ROOT.rar改成ROOT.war
上传ROOT.war到/usr/local/src文件夹中。
<property name="jdbcUrl">jdbc:mysql://172.18.0.11:3306/easymall
</property>
13.查看tomcat容器文件夹目录
docker ps -a
[root@localhost src]# docker exec -it easyMall-tomcat bash
root@c620df99d5e5:/usr/local/tomcat# cd /usr/local/tomcat
root@c620df99d5e5:/usr/local/tomcat# ls
LICENSE RELEASE-NOTES bin include logs temp work
NOTICE RUNNING.txt conf lib native-jni-lib webapps
root@c620df99d5e5:/usr/local/tomcat# cd webapps
root@c620df99d5e5:/usr/local/tomcat/webapps# ls
ROOT docs examples host-manager manager
root@c620df99d5e5:/usr/local/tomcat/webapps# exit
exit
[root@localhost src]#
14.上传root.war到tomcat容器中
[root@localhost src]# cd /usr/local/src
[root@localhost src]# ls
[root@localhost src]# docker cp ROOT.war easyMall-tomcat:/usr/local/tomcat/webapps/
7.Dockerfile制作镜像
Dockerfile是docker应用的核心,通过dockerfile能制作出新的镜像。
15.关键字及含义
序号 | 关键字 | 说明 |
FROM | 指定基础镜像的来源 | |
MAINTAINER | 作者 | |
ADD | 复制文件,会自动解压 | |
WORKDIR | 设置当前工作目录 cd | |
VOLUME | 设置数据卷,挂载主机目录 | |
EXPOSE | 指定对外暴漏的端口 | |
RUN | 执行命令 sh | |
CMD | 执行命令 exec,一个Dockerfile只能一个 | |
COPY | 复制文件 | |
ENTRYPOINT | docker run时参数可以覆盖,指定参数值 |
16.打包生成war
使用eclipse创建出一个maven webapp项目,项目名为oa,修改index.jsp中的内容,打包出war,到maven本地仓库中找到war,改名成oa.war
17.启动CentOS6.5容器
1.启动centos6.5容器
docker ps –a
docker start centos6.5
2.查看容器/usr/local/src文件夹中的内容,文件夹中没有内容。
[root@localhost ~]# docker exec -it centos6.5 bash
bash-4.1# cd /usr/local/src
bash-4.1# ls
bash-4.1# exit
18.生成镜像
-
上传“\培优课前资料\京淘课前资料下发-2\4-LinuxJDK1.7-1.8\”文件夹中的jdk-8u51-linux-x64.tar.gz到虚拟机的\usr\local\src。
-
上传\Day19-Docker\apache-tomcat-7.0.55.tar.gz到虚拟机的\usr\local\src
-
上传oa.war到虚拟机的\usr\local\src
-
执行ls命令,查看三个文件是否上传到虚拟机中。
-
打开记事本,新建一个文件,拷贝下面的内容,保存到Dockerfile.txt文件中,文件名中D是大写的,保存时文件格式必须为utf-8。将文件Dockerfile.txt改名成Dockerfile,去掉.txt扩展名。
#添加contos6.5文件
FROM a2a69ca5184a
#添加JDK1.8 centos+jdk
ADD jdk-8u51-linux-x64.tar.gz /usr/local/src
ENV JAVA_HOME=/usr/local/src/jdk1.8.0_51
ENV PATH=$JAVA_HOME/bin:$PATH
ENV CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
#centOS6.5+JDK1.8+tomcat7
ADD apache-tomcat-7.0.55.tar.gz /usr/local/src
ENV CATALINA_HOME /usr/local/src/apache-tomcat-7.0.55
ENV PATH=$PATH:$CATALINA_HOME/bin
#添加oa.war包文件
WORKDIR $CATALINA_HOME/webapps
RUN mkdir oa
COPY oa.war $CATALINA_HOME/webapps/oa/oa.war
WORKDIR $CATALINA_HOME/webapps/oa
RUN jar xvf oa.war
RUN rm oa.war
#对外暴露的端口号
EXPOSE 8080
CMD ["/usr/local/src/apache-tomcat-7.0.55/bin/catalina.sh","run"]
6.上传Dockerfile到宿主机的/usr/local/src中
7.制作新的镜像文件
在宿主机上执行docker build命令
镜像名称是oa-tomcat,版本是0.0.1
cd /usr/local/src
// ./代表当前目录
docker build -t oa-tomcat:0.0.1 ./
制作成功会显示“Successfully tagged oa-tomcat:0.0.1”
8.docker images 查看镜像
8.使用自己制作的镜像创建容器
1.创建容器
docker run -d -p 9080:8080 --net dockernet --ip 172.18.0.13 --name oa-tomcat oa-tomcat:0.0.1
2.访问tomcat和oa
访问http://虚拟机ip:9080能看到tomcat首页
访问http://虚拟机ip:9080/oa/index.jsp能看到oa首页
3.查看容器内容文件结构
[root@localhost src]# docker ps -a
[root@localhost src]# docker exec -it oa-tomcat bash
bash-4.1# cd /usr/local/src
bash-4.1# ls
apache-tomcat-7.0.55 jdk1.8.0_51
bash-4.1# cd apache-tomcat-7.0.55/
bash-4.1# ls
LICENSE RELEASE-NOTES bin lib temp work
NOTICE RUNNING.txt conf logs webapps
bash-4.1# cd webapps
bash-4.1# ls
ROOT docs examples host-manager manager oa
bash-4.1# cd oa
bash-4.1# ls
META-INF WEB-INF index.jsp
bash-4.1# cd ../
bash-4.1# cd ../
bash-4.1# pwd
/usr/local/src/apache-tomcat-7.0.55
bash-4.1# cd conf
bash-4.1# ls
server.xml
bash-4.1# exit
4.把容器中的文件拷贝出来
[root@localhost src]# pwd
/usr/local/src
//.代表当前目录
[root@localhost src]# docker cp oa-tomcat:/usr/local/src/apache-tomcat-7.0.55/conf/server.xml .
[root@localhost src]# ls
server.xml
5.修改server.xml
把server.xml拷贝到windows中。
在124行host标签中添加context标签
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
<Context path="" docBase="/usr/local/src/apache-tomcat-7.0.55/webapps/oa" debug="0" reloadable="true" crossContext="true"/>
6.拷贝server.xml到容器中
把windows中的server.xml拷贝到虚拟机的/usr/local/src中。
[root@localhost src]# pwd
/usr/local/src
[root@localhost src]# docker cp server.xml oa-tomcat:/usr/local/src/apache-tomcat-7.0.55/conf/
7.重启容器
[root@localhost src]# docker ps -a
[root@localhost src]# docker restart oa-tomcat
8.浏览器中访问
http://192.168.216.201:9080/index.jsp能看到oa首页
9.生成镜像文件
[root@localhost src]# docker images
[root@localhost src]# docker save -o oa-tomcat-0.0.1.tar oa-tomcat:0.0.1
[root@localhost src]# ls
oa-tomcat-0.0.1.tar
10.使用制作的镜像文件创建容器
[root@localhost ~]# docker ps -a
[root@localhost ~]# docker stop oa-tomcat
//移出容器
[root@localhost ~]# docker rm oa-tomcat
//移出镜像
[root@localhost ~]# docker rmi oa-tomcat:0.0.1
Untagged: oa-tomcat:0.0.1
Deleted: sha256:1274a0931b333171acc17751da76407b935999ad6ee0cb6078c503828c15a3e6
//没有oa-tomcat容器
[root@localhost src]# docker images
[root@localhost ~]# cd /usr/local/src
[root@localhost src]# ls
oa-tomcat-0.0.1.tar
[root@localhost src]# docker load -i oa-tomcat-0.0.1.tar
[root@localhost src]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
oa-tomcat 0.0.1 1274a0931b33 15 hours ago 653MB
[root@localhost src]# docker run -d -p 9180:8080 --net dockernet --ip 172.18.0.19 --name oa-tomcat2 oa-tomcat:0.0.1
b617901309f6db4e4e49a60be22f8e929b1b3b4d0c24e3a55588ff61224fbe61
[root@localhost src]# docker ps -a
浏览器访问
http://192.168.216.201:9180/oa/index.jsp
11.总结和拓展
Docker容器一般用于部署重复相同的服务,如:redis集群,tomcat集群等。适用于快速高效集群扩展。整个过程全自动,省去了运维成本。
3.移动端
12.c/s和b/s的区别
c/s client/server
微信,QQ,用户要安装client
优点:功能强大
缺点:客户端要安装
b/s browser/server
用户不用安装客户端,客户端是浏览器
优点:客户端不用安装
缺点:功能简单,界面加载慢,简单,使用html5 canvas把界面复杂。
做软件运行在电脑,推荐用b/s
做软件运行在手机上,叫app,推荐用c/s
电脑上和手机上开发软件都是用混合模式,c/s+b/s
Wireshark抓网卡上的数据包
13.部署服务器端
-
设置数据包的大小 ,如果不设置,sql文件态大,只能执行一部分sql
SET GLOBAL max_allowed_packet=1024*1024*400;
2.解压pdServer
部署pd_store数据库,sql文件是”pd.sql”
3.eclipse中import pdserver
Eclipse中打开server窗口,添加tomcat,
部署pdServer项目
把访问path设置成/
通过http://ip:port 能看到商城的首页
http://192.168.1.105:8090/mobileIndex.html
14.Mounted事件
在pdServer中创建网页,防止跨域
<script type="text/javascript" src="js/vue.js"></script>
</head>
<body>
<!-- app必须加在普通元素上,不能加载在body上 -->
<div id="app"></div>
</body>
</html>
<script type="text/javascript">
//必须放在</html>后面
var vue = new Vue({
el : "#app",
mounted : function() {
console.log("mounted")
}
});
</script>
15.数据绑定
<body>
<!-- app必须加在普通元素上,不能加载在body上 -->
<div id="app">
<div v-for="item in items">
<span>{{item.name}}</span>
<span>{{item.price}}</span>
</div>
</div>
</body>
</html>
<script type="text/javascript">
//必须放在</html>后面
var vue = new Vue({
el : "#app",
data:{items:[{name:"手机",price:2000},
{name:"电脑",price:3000}]},
mounted : function() {
console.log("mounted")
}
});
</script>
4.Vue 联网
16.测试服务器端接口
http://10.1.6.91:8090/api/getIndexItem.html
安装chrome插件jsonView
\培优课前资料\亿发,移动端课前资料\software\jsonView\ chrome中安装jsonView.doc
17.Index联网
mounted : function() {
console.log("mounted")
var url="/api/getIndexItem.html";
axios.get(url).then(function(response){
console.log(response);
})
.catch(function(err){console.log(err)});
}
测试两个this的区别
<script type="text/javascript"
src="js/axios.min.js">
</script>
</head>
<body>
<div id="app">
<div v-for="item in items">
<span>{{item.title}}</span>
<span>{{item.price}}</span><br/>
</div>
</div>
</body>
</html>
<script type="text/javascript">
//http://ip:port/pdIndex.html
//F12 查看console
var vue=new Vue({
el:"#app",
mounted:function(){
console.log("mounted this");
//this有items属性
console.log(this);
var mountedThis=this;
console.log("网页加载完");
//联网
var url="/api/getIndexItem.html";
//get函数指定url
//then函数是在联网成功后,vue来调用
//ajax input output system,封装了ajax
axios.get(url)
.then(
function(response)
{
console.log("联网成功后调的函数 this");
console.log(this);
console.log(response);
//response有headers,request,data
var serverData=response.data;
//服务器返回的数据有status,msg,data
//data中放的是商品数据
var items=serverData.data;
mountedThis.items=items;
}
)
.catch(
function(e){
console.log(e);
}
);
},
data:{
//items是空的数组,数据来之服务器
items:[ ]
}
});
</script>
//为每件商品添加超连接地址detailUrl
for (var i=0;i<items.length;i++)
{
var detailUrl=
"pdDetail.html?id="+items[i].id;
//动态为对象添加属性
items[i].detailUrl=detailUrl;
}
<div v-for="item in items">
<a v-bind:href="item.detailUrl">
<img v-bind:src="item.image" /></br>
<span>{{item.title}}</span></br>
<span>{{item.price}}</span></br>
</a>
</div>
18.Detail.html
19.得itemId
<script type="text/javascript" src="../js/vue.js"></script>
<script type="text/javascript" src="../js/axios.min.js"></script>
<script type="text/javascript" src="../js/util.js"></script>
</head>
<body>
<!-- app必须加在普通元素上,不能加载在body上 -->
<div id="app">
</div>
</body>
</html>
<script type="text/javascript">
//必须放在</html>后面
var vue = new Vue({
el:"#app",
data:{item:null},
mounted:function(){
console.log("mounted");
var _this=this;
var map=getParamMap();
var itemId=map.get("itemId");
console.log(itemId);
}
});
</script>
20.显示图片
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script type="text/javascript" src="js/vue.js">
</script>
<script type="text/javascript" src="js/axios.min.js">
</script>
<script type="text/javascript" src="js/util.js">
</script>
</head>
<body>
<div id="app">
<a href="pdIndex.html">返回</a>
<br/>
<span>{{item.title}}</span><br/>
<span>{{item.sellPoint}}</span><br/>
<img v-bind:src="item.image"/>
<div v-html="item.desc"></div>
</div>
</body>
</html>
<script type="text/javascript">
var vue=new Vue({
el:"#app",
mounted:function()
{
//?id=12&username=a
//得传过来的id
var hashMap=getParamMap();
var id=hashMap.get("id");
console.log(id);
var url="/api/getItemDetail.html?itemId="+id;
var mountedThis=this;
axios.get(url).then(
function(response)
{
var serverData=response.data;
var item=serverData.data;
mountedThis.item=item;
}
).catch();
},
data:{item:null}
});
</script>
5.安装android开发环境
链接:百度网盘-链接不存在
密码:3gxq
AndroidStudio开发环境搭建步骤详解.doc
6.
作者:Darren
QQ:603026148
以上内容归Darren所有,如果有什么错误或者不足的地方请联系我,希望我们共同进步。
标签:src,20,jt,py,factory,rabbitmq,docker,root,JTWEB02 来源: https://blog.csdn.net/weixin_39519454/article/details/122543895