目录
2.2 创建服务提供者工程(SringBoot+Mybatis+Web)
1 简介
- SpringBoot是SpringCloud的基础
- Spring Cloud是分布式系统开发的工具套件。
- 主要部分:注册中心、负载均衡、熔断器、远程调用、网关、配置中心、消息总线等
- 特点:Spring Cloud并没有重复制造轮子,它从架构层面,降低了对大型分布式系统构建的要求和难度,可以用低的成本(技术、硬件)搭建一套高效、分布式、容错的平台,适用于中小型公司。
- 缺点:不适用于小型项目。
spring cloud 架构如下:
2 模拟微服务业务场景
- 开发中的微服务之间的关系实际上就是生产者和消费者关系。接下来,我们要模拟开发过程中的服务间关系,然后进行一些思考。
- 目标:模拟一个最简单的服务调用场景,场景中包含微服务提供者(Producer)和微服务调用者(Consumer),方便后面学习微服务架构。
- 注意:实际开发中,每个微服务为一个独立的SpringBoot工程。
接下来会创建三个工程:
- 父工程:包含下面两个工程(Maven非骨架创建)
- 服务提供者:对外提供查询用户的服务(SpringBoot创建,SringBoot+Mybatis+Web,坐标勾选DevTools、lombok、web、Mybatis、drive)
- 服务消费者:调用用户微服务提供的查询用户服务(SpringBoot创建,SringBoot+Web,坐标勾选DevTools、lombok、web)
2.1 创建父工程
2.2 创建服务提供者工程(SringBoot+Mybatis+Web)
- 创建SpringBoot工程,勾选依赖坐标
- 创建User表、创建实体User
-- 创建数据库
CREATE database springcloud CHARACTER SET utf8 COLLATE utf8_general_ci;
-- 使用springcloud数据库
USE springcloud;
-- ----------------------------
-- Table structure for tb_user
-- ----------------------------
CREATE TABLE `tb_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(100) DEFAULT NULL COMMENT '用户名',
`password` varchar(100) DEFAULT NULL COMMENT '密码',
`name` varchar(100) DEFAULT NULL COMMENT '姓名',
`age` int(11) DEFAULT NULL COMMENT '年龄',
`sex` int(11) DEFAULT NULL COMMENT '性别,1男,2女',
`birthday` date DEFAULT NULL COMMENT '出生日期',
`created` date DEFAULT NULL COMMENT '创建时间',
`updated` date DEFAULT NULL COMMENT '更新时间',
`note` varchar(1000) DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='用户信息表';
-- ----------------------------
-- Records of tb_user
-- ----------------------------
INSERT INTO `tb_user` VALUES ('1', 'zhangsan', '123456', '张三', '13', '1', '2006-08-01', '2019-05-16', '2019-05-16', '张三');
INSERT INTO `tb_user` VALUES ('2', 'lisi', '123456', '李四', '13', '1', '2006-08-01', '2019-05-16', '2019-05-16', '李四');
- 编写三层架构:Mapper、Service、controller
@Mapper //该注解的作用:将该类的动态代理实现类注入到spring容器中
public interface UserMapper {
//根据id查询用户信息
@Select("select * from tb_user where id=#{id} ")
User findById(Integer id);
}
public interface UserService {
User findById(Integer id);
}
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public User findById(Integer id) {
return userMapper.findById(id);
}
}
/**
* 用户服务,提供查询用户信息的接口
*/
@RestController
public class UserController {
@Autowired
private UserService userService;
/**
* 请求参数绑定,举例:假如现在有Http请求:localhost:8080/user/findById?id111123
* 那么 @RequestParam("id") Integer id 注解的意思是:把id变量和请求参数中的的那个id变量进行绑定
*/
@RequestMapping("/user/findById")
public User findById(@RequestParam("id") Integer id){
return userService.findById(id);
}
}
- 编写查询方法,基于注解编写查询语句(直接写在持久层的接口上)
- 在application.properties中添加配置:端口,数据库连接信息
# 端口
server.port=9091
# 数据库连接配置信息
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/springcloud?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
spring.datasource.password=root
spring.datasource.username=root
- 访问测试地址
http://localhost:9091/user/findById?id=1
2.3 创建服务消费者工程(SringBoot+Web)
消费者工程很简单,没有三层架构,只有一个controller层(外带一个pojo的User对象),该层用来调用服务提供者的controller层的服务。
/**
* 消费者的控制层,提供服务,访问提供者接口,为真实用户返回信息
*/
@RestController
public class ConsumerController {
@Autowired
private RestTemplate restTemplate;
/**
* 这个方法做的事情很简单:调用提供者的findById接口获取用户数据
* 思考:消费者所访问的来自提供者的服务的url地址是硬编码的,如果地址端口变了怎么办?如果服务提供者的服务挂了消费者怎样才能知道呢?
*/
@RequestMapping("/consumer/{id}")
public User consumerSendRequest(@PathVariable("id") Integer id){
String url = "http://localhost:9091/user/findById?id="+id;
User user = restTemplate.getForObject(url, User.class);
return user;
}
/**
* 在这里补充一个知识点:
* 之前的url中通过?后面的内容来表示请求参数,比如: http://localhost:8080/consumer?id=1
* 但是呢,restful风格要求不能用问号,于是参数也要一直往后写:http://localhost:8080/consumer/1
* 然后获取到这个请求参数的格式也从原来的 “public User findById(@RequestParam("id") Integer id)”
* 转变成如下的形式:@RequestMapping中的参数用大括号括起来,然后和@PathVariable注解配合使用
*/
// @RequestMapping("/consumer/{id}")
// public Integer consumerSendRequest2(@PathVariable("id") Integer id){
// System.out.println(id);
// return id;
// }
}
思考:消费者所访问的来自提供者的服务的url地址是硬编码的,如果地址端口变了怎么办?如果服务提供者的服务挂了消费者怎样才能知道呢?其实问题还不止这些,往下看。
2.4 问题思考
- 在消费者服务中,访问提供者服务URL地址的硬编码、服务消费者不清楚服务提供者状态
- 为增加服务并发访问量,搭建集群,集群的负载均衡怎么实现?
- 服务提供者如果出现故障,会不会向用户抛出异常页面,该不该抛出错误页面?
- RestTemplate这种请求调用方式是否还有优化空间?从复用,管理,可读性角度来思考
- 多服务权限拦截如何实现?怎么保证所有微服务服务的安全性?
- 众多微服务的配置文件,每次都修改很多个,是不是很麻烦?