简易SpringCloud项目构建过程(下)
上编主要包括数据库设计说明、父级工程创建与配置、服务注册中心、统一配置中心、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);
}
}