携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第6天,点击查看活动详情
模拟微服务
1. 创建用户服务
需求:
根据id查询用户
步骤:
- 创建tb_user表
- 创建用户服务:lombok、web、mybatis、mysql
- 创建三层架构提供根据id查询用户的功能
- 在配置文件中配置db配置、mybatis配置
- 访问测试
过程:
-
建表语句
DROP TABLE if EXISTS 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=1 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', '李四'); -- ---------------------------- -- select tb_user -- ---------------------------- SELECT * FROM tb_user; -
代码
@Data @AllArgsConstructor @NoArgsConstructor public class User { private Integer id;//主键id private String username;//用户名 private String password;//密码 private String name;//姓名 private Integer age;//年龄 private Integer sex;//性别 1男性,2女性 private Date birthday; //出生日期 private Date created; //创建时间 private Date updated; //更新时间 private String note;//备注 } @RestController @RequestMapping("user") public class UserController { @Autowired private UserService userService; /* * @RequestMapping(value = "findById",method = RequestMethod.GET) 等同于 @GetMapping("findById") * * @RequestMapping(value = "findById",method = RequestMethod.POST) 等同于 @PostMapping * * 请求地址: http://localhost:8080/user/findById?id=1 */ //@RequestMapping(value = "findById",method = RequestMethod.POST) @GetMapping("findById") public User findById(Integer id){ return userService.findById(id); } /** * @Author: guodong * @Date: 9:15 2020/9/19 * @Parms [id] * @ReturnType: com.itheima.pojo.User * @Description: 根据id查询用户 * * 请求地址: http://localhost:8080/user/findUserById/2 */ @GetMapping("findUserById/{id}") public User findUserById(@PathVariable Integer id){ return userService.findById(id); } /* 请求地址: http://localhost:8080/user/findUserById/2 参数不一致如何去对应的情况 */ @GetMapping("findUserById02/{id}") public User findUserById02(@PathVariable("id") Integer uid){ return userService.findById(uid); } } public interface UserService { /** * @Author: guodong * @Date: 9:15 2020/9/19 * @Parms [id] * @ReturnType: com.itheima.pojo.User * @Description: 根据id查询用户 */ User findById(Integer id); } @Service public class UserServiceImpl implements UserService { @Autowired private UserDao userDao; /** * @Author: guodong * @Date: 9:15 2020/9/19 * @Parms [id] * @ReturnType: com.itheima.pojo.User * @Description: 根据id查询用户 */ @Override public User findById(Integer id) { return userDao.findById(id); } } @Mapper @Repository public interface UserDao { /** * @Author: guodong * @Date: 9:15 2020/9/19 * @Parms [id] * @ReturnType: com.itheima.pojo.User * @Description: 根据id查询用户 */ User findById(Integer id); } -
sql
<?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="com.itheima.dao.UserDao"> <select id="findById" resultType="user"> select * from tb_user WHERE id = #{id}; </select> </mapper> -
配置
#db spring: datasource: #serverTimezone=UTC url: jdbc:mysql://127.0.0.1/itheima?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC driver-class-name: com.mysql.cj.jdbc.Driver username: root password: root #mybatis mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.itheima.pojo
2. 创建消费者服务
需求:
使用RestTemplate完成用户服务接口的调用
步骤:
- 创建消费者服务
- 使用RestTemplate调用用户服务
过程:
@RestController
@RequestMapping("consumer")
public class ConsumerController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("findUserById/{id}")
public User findUserById(@PathVariable Integer id){
//第一次的url
String url = "http://localhost:9091/user/findUserById/" + id;
User user = restTemplate.getForObject(url, User.class);
return user;
}
}
3. 存在问题
1.写死了---》url硬编码----》Eureka注册中心解决
2.无法完成服务的负载均衡调用-----》Ribbon负载调用解决
3.消费者服务无法感知用户服务的状态,导致用户服务宕机后消费者服务直接异常-----》Hystrix熔断器来解决
........
二、Cloud介绍
1. 概念
springcloud不是一个框架,是一个全家桶(KFC)。cloud是由一个一个的组件构成的,这些组件是用springboot来进行开发的。
2. 组件
3. 版本
三、Eureka使用
1. 搭建Eureka注册中心
步骤:
- 创建一个eureka book项目---》web/eureka-server
- 在启动类上添加EurekaServer注解声明是一个eureka服务
- 编写配置文件----》声明注册中心的地址
过程:
- 选择依赖
-
启动类
@SpringBootApplication @EnableEurekaServer //声明是一个注册中心 public class EurekaServerApplication { public static void main(String[] args) { SpringApplication.run(EurekaServerApplication.class, args); } } -
配置文件
eureka: client: service-url: defaultZone: http://localhost:8761/eureka #注册中心的地址 register-with-eureka: false #禁止注册至 http://localhost:8761/eureka fetch-registry: false #禁止拉取服务列表 server: port: 8761 #服务应用名称 spring: application: name: eureka-server
2. 改造用户服务
需求:
将用户服务注册至eureka注册中心
步骤:
- 导入cloud版本管理
- 导入eureka-client依赖坐标
- 在启动类添加EurekaClient注解,声明是一个eureka客户端
- 修改配置文件----》服务的应用名称、声明eureka注册中心的地址
3. 改造消费者服务
需求:
将用户服务注册至eureka注册中心
步骤:
- 导入cloud版本管理
- 导入eureka-client依赖坐标
- 在启动类添加EurekaClient注解,声明是一个eureka客户端
- 修改配置文件----》服务的应用名称、声明eureka注册中心的地址
解决url硬编码问题:
@RestController
@RequestMapping("consumer")
public class ConsumerController {
@Autowired
private RestTemplate restTemplate;
/*
可以根据服务的应用名称从注册中心将服务的列表(ip+port)拉取下来
*/
@Autowired
private DiscoveryClient discoveryClient;
@GetMapping("findUserById/{id}")
public User findUserById(@PathVariable Integer id){
//第一次的url
/*
1.写死了---》url硬编码----》Eureka注册中心解决
2.无法完成服务的负载均衡调用-----》Ribbon负载调用解决
3.消费者服务无法感知用户服务的状态,导致用户服务宕机后消费者服务直接异常-----》Hystrix熔断器来解决
........
*/
//String url = "http://localhost:9091/user/findUserById/" + id;
//第二次的url --->Eureka注册中心解决
List<ServiceInstance> instances = discoveryClient.getInstances("user-service");
String url = instances.get(0).getUri() + "/user/findUserById/" + id;
User user = restTemplate.getForObject(url, User.class);
return user;
}
/**
* @Author: guodong
* @Date: 11:16 2020/9/19
* @Parms []
* @ReturnType: java.util.List<org.springframework.cloud.client.ServiceInstance>
* @Description: 获取服务列表
*/
@GetMapping("getInstances")
public List<ServiceInstance> getInstances(){
List<ServiceInstance> instances = discoveryClient.getInstances("user-service");
return instances;
}
}
4. Eureka集群搭建(了解)
目的:
为了防止单点故障问题,大号废了练小号。
步骤:
- 修改hosts
- 复制两个总配置文件---》application-server1.yml、application-server2.yml,并且需要将总配置文件注释
- 使用idea创建eureka两个启动类---》需要指定启动类的配置文件
- 直接访问测试
过程:
-
hosts
127.0.0.1 eureka1 eureka2 -
多配置文件
-
application-server1.yml
eureka: client: service-url: defaultZone: http://eureka2:8762/eureka #注册中心的地址 instance: hostname: eureka1 server: port: 8761 #服务应用名称 spring: application: name: eureka-server -
application-server2.yml
eureka: client: service-url: defaultZone: http://eureka1:8761/eureka #注册中心的地址 instance: hostname: eureka2 server: port: 8762 #服务应用名称 spring: application: name: eureka-server
-
-
idea创建多启动类
注意:
- 需要将总配置文件注释掉
5. 自动配置实现
- 在启动类添加@EnableEurekaServer----》会创建Marker.class
-
org.springframework.cloud.netflix.eureka.server.EurekaServerAutoConfiguration
6. Eureka详解
!=
客户端:
- 每隔30s发送一次心跳至eureka注册中心,最长时间是90s;
- 每隔60s拉取一次服务列表至本地(思考? 如果eureka服务停止运行,那么消费者服务是否还能够调用到用户服务?)
服务端:
-
失效剔除
- 每隔60s会执行一次定时任务去扫描过往90s内服务的状态,如果一次心跳都没有送达那么会将服务主动从服务列表移除
-
服务下线
- 客户端发送下线请求至eureka注册中心,eureka将服务从服务列表移除
-
自我保护机制
- 如果服务的心跳比例低于85%,那么会触发自我保护,一旦触发自我保护那么会锁定服务的列表,保证大部分服务的可用。