简易SpringCloud项目构建过程(下)

作者:陆金龙    发表时间:2022-03-23 09:47   

关键词:数据接口服务data-provider  消费者工程data-consumer  gateway工程  

上编主要包括数据库设计说明、父级工程创建与配置、服务注册中心、统一配置中心、Common工程5个部分。下篇包括数据接口服务提供者、数据消费者工程、网关工程3个部分。

源码地址:https://github.com/kinglong198404/spring-cloud-architecture.git下的spring-cloud-simple。

1. 数据接口服务(data-provider)

这里记录“知识库”生产者工程构建过程,其他业务模块的构建过程雷同,不再重复记录。

1.1 创建provider工程

在主工程下创建Maven module工程data-provider。

创建过程同EurekaServer、ConfigServer工程。

1.2 配置provider工程

1.2.1 配置pom.xml

主要是添加eureka 、数据库访问等相关的依赖项。其他依赖通过common工程间接配置。

<dependencies>

<!-- 引入自定义的common通用包,包含entity和util -->

<dependency>

<groupId>com.klfront.klblog</groupId>

<artifactId>common</artifactId>

<!-- 使用父工程版本号 -->

<version>${project.version}</version>

</dependency>

 

<!-- eureka client 依赖-->

<dependency>

<groupId>org.springframework.cloud</groupId>

<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>

</dependency>

 

<!-- 数据库访问  注意执行maven install 安装mysql-connector-java依赖包-->

<dependency>

<groupId>mysql</groupId>

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

</dependency>

<dependency>

<groupId>com.alibaba</groupId>

<artifactId>druid</artifactId>

</dependency>

<dependency>

<groupId>org.mybatis.spring.boot</groupId>

<artifactId>mybatis-spring-boot-starter</artifactId>

</dependency>

</dependencies>

1.2.2 配置bootstrap.yml文件

spring:
  cloud:
    config:
      name: base,datasource
      profile: dev
      label: develop
      uri: http://localhost:8888 #config-server的地址

1.2.3 配置application.yml文件

server:
  port: 8061  
spring:
   application:
    name: data-provider #微服务名,唯一标识一个服务
    #datasource通过config-server进行配置
eureka:
  instance:
    instance-id: data-provider-8061  #服务实例id,在eureka服务列表中Status处显示。微服务负载均衡的多个实例以此区分
    prefer-ip-address: true          #访问路径可以显示IP地址

    #eureka服务端地址等其他配置通过config-server进行配置

1.3 编写工程代码

1.3.1 主启动类

@SpringBootApplication
@EnableDiscoveryClient //服务发现
//@EnableEurekaClient只适用于Eureka作为注册中心,@EnableDiscoveryClient 可以是其他注册中心。
public class DataProviderApplication {
   public static void main(String[] args) {
      SpringApplication.run(ArticleProviderApplication.class, args);
   }
}

1.3.2 MybatisCodeHelper关联数据表生成代码

1)添加mybatis配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
   <settings>
      <setting name="cacheEnabled" value="true" />               <!--二级缓存开启-->
      <setting name="mapUnderscoreToCamelCase" value="true" />      <!--驼峰命名规则-->
      <setting name="logImpl" value="STDOUT_LOGGING" />           <!--打印sql语句-->
   </settings>
</configuration>

 

2)生成sql查询、Mapper、Service、ServiceImpl等基础代码。

 

1.3.3 业务接口代码

@RestController
@RequestMapping("/knowledgeBase")
public class KnowledgeBaseController {
    @Resource
    private KnowledgeBaseService knowledgeBaseService;

    @PostMapping("/add")
    public int add(@RequestBody KnowledgeBase item){
       return knowledgeBaseService.insertSelective(item);
    }


    @PostMapping("/update")
    public int update(@RequestBody KnowledgeBase item){
        return knowledgeBaseService.updateByPrimaryKeySelective(item);
    }


    @GetMapping("/get/{id}")
    public KnowledgeBase get(@PathVariable Long id){
        return knowledgeBaseService.selectByPrimaryKey(id);
    }
}

