Spring Cloud重构个人记账项目(账单微服务、网关微服务、配置微服务)

任务概述:

  1. 使用SpringCloud重构账单项目,开发基于RESTful的webservice接口,创建Eureka Server,把账单微服务注册到注册中心
  2. 重构账单项目,使用SpringCloud,开发搭建网关微服务
  3. 账单配置微服务,统一由码云管理账单微服务配置文件

MySql数据库表结构
在这里插入图片描述
创建Maven父工程项目,导入依赖

<?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.wcf</groupId>
    <artifactId>docker-bill-springcloud</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>
 
    <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>
            </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>
        </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>

新建Module (eureka-server)
导入依赖

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

    <artifactId>eureka-server</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
    </dependencies>

</project>

springboot启动器:EurekaServerApplication

@SpringBootApplication
@EnableEurekaServer  //声明当前服务是eureka服务
public class EurekaServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaServerApplication.class,args);
    }
}

配置文件:application.yml

server:
  port: 10086
spring:
  application:
    name: eureka-server

eureka:
  client:
    service-url:
      # eureka服务地址,如果是集群的话,需要指定其他集群eureka地址
      defaultZone: http://127.0.0.1:10086/eureka
    # 不注册自己
    register-with-eureka: false
    # 不拉取服务
    fetch-registry: false

新建Module (config-server)
导入依赖

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

    <artifactId>config-server</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-config-server</artifactId>
        </dependency>

    </dependencies>
</project>

springboot启动器:ConfigServerApplication

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

配置文件:application.yml

server:
  port: 12000
spring:
  application:
    name: config-server
  cloud:
    config:
      server:
        git:
          uri: https://gitee.com/request-peak/my-config.git
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka

注意:config-server配置文件里的cloud.config.server.git.uri的地址是自己gitee仓库里配置文件的链接!需自己提前写好。例如我的是bill-dev.yml,对应的uri获取如下:
在这里插入图片描述
新建Module (bill-service)
导入依赖

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

    <artifactId>bill-service</artifactId>

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

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <!-- 通用Mapper启动器 -->
        <dependency>
            <groupId>tk.mybatis</groupId>
            <artifactId>mapper-spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.54</version>
        </dependency>

        <!--Eureka客户端-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>

        <!-- 分页插件-->
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper-spring-boot-starter</artifactId>
            <version>1.2.3</version>
        </dependency>


    </dependencies>


</project>

SpringBoot启动器:BillManagerApplication

@SpringBootApplication
@MapperScan("com.wcf.dao")
@EnableDiscoveryClient //开启Eureka客户端发现功能
public class BillManagerApplication {
    public static void main(String[] args) {
        SpringApplication.run(BillManagerApplication.class,args);
    }
}

配置文件bootstrap.yml

spring:
  cloud:
    config:
      # 要与仓库中的配置文件的application保持一致
      name: bill
      # 要与仓库中的配置文件的profile保持一致
      profile: dev
      # 要与仓库中的配置文件所属的版本(分支)一样
      label: master
      discovery:
        # 使用配置中心
        enabled: true
        # 配置中心服务名
        service-id: config-server
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka

实体类
Bill

@Data
@Table(name = "bill_")
public class Bill implements Serializable {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id_")
    private Long id;
    
    @Column(name = "title_")
    private String title;
    
    @Column(name = "bill_time_")
    private Date bittTime;

    @Column(name = "type_id_")
    private Long typeId;

    @Column(name = "price_")
    private Double price;

    @Column(name = "explain_")
    private String explain;

    /**
     * 类别名称
     */
    @Transient //表示当前属性为瞬时属性,跟字段无映射
    private String typeName;

    /**
     * 开始时间
     */
    @Transient
    private Date date1;

    /**
     * 结束时间
     */
    @Transient
    private Date date2;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public Date getBittTime() {
        return bittTime;
    }

    public void setBittTime(Date bittTime) {
        this.bittTime = bittTime;
    }

    public Long getTypeId() {
        return typeId;
    }

    public void setTypeId(Long typeId) {
        this.typeId = typeId;
    }

    public Double getPrice() {
        return price;
    }

    public void setPrice(Double price) {
        this.price = price;
    }

    public String getExplain() {
        return explain;
    }

    public void setExplain(String explain) {
        this.explain = explain;
    }

    public String getTypeName() {
        return typeName;
    }

    public void setTypeName(String typeName) {
        this.typeName = typeName;
    }

    public Date getDate1() {
        return date1;
    }

    public void setDate1(Date date1) {
        this.date1 = date1;
    }

    public Date getDate2() {
        return date2;
    }

    public void setDate2(Date date2) {
        this.date2 = date2;
    }
}

BillType

@Table(name = "bill_type_")
public class BillType {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id_")
    private Long id;

    @Column(name = "name_")
    private String name;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

Result

public class Result {

    private int code; //响应状态
    private String msg;//响应消息
    private Long count;//数据条数
    private List<Bill> data;//响应数据

    @Override
    public String toString() {
        return "Result{" +
                "code=" + code +
                ", msg='" + msg + '\'' +
                ", count=" + count +
                ", data=" + data +
                '}';
    }

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public Long getCount() {
        return count;
    }

