SpringCloud(一)-手把手教你创建springcloud微服务父子项目

现在微服务思想大行其道,各个公司都在如火如荼的进行技术转型,掌握SpringCloud已经成为升职加薪的垫脚石,笔者将从本文开始从实战和原理记录SpringCloud的学习过程。

系列文章
SpringCloud(一)-手把手教你创建springcloud微服务父子项目
SpringCloud(二)-手把手教你搭建Eureka Server和Eureka Client
SpringCloud(三)-手把手教你通过Rinbbon实现客户端负载均衡
SpringCloud(四)-手把手教你使用OpenFeign
SpringCloud(五)-手把手教你使用Hystrix配置服务熔断和降级以及Hystrix Dashboard
SpringCloud(六)-手把手教你搭建SpringCloud Config配置中心
SpringCloud(七)-手把手教你使用消息总线Bus实现动态刷新
SpringCloud(八)-手把手教你使用Stream消息驱动

码云地址

https://gitee.com/zhaowenyi/springcloudtest/tree/master

初识SpringCloud

打开SpingCloud中文官网,一行"微服务集大成者,云计算最佳业务实践"赫然入目,由此可知SpringCloud在微服务领域的影响力。

SpringCloud 是由一系列技术栈组合而成:

  • Spring Cloud Config: 管理配置包,可以将配置文件放在远程服务器上集中化管理集群配置文件。支持本地存储,git,subversion。
  • Spring Cloud Bus:事件、消息总线,可以在集群中传播状态变化,比如配置文件变动,可以和Spring Cloud Config组合使用。
  • Netflix Eureka:服务注册与发现,一个基于REST服务,用于服务注册与发现,故障转移等功能
  • Netflix Hystrix:熔断器,容错管理工具,旨在通过熔断机制控制服务和第三方库的节点,从而为延迟和故障提供更强大的容错能力。
  • Netflix Zuul:提供动态路由,监控和安全等边缘服务的框架。
  • Netflix Ribbon:提供云端负载均衡,有多种负载均衡可供选择,可和熔断器和服务发现组合使用
  • Open Feign: 是一款申明式,模块化的HTTP客户端

这还只是列出的一部分常用的框架,还有很多都没有列出来,光通过定义都难以理解这些框架是干什么用的,只能千里之行始于足下,一步步开始攻克这些知识点了。

初识SpringBoot

上面的框架中没有列出SpringBoot,是因为SpringBoot作为最基础的框架,是基石,任何微服务都要从搭建一个Spring Boot项目开始。

那么什么是Spring Boot呢?

打开Spring Boot官网,我们了解到
使用Spring Boot可以轻松地创建独立的,基于生产级别的基于Spring的应用程序,我们可以“运行”它们。也就是Spring Boot帮我们配置了Spring 项目中哪些繁琐的配置,而且内置了服务器,比如tomcat,我们可以直接运行它。

总之其Spring思想还是没有变,只是帮我们简单快速的搭建一个Spring 项目。

构建SpringCloud项目

接下来就是开始搭建SpringCloud项目了。首选我们要搭建Spring Cloud项目父工程。

工具准备

  • JDK : 1.8
  • Spring Boot:2.0
  • IntelliJ IDEA:ULTIMATE 2017.02
  • Mysql:5.6.24
  • Win10
  • Maven: 3.6

搭建步骤

创建父工程
新建项目
输入gav坐标
选择项目路径.png

点击File - settings - Build Tools - Maven ,这里的Maven路径选择自己本地的maven路径。

设置maven路径

设置自动导入依赖

点击Enable Auto Import

Editor - File Types,末尾加上“.idea;.iml;”,设置隐藏impl文件和.idea文件

image.png

File - settings - Build - Complier - Java Complier ,需要将编译版本设置为1.8

设置编译的jdk版本

最终的项目结构