1.4 运行测试

先在父工程打包到本地仓库

依次运行eureka-server、config-server、data-provider工程,访问接口测试。

1.4.1 使用本工程配置datasource测试

遇到报错:Invalid bound statement (not found)

解决,是因为缺少以下配置,将config-server下的mybatis的配置复制到data-provider。

mybatis:
  profiles: dev
  config-location: classpath:mybatis/mybatis.cfg.xml         # mybatis配置文件所在路径
  type-aliases-package: com.klfront.klblog.common.entity    # 所有Entity别名类所在包
  mapper-locations:
    - classpath:mybatis/mapper/**/*.xml                        # mapper映射文件

1.4.2 改成使用config-server的配置

遇到报错: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

pom.xml配置文件问题:缺少spring-cloud-starter-config组件(该组件是spring cloud config的客户端组件),添加以下依赖配置就可以

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

然后在pom.xml文件右键->Maven->Reload project

2. 数据消费者工程(data-consumer)

工程使用了spring-cloud-starter-openfeign,基于Netflix feign实现,整合了Spring Cloud Ribbon(负载均衡)和Spring Cloud Hystrix(断路器)。

Feign是Netflix开发的声明式、模板化的HTTP客户端, Feign可以帮助我们更快捷、优雅地调用HTTP API。特点是面向接口调用微服务,相对于RestTemplate代码更优雅,也更符合MVC模式的开发习惯。

2.1 创建consumer工程

在主工程下创建Maven module工程data-consumer。

2.2 配置consumer工程

2.2.1 配置pom.xml

<dependencies>
    <dependency>
        <groupId>com.klfront.klblog</groupId>
        <artifactId>common</artifactId>
        <version>${project.version}</version>
    </dependency>
    <!-- feign依赖项 -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-openfeign-core</artifactId>
    </dependency>
    <!-- ribbon-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
    </dependency>

    <!--config中心 客户端 -->

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

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

2.2.2 配置application.yml

server:
  port: 8091
  
spring:
   application:
    name: data-consumer
  
feign:
  hystrix:
    enabled: true

# 负载均衡策略配置

DATA-PROVIDER: # 数据提供者服务名
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule #轮询策略

# NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #随机
#    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #权重
#    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.BestAvailableRule #最小连接数
#    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RetryRule #重试
#    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.AvailabilityFilteringRule    可用敏感性策略:AvailabilityFilteringRule,先过滤掉非健康的服务实例,然后再选择连接数较小的服务实例
#    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.ZoneAvoidanceRule #区域敏感策略

ribbon:
  ReadTimeout: 10000  
  ConnectTimeout: 10000
  
hystrix:
  command:
      default:
        execution:
          isolation:
            thread:
              timeoutInMilliseconds: 10000
eureka:
  instance:
    instance-id: data-consumer-8091
    prefer-ip-address: true      

2.2.3 配置bootstrap.yml

由于consumer工程不需要访问数据库,因此这里只从配置中心获取基础的配置。

spring:
  profiles:
    active:
      - dev

---
spring:
  profiles: dev
  cloud:
    config:
      name: base
      uri: http://localhost:8888

---
spring:
  profiles: prod
  cloud:
    config:
      name: base
      uri: http://localhost:8888

 

2.3 编写工程代码

2.3.1 主启动类

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients(basePackages= {"com.klfront.klblog"})
public class DataConsumerApplication
{
   public static void main(String[] args)
   {
      SpringApplication.run(DataConsumerApplication.class, args);
   }
}

2.3.2 基于@FeignClient注解的Service接口

@FeignClient(value = "DATA-PROVIDER",fallbackFactory=FeignClientServiceFallbackFactory.class)
@Service
public interface FeignClientService {
   /**
    * 新增知识库
    * @param item
    * @return
    */
   @PostMapping("/knowledgeBase/add")
   public int addKnowledgeBase(KnowledgeBase item);


   /**
    * 更新知识库
    * @param item
    * @return
    */
   @PostMapping("/knowledgeBase/update")
   public int updateKnowledgeBase(KnowledgeBase item);


