美文网首页
Spring Cloud(一)服务注册与发现

Spring Cloud(一)服务注册与发现

作者: Lord_East | 来源:发表于2019-01-21 09:39 被阅读0次

    Spring Cloud

    Spring Cloud是一个基于Spring Boot实现的云应用开发工具,它为基于JVM的云应用开发中的配置管理、服务发现、断路器、智能路由、微代理、控制总线、全局锁、决策竞选、分布式会话和集群状态管理等操作提供了一种简单的开发方式。
    Spring Cloud包含了多个子项目(针对分布式系统中涉及的多个不同开源产品),比如:Spring Cloud Config、Spring Cloud Netflix、Spring Cloud CloudFoundry、Spring Cloud AWS、Spring Cloud Security、Spring Cloud Commons、Spring Cloud Zookeeper、Spring Cloud CLI等项目。


    以下所有的Spring Cloud文章Demo都基于父工程parent实现
    新建一个基础Maven工程
    pom.xml文件如下:

        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>1.5.2.RELEASE</version>
        </parent>
    
        <properties>
            <java.version>1.8</java.version>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        </properties>
    
        <modules>
            <!-- Eureka注册中心-->
            <module>eureka</module>
            <!-- 配置中心-->
            <module>config</module>
            <!-- 智能网关路由-->
            <module>zuul</module>
            <!-- Zipkin服务追踪-->
            <module>zipkin</module>
            <!--服务生产者-->
            <module>service-producer</module>
            <!-- 服务消费者-->
            <module>service-consumer</module>
            <!-- Feign服务消费者-->
            <module>service-consumer-feign</module>
            <!-- 带有负载均衡的消费者-->
            <module>service-consumer-ribbon</module>
            <!-- 带有熔断机制的消费者-->
            <module>service-consumer-ribbon-hystrix</module>
        </modules>
    
    
        <dependencyManagement>
            <dependencies>
                <dependency>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-dependencies</artifactId>
                    <version>Edgware.SR5</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
            </dependencies>
        </dependencyManagement>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <!-- compiler -->
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.5.1</version>
                    <configuration>
                        <source>${java.version}</source>
                        <target>${java.version}</target>
                        <encoding>${project.build.sourceEncoding}</encoding>
                    </configuration>
                </plugin>
                <!-- resources -->
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-resources-plugin</artifactId>
                    <configuration>
                        <encoding>${project.build.sourceEncoding}</encoding>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    

    这里使用的org.springframework.boot版本为1.5.2.RELEASE
    依赖的org.springframework.cloud版本为Edgware.SR5

    为了配合后续数据库使用,请先执行sql目录下的initSQL.sql文件:

    CREATE SCHEMA spring_cloud_config;
    CREATE SCHEMA spring_cloud_demo;
    CREATE SCHEMA spring_cloud_zipkin;
    
    USE spring_cloud_demo;
    
    CREATE TABLE user
    (
      id       INT AUTO_INCREMENT
      COMMENT '主键'
        PRIMARY KEY,
      name     VARCHAR(64) NOT NULL
      COMMENT '姓名',
      birthday DATE
      COMMENT '生日',
      address  VARCHAR(256)
      COMMENT '地址'
    )
      CHARSET = utf8;
    
    INSERT INTO user ( name, birthday, address) VALUES('test-admin', '1994-12-21', '测试地址');
    

    服务注册中心(Spring Cloud Eureka)

    Spring Cloud Eureka是Spring Cloud Netflix项目下的服务治理模块。而Spring Cloud Netflix项目是Spring Cloud的子项目之一,主要内容是对Netflix公司一系列开源产品的包装,它为Spring Boot应用提供了自配置的Netflix OSS整合。通过一些简单的注解,开发者就可以快速的在应用中配置一下常用模块并构建庞大的分布式系统。它主要提供的模块包括:服务发现(Eureka),断路器(Hystrix),智能路由(Zuul),客户端负载均衡(Ribbon)等。

    创建一个"服务注册中心"工程

    在父工程中新建一个基础Spring的Maven Moudle工程命名为eureka

    pom.xml文件如下:

      <parent>
            <groupId>com.wkedong.springcloud</groupId>
            <artifactId>parent</artifactId>
            <version>0.0.1-SNAPSHOT</version>
            <relativePath/><!-- lookup parent from repository -->
        </parent>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-eureka-server</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-actuator</artifactId>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    
        <repositories>
            <repository>
                <id>spring-milestones</id>
                <name>Spring Milestones</name>
                <url>https://repo.spring.io/milestone</url>
            </repository>
        </repositories>
    

    申明一个eureka服务很简单,通过@EnableEurekaServer注解启动一个应用作为服务注册中心,提供给其他应用进行注册访问。

    
    /**
     * @author wkedong
     */
    @EnableEurekaServer
    @SpringBootApplication
    public class EurekaApplication {
    
        public static void main(String[] args) {
            new SpringApplicationBuilder(EurekaApplication.class).web(true).run(args);
        }
    
    }
    

    @EnableEurekaServer:表示该应用注册为eureka服务注册中心,提供给其他应用使用
    @SpringBootApplication:spring boot提供了一个统一的注解@SpringBootApplication,作为应用标识
    @SpringBootApplication = (默认属性)@Configuration + @EnableAutoConfiguration + @ComponentScan。

    1. @Configuration:提到@Configuration就要提到他的搭档@Bean。使用这两个注解就可以创建一个简单的spring配置类,可以用来替代相应的xml配置文件。
      @Configuration的注解类标识这个类可以使用Spring IoC容器作为bean定义的来源。@Bean注解告诉Spring,一个带有@Bean的注解方法将返回一个对象,该对象应该被注册为在Spring应用程序上下文中的bean。
    2. @EnableAutoConfiguration:能够自动配置spring的上下文,试图猜测和配置你想要的bean类,通常会自动根据你的类路径和你的bean定义自动配置。
    3. @ComponentScan:会自动扫描指定包下的全部标有@Component的类,并注册成bean,当然包括@Component下的子注解@Service,@Repository,@Controller。

    启动一个注册中心所有需要一些基础的配置,这里使用yml格式作为配置文件

    application.yml如下:

    server:
      port: 6060    #服务端口号为 6060
    eureka:
      instance:
        hostname: localhost #主机名
      client:
        healthcheck:
          enabled: true #健康检查开启
        registerWithEureka: false #禁止注册中心注册自己
        fetchRegistry: false      #禁止检索服务
        serviceUrl:
          defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ #注册中心地址
    

    此时我们启动应用并访问 http://localhost:6060/ 将会看到下面的页面,由于我们没有注册其他的服务,所以没有发现任何服务。

    eureka-service.png

    服务提供方(service-producer)

    有了服务注册中心,下面来新建一个服务的提供方即service-producer,并向eureka中注册自己

    在父工程中新建一个基础Spring的Maven Moudle工程命名为service-producer

    pom.xml如下:

       <parent>
            <groupId>com.wkedong.springcloud</groupId>
            <artifactId>parent</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </parent>
    
        <properties>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-eureka</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-actuator</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-config</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-sleuth-zipkin</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-sleuth</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-jdbc</artifactId>
            </dependency>
            <!-- fastjson依赖 -->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>1.2.39</version>
            </dependency>
            <!-- 与数据库操作相关的依赖 -->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
            </dependency>
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>1.3.2</version>
            </dependency>
            <dependency>
                <groupId>net.logstash.logback</groupId>
                <artifactId>logstash-logback-encoder</artifactId>
                <version>4.6</version>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    

    由于demo中后续使用到了MyBatis和json的相关操作,所以在service-producer中我们事先依赖了阿里的fastjson和mybatis相应的jar包。


    然后我们对工程做一些配置工作,
    bootstrap.yml如下:

    eureka:
      client:
        healthcheck:
          enabled: true #健康检查开启
        serviceUrl:
          defaultZone: http://localhost:6060/eureka/  #注册中心服务地址
    spring:
      ## 从配置中心读取文件
      cloud:
        config:
          uri: http://localhost:6010/
          label: develop
          profile: dev
          name: service-producer
      application:
        name: service-producer    #当前服务ID
      zipkin:
        base-url: http://localhost:6040 #zipkin服务地址
      sleuth:
        enabled: true #服务追踪开启
        sampler:
          percentage: 1 #zipkin收集率
      datasource: #数据库信息
        url: jdbc:mysql://127.0.0.1:3306/spring_cloud_demo?autoReconnect=true&useUnicode=true&characterEncoding=UTF-8
        username: root
        password: 123456
        driver-class-name: com.mysql.jdbc.Driver
    management:
      security:
        enabled: false
    #mybatis config
    mybatis:
      type-aliases-package: com.wkedong.springcloud.serviceproducer.entity #entity实体对象所在的路径位置
    
    

    通过spring:application:name属性,我们可以指定微服务的名称后续在调用的时候只需要使用该名称就可以进行服务的访问。

    eureka:client:serviceUrl:defaultZone属性对应服务注册中心的配置内容,指定服务注册中心的位置。
    多端口设置
    application-peer1.yml:

    spring:
      profiles:
        active: peer1
    server:
      port: 6070
    

    application-peer2.yml:

    spring:
      profiles:
        active: peer2
    server:
      port: 6080
    

    application-peer3.yml:

    spring:
      profiles:
        active: peer3
    server:
      port: 6090
    

    spring:profiles:active属性设置使用配置文件。
    server:port属性设置不同的端口。


    接下来我们来实现服务的提供,应用启动类Application,具体如下:

    /**
     * @author wkedong
     */
    @SpringBootApplication
    @EnableDiscoveryClient
    @MapperScan("com.wkedong.springcloud.serviceproducer.mapper")
    public class ServiceProducerApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(ServiceProducerApplication.class, args);
        }
    
    }
    

    @MapperScan:扫描mybatis所配置的mapper路径


    新建一个controller类来实现/testGet,/testPost,/testFile,/testConfig,/testRibbon,/testFeign,/testHystrix接口
    ProducerController.java:

    /**
     * @author wkedong
     * <p>
     * 2019/1/14
     */
    @RestController
    public class ProducerController {
    
        private final Logger logger = Logger.getLogger(getClass());
    
        @Autowired
        ProducerService producerService;
    
        @GetMapping(value = "/testGet")
        public String testGet() {
            logger.info("===<call testGet>===");
            return producerService.testGet();
        }
    
        @PostMapping(value = "/testPost")
        public String testPost(@RequestBody JSONObject jsonRequest) {
            logger.info("===<call testPost>===");
            return producerService.testPost(jsonRequest);
        }
    
        @PostMapping(value = "/testFile", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
        public String handleFileUpload(@RequestPart(value = "file") MultipartFile file) {
            logger.info("===<call testFile>===");
            return file.getOriginalFilename();
        }
    
        @GetMapping(value = "/testConfig")
        public String testConfig() {
            logger.info("===<call testConfig>===");
            return producerService.testConfig();
        }
    
        @GetMapping(value = "/testRibbon")
        public String testRibbon() {
            logger.info("===<call testRibbon>===");
            return producerService.testRibbon();
        }
    
        @GetMapping(value = "/testFeign")
        public String testFeign() {
            logger.info("===<call testFeign>===");
            return producerService.testFeign();
        }
    
        @GetMapping(value = "/testHystrix")
        public String testHystrix() {
            logger.info("===<call testHystrix>===");
            return producerService.testHystrix();
        }
    }
    

    controller调用的Service,新建了ProducerService接口类及ProducerServiceImpl实现类,分别如下:

    /**
     * @author wkedong
     * <p>
     * 2019/1/15
     */
    public interface ProducerService {
        String testGet();
    
        String testPost(JSONObject jsonRequest);
    
        String testConfig();
    
        String testRibbon();
    
        String testFeign();
    
        String testHystrix();
    }
    
    /**
     * @author wkedong
     * <p>
     * 2019/1/14
     */
    @Service
    public class ProducerServiceImpl implements ProducerService {
        private Logger logger = LoggerFactory.getLogger(ProducerServiceImpl.class);
    
        @Autowired
        private UserMapper userMapper;
    
        @Autowired
        private EurekaInstanceConfig eurekaInstanceConfig;
    
    
        @Value("${server.port}")
        private int serverPort = 0;
    
        @Value("${name}")
        private String configName = "";
    
        private String returnMessage = "";
    
        @Override
        public String testGet() {
            this.logger.info("/testGet, instanceId:{}, host:{}", eurekaInstanceConfig.getInstanceId(), eurekaInstanceConfig.getHostName(false));
            setReturnMessage(" Get info is testGet Success");
            return returnMessage;
        }
    
        @Override
        public String testPost(@RequestBody JSONObject jsonRequest) {
            this.logger.info("/testPost, instanceId:{}, host:{}", eurekaInstanceConfig.getInstanceId(), eurekaInstanceConfig.getHostName(false));
            setReturnMessage(" PostParam is " + jsonRequest.toString());
            return returnMessage;
        }
    
        @Override
        public String testConfig() {
            this.logger.info("/testConfig, instanceId:{}, host:{}", eurekaInstanceConfig.getInstanceId(), eurekaInstanceConfig.getHostName(false));
            setReturnMessage(" ConfigName is " + configName);
            return returnMessage;
        }
    
        @Override
        public String testRibbon() {
            this.logger.info("/testRibbon, instanceId:{}, host:{}", eurekaInstanceConfig.getInstanceId(), eurekaInstanceConfig.getHostName(false));
            setReturnMessage(" This is a testRibbon result");
            return returnMessage;
        }
    
        @Override
        public String testFeign() {
            this.logger.info("/testFeign, instanceId:{}, host:{}", eurekaInstanceConfig.getInstanceId(), eurekaInstanceConfig.getHostName(false));
            setReturnMessage(" This is a testFeign result");
            return returnMessage;
        }
    
        @Override
        public String testHystrix() {
            this.logger.info("/testHystrix, instanceId:{}, host:{}", eurekaInstanceConfig.getInstanceId(), eurekaInstanceConfig.getHostName(false));
            try {
                Thread.sleep(5000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            setReturnMessage(" This is a testHystrix result");
            return returnMessage;
        }
    
        private void setReturnMessage(String info) {
            returnMessage = "Hello, Spring Cloud! My port is " + String.valueOf(serverPort) + info;
        }
    
    }
    

    接口实现类里实现了后续各消费者所调用的各个接口功能,所以事先放出来

    数据库操作的mapper文件如下:

    /**
     * @author wkedong
     * <p>
     * 2019/1/15
     */
    public interface UserMapper {
    
        @Select("SELECT * FROM user")
        List<UserEntity> getAll();
    
        @Select("SELECT * FROM user WHERE id = #{id}")
        UserEntity getOne(int id);
    
        @Insert("INSERT INTO user(name,birthday,address) VALUES(#{name}, #{birthday}, #{address})")
        void insert(UserEntity user);
    
        @Update("UPDATE user SET name=#{name},birthday=#{birthday},address=#{address} WHERE id =#{id}")
        void update(UserEntity user);
    
        @Delete("DELETE FROM user WHERE id =#{id}")
        void delete(int id);
    }
    

    分别实现了增删查改基础的实现方法

    对应的实体类如下:

    /**
     * @author wkedong
     * 测试实体
     * 2019/1/15
     */
    @RefreshScope
    @Component//加入到Spring容器
    public class UserEntity {
    
        private String id;
        private String name;
        @DateTimeFormat(style = "yyyy-MM-dd")
        private String birthday;
        private String address;
    
        public String getId() {
            return id;
        }
    
        public void setId(String id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getBirthday() {
            return birthday;
        }
    
        public void setBirthday(String birthday) {
            this.birthday = birthday;
        }
    
        public String getAddress() {
            return address;
        }
    
        public void setAddress(String address) {
            this.address = address;
        }
    
        @Override
        public String toString() {
            return super.toString();
        }
    

    启动该工程后,再次访问:http://localhost:6060/ 可以如下图内容,我们定义的服务被成功注册了。

    service-producer.png

    访问 http://localhost:6070/testGet 得到以下返回值:

    Hello, Spring Cloud! My port is 6070 Get info By Mybatis is {"address":"江苏南京","birthday":"1994-12-21","name":"wkedong"}
    

    文章目录:

    整体demo的GitHub地址:Spring-Cloud

    相关文章

      网友评论

          本文标题:Spring Cloud(一)服务注册与发现

          本文链接:https://www.haomeiwen.com/subject/aiuqjqtx.html