Dubbo介绍
什么是分布式系统:
分布式系统原理与范型:
定义:
分布式系统是若干独立计算机的集合,这些计算机对于用户来说就像单个相关系统
分布式系统(distributed system)是建立在网络之上的软件系统
简单来说:多个(不同职责)人共同来完成一件事
任何一台服务器都无法满足淘宝的双十一的数据吞吐量,一定是很多台服务器公共来完成的
歇后语:“三个臭皮匠赛过诸葛亮”,就是分布式系统的真实写照
接下来说明一下应用架构的演变,也就是我们代码的操作,但不是数据的操作(后面的Redis的数据库架构演变会说明)
注意:下面的说明比较啰嗦,是为了演变而说明的,所以可以跳过,看这个简单的说明:
简单的说明:
单一:电脑放一个项目
垂直:电脑放页面,电脑放业务,电脑放其他(如mysql)
RPC:电脑放页面,电脑放业务访问,电脑放业务,电脑放其他
电脑可以是多个(包括集群的概念)或者一个
单一应用架构:
当网站流量很小时,只需要一个应用,将所有的功能部署到一起(所有业务都放在一个tomcat里)
从而减少部署节点(也就是服务器)和成本
此时,用于简化增删改查工作量的数据访问框架 (ORM)是关键;
例如:某个超市的收银系统,某个公司的员工管理系统
ORM:对象关系映射(Object Relational Mapping),在61章博客里也说明过
优点:
小项目开发快 成本低
架构简单
易于测试
易于部署
缺点:
大项目模块耦合严重 不易开发 维护 沟通成本高
新增业务困难
核心业务与边缘业务混合在一块,出现问题互相影响
实际情况理解:我们一般将所有的代码放在一个服务器上,假设访问量变大,我们一般添加服务器(节点),进行集群来解决
但是一般访问的量,在对应的模块,或者系统上是不同的,一些多的访问,可能使得对应的访问变慢,即我们虽然可以添加对应的服务器,但是这是非常浪费的,因为其他的业务并没有那么多访问量
浪费了很多不必要的空间,而为了节省这样的操作(小的服务器,就可以替代),我们就让对应的模块或者系统变成一个服务器
于是就出现了垂直应用架构
垂直应用架构:
当访问量逐渐增大,单一应用增加机器带来的加速度越来越小(或者说提升的效率,又或者说空间的利用率)
将应用拆成几个互不相干的几个应用,以提高效率
大模块按照mvc分层模式,进行拆分成多个互不相关的小模块
并且每个小模块都有独立的服务器,当然,这是好的拆分,实际上可能只是对应代码进行垂直,但任然在同一个服务器里面
实际上这也算是一个垂直,但实际上却是单一架构的,虽然代码在自己身上,但缺点是单一架构的缺点,如添加服务器浪费很多空间
如下面就分成了三个服务器,一个视图层的服务器,一个业务层的服务器,一个数据库层的服务器,所有说是mvc分层
实际上可以分很多层,但大体都是这一类的服务器,下面就是图片的演示
此时,用于加速前端页面开发的web框架(MVC)是关键,因为每个小应用都有独立的页面
MVC:模型视图控制器 (Model View Controller)
缺点:
模块之间不可能完全没有交集,即一个模块里面有很多个管理服务
假如出现了交集(同样的管理服务),举个例子,不同的模块之间可能都会有用户服务(也可以说是用户管理)
虽然你也可以放在自己身上,或者访问其他服务器的模块的用户服务,服务器里的模块里的服务
但无论是放在其他服务器模块里(死机就访问不了了,比单独死机的更加严重,因为会影响其他业务),还是自己的(重复),都是有缺点的
如我们操作用户服务,可能都会有需要用户模块
即公用模块无法单独重复利用(不是服务器之间的方法,死机就访问不了了)
实际情况理解:根据上面的单一应用架构的实际情况理解开始
虽然我们解决了实现局部的节点空间利用,但是如果这样的模块或者系统越来越多时
可以发现,他们之间,可能都会用到每个模块或者系统的一个操作,即服务(这里是服务,服务也可以说成模块)
如查询所有用户,或者查询姓名的回显等等(这只是某些例子),即会出现同样的代码(相同服务)
而这些同样的代码,由于他们是分开的,那么他们也是需要去对应的服务器进行访问或者自己再来一个服务
很明显,当这些代码越来越多时,对应服务器的联系也就越来越多,这时,当出现某个服务器死机或者重复模块越来越多
可能会影响整个业务或者需要空间变大,即耦合度越来越高或者重复代码越来越多
所以我们需要将这些重复的代码抽取出来,形成一个一个的服务器
于是就出现了分布式服务架构(完全的分布式了),使得将共用的代码放在一起
实际上就是将所有重复的服务分离出来,变成服务器,而不是模块变成服务器,从而实现真正的分布式的操作
然后我们只需要维护这些服务器即可,而不用防止其他服务器的死机造成访问不了(虽然也会死机,但变成了单独的了,即不影响其他业务),且解决了重复问题
垂直应用架构,也可以说是分布式的操作,也是由多个人(模块或者系统)组成一件事情(项目)
只是在上级的模块进行分布而已,对于整体来说,并没有进行完全分布式(服务并没有分布式,有重复,或者有很大的联系)
分布式服务架构(使得垂直架构,变成真正的分布式的概念,他也是一种服务):
当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,如服务抽取出来
作为独立的业务(服务器),逐渐形成稳健的服务中心
使前端应用能更快速的响应多变的市场需求
此时,用户提高业务复用及整合的分布式服务框架(RPC)远程调用是关键
下面由一个业务服务器,变成了三个服务器(这三个是重复的),服务分离出来了
RPC:独立的应用服务器之间,要依靠RPC(Romote Procedure Call)才能调用
实际情况理解:根据上面的垂直应用架构的实际情况理解开始
在解决了利用问题后,但出现的服务器互相访问的问题或者重复问题,为了解决这种情况
我们将对应模块的服务全部抽取出来,形成一个一个的服务器,那么他们只需要访问这些服务器即可,而他们之间可以实现远程调用,一般都是操作业务层的
使得对应的方法可以远程调用,而不是在一个服务器里,使得每个服务器可能都有对应的方法
但是虽然我们进行了抽取,可是这些公共的服务的访问一般不是平均的,就如下面一样的解释
物流服务不忙,有100台服务器,商品服务特别忙,也是100台服务器,也就是说
虽然是一个服务器,但是里面的服务访问不同,这就又回到了类似单一的架构
只是这些是服务,并不是服务器,所有我们需要进行其他优化(使用不了集群的操作)
那么就有下面的问题:
如何做到资源优化调配:
流动计算架构:
当服务越来越多,容量的评估,小服务资源的浪费等问题逐渐呈现
此时需增加一个调度中心基于访问压力实时管理集群容量,提高集群利用率
此时,用于提高机器利用率的资源调度和治理中心(SOA)是关键
SOA:面向服务架构(Service-Oriented Architecture),简单理解就是"服务治理",例如:公交车站的"调度员"
也可以理解为负载均衡的那个平均访问(只是例子)
但实际上通常整体来说并没有这样的平均操作,因为用户的选择我们基本干涉不了,我们只能对对应服务进行优化
但我们也可以将这些服务完全的分开(虽然他本身也可以,但我们通常将这种称为别名是微服务的了,所以说,微服务可以认为是分布式的一个极致而已,留下少数也可以称为微服务),这样就好操作了,就如垂直应用架构类似的分开(实际上并不是,只是比喻)
这样的操作就是微服务了,以后会进行说明,现在我们只操作该分布式服务架构
Dubbo简介:
Dubbo是分布式服务框架,是阿里巴巴的开源项目,现交给apache进行维护
Dubbo致力于提高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案
简单来说,dubbo是个服务框架,如果没有分布式的需求,是不需要用的
RPC:
RPC(Remote Procedure Call)是指远程过程调用,是一种进程间通信方式
RPC基本的通信原理(可以认为是网络编程上(tcp),进一步补充操作,当然,我们编程语言一般只能操作电脑上存在的层,在传输层后面就不行了,都不是电脑的怎么操作呢(具体可以百度)):
在客户端将对象进行序列化
底层通信框架使用netty(基于tcp协议的socket),将序列化的对象发给服务方提供方
服务提供方通过socket得到数据文件之后,进行反序列化,获得要操作的对象
对象数据操作完毕,将新的对象序列化,再通过服务提供方的socket返回给客户端
客户端获得序列化数据,再反序列化,得到最新的数据对象,至此,完成一次请求
RPC两个核心模块:通讯(socket),序列化
节点角色:
调用关系:
服务容器负责启动,加载,运行服务提供者
服务提供者在启动时,向注册中心注册自己提供的服务
服务消费者在启动时,向注册中心订阅自己所需的服务
在注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者
服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用
服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心
快速入门 :
官网:http://dubbo.apache.org/
注册中心:
Zookeeper:
官方推荐使用zookeeper注册中心,因为在上图中,我们发现,这个注册中心的操作就类似于zookeeper,即也有通知
如可以将对应的列表查询给你,而这样的操作,实际上在界面上我们并不会体会到,因为他只是文件系统加上通知而已
至于如何显示,要看你们如何操作
注册中心负责服务地址的注册与查找,相当于目录服务
服务提供者和消费者只在启动时与注册中心交互,注册中不转发请求,压力较小
Zookeeper是apache hadoop的子项目,是一个树形的目录服务,支持变更推送
适合作为dubbo的服务注册中心,工业强度较高,可用于生产环境;
dubbo即是求职的人,也是招聘单位,而zookeeper就是人才市场/招聘网站
安装:
安装jdk(具体看55章博客)
前面78章博客里有操作,若看过,并操作过,可以直接跳过
拷贝apache-zookeeper-3.6.0-bin.tar.gz到opt目录
解压安装包:
[root@localhost opt]
重命名:
[root@localhost opt]
在/opt/zookeeper/这个目录上创建zkData和zkLog目录
[root@localhost zookeeper]
[root@localhost zookeeper]
进入/opt/zookeeper/conf这个路径,复制一份 zoo_sample.cfg 文件并命名为 zoo.cfg
[root@localhost conf]
编辑zoo.cfg文件,修改dataDir路径:
dataDir=/opt/zookeeper/zkData
dataLogDir=/opt/zookeeper/zkLog
启动Zookeeper:
[root@localhost bin]
查看状态:
[root@localhost bin]
注意:上面只是单机启动,若有集群的配置,注意进行注释(可以选择百度查看,或者看78章博客)
服务提供方:
一个空的maven项目
提供一个服务接口即可
服务方的pom.xml:
各种依赖请严格按照下面的版本(因为不同的版本,可能会不兼容,后面的操作时,也尽量不要修改版本,防止启动失败)
<packaging>war</packaging>
<properties>
<spring.version>5.0.6.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.5.7</version>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.6</version>
</dependency>
<dependency>
<groupId>com.github.sgroschupf</groupId>
<artifactId>zkclient</artifactId>
<version>0.1</version>
</dependency>
<dependency>
<groupId>javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.11.0.GA</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven </groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<configuration>
<port>8001</port>
<path>/</path>
<uriEncoding>UTF-8</uriEncoding>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
服务方接口:
package service;
public interface HelloService {
String sayHello(String name);
}
服务方实现 :
package service.impl;
import com.alibaba.dubbo.config.annotation.Service;
import service.HelloService;
@Service
public class HelloServiceImpl implements HelloService {
@Override
public String sayHello(String name) {
return "Hello," + name + "!!!";
}
}
服务方的配置文件spring.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">
<dubbo:application name="dubbo-server"/>
<dubbo:registry address="zookeeper://192.168.164.128:2181"/>
<dubbo:annotation package="service.impl"/>
</beans>
服务方的web.xml :
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
id="WebApp_ID" version="3.1">
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/spring.xml</param-value>
</context-param>
</web-app>
打包运行,测试一下是否成功(一般的对应的依赖,会使得出现不同日志,这个不必理会)
服务消费方:
消费方的pom.xml :
与服务方一致,只需要修改tomcat的端口为8002
消费方的Controller
package controller;
import com.alibaba.dubbo.config.annotation.Reference;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import service.HelloService;
@Controller
public class HelloAction {
@Reference
private HelloService helloService;
@GetMapping(value = "hello",produces = "application/json; charset=utf-8")
@ResponseBody
public String sayHi(String name) {
String s = helloService.sayHello(name);
return s;
}
}
消费方的接口 :
注意:
controller中要依赖HelloService,所以我们创建一个接口
这里是消费方,不需要实现,因为实现会让服务方为我们搞定(上面的@Reference注解操作注入了)
package service;
public interface HelloService {
String sayHello(String name);
}
消费方的spring.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">
<dubbo:application name="dubbo-consumer"/>
<dubbo:registry address="zookeeper://192.168.164.128:2181"/>
<dubbo:annotation package="controller"/>
</beans>
但要注意:记得将对应别名改成dubbo-consumer(你也可以自己写别名,可以与这个不一样)
消费方的web.xml :
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
id="WebApp_ID" version="3.1">
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/spring.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
启动服务测试:
首先启动服务方(创建了对应节点),再启动消费方
访问:http://localhost:8002/hello?name=james
发现返回了结果,至此,我们进行了远程的调用的方式
监控中心 :
我们在开发时,需要知道注册中心都注册了哪些服务,以便我们开发和测试
图形化显示注册中心的中 服务列表
我们可以通过部署一个web应用版的管理中心来实现
主要看服务方数据的显示和消费方数据的显示
服务管理端:
安装管理端:
下载地址:
链接:https://pan.baidu.com/s/17BBhrPQsFr0o0_2MJBeujw
提取码:alsk
解压 dubbo-admin-master.zip,实际上也是一个项目,或者说web项目,只是他用来显示对应图形化界面而已
实际上主要操作对应dubbo在zookeeper的dubbo节点里的节点的显示或操作,使得图形化了,就如Mysql的图形化一样
修改配置文件
这个注册中心地址修改成自己的zookeeper所在的服务器地址
返回到项目根目录,使用maven打包:mvn clean package
在cmd窗口中,linux的ls命令不是查找目录文件的,而是dir来查找,我们可以到target目录下
即在dos下运行target目录中的jar文件:java -jar dubbo-admin-0.0.1-SNAPSHOT.jar,相当于服务器运行war包一样
只是war包是包括页面的,而jar基本只包括class(当然也可以包括页面,如jsp的输出流操作)
war包可以运行在服务器,而jar包不可以(只能使用)
注意:运行后,对应窗口不要删除,否则会停止服务器的操作,就如tomcat的窗口删除,也就关闭了服务器
此时打开浏览器输入:http://localhost:7001/
第一次访问时,需要登录,帐号密码都是root
看到如下界面,说明这个项目(可以说是web项目)执行成功
会操作获取的dubbo节点,并记录时间,以后会根据时间进行更新,没找到不更新,即也可以说是保存
管理端使用:
启动服务方,将服务注册到zookeeper
在这之前,我们首先点击一下上面的箭头:
启动dubbo-server服务方后,刷新管理端,服务注册成功,只是没有消费者
点击服务名,进入服务提供者页面
把消费者也运行起来,注意要运行,这个运行实际上是使用对应的@Reference注解操作的变量
即需要被操作,dubbo看到被使用后,监听的操作
会在对应节点加上数据,而这个数据使得下面显示正常(所以不要乱删除节点,使得数据被固定)
刷新服务,显示正常
注意:若出现特殊的问题,可以重新运行项目,如明明消费者已经启动且运行,却显示没有消费者
而且还有很多问题,而出现这些问题的原因,主要是因为我们手动去删除zookeeper的dubbo节点
可能该节点与该web项目有关系吧,而正是删除了,当该项目去访问时(访问对应时间的节点)
所以删除节点,在没有刷新时,也是会没有找到对应数据的,而没有找到对应节点数据时
就会使得原来的显示固定保存起来
而由于优先显示同样的固定的数据(实际上只会得到对应时间的节点,所以相当于保存起来了)
所以你会发现,明明我们消费者运行,却没有变化,因为使用的是固定的数据
所以,最好不要进行删除
查看消费者(远程调用的基本就是消费者,反之就是服务提供者):
监控统计中心:
Monitor:统计中心 ,记录服务被调用多少次等,主要看服务方数据的显示
下载地址:
链接:https://pan.baidu.com/s/1MmR6SBkvoZVsECLSqePRAg
提取码:alsk
解压dubbo-monitor-simple-2.5.3.zip
修改dubbo-monitor-simple-2.5.3\conf\dubbo.properties
其实只需要将对应的zookeeper的配置的注释去掉即可,原来的默认的可以注释掉,这里就直接修改默认的了
其中对应的端口是8080,会占用,这个要注意以下,防止端口冲突,使得启动不了(一般会让你按下任意键退出)
双击运行dubbo-monitor-simple-2.5.3\bin\start.bat
注意:对应的端口不要关闭,否则就如tomcat一样
服务器也就关闭了(可以这样说,但只是针对于服务),实际上就是服务关闭了
分别修改dubbo-server项目和dubbo-consumer项目的spring.xml,加入下面标签
<dubbo:monitor protocol="registry"/>
最好服务方和消费方都写上,防止对应的次数不正确(即不计数)
他的刷新不是实时的,有一定间隔刷新一次(即无论你怎么刷新网站都没有用,必须等待一定间隔才会更新数据)
但其中一个不写的话,可能会出现次数不一致(好像是不计数)
其中Statistics那里就是下面的操作
代表26个请求,成功26个请求,实际上左边的一般与消费方有关(调用次数,必须要调用完毕,中途报错不算)
右边的与服务方有关(被调用次数,基本只要执行就算,即被调用就算)
都与上面的监听有关(即没有配置,则不增加对应的次数)
而这些数据,就存放在对应的zookeeper里面,发送或者接受都会改变对应的数据,一般都会相同,除非有不监听的
综合实战:
配置说明:
启动时检查
在Dubbo的官网:http://dubbo.apache.org/里找到(快速开始),出现如下(可能现在与下面不同了):
启动时会在注册中心检查依赖的服务是否可用(或者是否有服务在zookeeper里面),不可用时会抛出异常
在消费方编写初始化容器的main方法启动(tomcat启动方式,必须访问一次action才能初始化spring)
package text;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.io.IOException;
public class Test {
public static void main(String[] args) throws IOException {
ClassPathXmlApplicationContext context = new
ClassPathXmlApplicationContext("classpath:spring/spring.xml");
System.in.read();
}
}
为了更好的观察,则配置系统级别日志,需要配合log4j才输出,在resources下添加log4j.properties,内容如下:
### log4j.appender.stdout = 表示输出方式
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
#日志输出到系统标准输出位置上,默认就是这个值
log4j.appender.stdout.Target=System.out
# 表示输出格式
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
# 打印信息格式
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %m%n
### log4j.appender.file = 表示文件输出方式
log4j.appender.file=org.apache.log4j.FileAppender
#文件存放位置
log4j.appender.file.File=dubbo.log
# log4j.appender.file.layout = 表示输出格式
log4j.appender.file.layout=org.apache.log4j.PatternLayout
# log4j.appender.file.layout.ConversionPattern = 表示打印格式
log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %l %m%n
# log4j.rootLogger = 表示根日志级别
log4j.rootLogger=error, stdout,file
写上后,我们再次进行运行,那么就出现了新的日志,即报没有对应的服务错误
实际上是自身读取配置文件时,提供不了服务或者对应没有服务,即只有是消费者(可以这样说)
但并不会影响运行,只是没有提供服务而已
而当有提供服务时(自己提供服务,在检查之前),就不会出现错误,但虽然不会影响运行,但是错误还是要进行消除的
实际上最好不要消除错误,而是要解决错误,因为可以根据错误知道那里有问题
虽然这个问题在启动时,不会中断,即可以启动,但是在运行时,若没有解决他的报错原因,通常来说,都会报错
我们在消费方的spring.xml加上如下配置:
<dubbo:consumer check="false" />
使得不会去检查服务是否有提供
超时时间:
由于网络或服务端不可靠,会导致调用过程中出现不确定的阻塞状态(超时)
为了避免超时导致客户端资源(线程)挂起耗尽,必须设置超时时间
我们在服务方的spring.xml加上如下配置
<dubbo:provider timeout="2000"/>
可以将服务实现HelloServiceImpl.java中加入模拟的网络延迟进行测试:
如下:
package service.impl;
import com.alibaba.dubbo.config.annotation.Service;
import service.HelloService;
@Service
public class HelloServiceImpl implements HelloService{
public String sayHello(String name) {
try {
Thread.sleep(3000);
} catch (Exception e) {
e.printStackTrace();
}
return "hello " + name;
}
}
超时设置2秒,而模拟的网络延迟有3秒,超出时限,报错,可以看到页面出现错误
配置原则:
dubbo推荐在Provider(服务方)上尽量多配置Consumer(消费方)端属性(因为服务方是被调用的):
作为服务的提供者,比服务使用方更清楚服务性能参数,如调用的超时时间,合理的重试次数,等等
在Provider(服务方)配置后,Consumer(消费方)不配置或者配置都会使用Provider(服务方)的配置值(对于不同的配置)
因为调用,在调用进行远程通信时
对应方法返回值给消费方,即处理过的数据,进行序列化给消费方的,然后消费方进行反序列化,一般只有返回值
而对应方法的执行给服务方,即处理中会进行调用,返回值会形成一个新的对象
且操作实现了对应的接口,可能是直接的写入,进行序列化,给消费方
所以一般使用服务方的配置,他们的使用依据,实际上的调用时序列化时进行的,每次调用都会将对应的配置参数进行赋值
即配置的操作基本是消费方在操作
使得使用服务方的配置(相同的配置下,不同配置使用自己的,即这里对应消费方)
当然,若服务方没有设置,而消费方设置了,则按照消费方的(这时消费方优先于默认)
所以就有如下解释:
在相同配置下(默认使用服务方的):
若服务方配置,消费方没有配置,则使用服务方的(服务方优先于默认)
若服务方没有配置,消费方配置,则使用消费方的(消费方优先于默认)
若服务方配置,消费方也配置,则使用服务方的(相同配置下,优先服务方),即相同配置,服务方会覆盖
在不同配置下(默认使用自己方的,通常都是消费方):
若服务方配置,消费方没有配置,则使用服务方的(服务方优先于默认)
对应的消费方的参数没写的话,则服务方相当于全局,如dubbo:reference标签没有写重试次数,则使用服务方的
若服务方没有配置,消费方配置,则使用消费方的(消费方优先于默认)
若服务方配置,消费方也配置,则使用消费方的(不同配置下,使用自己方,即对于消费方和服务方而言,优先服务方)
若对应的服务方不操作配置,如dubbo:reference在服务方里没有操作配置
由于会覆盖,那么操作服务方,而又由于服务方没有调用,即操作,那么则是默认值
即使用对应的服务方配置,而进行设置dubbo参数,所以也可以说,服务方是优先的
且无论是否自己有配置,都是服务方优先,或者被调用方优先
那么对应的操作也就是设置调用的,即Provider(服务方)配置可以作消费者的缺省值
你可以试着,将对应超时的设置,随便的在消费方里设置,会发现,还是操作服务方的超时设置
重试次数:
当出现失败,自动切换并重试其它服务器,dubbo重试的缺省值默认是2次(除了本次),我们可以自行设置
在provider提供方(也可以说是服务方)配置(添加):
<dubbo:provider timeout="2000" retries="3"/>
修改对应实现类来测试:
package service.impl;
import com.alibaba.dubbo.config.annotation.Service;
import service.HelloService;
@Service
public class HelloServiceImpl implements HelloService{
public String sayHello(String name) {
System.out.println("被调用一次");
try {
Thread.sleep(3000);
} catch (Exception e) {
e.printStackTrace();
}
return "hello " + name;
}
}
发现有四个输出"被调用一次"然后出现错误
并不是所有的方法都适合设置重试次数
幂等方法:适合(当参数一样,无论执行多少次,结果是一样的,例如:查询,修改)
非幂等方法:不适合(当参数一样,执行结果不一样,例如:删除,添加)
这时防止出现数据问题,如出错或者多余数据(删除添加)
当然还有某些业务方面的数据也是不适合的,如对应字段值的增加和减少等等,也是基本不适合的
单独设置某个方法
提供方接口添加sayNo()方法并实现:
package service;
public interface HelloService {
String sayHello(String name);
String sayNo();
}
package service.impl;
import com.alibaba.dubbo.config.annotation.Service;
import service.HelloService;
@Service
public class HelloServiceImpl implements HelloService{
public String sayHello(String name) {
System.out.println("被调用一次");
try {
Thread.sleep(3000);
} catch (Exception e) {
e.printStackTrace();
}
return "hello " + name;
}
@Override
public String sayNo() {
System.out.println("-------no被调用一次-------");
return "no!";
}
}
消费方接口添加sayNo()方法声明:
package service;
public interface HelloService {
String sayHello(String name);
String sayNo();
}
消费方controller:
package controller;
import com.alibaba.dubbo.config.annotation.Reference;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import service.HelloService;
@Controller
public class HelloAction {
@Autowired
private HelloService helloService;
@GetMapping("hello")
@ResponseBody
public String sayHi(String name) {
String s = helloService.sayHello(name);
return s;
}
@GetMapping("no")
@ResponseBody
public String no() {
String s = helloService.sayNo();
return s;
}
}
消费方配置方法重试次数(在消费方的spring.xml添加如下):
<dubbo:reference interface="service.HelloService" id="HelloService">
<dubbo:method name="sayHello" retries="3"/>
<dubbo:method name="sayNo" retries="0"/>
</dubbo:reference>
多版本:
一个接口,多个(版本的)实现类,可以使用定义版本的方式引入
为HelloService接口定义两个实现类,提供者添加如下配置:
<dubbo:service interface="service.HelloService"
class="service.impl.HelloServiceImpl1" version="1.0.0"/>
<dubbo:service interface="service.HelloService"
class="service.impl.HelloServiceImpl2" version="2.0.0"/>
消费者就可以根据version的版本,选择具体的服务版本:
<dubbo:reference interface="service.HelloService" id="HelloService" version="1.0.0">
<dubbo:method name="sayHello" retries="3"/>
<dubbo:method name="sayNo" retries="0"/>
</dubbo:reference>
注意:消费者的控制层要改为自动注入,因为@Reference注解和 dubbo:reference在这里冲突
因为一起的话,这个dubbo:reference标签不起作用
修改对应的controller层的类:
@Autowired
private HelloService helloService;
当消费者的版本修改为 version=“*”,那么就会随机调用服务提供者的版本(随机操作设置的版本),若没有对应的设置版本
即操作的这个dubbo:service标签,就不会执行方法,即报错(不是对应的超时错误)
本地存根:
目前我们的分布式架构搭建起来有一个严重的问题,就是所有的操作全都是消费者发起,由服务提供者执行
消费者动动嘴皮子却什么活都不干,这样会让提供者很累
例如简单的参数验证,消费者完全能够胜任,把合法的参数再发送给提供者执行,效率高了,提供者也没那么累了
例如:去房产局办理房屋过户,请带好自己的证件和资料,如果什么都不带,那么办理过户手续会很麻烦
得先调查你有什么贷款,有没有抵押,不动产证是不是你本人,复印资料等操作,一天肯定办不完,明天还要来
如果你能提前将这些东西准备好,办理过户,1个小时足矣,这就是"房产中介办事效率高的原因"
话不多说,先在消费者处理一些业务逻辑,再调用提供者的过程,就是"本地存根"
代码实现肯定在 消费者,创建一个HelloServiceStub类并且实现HelloService接口
注意:必须使用构造方法的方式注入
package stub;
import org.springframework.util.StringUtils;
import service.HelloService;
public class HelloServiceStub implements HelloService {
private HelloService helloService;
public HelloServiceStub(HelloService helloService) {
this.helloService = helloService;
}
@Override
public String sayHello(String name) {
if(!StringUtils.isEmpty(name)) {
return helloService.sayHello(name);
}
return "哈哈哈";
}
@Override
public String sayNo() {
return null;
}
}
修改消费者配置:
<dubbo:reference interface="service.HelloService" id="HelloService" version="1.0.0"
stub="stub.HelloServiceStub">
<dubbo:method name="sayHello" retries="3"/>
<dubbo:method name="sayNo" retries="0"/>
</dubbo:reference>
负载均衡策略:
负载均衡(Load Balance), 其实就是将请求分摊到多个操作单元上进行执行,从而共同完成工作任务
简单的说,好多台服务器,不能总是让一台服务器干活,应该"雨露均沾"
dubbo一共提供4种策略,缺省(默认的)为 random 随机分配调用
权重默认是一样的,都是初始值,默认为100,在dubbo的web项目里可以看到
一般的权重都会有检查,也就是说,对应的权重必须是大于0的整数,否则会报错(nginx的也是如此)
随机的:基本是根据几率了
轮询的:这里是根据侧重的显示,即并不是完全的一定是一次一次的操作,但是顺序还是有的,如到了2这里,可能会多出现几次
但总体来说,是按照顺序,局部来说,可以说是按照几率
活跃的:根据速度来决定是否访问,相同活跃就是随机(真正的随机,不是几率),不同,则进行侧重,可以理解为变化的权重
一致的:根据自己来决定
修改提供者配置并启动3个提供者,让消费者对其进行访问
tomcat端口8001,8002,8003
provider端口20881,20882,20883
<dubbo:provider timeout="2000" port="20883"/>
对应的2.0.0版本的类,可以给打印语句,加上标识,如服务器1,服务器2,服务器3等等
打开对应的dubbo的web项目,发现有4个,总共两个
第一个,其中三个代表对应两个方法和自己的版本(即下面的第二个),即总体
第二个,一个,则是对应版本的一个,上面都是一个端口的,不同端口又是一组
这样的说明,只有等你进行测试时,可以更加明白
启动consumer进行测试
改变策略:
<dubbo:reference loadbalance="roundrobin" interface="service.HelloService" id="HelloService"
version="2.0.0" stub="stub.HelloServiceStub">
<dubbo:method name="sayHello" retries="3"/>
<dubbo:method name="sayNo" retries="0"/>
</dubbo:reference>
再次启动consumer进行测试
消费方修改权重:
最好使用管理端修改权重:
这个倍权(乘2,加一倍)和半权(除2,减一半),用来改变权重,这个权重是全局的
绑定了对应服务方的名称,即无论是重启还是刷新,都是这个改变后的权重,除非删除节点
高可用:
zookeeper宕机:
zookeeper注册中心宕机,还可以消费dubbo暴露的服务
监控中心宕掉不影响使用,只是丢失部分采样数据
数据库宕掉后,注册中心仍能通过缓存提供服务列表查询,但不能注册新服务
注册中心对等集群,任意一台宕掉后,将自动切换到另一台
注册中心全部宕掉后,服务提供者和服务消费者仍能通过本地缓存通讯(下面的测试)
服务提供者无状态,任意一台宕掉后,不影响使用
服务提供者全部宕掉后,服务消费者应用将无法使用,并无限次重连等待服务提供者恢复
测试:
先正常发出请求
关闭zookeeper:./zkServer.sh stop
消费者仍然可以正常消费
服务降级:
壁虎遇到危险会自动脱落尾巴,目的是损失不重要的东西,保住重要的
服务降级,就是根据实际的情况和流量,对一些服务有策略的停止或换种简单的方式处理
从而释放服务器的资源来保证核心业务的正常运行
为什么要服务降级:
而为什么要使用服务降级,这是防止分布式服务发生雪崩效应
什么是雪崩:
就是蝴蝶效应,当一个请求发生超时,一直等待着服务响应,那么在高并发情况下
很多请求都是因为这样一直等着响应,直到服务资源耗尽产生宕机,
而宕机之后会导致分布式其他服务调用该宕机的服务也会出现资源耗尽宕机
这样下去将导致整个分布式服务都瘫痪,这就是雪崩
简单来说就是:分布式系统中经常会出现某个基础服务不可用造成整个系统不可用的情况,这种现象被称为服务雪崩效应
服务降级实现方式:
在 管理控制台配置服务降级:屏蔽和容错
屏蔽:mock=force:return+null 表示消费方对该服务的方法调用都 直接返回 null 值,即消费者不发起远程调用
用来屏蔽不重要服务不可用时对调用方的影响(对于当前的版本服务方而言)
容错:mock=fail:return+null 表示消费方对该服务的方法调用在 失败后,再返回 null 值,不抛异常
用来容忍不重要服务不稳定时对调用方的影响(对于当前的版本服务方而言)
这里是2.0.0版本(自己设置的,你可以修改)
整合MyBatis实现用户注册:
初始化数据库:
CREATE DATABASE smd CHARACTER SET utf8;
USE smd;
CREATE TABLE users(
uid INT(11) AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL,
PASS VARCHAR(50) NOT NULL,
phone VARCHAR(50) NOT NULL,
createtime VARCHAR(50) NOT NULL
)
创建聚合项目-项目模块化:
lagou-dubbo(项目目录),创建一个空的项目
lagou-dubbo-parent(父工程,聚合项目(主要作用):定义所有模块用的依赖版本)
<packaging>pom</packaging>
<properties>
<spring.version>5.0.6.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<scope>provided</scope>
<version>2.5</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jsp-api</artifactId>
<scope>provided</scope>
<version>2.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.2.8</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.9</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.32</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.5.7</version>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.6</version>
</dependency>
<dependency>
<groupId>com.github.sgroschupf</groupId>
<artifactId>zkclient</artifactId>
<version>0.1</version>
</dependency>
<dependency>
<groupId>javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.11.0.GA</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.47</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
lagou-dubbo-entity(实体工程,jar项目):
对应的实体类(Users类):
package entity;
import java.io.Serializable;
public class Users implements Serializable {
private int uid;
private String username;
private String pass;
private String phone;
private String createtime;
@Override
public String toString() {
return "Users{" +
"uid=" + uid +
", username='" + username + '\'' +
", pass='" + pass + '\'' +
", phone='" + phone + '\'' +
", createtime='" + createtime + '\'' +
'}';
}
public Users() {
}
public Users(String username, String pass, String phone, String createtime) {
this.username = username;
this.pass = pass;
this.phone = phone;
this.createtime = createtime;
}
public int getUid() {
return uid;
}
public void setUid(int uid) {
this.uid = uid;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPass() {
return pass;
}
public void setPass(String pass) {
this.pass = pass;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getCreatetime() {
return createtime;
}
public void setCreatetime(String createtime) {
this.createtime = createtime;
}
}
lagou-dubbo-dao(数据访问层工程,jar项目):
对应的UserMapper接口:
package mapper;
import entity.Users;
public interface UserMapper {
int register(Users users);
}
对应要添加的依赖:
<dependencies>
<dependency>
<groupId>lagou.com</groupId>
<artifactId>lagou-dubbo-entity</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
</dependencies>
对应的UserMapper.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="mapper.UserMapper">
<insert id="register">
insert into users (username,pass,phone,createtime)
values (#{username},#{pass},#{phone},#{createtime})
</insert>
</mapper>
对应的mybatis-config.xml:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
</configuration>
对应的spring-dao.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:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
destroy-method="close">
<property name="url" value="jdbc:mysql://192.168.164.128:3306/smd?
serverTimezone=GMT&characterEncoding=utf8"/>
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="username" value="root"/>
<property name="password" value="QiDian@666"/>
</bean>
<bean id="sqlSessionFactory"
class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="configLocation" value="classpath:mybatis/mybatis-config.xml"></property>
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="mapper"></property>
</bean>
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<tx:annotation-driven/>
</beans>
对应的测试类TestDao类:
package test;
import entity.Users;
import mapper.UserMapper;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:spring/spring-dao.xml"})
public class TestDao {
@Autowired
private UserMapper userMapper;
@Test
public void register(){
Users u = new Users("a1","11","111","1111");
int register = userMapper.register(u);
System.out.println(register);
}
}
运行测试类,试一试成不成功
lagou-dubbo-interface(服务接口定义工程,jar项目):
对应要添加的依赖:
<dependencies>
<dependency>
<groupId>lagou.com</groupId>
<artifactId>lagou-dubbo-dao</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
对应的UserService接口:
package service;
import entity.Users;
public interface UserService {
int register(Users users);
}
lagou-dubbo-service(privoder服务提供者工程,jar项目):
对应要添加的依赖:
<dependencies>
<dependency>
<groupId>lagou.com</groupId>
<artifactId>lagou-dubbo-interface</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<configuration>
<port>8001</port>
<path>/</path>
</configuration>
</plugin>
</plugins>
</build>
对应的UserServiceImpl类:
package service.impl;
import com.alibaba.dubbo.config.annotation.Service;
import entity.Users;
import mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import service.UserService;
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public int register(Users users) {
return userMapper.register(users);
}
}
对应的spring-service.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">
<dubbo:application name="lagou-dubbo-server"/>
<dubbo:registry address="zookeeper://192.168.164.128:2181"/>
<dubbo:annotation package="service.impl"/>
</beans>
对应的web.xml(这个对测试并没有操作,即可以不写,但写上是为了后面的操作):
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
id="WebApp_ID" version="3.1">
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:spring/spring-*.xml</param-value>
</context-param>
</web-app>
对应的测试类TestService:
import entity.Users;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import service.UserService;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath*:spring/spring-*.xml"})
public class TestService {
@Autowired
private UserService userService;
@Test
public void register(){
Users u = new Users("a2","12","112","1112");
int register = userService.register(u);
System.out.println(register);
}
}
lagou-dubbo-web(consumer服务消费者工程,war项目):
对应要添加的依赖:
<packaging>war</packaging>
<dependencies>
<dependency>
<groupId>lagou.com</groupId>
<artifactId>lagou-dubbo-service</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<configuration>
<port>8002</port>
<path>/</path>
</configuration>
</plugin>
</plugins>
</build>
对应的UserAction类:
package controller;
import com.alibaba.dubbo.config.annotation.Reference;
import entity.Users;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import service.UserService;
import java.text.SimpleDateFormat;
import java.util.Date;
@Controller
public class UserAction {
@Reference
private UserService userService;
@RequestMapping("register")
@ResponseBody
public String register(Users users){
String a = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
users.setCreatetime(a);
int i = userService.register(users);
if(i >0){
return "注册成功";
}else{
return "注册失败";
}
}
}
对应的spring-mvc.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"
xmlns:mvc="http://www.springframework.org/schema/mvc"
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
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<dubbo:application name="lagou-dubbo-server"/>
<dubbo:registry address="zookeeper://192.168.164.128:2181"/>
<dubbo:annotation package="controller"/>
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
<property name="supportedMediaTypes" value="application/json"></property>
<property name="features">
<array>
<value>WriteMapNullValue</value>
<value>WriteDateUseDateFormat</value>
</array>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
<mvc:default-servlet-handler/>
</beans>
对应的web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
id="WebApp_ID" version="3.1">
<filter>
<filter-name>charset</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>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>charset</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>
<param-value>classpath:spring/spring-mvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
对应的index.jsp:
<%--
Created by IntelliJ IDEA.
User: 33988
Date: 2022/6/10
Time: 9:36
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form>
<p>username:<input type="text" name="username"></p>
<p>password:<input type="text" name="pass"></p>
<p>phone:<input type="text" name="phone"></p>
<p><input type="button" id="btn" value="注册"></p>
</form>
<script src="js/jquery-3.4.1.min.js"></script>
<script>
$("#btn").on("click",function () {
$.post("register",$("form").serialize(),function (result) {
alert(result)
},"json")
})
</script>
</body>
</html>
启动测试:
先选择父项目(聚合工程)进行全员安装成jar包或war包,其中会经过测试(因为按照顺序来的),也就是说
对应测试类会执行(只有对应的测试环境的@Test会进行执行,如@RunWith(SpringJUnit4ClassRunner.class)注解的配置)
而普通的@Test不会,即直接写@Test注解的不会
那么对应数据库里应该会有两个数据(两个测试类执行造成的)
安装完后,对应的pom.xml那里,可以解除注释了,maven的导入是可以导入jar包的,而不能导入war包
所以通常都是web项目来导入其他项目的包,当然了只有对应的包进行覆盖
即war包安装后,那么就算改变成jar包,也不会删除war包,而是添加上jar包,相同的就会覆盖
但在工程之间,可以直接使用(一般只是对maven中,单纯的idea创建的可能不会,因为他不操作pom文件,即并不是操作工程,工程一般有很多操作,使得路径到当前项目,具体工程化的知识可以百度查看),无论是否是jar包还是war包还是没有进行打包都可以直接使用,只是idea的处理,具体部署时,其实也是打包的意思,继续看后面吧
优先当前工程(子到父到子进行获取),只是优先,即无论是否是父子工程都可以,但是在服务器里或者不是当前显示的idea的maven,工程直接就不会互相调用了,即基本都是仓库里操作调用(虽然通常是下载好的,那么按照这样的说法,其实他们是一样的意思,都是拿取下载好的,只不过当前工程忽略了这个过程而已,使得看起来是调用,简单来说,上面只是考虑xml的移动,而没有考虑真实的jar包的本质上的移动,所以无论是当前还是打包,其实都是操作合体的jar包,他们本质是完全一样的),且要注意,父子之间,一般父如果使用子,那么不能打包,且打包不看工程之间,而是看是否安装,这是要注意的
解除注释后:
启动服务service
启动调用者web
进行操作,若返回注册成功,且数据库有数据,则操作成功
最后注意:在dubbo中,服务提供方的方法,最好不要抛出异常,因为会报错,dubbo的操作
但是可以操作RuntimeExcepton异常,会自动的识别,即抛出,所以对应的接口,可以不指定
实际上dubbo就是基本上只识别这个异常,所以其他的异常不能操作
这里在写css要注意一点,自己标签设置的属性一般都会被style覆盖,也可以说是优先级最低
对于端口的占用,如果找不到,可以使用如下操作:
在windows的命令窗口中,输入如下:
netstat -aon|findstr 8080
taskkill /f /PID 6666
location.href的优先级比表单提交低,可以说是被覆盖
最后说明以下mybatis:mybatis在不同的版本下,对应String操作不同,在动态sql中,在低版本,mybatis会将String当成类
那么使用动态sql替换时,则是使用get进行获取,而高版本,则当成参数,所有对应的if默认为true(参数基本都是默认true)
而在低版本下,若要使用对应参数,即String的对应值,需要操作_parameter,即String的get得到他的值即可
对于URL中文可能出现乱码问题,主要是浏览器会对中文做特殊处理,而后端却没有对应的解码
我们可以先进行对该中文操作一个新的编码,如使用: