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

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,333评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,812评论 1 298
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 110,016评论 0 246
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,293评论 0 214
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,650评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,788评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,003评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,741评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,462评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,681评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,168评论 1 262
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,528评论 3 258
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,169评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,119评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,902评论 0 198
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,846评论 2 283
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,719评论 2 274

推荐阅读更多精彩内容