   /**
    * 根据id获取知识库
    * @param id
    * @return
    */
   @GetMapping("/knowledgeBase/get/{id}")
   public KnowledgeBase getKnowledgeById(@PathVariable("id") String id);
}

2.3.3 基于断路器的异常处理类

import feign.hystrix.FallbackFactory;
import org.springframework.stereotype.Component;

@Component // 不要忘记添加
public class FeignClientServiceFallbackFactory implements FallbackFactory{

   @Override
   public com.klfront.klblog.dataconsumer.feignclient.FeignClientService create(Throwable cause) {
      //统一处理FeignClientService中各接口的异常
      return new com.klfront.klblog.dataconsumer.feignclient.FeignClientService() {

         @Override
         public int addKnowledgeBase(KnowledgeBase item) {
            return 0;
         }

         @Override
         public int updateKnowledgeBase(KnowledgeBase item) {
            return 0;
         }

         @Override
         public KnowledgeBase getKnowledgeById(Long id) {
            return null;
         }
      }
   }
}

2.3.4 依赖FeignClientService的Controller

@RestController
@RequestMapping("/knowledgeBase")
public class KnowledgeBaseConsumerController {
    @Resource
    private FeignClientService service;

    @PostMapping("/add")
    public int add(@RequestBody KnowledgeBase item){
        return service.addKnowledgeBase(item);
    }

    @PostMapping("/update")
    public int update(@RequestBody KnowledgeBase item){
        return service.updateKnowledgeBase(item);
    }

    @GetMapping("/get/{id}")
    public KnowledgeBase get(@PathVariable Long id){
        return service.getKnowledgeById(id);
    }
}

注意:data-consumer代码的命名空间要在com.klfront.klblog 下,即与@EnableFeignClients(basePackages= {"com.klfront.klblog"})一致,否则接口访问不通。

依次启动eureka-server、config-server、data-provider、consumer-provider微服务,测试localhost:8091/knowledgeBase/get/1,效果如下:

2.3.5 附:IDEA中一个微服务,同时多个端口启动。

同时多端口启动,在有的端口服务中断后,consumer会自动切换到另外可用的data-provider服务。

2.3.6附: 同一微服务多端口启动,Eureka只出现一个实例的问题

因为实例id重复了,将实例id改成以下动态的随机值:${spring.application.name}:${random.value}。这样Eureka中一个微服务显示有多个实例了,consumer的负载均衡也生效了。

eureka:
  instance:

# instance-id: data-provider-8061
    instance-id: ${spring.application.name}:${random.value}
    prefer-ip-address: true

3. 网关工程(gateway)

3.1 创建gateway工程

在主工程下创建Maven module工程zuul-gateway。

3.2 配置gateway工程

3.2.1 配置pom.xml

<dependencies>
    <!--gateway -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>

    <!--config 客户端 -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-config</artifactId>
    </dependency>

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

3.2.2 配置bootstrap.yml

spring:
  profiles:
    active:
      - dev

---
spring:
  profiles: dev
  cloud:
    config:
      name: base
      uri: http://localhost:8888

---
spring:
  profiles: beta
  cloud:
    config:
      name: base
      uri: http://localhost:8888

3.2.3 配置application.yml

server:
  port: 9201

spring:
  application:
    name: zuul-gateway
  servlet:
    multipart:
      enabled: true
      max-file-size: -1
      max-request-size: -1

zuul:
  host:
    connect-timeout-millis: 55000
    socket-timeout-millis: 60000
  routes:
    DATA-PROVIDER: /data-provider/**
    DATA-CONSUMER: /data-consumer/**

  ignored-patterns: /*-service/**
  sensitive-headers: Cookie,Set-Cookie

ribbon:
  ReadTimeout: 50000
  ConnectTimeout: 50000

feign:
  hystrix:
    enabled: true
  compression:
    request:
      enabled: true
      mime-types[0]: text/xml
      mime-types[1]: application/xml
      mime-types[2]: application/json
      min-request-size: 2048
    response.enabled: true

3.3 创建主启动类

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