Spring Cloud微服务教程-第一篇

Spring Cloud微服务教程-第一篇

1.教程大纲
2.统一开发环境
3.微服务架构
4.Spring Cloud简介
5.使用 Spring Boot 实现微服务
6.Spring Cloud快速入门

1.教程大纲
统一开发环境
了解微服务架构
了解Spring Cloud
Spring Cloud快速入门
Eureka服务注册中心
使用Ribbon实现负载均衡
使用Hystrix实现容错
2.统一开发环境
JDK: 1.8
IDE: IntelliJ IDEA
Maven:3.3.9
OS: Windows 10 10.0
Springboot版本:2.0+
3.微服务架构
目前微服务是非常火的架构或者说概念,也是在构建大型互联网项目时采用的架构方式。

3.1.单体架构
单体架构,是指将开发好的项目打成war包,然后发布到tomcat等容器中的应用。

假设你正准备开发一款与Uber和滴滴竞争的出租车调度软件,经过初步会议和需求分析,你可能会手动或者使用基于Spring Boot、Play或者Maven的生成器开始这个新项目,它的六边形架构是模块化的
应用核心是业务逻辑,由定义服务、领域对象和事件的模块完成。围绕着核心的是与外界打交道的适配器。适配器包括数据库访问组件、生产和处理消息的消息组件,以及提供API或者UI访问支持的web模块等。

尽管也是模块化逻辑,但是最终它还是会打包并部署为单体式应用。具体的格式依赖于应用语言和框架。例如,许多Java应用会被打包为WAR格式,部署在Tomcat或者Jetty上,而另外一些Java应用会被打包成自包含的JAR格式,类似的,Rails和Node.js会被打包成层级目录。

这种应用开发风格很常见,因为IDE和其它工具都擅长开发一个简单应用,这类应用也很易于调试,只需要简单运行此应用,用Selenium链接UI就可以完成端到端测试。单体式应用也易于部署,只需要把打包应用拷贝到服务器端,通过在负载均衡器后端运行多个拷贝就可以轻松实现应用扩展。在早期这类应用运行的很好。

3.2.单体架构存在的问题
Spring Cloud微服务教程-第一篇_第1张图片
Spring Cloud微服务教程-第一篇_第2张图片
如何解决以上问题呢? – 使用微服务架构。使得应用由重变轻。

3.3.什么是微服务?
Spring Cloud微服务教程-第一篇_第3张图片
微服务架构的特征
Spring Cloud微服务教程-第一篇_第4张图片
3.5.微服务架构示例
Spring Cloud微服务教程-第一篇_第5张图片
每一个应用功能区都使用微服务完成。

4.Spring Cloud简介
4.1.简介
Spring Cloud项目的官方网址:
http://projects.spring.io/spring-cloud/
4.2.Spring Cloud子项目
Spring Cloud微服务教程-第一篇_第6张图片
4.3.版本说明
Spring Cloud微服务教程-第一篇_第7张图片
4.4.Spring Cloud框架特点
Spring Cloud微服务教程-第一篇_第8张图片
5.使用 Spring Boot 实现微服务
在正式学习Spring Cloud之前我们先使用Spring Boot实现一个微服务。

业务非常简单:
1、商品微服务:通过商品id查询商品的服务;
2、订单微服务:创建订单时通时,通过调用商品的微服务进行查询商品数据;

图示:Spring Cloud微服务教程-第一篇_第9张图片
说明:
1、对于商品微服务而言,商品微服务是服务的提供者,订单微服务是服务的消费者;
2、对于订单微服务而言,订单微服务是服务的提供者,人是服务的消费者。

5.1.实现商品微服务
5.1.1.创建maven工程
地址git:
5.1.2.导入依赖
重点是导入Spring Boot的依赖:

<?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>

    <groupId>com.zpc.microservice</groupId>
    <artifactId>microservice-item</artifactId>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.6.RELEASE</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

    <build>
        <finalName>${project.artifactId}</finalName>
        <plugins>
            <!-- 资源文件拷贝插件 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-resources-plugin</artifactId>
                <configuration>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <!-- java编译插件 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
        </plugins>
    </build>
    </project>
5.1.3.创建实体Item
package com.zpc.item.entity;

public class Item {

    private Long id;

    private String title;

    private String pic;

    private String desc;

    private Long price;

    public Item(){}

