服务的拆分原则:
单体应用向微服的一个改造:
搭建一个聚合项目
创建一个maven项目 父项目
pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.pb</groupId><artifactId>parent</artifactId><packaging>pom</packaging><version>1.0-SNAPSHOT</version><modules><module>register</module><module>gateway</module><module>member</module></modules><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.2.5.RELEASE</version><relativePath/> <!-- lookup parent from repository --></parent><properties><java.version>1.8</java.version><spring-cloud.version>Hoxton.SR5</spring-cloud.version><mysql.version>5.1.48</mysql.version><mybatis-spring.version>2.2.1</mybatis-spring.version><lombok.version>1.16.18</lombok.version></properties><dependencyManagement><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>${spring-cloud.version}</version><type>pom</type><scope>import</scope></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>${mysql.version}</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>${lombok.version}</version></dependency></dependencies></dependencyManagement></project>
yml
spring:application:name: register
server:port: 8761
eureka:client:fetch-registry: falseregister-with-eureka: false
启动类:
@EnableEurekaServer
@SpringBootApplication
public class RegisterApp {public static void main(String[] args) {SpringApplication.run(RegisterApp.class,args);}
}
yml
spring:application:name: gatewayserver:port: 9000eureka:client:service-url:defaultZone: http://localhost:8761/eureka/
启动类:
创建模块:(member)
pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>parent</artifactId><groupId>com.pb</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>member</artifactId><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency></dependencies></project>
创建数据库
CREATE TABLE `t_member` (`mid` int(11) NOT NULL AUTO_INCREMENT,`name` varchar(50) DEFAULT NULL,`idno` varchar(50) DEFAULT NULL,`mobile` varchar(50) DEFAULT NULL,`regdate` date DEFAULT NULL,`expdate` date DEFAULT NULL,PRIMARY KEY (`mid`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=gbk
实体类:
@Data
@Entity
@Table(name="t_member")
public class Member implements Serializable {private static final long serialVersionUID = 7796360948254473734L;@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long mid;private String name;private String idno;private String mobile;private Date regdate;private Date expdate;
}
repositroy--MemberRepository
public interface MemberRepository extends JpaRepository<Member, Long> {/*** 根据手机号码查询会员信息* @param mobile* @return*/List<Member> findMemberByMobile(String mobile);
}
自定义异常
package com.pb.exception;
//spring的事物有回滚,默认出现异常回滚的是RuntimeException
public class MemberNotFoundException extends RuntimeException {public MemberNotFoundException(String message) {super(message);}
}
service
impl
@Service
public class MemberServiceImpl implements MemberService {@Autowiredprivate MemberRepository memberRepository;//@Transactional(rollbackFor=Exception.class)@Overridepublic Member findMemberByMobile(String mobile) {List<Member> list = memberRepository.findMemberByMobile(mobile);if(list.size() <= 0) {throw new MemberNotFoundException("会员未找到"); //这里自动异常回向上抛出MemberNotFoundException}Member member = list.get(0);return member;}
}
controller
@RestController
public class MemberController {@Autowiredprivate MemberService memberService;@CrossOrigin@RequestMapping("/selectMemberByMobile")public Map<String, Object> selectMemberByMobile(String mobile) {Map<String, Object> map = new HashMap<>();//给异常try {Member member = memberService.findMemberByMobile(mobile);map.put("code", 200);map.put("msg", "success");map.put("data", member);} catch (Exception e) {e.printStackTrace();map.put("code", 500);map.put("msg", e.getMessage());System.out.println(e);System.out.println(e.getMessage());}return map;}}
启动getway
引入一个html
<!DOCTYPE html>
<html>
<head><meta charset="utf-8"><title>layui</title><meta name="renderer" content="webkit"><meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"><meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"><link rel="stylesheet" href="https://cdn.bootcss.com/weui/1.1.2/style/weui.min.css"><link rel="stylesheet" href="https://cdn.bootcss.com/jquery-weui/1.2.0/css/jquery-weui.min.css"><!-- 注意:如果你直接复制所有代码到本地,上述css路径需要改成你本地的 -->
</head>
<body>
<h2 style="text-align: center">图书借阅登记</h2>
<form id="frmBorrow" action=""><div class="weui-cells weui-cells_form"><div class="weui-cells__title">手机号<span id="errMobile" style="margin-left:10px;color: red"></span><span id="succMobile" style="margin-left:10px;color: green"></span></div><div class="weui-cell weui-cell_vcode"><div class="weui-cell__bd"><input id="mobile" name="mobile" class="weui-input" type="tel" placeholder="请输入手机号"></div><div class="weui-cell__ft"><button id="btnCheck" type="button" class="weui-vcode-btn">获取验证码</button></div></div><div class="weui-cells__title">验证码</div><div class="weui-cell weui-cell_vcode"><div class="weui-cell__bd"><input class="weui-input" type="number" placeholder="请输入6位验证码"></div></div><div class="weui-cells__title">借阅图书</div><div class="weui-cell"><div class="weui-cell__bd"><input class="weui-input" id="name" name="name" type="text" placeholder="请选择要借阅的图书"><input type="hidden" name="bid" id="bid"></div></div><div class="weui-cells__title">借阅时间</div><div class="weui-cell"><div class="weui-cell__bd"><input type="text" name="takedate" class="weui-input" placeholder="请选择取书日期" id='takedate'/></div></div><div class="weui-cells__title">归还时间</div><div class="weui-cell"><div class="weui-cell__bd"><input type="text" name="returndate" class="weui-input" placeholder="请选择还书日期" id='returndate'/></div></div><br><a href="javascript:;" id="btnSubmit" class="weui-btn weui-btn_primary">提交</a></div></form>
<script src="https://cdn.bootcss.com/jquery/1.11.0/jquery.min.js"></script>
<script src="https://cdn.bootcss.com/jquery-weui/1.2.0/js/jquery-weui.min.js"></script><script>$("#takedate,#returndate").calendar({dateFormat: "yyyy-mm-dd"});$("#btnCheck").click(function(){var mobile = $("#mobile").val();$.ajax({type:"post",url:"http://localhost:9000/member/selectMemberByMobile",data:{mobile: mobile},dataType:"json",success:function (res){console.log(res);}})});//我们先来通过ajax进行所有数据的查询超操作,并通过下拉菜单进行展示var name = $("#name").val();</script>
</body>
</html>
$("#btnCheck").click(function(){var mobile = $("#mobile").val();$.ajax({type:"post",url:"http://localhost:9000/member/selectMemberByMobile",data:{mobile: mobile},dataType:"json",success:function (res){console.log(res);if (res.code == 200){$("#succMobile").text("会员:"+res.data.name);$("#succMobile").show();$("#errMobile").hide();}else {$("#errMobile").text(res.msg);$("#succMobile").hide();$("#errMobile").show();}}})});
配置中心Config
进入到码云来做一个配置中心,官方网址为:Gitee - 企业级 DevOps 研发效能平台
然后你可以申请一个密码进行登录即可
然后我们点击红色的部门新建一个项目:
创建项目:
创建完成后会出现如下界面:
然后点击文件里面的新建文件进行操作:
然后点击提交,如下图所示:
让后我们通过idea创建配置中心项目:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>parent</artifactId><groupId>com.pb</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>config</artifactId><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency><!--配置中心的服务端--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-config-server</artifactId></dependency></dependencies></project>
然后设置config项目的yml文件:
spring: |
设置主启动类:
package com.laosan.config; |
最后启动所有的服务包括配置中心,如下图所示:
我们所有的服务都启动起来了,然后我们把浏览器打开进行对配置中心的测试,但是还是会报错,根本查询不到我要的数据不能从配置中心把数据拿回来,如下图所示:
这个是非常容易犯的一个错误
因为我们在访问配置中心的时候是需要一定的格式的,具体的格式是http://localhost:9100/clientid-profile.yml|xml|json,这么写才可以,我们上面浏览器里面写的地址是不正确的不符合我们需要的格式
在配置中心我们添加2个文件名字分别member-dev.yml 和member-prd.yml,具体的内容如下所述:
我们看到我们根据配置中心来添加两个对应的文件,根据这两个文件我们可以在浏览器中输入如下地址,如下图所示:
你会发现当我们输入上图的地址后他给我们现实了member-dev.yml里面的端口还会把member.yml的公共部分现实出来呢,server.port会以member-dev.yml的为准
我们来换个地址,如下图所示:
你也会发现他把所有的member.yml文件的公共部分显示过来,至于端口号部分只显示member-prd.yml里面设置的端口号
综上所述我们在配置中心可以设置一个公共的yml文件,然后设置几个不同的配置的yml文件,反问某一个的时候会把公共的部分也进行加载
当然配置中心不仅仅能显示一种yml文件,它还能显示json, properties,xml等格式的文件,你只需要把后缀名切换成json,properties或者是xml即可,但是xml文件不支持暂时
如下图:
现在我们要把我们的配置中心的项目member做远程的连接和我的配置中心做连接,具体的操作如下所示:
我们要把resources目录下的application.yml删除
然后在resources目录中创建一个新的文件叫bootstrap.yml,为什么要切换成这个文件呢,是因为配置中心强制要求bootstrap.yml这个文件,并且必须是这个
具体的配置,如下所示:
spring: |
让后我们启动项目member,但是它会报错,因为我们少了一个依赖,就是配置中心的客户端:
<!--配置中心的客户端--> |
但是我们启动后依旧会报错,我们需要再次更改bootstrap.yml配置文件:
spring: #找的是配置中心的哪个配置文件,因为在配置中心中我有两个类型的文件一个是dev一个是prd #注意上线的时候把dev切换成prd就OK了 |
eureka可以不写
#注册进服务
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
见到如下操作表明成功
我们可以在浏览器中输入地址进行测试即可,如返回对应信息表明OK:
构建图书管理模块:
创建项目:(book)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>parent</artifactId><groupId>com.pb</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>book</artifactId><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-config-client</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency></dependencies></project>
bootstrap.yml
spring:application:name: book #这里的名字必须要和Eureka中的服务名称,和配置中心的名字保持一致#开启配置中心cloud:config:discovery:enabled: trueservice-id: config #这里的名字要和我Eureka服务中的配置中心名称的服务保持一致#找的是配置中心的哪个配置文件,因为在配置中心中我有两个类型的文件一个是dev一个是prd#注意上线的时候把dev切换成prd就OK了profile: dev
然后到我们的git的仓库中进行创建yml文件名字叫做book-dev.yml,具体的内容如下所示:
好下面我们开始对book项目进行操作:
数据库:
/*
SQLyog Professional v13.1.1 (64 bit)
MySQL - 5.7.29 : Database - scbook
*********************************************************************
*//*!40101 SET NAMES utf8 */;/*!40101 SET SQL_MODE=''*/;/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
CREATE DATABASE /*!32312 IF NOT EXISTS*/`scbook` /*!40100 DEFAULT CHARACTER SET utf8 */;USE `scbook`;/*Table structure for table `t_book` */DROP TABLE IF EXISTS `t_book`;CREATE TABLE `t_book` (`bid` int(11) NOT NULL AUTO_INCREMENT,`sn` varchar(20) DEFAULT NULL,`name` varchar(20) DEFAULT NULL,`author` varchar(20) DEFAULT NULL,`publishing` varchar(20) DEFAULT NULL,`bprice` float DEFAULT NULL,`sprice` float DEFAULT NULL,`btype` varchar(20) DEFAULT NULL,`stock` int(11) DEFAULT NULL,PRIMARY KEY (`bid`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;/*Data for the table `t_book` */insert into `t_book`(`bid`,`sn`,`name`,`author`,`publishing`,`bprice`,`sprice`,`btype`,`stock`) values
(1,'aaa','java基础','admin','清华出版社',250,20,'动作',91),
(2,'bbb','spring强化','sasa','北大出版社',210,30,'言情',100),
(3,'admin','springboot','any','信息出版社',150,90,'java',99);/*Table structure for table `t_borrow` */DROP TABLE IF EXISTS `t_borrow`;CREATE TABLE `t_borrow` (`brid` int(11) NOT NULL AUTO_INCREMENT,`bid` int(11) DEFAULT NULL,`mid` int(11) DEFAULT NULL,`takedate` datetime DEFAULT NULL,`returndate` datetime DEFAULT NULL,`createtime` datetime DEFAULT NULL,PRIMARY KEY (`brid`)
) ENGINE=InnoDB AUTO_INCREMENT=93 DEFAULT CHARSET=utf8;/*Data for the table `t_borrow` */insert into `t_borrow`(`brid`,`bid`,`mid`,`takedate`,`returndate`,`createtime`) values
(89,1,1,'2019-01-01 00:00:00','2019-05-05 00:00:00','2023-05-24 12:18:03'),
(90,1,1,'2019-01-01 00:00:00','2019-05-05 00:00:00','2023-05-24 12:20:04'),
(91,1,1,'2019-01-01 00:00:00','2019-05-05 00:00:00','2023-05-24 12:20:34'),
(92,3,1,'2023-05-02 00:00:00','2023-05-15 00:00:00','2023-05-24 12:32:24');/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
先创建好book项目中对应的实体类Book;
package com.laosan.book.entity; |
创建Dao层的代码:
package com.laosan.book.repository; |
创建Service层的代码:
package com.laosan.book.service; |
创建Controller层的代码:
package com.laosan.book.controller; @CrossOrigin //进行该Controller的跨域操作 |
写完之后我们先对book项目进行单体应用的测试操作即可,好重启我们的book项目,如下图所示:
我们再测试上通过网关的路由能够获得我们对应的book请求呢,如下图所示:以为我的book 项目也已经注册到了Eureka的服务中了
下面我们完成对HTML页面部分ajax的开发:
<!DOCTYPE html> |
//我们先来通过ajax进行所有数据的查询超操作,并通过下拉菜单进行展示var name = $("#name").val();$.ajax({type:"GET",url:"http://localhost:9000/book/selectBookAll",dataType:"json",success:function (res) {console.log(res);var bs = new Array(); //javascript创建了一个数组for(var i = 0; i < res.length; i++) {var result = {}; //创建一个对象,这里JavaScript的一个对象操作,属性自己定义result.value= res[i].bidresult.title = res[i].namebs.push(result);}$("#name").select({ //下拉组件title: "请选择图书", //小标题items: bs //这里把刚才封装好的数组直接拿过来就可以了});}});
具体的效果如下图所示: