zoukankan      html  css  js  c++  java
  • 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消费。

  • 相关阅读:
    不足百行代码 实体数组转DataTable通用类
    【翻译】WEB安全设计规范(4.1)
    也为读者说几句(兼为什么要骂烂书译者)
    重用之前应仔细分析问题用错轮子有感
    最长代码有多长:不符[单一职责原则(SRP)]的常见设计
    "千里之堤毁于蚁穴"重点项目不能交付之谜(一)泥淖中的验收测试
    企业快速开发框架基于配置文件
    从面试题看高级软件工程师需要哪些技艺
    面试英语【转】
    测试
  • 原文地址:https://www.cnblogs.com/woshimrf/p/springcloud-provider.html
Copyright © 2011-2022 走看看