项目结构

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">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.elio.springcloud</groupId>
    <artifactId>springcloudtest</artifactId>
    <version>1.0-SNAPSHOT</version>

    <!--统一管理jar包版本-->
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <junit.version>4.12</junit.version>
        <lombok.version>1.18.10</lombok.version>
        <log4j.version>1.2.17</log4j.version>
        <mysql.version>5.1.47</mysql.version>
        <druid.version>1.1.16</druid.version>
        <mybatis.spring.boot.version>1.3.0</mybatis.spring.boot.version>
    </properties>

    <!--子模块继承之后,提供作用:锁定版本+子module不用写groupId和version-->
    <dependencyManagement><!--定义规范,但不导入-->
        <dependencies>
            <dependency>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-project-info-reports-plugin</artifactId>
                <version>3.0.0</version>
            </dependency>
            <!--spring boot 2.2.2-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.2.2.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!--spring cloud Hoxton.SR1-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Hoxton.SR1</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!--spring cloud 阿里巴巴-->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>2.1.0.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!--mysql-->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>${mysql.version}</version>
                <scope>runtime</scope>
            </dependency>
            <!-- druid-->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid</artifactId>
                <version>${druid.version}</version>
            </dependency>
            <!--mybatis-->
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>${mybatis.spring.boot.version}</version>
            </dependency>
            <!--junit-->
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>${junit.version}</version>
            </dependency>
            <!--log4j-->
            <dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
                <version>${log4j.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <!--热启动插件-->
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <fork>true</fork>
                    <addResources>true</addResources>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>
创建子工程

父工程完成后,我们接下来创建两个子项目,一个是商品服务的提供者,一个是商品服务的消费者。首先来创建商品服务的提供者

右键项目名,点击New - module

新增子项目
点击next

输入子项目名称 ”springcloud-product-provider-8100“,表示的商品的提供者,使用8100端口

输入子项目名称
点击next

点击finish后,我们可以看到构建成功的子项目

商品服务提供者8100

添加商品服务提供者的pom.xml配置

右键商品服务提供者项目的resources文件夹,新增application.yml配置文件,新增之后application.yml前面有个绿色的小叶子标识

新增yml配置文件

yml中的配置文件如下

server:
  port: 8100 #端口号

spring:
  application:
    name: springcloud-product-provider-8100
  datasource:
      url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&userSSL=false
      driverClassName: com.mysql.jdbc.Driver
      username: root
      password: 111111

mybatis:
  mapper-locations: classpath:mapping/*mapper.xml # Mybatis 映射文件位置
  type-aliases-package: com.elio.springcloud.entity  # 表对应的实体类包


右键java文件夹新增,实体类Product,这将是我们接下来的product表对应的实体类

package com.elio.springcloud.entity;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@NoArgsConstructor
@AllArgsConstructor
@Setter
@Getter
public class Product {

    private Long id; //自增id
    private String name;// 产品名称
    private int stock;// 库存

}

新增dao层接口ProductMapper

package com.elio.springcloud.dao;

import com.elio.springcloud.entity.Product;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;

@Repository
public interface ProductMapper {
    /**
     * 查询
     * @param id
     * @return
     */
    public Product selectById(@Param("id") Long id);


    /**
     * 删除
     * @param id
     * @return
     */
    public int deleteById(@Param("id") Long id);

    /**
     * 修改
     * @param id
     * @param name
     * @return
     */
    public int updateById(@Param("id") Long id, @Param("name") String name);

    /**
     * 新增
     * @param product
     * @return
     */
    public int insertOne(Product product);
}