    public Item(long id, String title, String pic, String desc, Long price) {
        this.id=id;
        this.title=title;
        this.pic=pic;
        this.desc=desc;
        this.price=price;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getPic() {
        return pic;
    }

    public void setPic(String pic) {
        this.pic = pic;
    }

    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }

    public Long getPrice() {
        return price;
    }

    public void setPrice(Long price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "Item [id=" + id + ", title=" + title + ", pic=" + pic + ", desc=" + desc + ", price=" + price + "]";
    }

}

5.1.4.编写ItemService
编写ItemService用于实现具体的商品查询逻辑,为了演示方便,我们并不真正的连接数据库,而是做模拟实现。

package com.zpc.item.service;
import com.zpc.item.entity.Item;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;

@Service
public class ItemService {

    private static final Map<Long, Item> ITEM_MAP = new HashMap<Long, Item>();

    static {// 准备一些静态数据,模拟数据库
        ITEM_MAP.put(1L, new Item(1L, "商品1", "http://图片1", "商品描述1", 1000L));
        ITEM_MAP.put(2L, new Item(2L, "商品2", "http://图片2", "商品描述2", 2000L));
        ITEM_MAP.put(3L, new Item(3L, "商品3", "http://图片3", "商品描述3", 3000L));
        ITEM_MAP.put(4L, new Item(4L, "商品4", "http://图片4", "商品描述4", 4000L));
        ITEM_MAP.put(5L, new Item(5L, "商品5", "http://图片5", "商品描述5", 5000L));
        ITEM_MAP.put(6L, new Item(6L, "商品6", "http://图片6", "商品描述6", 6000L));
        ITEM_MAP.put(7L, new Item(7L, "商品7", "http://图片7", "商品描述7", 7000L));
        ITEM_MAP.put(8L, new Item(8L, "商品8", "http://图片8", "商品描述8", 8000L));
        ITEM_MAP.put(8L, new Item(9L, "商品9", "http://图片9", "商品描述9", 9000L));
        ITEM_MAP.put(8L, new Item(10L, "商品10", "http://图片10", "商品描述10", 10000L));
    }

    /**
     * 模拟实现商品查询
     *
     * @param id
     * @return
     */
    public Item queryItemById(Long id) {
        return ITEM_MAP.get(id);
    }

}

5.1.5.编写ItemController

package com.zpc.item.controller;

import com.zpc.item.entity.Item;
import com.zpc.item.service.ItemService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ItemController {

    @Autowired
    private ItemService itemService;

    /**
     * 对外提供接口服务,查询商品信息
     *
     * @param id
     * @return
     */
    @GetMapping(value = "item/{id}")
    public Item queryItemById(@PathVariable("id") Long id) {
        return this.itemService.queryItemById(id);
    }

}

@RestController注解的说明:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {

   /**
    * The value may indicate a suggestion for a logical component name,
    * to be turned into a Spring bean in case of an autodetected component.
    * @return the suggested component name, if any (or empty String otherwise)
    * @since 4.0.1
    */
   @AliasFor(annotation = Controller.class)
   String value() default "";

}

从源码可以看出,这是一个组合注解,组合了@Controller和@Response注解。相当于我们同时写了这2个注解。
@GetMapping注解是@RequestMapping(method = RequestMethod.GET)简写方式。其功能都是一样的。

同理还有其它注解:@PostMapping @PutMapping @ResttMapping等
5.1.6.编写程序入口

package com.zpc.item.runner;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;

/**
 * @author Evan
 */

@SpringBootApplication//申明这是一个Spring Boot项目
@ComponentScan(basePackages = {"com.zpc.item.controller","com.zpc.item.service"})//手动指定bean组件扫描范围
public class ItemApp {
    public static void main(String[] args) {
        SpringApplication.run(ItemApp.class, args);
    }
}

5.1.7.创建application.yml配置文件
Spring Boot以及Spring Cloud项目支持yml和properties格式的配置文件。

yml格式是YAML(Yet Another Markup Language)编写的格式,YAML和properties格式的文件是可以相互转化的。如:

server:
  port: 8081 #服务端口

等价于properties文件的配置:
server.port=8081

配置文件的示例:

server:
  port: 8081 #服务端口

5.1.8.启动程序测试
Spring Cloud微服务教程-第一篇_第10张图片
5.2.实现订单微服务
5.2.1.创建工程
地址git:
5.2.2.导入依赖

<?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>

    <groupId>com.zpc.microservice</groupId>
    <artifactId>microservice-item</artifactId>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.6.RELEASE</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

    <build>
        <finalName>${project.artifactId}</finalName>
        <plugins>
            <!-- 资源文件拷贝插件 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-resources-plugin</artifactId>
                <configuration>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <!-- java编译插件 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
        </plugins>
    </build>
    </project>

