服务的拆分原则:
单体应用向微服的一个改造:
搭建一个聚合项目
创建一个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: false
register-with-eureka: false
启动类:
@EnableEurekaServer
@SpringBootApplication
public class RegisterApp {
public static void main(String[] args) {
SpringApplication.run(RegisterApp.class,args);
}
}
yml
spring:
application:
name: gateway
server:
port: 9000
eureka:
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 {
@Autowired
private MemberRepository memberRepository;
//@Transactional(rollbackFor=Exception.class)
@Override
public 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 {
@Autowired
private 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: true
service-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].bid
result.title = res[i].name
bs.push(result);
}
$("#name").select({ //下拉组件
title: "请选择图书", //小标题
items: bs //这里把刚才封装好的数组直接拿过来就可以了
});
}
});
具体的效果如下图所示: