Spring Cloud开发实践(三): 接口实现和下游调用
目录
- Spring Cloud开发实践(一): 简介和根模块
- Spring Cloud开发实践(二): Eureka服务和接口定义
- Spring Cloud开发实践(三): 接口实现和下游调用
- Spring Cloud开发实践(四): Docker部署
- Spring Cloud开发实践(五): Consul - 服务注册的另一个选择
- Spring Cloud开发实践(六): 基于Consul和Spring Cloud 2021.0的演示项目
接口实现 Scot Commons Impl
接口实现模块 scot-commons-impl, 一方面实现了 scot-commons-api 的接口, 一方面将自己暴露为 REST 服务. 有4个文件, 分别为 pom.xml, application.yml, ServiceImplApplication.xml 和 UserDTOServiceImpl.java
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>
<parent>
<groupId>com.rockbb</groupId>
<artifactId>scot</artifactId>
<version>1.0-SNAPSHOT</version>
<relativePath>../scot/pom.xml</relativePath>
</parent>
<artifactId>scot-commons-impl</artifactId>
<packaging>jar</packaging>
<name>Scot: Commons Implementation</name>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>com.rockbb</groupId>
<artifactId>scot-commons-api</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<build>
<finalName>scot-commons</finalName>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
</project>
application.yml
server:
port: ${PORT:8762}
spring:
application:
name: scot-commons
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
ServiceImplApplication.java
这里不能引入FeignClient, 以免激活api中的注解.
@EnableEurekaClient
@SpringBootApplication
public class ServiceImplApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceImplApplication.class, args);
}
}
UserDTOServiceImpl.java
在UserDTOServiceImpl.java中, 实现了UserDTOService的接口, 因为这里的RequestMapping就是FeignClient将要读取的路径, 所以可以原封不动地将UserDTOService中的定义复制到这里. 统一了接口与实现.
@RequestMapping("/user")
@RestController
public class UserDTOServiceImpl implements UserDTOService {
@Value("${spring.cloud.client.hostname}")
String ipAddress;
@Value("${server.port}")
String port;
@Value("${spring.application.name}")
String applicationName;
@Override
@RequestMapping(value = "/diagnos", method = RequestMethod.GET)
public String diagnos(@RequestParam String name) {
return ipAddress+":"+port+":"+applicationName+": " + name;
}
@Override
@RequestMapping(value = "/get", method = RequestMethod.GET)
public UserDTO get(@RequestParam String id) {
return new UserDTO().initialize("3utowired5nnotation9ean0osterocessor", "system");
}
@Override
@RequestMapping(value = "/list", method = RequestMethod.GET)
public List<UserDTO> list() {
return new ArrayList<>();
}
@Override
@RequestMapping(value = "/count", method = RequestMethod.GET)
public long count() {
return 99;
}
}
下游调用 Scot Web
在这个模块中主要实现了两个功能, 一是通过scot-commons-api的定义引用scot-commons-impl中的服务, 二是使用redis保存HttpSession. 这个模块一共有4个文件: pom.xml, application.yml, WebApplication.java 和 IndexController.java
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>
<parent>
<groupId>com.rockbb</groupId>
<artifactId>scot</artifactId>
<version>1.0-SNAPSHOT</version>
<relativePath>../scot/pom.xml</relativePath>
</parent>
<artifactId>scot-web</artifactId>
<packaging>jar</packaging>
<name>Scot: Web</name>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
<dependency>
<groupId>com.rockbb</groupId>
<artifactId>scot-commons-api</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<build>
<finalName>scot-web</finalName>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
</project>
application.yml
server:
port: ${PORT:8763}
servlet:
session:
timeout: 600
spring:
application:
name: scot-web
session:
store-type: redis
redis:
flush-mode: ON_SAVE
namespace: spring:session
redis:
host: 127.0.0.1
port: 6379
database: 2
password: foobar
logging:
pattern:
console: '%d{yyMMdd HH:mm:ssSSS} %8.8thread %1.-1level %25.25logger{50}#%4.4line %msg%n'
level:
com.rockbb: DEBUG
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
registerWithEureka: false
WebApplication.java
@EnableFeignClients 需要指定 basePackages, 否则不会去jar中扫描
@EnableDiscoveryClient
@EnableFeignClients(basePackages = {"com.rockbb.scot.commons.api"})
@SpringBootApplication
public class WebApplication {
public static void main(String[] args) {
SpringApplication.run(WebApplication.class, args);
}
}
IndexController.java
这里可以直接Autowire或Resource引用scot-commons的服务.
直接用浏览器访问的话, 返回的格式实际上是根据request header来判断的, 如果要强制返回json格式, 需要加上 produces = "application/json"
@RestController
public class IndexController {
private static Logger logger = LoggerFactory.getLogger(IndexController.class);
@Resource
private UserDTOService userDTOService;
@RequestMapping(value = "/test1",method = RequestMethod.GET)
public String test1(@RequestParam String name){
logger.debug("/test1");
return userDTOService.diagnos(name);
}
@RequestMapping(value = "/test11",method = RequestMethod.GET, produces = "application/json")
public String test11(@RequestParam String name){
return userDTOService.diagnos(name);
}
@RequestMapping(value = "/test2",method = RequestMethod.GET)
public UserDTO test2(@RequestParam String id){
return userDTOService.get(id);
}
@RequestMapping(value = "/test21",method = RequestMethod.GET, produces = "application/json")
public UserDTO test21(@RequestParam String id){
return userDTOService.get(id);
}
@RequestMapping(value = "/test3",method = RequestMethod.GET)
public List<UserDTO> users(@RequestParam String id){
return userDTOService.list();
}
@RequestMapping(value = "/test4",method = RequestMethod.GET)
public long users(){
return userDTOService.count();
}
@GetMapping("/session")
public String session(HttpSession session) {
return session.getId();
}
}
Feign Client传参的坑
使用Date类型参数会产生14小时的时差
接口使用Date类型时, 会发现服务提供方收到的时间, 比调用方传递的时间要晚14个小时, 例如调用为 Thu Jul 04 19:00:00 CST 2019 收到的为 Fri Jul 05 09:00:00 CST 2019. 原因是服务端将接收的String类型日期转换为Date类型采用的是Date的默认构造器 new Date('Thu Jul 04 19:00:00 CST 2019'), 这里就产生错误了, 因为CTS代表的时区有四个Central Standard Time (USA) UT-6:00、Central Standard Time (Australia) UT+9:30、China Standard Time UT+8:00、Cuba Standard Time UT-4:00, 同时表示美澳中古巴四个国家的标准时间, jvm会将CTS理解成了美国中部时区, 而美国的-6和中国的+8正好相差14个小时.
解决方案:
- 使用json化的对象传输, 不会有问题
- 使用long类型的timestamp
- 使用字符串, 自己做转换