右键resources文件,新增mapping\product_mapper.xml文件,这个是product表对应的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.elio.springcloud.dao.ProductMapper">

    <resultMap id="BaseResultMap" type="com.elio.springcloud.entity.Product">
        <result column="id" jdbcType="BIGINT" property="id" />
        <result column="name" jdbcType="VARCHAR" property="name" />
        <result column="stock" jdbcType="INTEGER" property="stock" />
    </resultMap>

    <!--查询-->
    <select id="selectById" resultType="Product">
        select * from product where id = #{id}
    </select>

    <!--删除-->
    <delete id="deleteById" parameterType="Long">
        delete from product where id = #{id}
    </delete>

    <!--修改-->
    <update id="updateById" parameterType="Product">
        update product set name = #{name} where id = #{id}
    </update>

    <!--新增-->
    <insert id="insertOne" parameterType="Product">
       insert into product(name, stock) values (#{name}, #{stock})
    </insert>

</mapper>

新增ProductService.java文件

package com.elio.springcloud.service;

import com.elio.springcloud.entity.Product;
import org.apache.ibatis.annotations.Param;

/**
 * 商品服务类
 */
public interface ProductService {

    /**
     * 查询
     * @param id
     * @return
     */
    public Product selectById(Long id);


    /**
     * 删除
     * @param id
     * @return
     */
    public int deleteById(Long id);

    /**
     * 修改
     * @param id
     * @param name
     * @return
     */
    public int updateById(Long id, String name);

    /**
     * 新增
     * @param product
     * @return
     */
    public int insertOne(Product product);

}


新增ProductServiceImpl.java 类

package com.elio.springcloud.service.impl;

import com.elio.springcloud.dao.ProductMapper;
import com.elio.springcloud.entity.Product;
import com.elio.springcloud.service.ProductService;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

@Service
public class ProductServiceImpl implements ProductService{

    @Resource
    private ProductMapper productMapper;

    public Product selectById(Long id) {
        return productMapper.selectById(id);
    }

    public int deleteById(Long id) {
        return productMapper.deleteById(id);
    }

    public int updateById(Long id, String name) {
        return productMapper.updateById(id, name);
    }

    public int insertOne(Product product) {
        return productMapper.insertOne(product);
    }
}


新增Result.java

package com.elio.springcloud.dto;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@AllArgsConstructor
@NoArgsConstructor
@Setter
@Getter
public class Result {

    private int code;
    private String message;
    private Object result;
}

新增ProductProviderController.java

package com.elio.springcloud.controller;

import com.elio.springcloud.dto.Result;
import com.elio.springcloud.entity.Product;
import com.elio.springcloud.service.ProductService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;

@RestController
@Slf4j
@RequestMapping("/")
public class ProductProviderController {

    @Resource
    private ProductService productService;

    /**
     * 查询
     * @param id
     * @return
     */
    @GetMapping("product/provider/get/{id}")
    public Result selectById(@PathVariable("id") Long id){
        return new Result(200, "查询成功", productService.selectById(id));
    }

    /**
     * 删除
     * @param id
     * @return
     */
    @GetMapping("product/provider/delete/{id}")
    public Result deleteById(@PathVariable("id") Long id){
        return new Result(200, "删除成功", productService.deleteById(id));
    }

    /**
     * 修改
     * @param product
     * @return
     */
    @PostMapping("product/provider/update")
    public Result updateById(@RequestBody Product product){
        return new Result(200, "修改成功", productService.updateById(product.getId(), product.getName()));

    }

    /**
     * 新增
     * @return
     */
    @PutMapping( "product/provider/add")
    public Result insertById(@RequestBody Product product){
        return new Result(200, "修改成功", productService.insertOne(product));
    }
}

右键java文件夹,新增主启动类ProductProvider8100

新增主启动类

ProductProvider8100内容如下,加上@SpringBootApplication和MapperScan注解

package com.elio.springcloud;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@MapperScan("com.elio.springcloud.dao")
public class ProductProvider8100 {

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


创建表结构


-- ----------------------------
-- Table structure for product
-- ----------------------------
DROP TABLE IF EXISTS `product`;
CREATE TABLE `product` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `stock` int(10) unsigned DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;


测试

至此微服务的商品提供者,我们已经创建成功,接下来就是测试该提供者了


右键主启动类DEBUG

启动成功后可以查看结果如下


启动成功

浏览器地址输入 http://localhost:8100/product/provider/get/1

查询结果

创建商品服务消费者

右键父项目,点击next

点击Next
输入项目名
输入项目名

配置商品消费者的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>springcloudtest</artifactId>
        <groupId>com.elio.springcloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>springcloud-product-consumer-8200</artifactId>


    <dependencies>
        <!--spring boot -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--热部署-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>
        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>

    <!--热启动插件-->
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <fork>true</fork>
                    <addResources>true</addResources>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

resources文件夹下新增application.yml文件

server:
  port: 8200

spring:
  application:
    name: springcloud-product-consumer-8200

新增主启动类 ProductConsumer8200,因为消费者不需要连接数据库,所以pom.xml中也没有导入相关依赖,因此需要将自动注入数据源的的类过滤掉 @SpringBootApplication(exclude={DataSourceAutoConfiguration.class,HibernateJpaAutoConfiguration.class})

package com.elio.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication(exclude={DataSourceAutoConfiguration.class,HibernateJpaAutoConfiguration.class})
public class ProductConsumer8200 {

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

新增业务类 ProductConsumerController

package com.elio.springcloud.controller;

import com.elio.springcloud.dto.Result;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;

@RestController
public class ProductConsumerController {

    @Resource
    RestTemplate restTemplate;

    public static String url = "http://localhost:8100/";

    /**
     * 查询
     * @param id
     * @return
     */
    @GetMapping("product/consumer/get/{id}")
    public Result selectById(@PathVariable("id") Long id){

        return new Result(200, "查询成功",
                restTemplate.getForObject(url+"product/provider/get/"+id, Result.class));
    }

}

**新增结果返回实体类Result **

package com.elio.springcloud.dto;


import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@AllArgsConstructor
@NoArgsConstructor
@Setter
@Getter
public class Result {

    private int code;
    private String message;
    private Object result;
}

因为要调用消费者的api, 因此我们需要注入restTemplate对象,新增RestConfig

package com.elio.springcloud.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class RestConfig {

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

至此,我们已经创建好了消费者项目,接下来就是测试了

消费者项目架构

右键主启动类,启动成功后会显示如下结果

启动日志

浏览器地址输入 http://localhost:8200/product/consumer/get/1
这是使用消费者的路径访问提供者的api,结果如下

返回成功的结果

总结

我们至此创建了一个父项目springcloudtest,然后有一个商品服务提供者
springcloud-product-provider-8100 和一个商品服务消费者springcloud-product-consumer-8200 。然后这就是最简单的两个微服务了,实现了功能的解耦,但是这个简单的微服务存在着很多问题,比如都用公共的实体类Result,还有提供者地址在消费者里面写死了等等,这些问题,我们接下来会一一解决。如有问题,请在评论区指正。

项目架构
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269