Springcloud第一天

89 阅读6分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第6天,点击查看活动详情

模拟微服务

image-20200919085710799.png

1. 创建用户服务

需求:

根据id查询用户

步骤:

  1. 创建tb_user表
  2. 创建用户服务:lombok、web、mybatis、mysql
  3. 创建三层架构提供根据id查询用户的功能
  4. 在配置文件中配置db配置、mybatis配置
  5. 访问测试

过程:

  • 建表语句

    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完成用户服务接口的调用

步骤:

  1. 创建消费者服务
  2. 使用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. 组件

image-20200919101322488.png

3. 版本

image-20200919101921544.png

三、Eureka使用

1. 搭建Eureka注册中心

步骤:

  1. 创建一个eureka book项目---》web/eureka-server
  2. 在启动类上添加EurekaServer注解声明是一个eureka服务
  3. 编写配置文件----》声明注册中心的地址

过程:

  • 选择依赖

image-20200919103528603.png

  • 启动类

    @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注册中心

步骤:

  1. 导入cloud版本管理
  2. 导入eureka-client依赖坐标
  3. 在启动类添加EurekaClient注解,声明是一个eureka客户端
  4. 修改配置文件----》服务的应用名称、声明eureka注册中心的地址
3. 改造消费者服务

需求:

将用户服务注册至eureka注册中心

步骤:

  1. 导入cloud版本管理
  2. 导入eureka-client依赖坐标
  3. 在启动类添加EurekaClient注解,声明是一个eureka客户端
  4. 修改配置文件----》服务的应用名称、声明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集群搭建(了解)

目的:

为了防止单点故障问题,大号废了练小号。

步骤:

  1. 修改hosts
  2. 复制两个总配置文件---》application-server1.yml、application-server2.yml,并且需要将总配置文件注释
  3. 使用idea创建eureka两个启动类---》需要指定启动类的配置文件
  4. 直接访问测试

过程:

  • 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创建多启动类

image-20200919113826873.png

image-20200919113904828.png

image-20200919114035809.png 注意:

  • 需要将总配置文件注释掉
5. 自动配置实现
  • 在启动类添加@EnableEurekaServer----》会创建Marker.class
  • org.springframework.cloud.netflix.eureka.server.EurekaServerAutoConfiguration
    

image-20200919115839836.png image-20200919115759965.png

6. Eureka详解

!=

image-20200919121812627.png 客户端:

  • 每隔30s发送一次心跳至eureka注册中心,最长时间是90s;
  • 每隔60s拉取一次服务列表至本地(思考? 如果eureka服务停止运行,那么消费者服务是否还能够调用到用户服务?)

服务端:

  • 失效剔除

    • 每隔60s会执行一次定时任务去扫描过往90s内服务的状态,如果一次心跳都没有送达那么会将服务主动从服务列表移除
  • 服务下线

    • 客户端发送下线请求至eureka注册中心,eureka将服务从服务列表移除
  • 自我保护机制

    • 如果服务的心跳比例低于85%,那么会触发自我保护,一旦触发自我保护那么会锁定服务的列表,保证大部分服务的可用。