5.2.3.创建订单Order实体

package com.zpc.order.entity;

import java.util.Date;
import java.util.List;

public class Order {

    private String orderId;

    private Long userId;

    private Date createDate;

    private Date updateDate;

    private List<OrderDetail> orderDetails;

    public Order() {

    }

    public Order(String orderId, Long userId, Date createDate, Date updateDate) {
        this.orderId = orderId;
        this.userId = userId;
        this.createDate = createDate;
        this.updateDate = updateDate;
    }

    public String getOrderId() {
        return orderId;
    }

    public void setOrderId(String orderId) {
        this.orderId = orderId;
    }

    public Long getUserId() {
        return userId;
    }

    public void setUserId(Long userId) {
        this.userId = userId;
    }

    public Date getCreateDate() {
        return createDate;
    }

    public void setCreateDate(Date createDate) {
        this.createDate = createDate;
    }

    public Date getUpdateDate() {
        return updateDate;
    }

    public void setUpdateDate(Date updateDate) {
        this.updateDate = updateDate;
    }


    public List<OrderDetail> getOrderDetails() {
        return orderDetails;
    }

    public void setOrderDetails(List<OrderDetail> orderDetails) {
        this.orderDetails = orderDetails;
    }

    @Override
    public String toString() {
        return "Order [orderId=" + orderId + ", userId=" + userId
                + ", createDate=" + createDate + ", updateDate=" + updateDate
                + "]";
    }}

5.2.4.创建订单详情OrderDetail实体
订单与订单详情是一对多的关系。

package com.zpc.order.entity;

public class OrderDetail {

    private String orderId;

    private Item item = new Item();

    public OrderDetail() {

    }

    public OrderDetail(String orderId, Item item) {
        this.orderId = orderId;
        this.item = item;
    }

    public String getOrderId() {
        return orderId;
    }

    public void setOrderId(String orderId) {
        this.orderId = orderId;
    }

    public Item getItem() {
        return item;
    }

    public void setItem(Item item) {
        this.item = item;
    }

    @Override
    public String toString() {
        return "OrderDetail [orderId=" + orderId + ", item=" + item + "]";
    }

}

5.2.5.将商品微服务项目中的Item类拷贝到当前工程
Spring Cloud微服务教程-第一篇_第11张图片
5.2.6.编写OrderService
该Service实现的根据订单Id查询订单的服务,为了方便测试,我们将构造数据实现,不采用查询数据库的方式。

package com.zpc.order.service;

import com.zpc.order.entity.Item;
import com.zpc.order.entity.Order;
import com.zpc.order.entity.OrderDetail;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;

@Service
public class OrderService {

    private static final Map<String, Order> ORDER_DATA = new HashMap<String, Order>();

    static {
        // 模拟数据库,构造测试数据
        Order order = new Order();
        order.setOrderId("201810300001");
        order.setCreateDate(new Date());
        order.setUpdateDate(order.getCreateDate());
        order.setUserId(1L);
        List<OrderDetail> orderDetails = new ArrayList<OrderDetail>();

        Item item = new Item();// 此处并没有商品的数据,只是保存了商品ID,需要调用商品微服务获取
        item.setId(1L);
        orderDetails.add(new OrderDetail(order.getOrderId(), item));

        item = new Item(); // 构造第二个商品数据
        item.setId(2L);
        orderDetails.add(new OrderDetail(order.getOrderId(), item));

        order.setOrderDetails(orderDetails);

        ORDER_DATA.put(order.getOrderId(), order);
    }

    @Autowired
    private ItemService itemService;

    /**
     * 根据订单id查询订单数据
     *
     * @param orderId
     * @return
     */
    public Order queryOrderById(String orderId) {
        Order order = ORDER_DATA.get(orderId);
        if (null == order) {
            return null;
        }
        List<OrderDetail> orderDetails = order.getOrderDetails();
        for (OrderDetail orderDetail : orderDetails) {
            // 通过商品微服务查询商品详细数据
            Item item = this.itemService.queryItemById(orderDetail.getItem()
                    .getId());
            if (null == item) {
                continue;
            }
            orderDetail.setItem(item);
        }

        return order;
    }

}

5.2.7.实现ItemService

package com.zpc.order.service;


import com.zpc.order.entity.Item;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

@Service
public class ItemService {

