微服务Spring Cloud17_微服务场景模拟4

首先,我们需要模拟一个服务调用的场景。方便后面学习微服务架构 

需求:查询数据库中的用户数据并输出到浏览器

父工程heima-springcloud:添加springboot父坐标和管理其他组件的依赖

用户服务工程user-service:整合mybatis查询数据库中用户数据;提供查询用户服务

用户消费工程cousumer-demo:利用查询用户服务获取用户数据并输出到浏览器

一、创建父工程

 微服务中需要同时创建多个项目,为了方便课堂演示,先创建一个父工程,然后后续的工程都以这个工程为父,实现 maven的聚合。这样可以在一个窗口看到所有工程,方便讲解。在实际开发中,每个微服务可独立一个工程。

  

 编写项目信息:

  

 编写保存位置:

  

 然后将 pom.xml 修改成如下(请从 资料\heima-springcloud.xml 文件中复制):

<?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.itheima</groupId>
    <artifactId>heima-springcloud</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>user-service</module>
        <module>consumer-demo</module>
    </modules>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.5.RELEASE</version>
        <relativePath/>
    </parent>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Greenwich.SR1</spring-cloud.version>
        <mapper.starter.version>2.1.5</mapper.starter.version>
        <mysql.version>5.1.46</mysql.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <!-- springCloud -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope><!--通过scope的import可以继承spring-cloud-dependencies工程中的依赖-->
            </dependency>
            <!-- 通用Mapper启动器 -->
            <dependency>
                <groupId>tk.mybatis</groupId>
                <artifactId>mapper-spring-boot-starter</artifactId>
                <version>${mapper.starter.version}</version>
            </dependency>
            <!-- mysql驱动 -->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>${mysql.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-config</artifactId>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

 这里已经对大部分要用到的依赖的版本进行了管理,方便后续使用

二、服务提供者

 新建一个项目user-service,对外提供查询用户的服务。 

 需求:可以访问http://localhost:9091/user/8输出用户数据

 步骤:

  1、创建module

  2、添加启动器依赖(web、通用Mapper)

  3、编写启动引导类和配置文件

  4、编写代码(UserMapper,UserService,UserController)

  5、启动并测试

  1、创建module

 实现:

 1、创建module

  选中父工程:heima-springcloud 

   

  填写module信息:   

   

  注意,子模块要在父工程的下级目录:

   

 2、添加依赖

  pom.xml 文件中的内容如下: 

<?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>heima-springcloud</artifactId>
        <groupId>com.itheima</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>user-service</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- 通用Mapper启动器 -->
        <dependency>
            <groupId>tk.mybatis</groupId>
            <artifactId>mapper-spring-boot-starter</artifactId>
        </dependency>
        <!-- mysql驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
    </dependencies>
</project>

  项目结构: 

   

 3、编写启动引导类和配置文件

  IDEA 安装JBLSpringBootAppGen插件,通过插件去创建启动引导类和配置文件

      

  创建 user-service\src\main\java\com\itheima\user\UserApplication.java 启动类: 

package com.itheima.user;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import tk.mybatis.spring.annotation.MapperScan;
@SpringBootApplication
@MapperScan("com.itheima.user.mapper")
public class UserApplication {
    public static void main(String[] args) {
        SpringApplication.run(UserApplication.class, args);
   }
}

  创建 user-service\src\main\resources\application.yml 属性文件,这里我们采用了yaml语法,而不是 properties:

server:
 port: 9091
spring:
 datasource:
   driver-class-name: com.mysql.jdbc.Driver
   url: jdbc:mysql://localhost:3306/springcloud
   username: root
   password: root
mybatis:
 type-aliases-package: com.itheima.user.pojo

  使用mysql图形界面工具创建 springcloud 数据库,将数据导入; 

/*
Navicat MySQL Data Transfer

Source Server         : local3306
Source Server Version : 50622
Source Host           : localhost:3306
Source Database       : springboot_test

Target Server Type    : MYSQL
Target Server Version : 50622
File Encoding         : 65001

Date: 2019-04-14 18:39:07
*/

SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- Table structure for tb_user
-- ----------------------------
DROP TABLE IF EXISTS `tb_user`;
CREATE TABLE `tb_user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `user_name` varchar(100) DEFAULT NULL COMMENT '用户名',
  `password` varchar(100) DEFAULT NULL COMMENT '密码',
  `name` varchar(100) DEFAULT NULL COMMENT '姓名',
  `age` int(10) DEFAULT NULL COMMENT '年龄',
  `sex` tinyint(1) DEFAULT NULL COMMENT '性别,1男性,2女性',
  `birthday` date DEFAULT NULL COMMENT '出生日期',
  `note` varchar(255) DEFAULT NULL COMMENT '备注',
  `created` datetime DEFAULT NULL COMMENT '创建时间',
  `updated` datetime DEFAULT NULL COMMENT '更新时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `username` (`user_name`)
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of tb_user
-- ----------------------------
INSERT INTO `tb_user` VALUES ('1', 'zhangsan', '123456', '张三', '30', '1', '1964-08-08', '张三同学在学Java', '2014-09-19 16:56:04', '2014-09-21 11:24:59');
INSERT INTO `tb_user` VALUES ('2', 'lisi', '123456', '李四', '21', '2', '1995-01-01', '李四同学在传智学Java', '2014-09-19 16:56:04', '2014-09-19 16:56:04');
INSERT INTO `tb_user` VALUES ('3', 'wangwu', '123456', '王五', '22', '2', '1994-01-01', '王五同学在学php', '2014-09-19 16:56:04', '2014-09-19 16:56:04');
INSERT INTO `tb_user` VALUES ('4', 'zhangliu', '123456', '张六', '20', '1', '1996-09-01', '张六同学在传智播客学Java', '2014-09-19 16:56:04', '2014-09-19 16:56:04');
INSERT INTO `tb_user` VALUES ('5', 'lina', '123456', '李娜', '28', '1', '1988-01-01', '李娜同学在传智播客学Java', '2014-09-19 16:56:04', '2014-09-19 16:56:04');
INSERT INTO `tb_user` VALUES ('6', 'lilei', '123456', '李雷', '23', '1', '1993-08-08', '李雷同学在传智播客学Java', '2014-09-20 11:41:15', '2014-09-20 11:41:15');
INSERT INTO `tb_user` VALUES ('7', 'hanmeimei', '123456', '韩梅梅', '24', '2', '1992-08-08', '韩梅梅同学在传智播客学php', '2014-09-20 11:41:15', '2014-09-20 11:41:15');
INSERT INTO `tb_user` VALUES ('8', 'itcast', '123456', '传智播客', '21', '2', '2008-07-08', '传智播客搞IT教育', '2014-09-20 11:41:15', '2014-09-20 11:41:15');
INSERT INTO `tb_user` VALUES ('9', 'heima', '123456', '黑马', '18', '2', '2012-08-08', '黑马是传智播客高端品牌', '2014-09-20 11:41:15', '2014-09-20 11:41:15');
INSERT INTO `tb_user` VALUES ('10', 'linus', '123456', '林纳斯', '45', '2', '1971-08-08', '林纳斯搞了linux又搞git', '2014-09-20 11:41:15', '2014-09-20 11:41:15');
INSERT INTO `tb_user` VALUES ('11', 'leijun', '123456', '雷布斯', '33', '2', '1983-08-08', '小爱同学;are you ok', '2014-09-20 11:41:15', '2014-09-20 11:41:15');
INSERT INTO `tb_user` VALUES ('12', 'madaye', '123456', '马大爷', '46', '2', '1980-08-08', '马大爷花呗可以不还吗', '2014-09-20 11:41:15', '2014-09-20 11:41:15');

 4、编写代码

    编写 user-service\src\main\java\com\itheima\user\pojo\User.java 实体类: 

package com.itheima.user.pojo;
import lombok.Data; import tk.mybatis.mapper.annotation.KeySql; import javax.persistence.Id; import javax.persistence.Table; import java.util.Date; @Table(name = "tb_user") @Data public class User {    @Id    @KeySql(useGeneratedKeys = true)    private Long 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;// 备注 }

  编写 user-service\src\main\java\com\itheima\user\mapper\UserMapper.java 

package com.itheima.user.mapper;
import com.itheima.user.pojo.User; import tk.mybatis.mapper.common.Mapper; public interface UserMapper extends Mapper<User> { }

  编写 user-service\src\main\java\com\itheima\user\service\UserService.java

package com.itheima.user.service;

import com.itheima.user.pojo.User;
import com.itheima.user.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserService {

    @Autowired
    private UserMapper userMapper;

    /**
     * 根据主键查询用户
     * @param id    用户id
     * @return      用户信息
     */
    public User queryByID(Long id){
        return userMapper.selectByPrimaryKey(id);
    }
}
  添加一个对外查询的接口处理器user-service\src\main\java\com\itheima\user\controller\UserController.java
package com.itheima.user.controller;

import com.itheima.user.pojo.User;
import com.itheima.user.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/user")
public class UserController { @Autowired private UserService userService; @GetMapping("/{id}")  //GetMapping是RequestMapping(method=RequestMethod.GET)的缩写 public User queryById(@PathVariable Long id){ return userService.queryByID(id); } }
  创建完上述代码后项目结构:

   

 5、启动并测试

  启动 user-service 项目启动类,访问接口:http://localhost:9091/user/8

   

  如果启动失败,出现如下错误

Description:
Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.
Reason: Failed to determine a suitable driver class
Action:
Consider the following:
    If you want an embedded database (H2, HSQL or Derby), please put it on the classpath.
    If you have database settings to be loaded from a particular profile you may need to activate it (no profiles are currently active).

  pom问题导致编译时没有将配置文件生成到target目录
  简单来说,就是你每次启动时,一般都会重新编译并且将文件生成到target目录,如果你在target目录没有发现配置文件,说明编译时,没有将resource目录下的配置文件编译到target目录下,因此Spring Boot无法加载配置文件。

  解决办法:在pom文件中加载配置文件

    <build>
    <resources>
        <resource>
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.yml</include>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>false</filtering>
        </resource>
        <resource>
            <directory>src/main/resources</directory>
            <includes>
                <include>**/*.yml</include>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>false</filtering>
        </resource>
    </resources>
    </build>

三、服务调用者

 目标:编写测试类使用restTemplate访问user-service的路径根据id查询用户

 需求:访问http://localhost:8080/consumer/8使用RestTemplate获取http://localhost:9091/user/8的数据

 实现步骤:

  1.添加启动器依赖

  2.创建启动引导类(注册RestTemplate)和配置文件;

  3.编写测试代码(ConsumerController中使用restTemplate访问服务获取数据)

 1、创建工程

  与上面类似,这里不再赘述,需要注意的是,我们调用user-service的功能,因此不需要mybatis相关依赖了。

   

   

  pom.xml文件内容如下:

<?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>heima-springcloud</artifactId>
        <groupId>com.itheima</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>consumer-demo</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

</project>

  项目结构如下:

   

 2、编写代码

  编写启动类 consumer-demo\src\main\java\com\itheima\consumer\ConsumerApplication.java 并在其中注册RestTemplate 具体如下:

package com.itheima.consumer;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
public class ConsumerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class, args);
    }

    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}

  创建实体类 consumer-demo\src\main\java\com\itheima\consumer\pojo\User.java

package com.itheima.consumer.pojo;

import lombok.Data;
import java.util.Date;

@Data
public class User {
    private Long 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;// 备注
}

  编写consumer-demo\src\main\java\com\itheima\consumer\controller\ConsumerController.java,在controller中直接调用RestTemplate,远程访问user-service的服务接口:

package com.itheima.consumer.controller;

import com.itheima.consumer.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
@RequestMapping("/consumer")
public class ConsumerController {

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("/{id}")
    public User queryById(@PathVariable Long id){
        String url="http://localhost:9091/user/"+id;
        return restTemplate.getForObject(url,User.class);

    }
}

  服务调用者项目结构:

   

 3、启动测试

  启动 consumer-demo引导启动类;因为 consumer-demo 项目没有配置端口,那么默认就是8080,我们访问:http://localhost:8080/consumer/8

   

  一个简单的远程服务调用案例就实现了。

四、思考问题

 简单回顾一下,刚才我们写了什么:
  user-service:对外提供了查询用户的接口
  consumer-demo:通过RestTemplate访问http://locahost:9091/user/{id}接口,查询用户数据存在什么问题?
  • 在consumer中,我们把url地址硬编码到了代码中,不方便后期维护
  • consumer需要记忆user-service的地址,如果出现变更,可能得不到通知,地址将失效consumer不清楚user-service的状态,服务宕机也不知道
  • user-service只有1台服务,不具备高可用性
  • 即便user-service形成集群,consumer还需自己实现负载均衡
 其实上面说的问题,概括一下就是分布式服务必然要面临的问题:
  • 服务管理
    • 如何自动注册和发现
    • 如何实现状态监管
    • 如何实现动态路由
  • 服务如何实现负载均衡
  • 服务如何解决容灾问题
  • 服务如何实现统一配置
 以上的问题,都将在SpringCloud中得到答案。

 

posted on 2024-04-17 13:52  花溪月影  阅读(17)  评论(0编辑  收藏  举报