    public void setCount(Long count) {
        this.count = count;
    }

    public List<Bill> getData() {
        return data;
    }

    public void setData(List<Bill> data) {
        this.data = data;
    }
}

dao
BillMapper

public interface BillMapper extends Mapper<Bill> {
    public List<Bill> select(Bill b);
}

TypeMapper

public interface TypeMapper extends Mapper<BillType> {
}

service
BillService

@Service
public class BillService {

    @Autowired
    private BillMapper billMapper;

    public List<Bill> list(Bill b){
        return billMapper.select(b);
    }

    public int add(Bill b){
        return billMapper.insert(b);
    }
    
    public Bill get(Long id){
        return billMapper.selectByPrimaryKey(id);
    }
    
    public int delete(Long id){
        return billMapper.deleteByPrimaryKey(id);
    }
    
    public int update(Bill b){
        return billMapper.updateByPrimaryKey(b);
    }
    
    public PageInfo<Bill> listPage(Bill b, int pageNum, int pageSize){
        return PageHelper.startPage(pageNum, pageSize).doSelectPageInfo(()->{
            billMapper.select(b);
        });
    }
}

TypeService

@Service
public class TypeService {

    @Autowired
    private TypeMapper typeMapper;

    public List<BillType> list(){
        return typeMapper.selectAll();
    }
}

controller

@RestController
@RefreshScope //刷新配置
@RequestMapping("/bill")
public class BillController {
    
    @Autowired
    private BillService billService;
    @Autowired
    private TypeService typeService;

    /**
     * 分页查询
     * @param page
     * @param limit
     * @param date1
     * @param date2
     * @param typeId
     * @return
     */
    @RequestMapping("/list-page")
    public String listPage(@RequestParam(defaultValue = "1") int page,
                           @RequestParam(defaultValue = "10") int limit,
                           Date date1, Date date2,@RequestParam(defaultValue = "0") String typeId){
        Bill bill=new Bill();
        bill.setTypeId(new Long(typeId));
        bill.setDate1(date1);
        bill.setDate2(date2);
        List<BillType> types = typeService.list();
        PageInfo<Bill> pageInfo = billService.listPage(bill, page, limit);
        List<Bill> list = pageInfo.getList();
        Result result=new Result();
        result.setData(list);
        result.setCode(0);
        result.setCount(pageInfo.getTotal());
        String jsonData = JSON.toJSONString(result);
        return jsonData;
    }

    /**
     * 查询
     * @param b
     * @param model
     * @return
     */
    @RequestMapping("/list")
    public String list(Bill b, Model model){
        List<BillType> types = typeService.list();
        model.addAttribute("types", types);
        List<Bill> bills = billService.list(b);
        model.addAttribute("bills", bills);
        Result result = new Result();
        result.setData(bills);
        result.setCode(0);
        System.out.println(result);
        String jsonData = JSON.toJSONString(result);
        return jsonData;
    }

    /**
     * 添加
     * @param b
     * @return
     */
    @RequestMapping("/add")
    public String add(Bill b,String feedback){
        System.out.println(feedback);
        System.out.println(b.getTitle());
        int add = billService.add(b);
        Result result = new Result();
        if (add > 0) {
            result.setMsg("1");
            String jsonData = JSON.toJSONString(result);
            return feedback+"["+jsonData+"]";
        }
        result.setMsg("0");
        String jsonData = JSON.toJSONString(result);
        System.out.println(jsonData);
        return feedback+"["+jsonData+"]";
    }

    /**
     * 修改
     * @param b
     * @return
     */
    @RequestMapping("/update")
    public String update(Bill b,String feedback){
        System.out.println(b);
        int update = billService.update(b);
        Result result = new Result();
        if (update > 0) {
            result.setMsg("1");
            String jsonData = JSON.toJSONString(result);
            return feedback+"["+jsonData+"]";
        }
        result.setMsg("0");
        String jsonData = JSON.toJSONString(result);
        return feedback+"["+jsonData+"]";
    }

    @RequestMapping("/toUpdate/{id}")
    public String toUpdate(@PathVariable("id") Long id, Model model){
        List<BillType> types = typeService.list();
        model.addAttribute("types",types);
        Bill bill = billService.get(id);
        model.addAttribute("bill",bill);
        return "/bill/update";
    }

    /**
     * 删除
     * @param id
     * @return
     */
    @RequestMapping("/delete/{id}")
    public String delete(@PathVariable("id") Long id){
        int i = billService.delete(id);
        Result result=new Result();
        if(i>0){
            result.setMsg("1");
            String jsonData = JSON.toJSONString(result);
            return jsonData;
        }
        result.setMsg("0");
        String jsonData=JSON.toJSONString(result);
        return jsonData;
    }

}

mappers
BillMapper.xml

<?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.wcf.dao.BillMapper">
    <sql id="selectSql">
       SELECT
          b.id_ as id,
          b.title_ as title,
          b.bill_time_ as billTime,
          b.type_id_ as typeId,
          b.price_ as price,
          b.explain_ as `explain`,
          t.name_ as typeName
       FROM
          bill_ as b
       left join
          bill_type_ as t
       on
          b.type_id_ = t.id_
    </sql>