    // Spring框架对RESTful方式的http请求做了封装,来简化操作
    @Autowired
    private RestTemplate restTemplate;

    public Item queryItemById(Long id) {
        return this.restTemplate.getForObject("http://127.0.0.1:8081/item/"
                + id, Item.class);
    }

}
5.2.8.编写OrderController
package com.zpc.order.controller;

import com.zpc.order.entity.Order;
import com.zpc.order.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class OrderController {
    @Autowired
    private OrderService orderService;

    @GetMapping(value = "order/{orderId}")
    public Order queryOrderById(@PathVariable("orderId") String orderId) {
        return this.orderService.queryOrderById(orderId);
    }
}

5.2.9.编写程序入口

package com.zpc.order.runner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.web.client.RestTemplate;

/**
 * @author Evan
 */
@SpringBootApplication//申明这是一个Spring Boot项目
@ComponentScan(basePackages = {"com.zpc.order.controller", "com.zpc.order.service"})//手动指定bean扫描范围
public class OrderApp {
    public static void main(String[] args) {
        SpringApplication.run(OrderApp.class, args);
    }

    /**
     * 向Spring容器中定义RestTemplate对象
     * @return
     */
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

5.2.10.编写application.yml配置文件

server:
  port: 8082 #服务端口

5.2.11.启动测试
在这里插入图片描述
5.3.添加okHttp的支持
okhttp是一个封装URL,比HttpClient更友好易用的工具。看个人习惯,HttpClient用的人也很多。
RestTemplate底层默认使用的jdk的标准实现,如果我们想让RestTemplate的底层使用okhttp,非常简单:
1、添加okhttp依赖

<dependency>
    <groupId>com.squareup.okhttp3</groupId>
    <artifactId>okhttp</artifactId>
    <version>3.9.0</version>
</dependency>

2、设置requestFactory
return new RestTemplate(new OkHttp3ClientHttpRequestFactory());

@Bean
public RestTemplate restTemplate() {
    return new RestTemplate(new OkHttp3ClientHttpRequestFactory());
}

Spring Cloud微服务教程-第一篇_第12张图片
查看 http://localhost:8082/order/201810300001 测试结果是一样的。
5.4.解决订单系统中的url硬编码问题

  1. 通过以上的测试我们发现,在订单系统中要调用商品微服务中的查询接口来获取数据,在订单微服务中将url硬编码到代码中,这样显然不好,因为,运行环境一旦发生变化这个url地址将不可用。
  2. 如何解决呢?
    解决方案:将url地址写入到application.yml配置文件中。

实现:
修改application.yml文件:

server:
  port: 8082 #服务端口
myspcloud:
  item:
    url: http://127.0.0.1:8081/item/

修改ItemService中的实现:

@Service
public class ItemService {

    // Spring框架对RESTful方式的http请求做了封装,来简化操作
    @Autowired
    private RestTemplate restTemplate;

    @Value("${myspcloud.item.url}")
    private String itemUrl;

    public Item queryItemById(Long id) {
        return this.restTemplate.getForObject(itemUrl
                + id, Item.class);
    }

}

5.5.继续优化解决硬编码的问题
在SpringBoot中使用@ConfigurationProperties注解可以非常简单的将配置文件中的值映射成对象。
第一步,创建ItemProperties类:

package com.zpc.order.properties;

public class ItemProperties {
    private String url;

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

}

第二步,创建OrderProperties类:

package com.zpc.order.properties;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

/**
 * 以myspcloud开头的配置被匹配到
 * @author Evan
 */
@Component
@ConfigurationProperties(prefix="myspcloud")
public class OrderProperties {

    private ItemProperties item = new ItemProperties();

    public ItemProperties getItem() {
        return item;
    }

    public void setItem(ItemProperties item) {
        this.item = item;
    }
}

第三步,在Itemservice中注入该对象:

@Service
public class ItemService {

    // Spring框架对RESTful方式的http请求做了封装,来简化操作
    @Autowired
    private RestTemplate restTemplate;

    //@Value("${myspcloud.item.url}")
    //private String itemUrl;

    @Autowired
    OrderProperties orderProperties;

