SpringCloud學習5-如何創建一個服務提供者provider


前幾篇主要集中在注冊中心eureka的使用上,接下來可以創建服務提供者provider來注冊到eureka。

demo源碼見: https://github.com/Ryan-Miao/spring-cloud-Edgware-demo/tree/master/provider-demo

為了方便版本控制,接下來的項目都是基於https://github.com/Ryan-Miao/spring-cloud-Edgware-demo 這個parent配置的。

創建子moudle provider-demo

創建一個子module,項目名叫provider-demo. 填充springboot和springcloud依賴

  <dependencies>
    <!--springboot 依賴start-->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-devtools</artifactId>
      <optional>true</optional>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.datatype</groupId>
      <artifactId>jackson-datatype-jsr310</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-eureka</artifactId>
    </dependency>
    <!--springboot 依賴結束-->


    <dependency>
      <groupId>io.springfox</groupId>
      <artifactId>springfox-swagger2</artifactId>
    </dependency>
    <dependency>
      <groupId>io.springfox</groupId>
      <artifactId>springfox-swagger-ui</artifactId>
    </dependency>


    <!--工具類 start-->
    <dependency>
      <groupId>com.google.guava</groupId>
      <artifactId>guava</artifactId>
    </dependency>
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <optional>true</optional>
    </dependency>

    <dependency>
      <groupId>net.logstash.logback</groupId>
      <artifactId>logstash-logback-encoder</artifactId>
    </dependency>
    <!--工具類end-->


  </dependencies>
  • spring-boot-starter-web 提供web能力,必須
  • spring-boot-starter-actuator提供項目統計和基礎的監控endpoint, 想要使用spring-boot-admin監控就必須添加了
  • spring-boot-devtools開發模式
  • jackson-datatype-jsr310可以解決Java8新的時間API LocalDate解體
  • spring-cloud-starter-eureka eureka客戶端,負責維護心跳和注冊
  • swagger 提供Restful契約
  • lombok 看起來很清爽的編譯級別getter setter工具
  • guava 大而全的Java必備類庫
  • logstash-logback-encoder 想要收集日志到ELK,使用這個appender

啟動類

@EnableDiscoveryClient
@SpringBootApplication
public class ProviderDemoApplication {

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

}
  • @EnableDiscoveryClient來啟用服務注冊

這個ProviderDemoApplication應該放置於項目包的最外層,因為@SpringbootAppliatin包含了@ComponentScan的注解,默認掃描本類包下,否則必須手動指定scan。

Swagger

swagger就是一個配置類

@EnableSwagger2
@Configuration
public class SwaggerConfiguration {

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
            .title("服務提供者 API")
            .description("提供用戶信息查詢")
            .termsOfServiceUrl("")
            .version("1.0.0")
            .build();
    }

    /**
     * 定義api配置.
     */
    @Bean
    public Docket api() {
        return new Docket(DocumentationType.SWAGGER_2)
            .select()
            .apis(RequestHandlerSelectors.withClassAnnotation(Api.class))
            .build()
            .apiInfo(apiInfo());
    }

}

對於swagger頁面的路由,需要我們來引導下:

創建一個controller來導航

@Controller
public class HomeController {
    @GetMapping(value = {"/api", "/"})
    public String api() {
        return "redirect:/swagger-ui.html";
    }

}


來一個Controller 接口

@Api
@RestController
@RequestMapping("/api/v1/users")
public class UserController{

    private List<User> users = Lists.newArrayList(
        new User(1, "譚浩強", 100, LocalDate.now()),
        new User(2, "嚴蔚敏", 120, LocalDate.now()),
        new User(3, "譚浩強", 100, LocalDate.now()),
        new User(4, "James Gosling", 150, LocalDate.now()),
        new User(6, "Doug Lea", 150, LocalDate.now())
    );

    @GetMapping("/")
    public List<UserVo> list() {
        return users.stream()
            .map(u -> new UserVo(u.getId(), u.getName(), u.getAge(), u.getBirth()))
            .collect(Collectors.toList());
    }
}

一些簡單的環境配置

application.yml

spring:
  application:
    name: provider-demo
  jackson:
    serialization:
      WRITE_DATES_AS_TIMESTAMPS: false
    default-property-inclusion: non_null

#服務過期時間配置,超過這個時間沒有接收到心跳EurekaServer就會將這個實例剔除
#注意,EurekaServer一定要設置eureka.server.eviction-interval-timer-in-ms否則這個配置無效,這個配置一般為服務刷新時間配置的三倍
#默認90s
eureka.instance.lease-expiration-duration-in-seconds: 15
#服務刷新時間配置,每隔這個時間會主動心跳一次
#默認30s
eureka.instance.lease-renewal-interval-in-seconds: 5

server:
  port: 8082


springfox:
  documentation:
    swagger:
      v2:
        path: /swagger-resources/api-docs

log:
  path: logs

application-dev.yml

management:
  security:
    enabled: false
eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/


logstash:
  url: localhost:4560