    <select id="select" resultType="bill">
        <include refid="selectSql"/>
        <where>
            <if test="typeId !=null and typeId!=0">
                b.type_id_ = #{typeId}
            </if>
            <if test="title !=null">
                and b.title_ like '%${title}%'
            </if>
            <if test="date1 !=null">
                and b.bill_time_ &gt;= #{date1}
            </if>
            <if test="date2 !=null">
                and b.bill_time_ &lt;= #{date2}
            </if>
        </where>
    </select>

</mapper>

PS:之前启动bill-service测试账单微服务一直都没问题,网页上能正常返回json格式的数据列表。后期做账单微服务测试时,启动bill-service后一直报错,提示信息“ Failed to configure a DataSource: ‘url’ attribute is not specified and no embedded datasource could be configured. ” ,自此再也无法正常启动bill-service。我试过很多方法,仔细检查修改了config-server和bill-service里配置文件里的路径、数据库的信息以及gitee仓库里的bill-dev.yml,但还是报这个(很魔性,明明之前都可以)。经过一番折腾最后归纳总结得出:原因可能就是没有读取到远程的配置文件
为了解决这个问题,我重写了一个配置文件application.properties加进来(就是将仓库里bill-dev.yml里的配置信息拷过来,如下),注释掉原来bootstrap.yml里的配置信息,就能再次成功启动账单微服务bill-service了!

server.port=${port:9091}
#连接池
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.url=jdbc:mysql://localhost:3306/bill-manager
spring.application.name=bill-service
spring.thymeleaf.cache=false

# 整合mybatis
mybatis.type-aliases-package=com.wcf.bean
mybatis.mapper-locations=classpath:/mappers/*.xml

#注册实例时,更倾向于使用ip地址,而不是host名
prefer-id-address: true
# #ip地址
ip-address: 127.0.0.1
#续约间隔,默认30s
lease-renewal-interval-in-seconds: 5
#服务时效时间,默认90s
lease-expiration-duration-in-seconds: 5

eureka.client.service-url.defaultZone: http://127.0.0.1:10086/eureka

最终bill-service项目结构如下:
在这里插入图片描述
新建Module (bill-gateway)
导入依赖

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

    <artifactId>bill-gateway</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
    </dependencies>


</project>

SpringBoot启动器:GatewayApplication

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

配置文件:application.yml

server:
  port: 8086
spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      routes:
        #路由id,可以随意写
        - id: bill-service-route
          #代理的服务地址
          # uri: http://127.0.0.1:9091
          uri: lb://bill-service
          #路由断言,可以配置映射路径
          predicates:
            - Path=/api/bill/list/**
          filters:
            # 表示过滤1个路径,2表示过滤2个路径,依次类推
            - StripPrefix=1
            # 自定义过滤器
            - MyParam=name
      # 默认过滤器,对所有路由都生效
      default-filters:
        - AddResponseHeader=X-Response-Foo, Bar
        - AddResponseHeader=myname, wcf
      globalcors:
        corsConfigurations:
          '[/**]':
            #allowedOrigins: * # 这种写法或者下面的都可以,*表示全部
            allowedOrigins:
              - "http://docs.spring.io"
            allowedMethods:
              - GET
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka

hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 6000 #服务降级超时时间,默认1s
ribbon:
  ConnectTimeout: 1000 #连接超时时长
  ReadTimeout: 2000 #数据通信超时时长
  MaxAutoRetries: 0 # 当前服务器的重试次数
  MaxAutoRetriesNextServer: 0 # 重试多少次服务

filter

@Component
public class MyParamGatewayFilterFactory extends AbstractGatewayFilterFactory<MyParamGatewayFilterFactory.Config> {
    public MyParamGatewayFilterFactory(){
        super(Config.class);
    }

    public List<String> shortcutFieldOrder(){
        return Arrays.asList("param");
    }

    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            ServerHttpRequest request=exchange.getRequest();
            if(request.getQueryParams().containsKey(config.param)){
                request.getQueryParams().get(config.param).forEach((v)->{
                    System.out.printf("--局部过滤器--获得参数 %s = %s ----",config.param,v);
                });
            }
            return chain.filter(exchange);//执行请求
        };
    }


    //读取过滤器配置的参数
    public static class Config{
        //对应配置在application.yml配置文件中的过滤器参数名
        private String param;

        public String getParam() {
            return param;
        }

        public void setParam(String param) {
            this.param = param;
        }
    }
}

到这里,eureka-server、config-server(账单配置微服务)、bill-gateway(账单网关微服务)、bill-service(账单微服务)均创建完毕!项目结构及启动微服务成功后如下:在这里插入图片描述
3项微服务均成功注册进eureka界面如下:
在这里插入图片描述
使用postman访问账单微服务查询的返回界面:
在这里插入图片描述
访问账单微服务分页查询的返回界面:
在这里插入图片描述
网关微服务访问账单微服务(前端统一访问地址 http://locahost:8086/api/bill/list):
在这里插入图片描述

  • 7
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值