    public Item queryItemById(Long id) {
        return this.restTemplate.getForObject(orderProperties.getItem().getUrl()
                + id, Item.class);
    }

第四步,启动类中增加bean扫描路径

@ComponentScan(basePackages = {"com.zpc.order.controller","com.zpc.order.service","com.zpc.order.properties"})//
手动指定bean扫描范围

可以看出,这种解决方案比第一种好很多,更加的方便的。
6.Spring Cloud快速入门
6.1.分析硬编码的问题
通过前面5.4、5.5的实现,我们视乎已经解决了url硬编码的问题,但是我们想想:
1、如果商品微服务的ip地址发生了变更,订单微服务中的配置文件也需要跟着修改
2、如果商品微服务有多个,那么在订单微服务中又该如何写地址?

那应该怎么解决呢? – 通过服务注册、发现的机制来完成。

6.2.微服务注册与发现
Spring Cloud微服务教程-第一篇_第13张图片
由上图可以看出:
1、服务提供者将服务注册到注册中心
2、服务消费者通过注册中心查找服务
3、查找到服务后进行调用(这里就是无需硬编码url的解决方案)
4、服务的消费者与服务注册中心保持心跳连接,一旦服务提供者的地址发生变更时,注册中心会通知服务消费者

6.3.注册中心:Eureka
Spring Cloud提供了多种注册中心的支持,如:Eureka、consul、ZooKeeper等。Eureka已经闭源了。本教程第二篇也会介绍使用其它两种方式作为注册中心。ureka由两个组件组成:Eureka服务器和Eureka客户端。Eureka服务器用作服务注册服务器。Eureka客户端是一个java客户端,用来简化与服务器的交互、作为轮询负载均衡器,并提供服务的故障切换支持。Netflix在其生产环境中使用的是另外的客户端,它提供基于流量、资源利用率以及出错状态的加权负载均衡。
6.3.1.原理
Spring Cloud微服务教程-第一篇_第14张图片
Eureka包含两个组件:Eureka Server和Eureka Client。
Eureka Server提供服务注册服务,各个节点启动后,会在Eureka Server中进行注册,这样EurekaServer中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观的看到。
Eureka Client是一个java客户端,用于简化与Eureka Server的交互,客户端同时也就别一个内置的、使用轮询(round-robin)负载算法的负载均衡器。
在应用启动后,将会向Eureka Server发送心跳,默认周期为30秒,如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,Eureka Server将会从服务注册表中把这个服务节点移除(默认90秒)。
Eureka Server之间通过复制的方式完成数据的同步,Eureka还提供了客户端缓存机制,即使所有的Eureka Server都挂掉,客户端依然可以利用缓存中的信息消费其他服务的API。综上,Eureka通过心跳检查、客户端缓存等机制,确保了系统的高可用性、灵活性和可伸缩性。
6.3.2.编写Eureka Server
第一步:创建Maven工程:
git代码地址:
第二步,导入依赖:
这里需要导入Spring Cloud的管理依赖。

<?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>

    <groupId>com.zpc.springcloud.eureka</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>demo</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.6.RELEASE</version>
    </parent>
    <dependencyManagement>
        <dependencies>
            <!-- 导入Spring Cloud的依赖管理 -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Finchley.SR1</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <dependencies>
    	<!--springboot 整合eureka服务端-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

第三步,编写程序启动类:

package com.zpc.springcloud.eureka;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

/**
 * Eureka注册中心
 */
@SpringBootApplication
@EnableEurekaServer //申明这是一个Eureka服务
public class AppEureka {

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

第四步,编写application.yml配置文件:

###服务端口号
server:
  port: 8100

###服务名称
spring:
  application:
    name: app-eureka-center

eureka:
  instance:
    #注册中心地址
    hostname: 127.0.0.1
###客户端调用地址
  client:
    serviceUrl:
      defaultZone: http://${eureka.instance.hostname}:8100/eureka/
###是否将自己注册到Eureka服务中,因为该应用本身就是注册中心,不需要再注册自己(集群的时候为true)
    register-with-eureka: false
###是否从Eureka中获取注册信息,因为自己为注册中心,不会在该应用中的检索服务信息
    fetch-registry: false

第五步,启动程序做测试:
Spring Cloud微服务教程-第一篇_第15张图片
6.4.将商品微服务注册到Eureka
接下来,我们需要将商品的微服务注册到Eureka服务中。

第一步:修改pom文件,引入Spring Cloud的管理依赖以及eureka服务依赖。

<?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>

    <groupId>com.zpc.microservice</groupId>
    <artifactId>microservice-item</artifactId>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.6.RELEASE</version>
    </parent>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Finchley.SR1</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--springboot 整合eureka客户端-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
    </dependencies>