這里需要提一點,由於我集成了logstash, 所以必須安裝好logstash, 見ELK入門使用。 當然可以跳過,只要不提供logback.xml的配置就行,把依賴中logstash移除即可。

Log配置

默認采用logback作為日志框架,簡單配置如下,對於不想使用logstash的,移除logstash的appender即可。

在resource下新建logback-spring.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="false">

  <springProperty scope="context" name="appName" source="spring.application.name"
    defaultValue="unknown"/>
  <springProperty scope="context" name="log.path" source="log.path"
    defaultValue="logs"/>
  <springProperty scope="context" name="logstashurl" source="logstash.url"
    defaultValue="localhost:4560"/>

  <include resource="org/springframework/boot/logging/logback/base.xml"/>
  <!--輸出到控制台-->
  <appender name="console" class="ch.qos.logback.core.ConsoleAppender">LoggingInterceptor

    <encoder>
      <pattern>%d{HH:mm:ss.SSS} %X{req.remoteHost} %X{req.requestURI}
        ${appName} [%thread] %-5level %logger{36} - %msg%n
      </pattern>
    </encoder>
  </appender>

  <!--輸出到文件-->
  <appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
      <fileNamePattern>${log.path}/${appName}.%d{yyyy-MM-dd}.log</fileNamePattern>
    </rollingPolicy>
    <encoder>
      <pattern>%d{HH:mm:ss.SSS} ${appName} %X{req.remoteHost} %X{req.requestURI}
        %X{req.userAgent}
        %X{req.method} - [%thread] %-5level %logger{36} - %msg%n
      </pattern>
    </encoder>
  </appender>

  <!-- 輸出到logstash-->
  <appender name="LOGSTASH" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
    <destination>${logstashurl}</destination>
    <encoder charset="UTF-8" class="net.logstash.logback.encoder.LogstashEncoder"/>
  </appender>


  <springProfile name="dev">
    <root level="info">
      <appender-ref ref="console"/>
      <appender-ref ref="file"/>
      <appender-ref ref="LOGSTASH"/>
    </root>

  </springProfile>
  <springProfile name="test, prod">
    <root level="info">
      <appender-ref ref="file"/>
      <appender-ref ref="LOGSTASH"/>
    </root>
  </springProfile>
</configuration>

啟動

確保eureka已啟動,admin最好也啟動,方便查看app狀態,ELK的日志系統也最好可以使用。當然,只有eureka是剛需。

編譯打包

mvn clean install package spring-boot:repackage

運行main方法,指定profile為dev, 可以在idea中編輯運行配置,添加參數

--spring.profiles.active=dev

或者命令行jar啟動

java -Xms256m -Xmx1g -XX:+UseG1GC -jar ./target/provider-demo-0.0.1-SNAPSHOT.jar --spring.profiles.active=dev

啟動后,訪問eureka

訪問admin

訪問provider-demo

暴露我們的API給consumer

既然有服務提供者,必然是為了consumer消費。consumer應該如何消費?手動調用這個http請求即可。前面提到swagger Restful契約,就是服務提供者提供請求訪問的參數和要求。consumer如果手動去開發這個client必然耗時,而且容易出錯。所以,作為服務提供者,理應提供sdk或者client給consumer來用。

在spring cloud技術體系中,遠程調用自然是重中之重。目前我找到的具體用法為Feign+Ribbon+Hystrix.

通過Feign的聲明式接口對接,實現了consumer對provider的調用。ribbon客戶端負載均衡,hystrix作健康熔斷。

在這里,我們就首先要提供Feign的接口了。

把controller的api提煉成一個接口。首先,我們創建一個新的項目

https://github.com/Ryan-Miao/spring-cloud-Edgware-demo/tree/master/provider-api

將這個項目放到provider-demo的依賴列表里

<!--內部依賴-->
<dependency>
  <groupId>com.test</groupId>
  <artifactId>provider-api</artifactId>
  <version>0.0.1-SNAPSHOT</version>
</dependency>
<!--內部依賴end-->

抽離UserApi接口道provider-api項目中

@RequestMapping("/api/v1/users")
public interface UserApi {

    @GetMapping("/")
    List<UserVo> list();
}

在provider-demo的controller里改造如下

@Api
@RestController
public class UserController implements UserApi {

    private List<User> users = Lists.newArrayList(
        new User(1, "譚浩強", 100, LocalDate.now()),
        new User(2, "嚴蔚敏", 120, LocalDate.now()),
        new User(3, "譚浩強", 100, LocalDate.now()),
        new User(4, "James Gosling", 150, LocalDate.now()),
        new User(6, "Doug Lea", 150, LocalDate.now())
    );

    @Override
    public List<UserVo> list() {
        return users.stream()
            .map(u -> new UserVo(u.getId(), u.getName(), u.getAge(), u.getBirth()))
            .collect(Collectors.toList());
    }
}

這樣,controller沒有變化,只是被抽離了api路徑。而獨立出來的module provider-api就是我們給consumer提供的client。下一節使用consumer消費。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM