Spring Cloud开发实践(三): 接口实现和下游调用

目录

接口实现 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个小时.

解决方案:

  1. 使用json化的对象传输, 不会有问题
  2. 使用long类型的timestamp
  3. 使用字符串, 自己做转换

posted on 2018-10-25 23:23  Milton  阅读(1606)  评论(0编辑  收藏  举报

导航