    <build>
        <finalName>${project.artifactId}</finalName>
        <plugins>
            <!-- 资源文件拷贝插件 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-resources-plugin</artifactId>
                <configuration>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <!-- java编译插件 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

第二步,修改application.yml配置文件:

###服务端口号(本身是一个web项目)
server:
  port: 8081
###起个名字作为服务名称(该服务注册到eureka注册中心的名称,比如商品服务)
spring:
    application:
        name: app-item
###服务注册到eureka注册中心的地址
eureka:
  client:
    service-url:
           defaultZone: http://127.0.0.1:8100/eureka
###因为该应用为服务提供者,是eureka的一个客户端,需要注册到注册中心
    register-with-eureka: true
###是否需要从eureka上检索服务
    fetch-registry: true

第三步,修改启动类,增加@EnableEurekaClient 注解:

package com.zpc.item.runner;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.ComponentScan;

/**
 * @author Evan
 */
//申明这是一个Spring Boot项目
@SpringBootApplication
@EnableEurekaClient
@ComponentScan(basePackages = {"com.zpc.item.controller","com.zpc.item.service"})
public class ItemApp {
    public static void main(String[] args) {
        SpringApplication.run(ItemApp.class, args);
    }

}

第四步,启动测试:
在这里插入图片描述
至此,我们已经将自己的微服务注册到Eureka server中了。

6.5.订单系统从Eureka中发现商品服务
之前我们在订单系统中是将商品微服务的地址进行了硬编码,现在,由于已经将商品服务注册到Eureka中,所以,只需要从Eureka中发现服务即可。

第一步,在订单系统中添加依赖:

<?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>

    <groupId>com.zpc.microservice</groupId>
    <artifactId>microservice-order</artifactId>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.6.RELEASE</version>
    </parent>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Finchley.SR1</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--springboot 整合eureka客户端-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

        <dependency>
            <groupId>com.squareup.okhttp3</groupId>
            <artifactId>okhttp</artifactId>
            <version>3.9.0</version>
        </dependency>

    </dependencies>

    <build>
        <finalName>${project.artifactId}</finalName>
        <plugins>
            <!-- 资源文件拷贝插件 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-resources-plugin</artifactId>
                <configuration>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <!-- java编译插件 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

第二步,修改application.yml配置文件:

server:
  port: 8082 #服务端口
myspcloud:
  item:
    url: http://127.0.0.1:8081/item/
###起个名字作为服务名称(该服务注册到eureka注册中心的名称,比如订单服务)
spring:
    application:
        name: app-order
###服务注册到eureka注册中心的地址
eureka:
  client:
    service-url:
           defaultZone: http://127.0.0.1:8100/eureka
###因为该应用为服务提供者,是eureka的一个客户端,需要注册到注册中心
    register-with-eureka: true
###是否需要从eureka上检索服务
    fetch-registry: true

第三步,修改ItemService的实现逻辑:

package com.zpc.order.service;
import com.zpc.order.entity.Item;
import com.zpc.order.properties.OrderProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

@Service
public class ItemService {

    // Spring框架对RESTful方式的http请求做了封装,来简化操作
    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    OrderProperties orderProperties;

    public Item queryItemById(Long id) {

        // 该方法走eureka注册中心调用(去注册中心根据app-item查找服务,这种方式必须先开启负载均衡@LoadBalanced)
        String itemUrl = "http://app-item/item/{id}";
        Item result = restTemplate.getForObject(itemUrl, Item.class, id);
        System.out.println("订单系统调用商品服务,result:" + result);
        return result;
    }
}

第四步,在启动类中添加@EnableEurekaClient注解 ,获取RestTemplate的方法上加 @LoadBalanced注解

package com.zpc.order.runner;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.http.client.OkHttp3ClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

/**
 * @author Evan
 */

@SpringBootApplication//申明这是一个Spring Boot项目
@EnableEurekaClient
@ComponentScan(basePackages = {"com.zpc.order.controller", "com.zpc.order.service","com.zpc.order.properties"})//手动指定bean扫描范围
public class OrderApp {
    public static void main(String[] args) {
        SpringApplication.run(OrderApp.class, args);
    }

    /**
     * 向Spring容器中定义RestTemplate对象
     * @return
     */
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate(new OkHttp3ClientHttpRequestFactory());
    }

}

第五步,启动测试(此时有3个应用:Eureka注册中心、Item服务、order服务)
Spring Cloud微服务教程-第一篇_第16张图片
在注册中心http://localhost:8100看到有2个客户端。

你可能感兴趣的:(java,springcloud)