SpringCload入门操作

spring cloud 介绍

spring cloud 是一系列框架的集合。它利用 spring boot 的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用 spring boot 的开发风格做到一键启动和部署。spring cloud 并没有重复制造轮子,它只是将目前各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过 spring boot 风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。

spring cloud 对于中小型互联网公司来说是一种福音,因为这类公司往往没有实力或者没有足够的资金投入去开发自己的分布式系统基础设施,使用 spring cloud 一站式解决方案能在从容应对业务发展的同时大大减少开发成本。同时,随着近几年微服务架构和 docker 容器概念的火爆,也会让 spring cloud 在未来越来越“云”化的软件开发风格中立有一席之地,尤其是在目前五花八门的分布式解决方案中提供了标准化的、一站式的技术方案,意义可能会堪比当年 servlet 规范的诞生,有效推进服务端软件系统技术水平的进步。

spring cloud 技术组成

在这里插入图片描述

  1. eureka
    微服务治理,服务器注册和发现

  2. ribbon
    负载均衡,请求重试

  3. hystrix
    断路器,服务降级,熔断

  4. feign
    ribbon + hystrix 集成,并提供声明式客户端

  5. hystrix dashboard 和 turbine
    hystrix 数据监控

  6. zuul
    API 网关,提供微服务的统一入口,并提供统一的权限验证

  7. config
    配置中心

  8. bus
    消息总线, 配置刷新

  9. sleuth+zipkin
    链路跟踪

spring cloud 对比 Dubbo

在这里插入图片描述

Dubbo

  1. Dubbo只是一个远程调用(RPC)框架
    默认基于长连接,支持多种序列化格式

Spring Cloud

  1. 框架集
    提供了一整套微服务解决方案(全家桶)
    基于http调用, Rest API
    不是一个独立的框架

一.service - 服务

在这里插入图片描述

  • 商品服务 item service,端口 8001
  • 用户服务 user service,端口 8101
  • 订单服务 order service,端口 8201

在这里插入图片描述


二 、创建 commons 通用项目

新建空工程

在这里插入图片描述

新工程中创建maven模块

在这里插入图片描述

下载依赖
  1. 在这里插入图片描述
  2. 在这里插入图片描述
  3. 在这里插入图片描述
  4. 在这里插入图片描述
<?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>cn.tedu</groupId>
    <artifactId>sp01-commons</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
    <dependencies>
        <dependency>
            <groupId>com.fasterxml.jackson.module</groupId>
            <artifactId>jackson-module-parameter-names</artifactId>
            <version>2.9.8</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.datatype</groupId>
            <artifactId>jackson-datatype-jdk8</artifactId>
            <version>2.9.8</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.datatype</groupId>
            <artifactId>jackson-datatype-jsr310</artifactId>
            <version>2.9.8</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.datatype</groupId>
            <artifactId>jackson-datatype-guava</artifactId>
            <version>2.9.8</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.18</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.26</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.9</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.0</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>


</project>
下载IDEA插件
  • IDEA

  • Lombok

  • EditStarters

  • Free MyBatis Plugin

  • Maven Helper

创建源文件

在这里插入图片描述

pojo

快速复制一行代码快捷键 Ctrl+Alt+ ↓ 或 Ctrl + d

商品

@Data// 注在类上,提供类的get、set、equals、hashCode、canEqual、toString方法
@NoArgsConstructor//无参构造
@AllArgsConstructor//全参构造
public class Item {
    private Integer id;         //商品ID
    private String name;        //商品名称
    private Integer number;     //商品数量
}

lombok 插件 和lombok 版本的依赖需要匹配,
如果不匹配,代码无法生成。测试正常输出就🆗

//测试lombol插件
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Order {
    //快速赋值一行  Ctrl+Alt + ↓
    private String id;
    private List<Item> items; //订单中包含商品列表
    private User user;//订单所属用户


    public static void main(String[] args) {
        Item item=new Item();
        item.setId(111);
        System.out.println(item.getId());
    }
}

订单

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Order {
    //快速赋值一行  Ctrl+Alt + ↓
    private String id;
    private List<Item> items; //订单中包含商品列表
    private User user;//订单所属用户
}

用户

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private Integer id;
    private String username;
    private String password;
}
Service

ItemService

/**
 * 获取订单信息
 */
public interface ItemService {

    //获取订单的商品列表
    //需要去根据订单ID 获取订单的商品列表
    List<Item> getItems(String order);


    //减少商品库存
    void decreaseNumber(List<Item> items);
    //每下一次单,商品库存减一次
}


OrderService

public interface OrderService {
    //获取订单
    Order getOrder(String orderId);
    //添加订单
    void addOrder(Order order);
}

UserService

public interface UserService {
    //获取用户
    User getUser(Integer userId);

    //增加用户积分
    //每下一次单,用户增加一次积分
    void addScore(Integer userId,Integer score);
}

web.util 工具类

CookieUtil
作用:添加cookie,获取Cookie

package cn.tedu.web.util;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class CookieUtil {

    /**
     * @param response
     * @param name
     * @param value
     * @param maxAge
     */
    public static void setCookie(HttpServletResponse response,
                                 String name, String value, String domain, String path, int maxAge) {
        Cookie cookie = new Cookie(name, value);
        if(domain != null) {
            cookie.setDomain(domain);
        }
        cookie.setPath(path);
        cookie.setMaxAge(maxAge);
        response.addCookie(cookie);
    }
    public static void setCookie(HttpServletResponse response, String name, String value, int maxAge) {
        setCookie(response, name, value, null, "/", maxAge);
    }
    public static void setCookie(HttpServletResponse response, String name, String value) {
        setCookie(response, name, value, null, "/", 3600);
    }
    public static void setCookie(HttpServletResponse response, String name) {
        setCookie(response, name, "", null, "/", 3600);
    }

    /**
     * @param request
     * @param name
     * @return
     */
    public static String getCookie(HttpServletRequest request, String name) {
        String value = null;
        Cookie[] cookies = request.getCookies();
        if (null != cookies) {
            for (Cookie cookie : cookies) {
                if (cookie.getName().equals(name)) {
                    value = cookie.getValue();
                }
            }
        }
        return value;
    }

    /**
     * @param response
     * @param name
     * @return
     */
    public static void removeCookie(HttpServletResponse response, String name, String domain, String path) {
        setCookie(response, name, "", domain, path, 0);
    }

}

JsonResult
作用:错误处理类

package cn.tedu.web.util;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class JsonResult<T> {
    /** 成功 */
    public static final int SUCCESS = 200;

    /** 没有登录 */
    public static final int NOT_LOGIN = 400;

    /** 发生异常 */
    public static final int EXCEPTION = 401;

    /** 系统错误 */
    public static final int SYS_ERROR = 402;

    /** 参数错误 */
    public static final int PARAMS_ERROR = 403;

    /** 不支持或已经废弃 */
    public static final int NOT_SUPPORTED = 410;

    /** AuthCode错误 */
    public static final int INVALID_AUTHCODE = 444;

    /** 太频繁的调用 */
    public static final int TOO_FREQUENT = 445;

    /** 未知的错误 */
    public static final int UNKNOWN_ERROR = 499;

    private int code;
    private String msg;
    private T data;



    public static JsonResult build() {
        return new JsonResult();
    }
    public static JsonResult build(int code) {
        return new JsonResult().code(code);
    }
    public static JsonResult build(int code, String msg) {
        return new JsonResult<String>().code(code).msg(msg);
    }
    public static <T> JsonResult<T> build(int code, T data) {
        return new JsonResult<T>().code(code).data(data);
    }
    public static <T> JsonResult<T> build(int code, String msg, T data) {
        return new JsonResult<T>().code(code).msg(msg).data(data);
    }

    public JsonResult<T> code(int code) {
        this.code = code;
        return this;
    }
    public JsonResult<T> msg(String msg) {
        this.msg = msg;
        return this;
    }
    public JsonResult<T> data(T data) {
        this.data = data;
        return this;
    }


    public static JsonResult ok() {
        return build(SUCCESS);
    }
    public static JsonResult ok(String msg) {
        return build(SUCCESS, msg);
    }
    public static <T> JsonResult<T> ok(T data) {
        return build(SUCCESS, data);
    }
    public static JsonResult err() {
        return build(EXCEPTION);
    }
    public static JsonResult err(String msg) {
        return build(EXCEPTION, msg);
    }

    @Override
    public String toString() {
        return JsonUtil.to(this);
    }
}

JsonUtil
作用:处理json的格式转化

package cn.tedu.web.util;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Writer;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang3.StringUtils;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.datatype.guava.GuavaModule;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class JsonUtil {
    private static ObjectMapper mapper;
    private static JsonInclude.Include DEFAULT_PROPERTY_INCLUSION = JsonInclude.Include.NON_DEFAULT;
    private static boolean IS_ENABLE_INDENT_OUTPUT = false;
    private static String CSV_DEFAULT_COLUMN_SEPARATOR = ",";
    static {
        try {
            initMapper();
            configPropertyInclusion();
            configIndentOutput();
            configCommon();
        } catch (Exception e) {
            log.error("jackson config error", e);
        }
    }

    private static void initMapper() {
        mapper = new ObjectMapper();
    }

    private static void configCommon() {
        config(mapper);
    }

    private static void configPropertyInclusion() {
        mapper.setSerializationInclusion(DEFAULT_PROPERTY_INCLUSION);
    }

    private static void configIndentOutput() {
        mapper.configure(SerializationFeature.INDENT_OUTPUT, IS_ENABLE_INDENT_OUTPUT);
    }

    private static void config(ObjectMapper objectMapper) {
        objectMapper.enable(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN);
        objectMapper.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);
        objectMapper.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
        objectMapper.enable(DeserializationFeature.FAIL_ON_READING_DUP_TREE_KEY);
        objectMapper.enable(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS);
        objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
        objectMapper.disable(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES);
        objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
        objectMapper.enable(JsonParser.Feature.ALLOW_COMMENTS);
        objectMapper.disable(JsonGenerator.Feature.ESCAPE_NON_ASCII);
        objectMapper.enable(JsonGenerator.Feature.IGNORE_UNKNOWN);
        objectMapper.enable(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES);
        objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
        objectMapper.enable(JsonParser.Feature.ALLOW_SINGLE_QUOTES);
        objectMapper.registerModule(new ParameterNamesModule());
        objectMapper.registerModule(new Jdk8Module());
        objectMapper.registerModule(new JavaTimeModule());
        objectMapper.registerModule(new GuavaModule());
    }
    public static void setSerializationInclusion(JsonInclude.Include inclusion) {
        DEFAULT_PROPERTY_INCLUSION = inclusion;
        configPropertyInclusion();
    }

    public static void setIndentOutput(boolean isEnable) {
        IS_ENABLE_INDENT_OUTPUT = isEnable;
        configIndentOutput();
    }

    public static <V> V from(URL url, Class<V> c) {
        try {
            return mapper.readValue(url, c);
        } catch (IOException e) {
            log.error("jackson from error, url: {}, type: {}", url.getPath(), c, e);
            return null;
        }
    }

    public static <V> V from(InputStream inputStream, Class<V> c) {
        try {
            return mapper.readValue(inputStream, c);
        } catch (IOException e) {
            log.error("jackson from error, type: {}", c, e);
            return null;
        }
    }

    public static <V> V from(File file, Class<V> c) {
        try {
            return mapper.readValue(file, c);
        } catch (IOException e) {
            log.error("jackson from error, file path: {}, type: {}", file.getPath(), c, e);
            return null;
        }
    }

    public static <V> V from(Object jsonObj, Class<V> c) {
        try {
            return mapper.readValue(jsonObj.toString(), c);
        } catch (IOException e) {
            log.error("jackson from error, json: {}, type: {}", jsonObj.toString(), c, e);
            return null;
        }
    }

    public static <V> V from(String json, Class<V> c) {
        try {
            return mapper.readValue(json, c);
        } catch (IOException e) {
            log.error("jackson from error, json: {}, type: {}", json, c, e);
            return null;
        }
    }

    public static <V> V from(URL url, TypeReference<V> type) {
        try {
            return mapper.readValue(url, type);
        } catch (IOException e) {
            log.error("jackson from error, url: {}, type: {}", url.getPath(), type, e);
            return null;
        }
    }

    public static <V> V from(InputStream inputStream, TypeReference<V> type) {
        try {
            return mapper.readValue(inputStream, type);
        } catch (IOException e) {
            log.error("jackson from error, type: {}", type, e);
            return null;
        }
    }

    public static <V> V from(File file, TypeReference<V> type) {
        try {
            return mapper.readValue(file, type);
        } catch (IOException e) {
            log.error("jackson from error, file path: {}, type: {}", file.getPath(), type, e);
            return null;
        }
    }

    public static <V> V from(Object jsonObj, TypeReference<V> type) {
        try {
            return mapper.readValue(jsonObj.toString(), type);
        } catch (IOException e) {
            log.error("jackson from error, json: {}, type: {}", jsonObj.toString(), type, e);
            return null;
        }
    }

    public static <V> V from(String json, TypeReference<V> type) {
        try {
            return mapper.readValue(json, type);
        } catch (IOException e) {
            log.error("jackson from error, json: {}, type: {}", json, type, e);
            return null;
        }
    }

    public static <V> String to(List<V> list) {
        try {
            return mapper.writeValueAsString(list);
        } catch (JsonProcessingException e) {
            log.error("jackson to error, obj: {}", list, e);
            return null;
        }
    }

    public static <V> String to(V v) {
        try {
            return mapper.writeValueAsString(v);
        } catch (JsonProcessingException e) {
            log.error("jackson to error, obj: {}", v, e);
            return null;
        }
    }

    public static <V> void toFile(String path, List<V> list) {
        try (Writer writer = new FileWriter(new File(path), true)) {
            mapper.writer().writeValues(writer).writeAll(list);
            writer.flush();
        } catch (Exception e) {
            log.error("jackson to file error, path: {}, list: {}", path, list, e);
        }
    }

    public static <V> void toFile(String path, V v) {
        try (Writer writer = new FileWriter(new File(path), true)) {
            mapper.writer().writeValues(writer).write(v);
            writer.flush();
        } catch (Exception e) {
            log.error("jackson to file error, path: {}, obj: {}", path, v, e);
        }
    }

    public static String getString(String json, String key) {
        if (StringUtils.isEmpty(json)) {
            return null;
        }
        try {
            JsonNode node = mapper.readTree(json);
            if (null != node) {
                return node.get(key).toString();
            } else {
                return null;
            }
        } catch (IOException e) {
            log.error("jackson get string error, json: {}, key: {}", json, key, e);
            return null;
        }
    }

    public static Integer getInt(String json, String key) {
        if (StringUtils.isEmpty(json)) {
            return null;
        }
        try {
            JsonNode node = mapper.readTree(json);
            if (null != node) {
                return node.get(key).intValue();
            } else {
                return null;
            }
        } catch (IOException e) {
            log.error("jackson get int error, json: {}, key: {}", json, key, e);
            return null;
        }
    }

    public static Long getLong(String json, String key) {
        if (StringUtils.isEmpty(json)) {
            return null;
        }
        try {
            JsonNode node = mapper.readTree(json);
            if (null != node) {
                return node.get(key).longValue();
            } else {
                return null;
            }
        } catch (IOException e) {
            log.error("jackson get long error, json: {}, key: {}", json, key, e);
            return null;
        }
    }

    public static Double getDouble(String json, String key) {
        if (StringUtils.isEmpty(json)) {
            return null;
        }
        try {
            JsonNode node = mapper.readTree(json);
            if (null != node) {
                return node.get(key).doubleValue();
            } else {
                return null;
            }
        } catch (IOException e) {
            log.error("jackson get double error, json: {}, key: {}", json, key, e);
            return null;
        }
    }

    public static BigInteger getBigInteger(String json, String key) {
        if (StringUtils.isEmpty(json)) {
            return new BigInteger(String.valueOf(0.00));
        }
        try {
            JsonNode node = mapper.readTree(json);
            if (null != node) {
                return node.get(key).bigIntegerValue();
            } else {
                return null;
            }
        } catch (IOException e) {
            log.error("jackson get biginteger error, json: {}, key: {}", json, key, e);
            return null;
        }
    }

    public static BigDecimal getBigDecimal(String json, String key) {
        if (StringUtils.isEmpty(json)) {
            return null;
        }
        try {
            JsonNode node = mapper.readTree(json);
            if (null != node) {
                return node.get(key).decimalValue();
            } else {
                return null;
            }
        } catch (IOException e) {
            log.error("jackson get bigdecimal error, json: {}, key: {}", json, key, e);
            return null;
        }
    }

    public static boolean getBoolean(String json, String key) {
        if (StringUtils.isEmpty(json)) {
            return false;
        }
        try {
            JsonNode node = mapper.readTree(json);
            if (null != node) {
                return node.get(key).booleanValue();
            } else {
                return false;
            }
        } catch (IOException e) {
            log.error("jackson get boolean error, json: {}, key: {}", json, key, e);
            return false;
        }
    }

    public static byte[] getByte(String json, String key) {
        if (StringUtils.isEmpty(json)) {
            return null;
        }
        try {
            JsonNode node = mapper.readTree(json);
            if (null != node) {
                return node.get(key).binaryValue();
            } else {
                return null;
            }
        } catch (IOException e) {
            log.error("jackson get byte error, json: {}, key: {}", json, key, e);
            return null;
        }
    }

    public static <T> ArrayList<T> getList(String json, String key) {
        if (StringUtils.isEmpty(json)) {
            return null;
        }
        String string = getString(json, key);
        return from(string, new TypeReference<ArrayList<T>>() {});
    }

    public static <T> String add(String json, String key, T value) {
        try {
            JsonNode node = mapper.readTree(json);
            add(node, key, value);
            return node.toString();
        } catch (IOException e) {
            log.error("jackson add error, json: {}, key: {}, value: {}", json, key, value, e);
            return json;
        }
    }

    private static <T> void add(JsonNode jsonNode, String key, T value) {
        if (value instanceof String) {
            ((ObjectNode) jsonNode).put(key, (String) value);
        } else if (value instanceof Short) {
            ((ObjectNode) jsonNode).put(key, (Short) value);
        } else if (value instanceof Integer) {
            ((ObjectNode) jsonNode).put(key, (Integer) value);
        } else if (value instanceof Long) {
            ((ObjectNode) jsonNode).put(key, (Long) value);
        } else if (value instanceof Float) {
            ((ObjectNode) jsonNode).put(key, (Float) value);
        } else if (value instanceof Double) {
            ((ObjectNode) jsonNode).put(key, (Double) value);
        } else if (value instanceof BigDecimal) {
            ((ObjectNode) jsonNode).put(key, (BigDecimal) value);
        } else if (value instanceof BigInteger) {
            ((ObjectNode) jsonNode).put(key, (BigInteger) value);
        } else if (value instanceof Boolean) {
            ((ObjectNode) jsonNode).put(key, (Boolean) value);
        } else if (value instanceof byte[]) {
            ((ObjectNode) jsonNode).put(key, (byte[]) value);
        } else {
            ((ObjectNode) jsonNode).put(key, to(value));
        }
    }

    public static String remove(String json, String key) {
        try {
            JsonNode node = mapper.readTree(json);
            ((ObjectNode) node).remove(key);
            return node.toString();
        } catch (IOException e) {
            log.error("jackson remove error, json: {}, key: {}", json, key, e);
            return json;
        }
    }

    public static <T> String update(String json, String key, T value) {
        try {
            JsonNode node = mapper.readTree(json);
            ((ObjectNode) node).remove(key);
            add(node, key, value);
            return node.toString();
        } catch (IOException e) {
            log.error("jackson update error, json: {}, key: {}, value: {}", json, key, value, e);
            return json;
        }
    }

    public static String format(String json) {
        try {
            JsonNode node = mapper.readTree(json);
            return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(node);
        } catch (IOException e) {
            log.error("jackson format json error, json: {}", json, e);
            return json;
        }
    }

    public static boolean isJson(String json) {
        try {
            mapper.readTree(json);
            return true;
        } catch (Exception e) {
            log.error("jackson check json error, json: {}", json, e);
            return false;
        }
    }

    private static InputStream getResourceStream(String name) {
        return JsonUtil.class.getClassLoader().getResourceAsStream(name);
    }

    private static InputStreamReader getResourceReader(InputStream inputStream) {
        if (null == inputStream) {
            return null;
        }
        return new InputStreamReader(inputStream, StandardCharsets.UTF_8);
    }
}

三 、创建 itemservice 商品服务

  1. 新建项目
  2. 配置依赖pom.xml
  3. 配置application.yml
  4. 配置主程序
  5. 编写代码
新建spring boot 起步项目

在这里插入图片描述

选择依赖项

只选择 web 依赖
在这里插入图片描述

配置pom文件

填加 sp01-commons 项目依赖
在 标签内部,直接输入 sp01 回车就可以添加

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.9.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>cn.tedu</groupId>
    <artifactId>sp02-itemservice</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>sp02-itemservice</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <!-- 添加父依赖 直接输入模块名  -->
        <dependency>
            <groupId>cn.tedu</groupId>
            <artifactId>sp01-commons</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <!-- Alt+Insert 选择 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

    </dependencies>


    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Hoxton.SR10</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

</project>

配置yml文件

修改后缀为 yml
在这里插入图片描述

配置信息
# yml - yaml   夜毛
# yet another markup language

#快捷输方式  app name
#向注册中心注册的“服务ID”
spring:
  application:
    name: item-service

#设置端口号
#商品ID:8001
#用户ID:8101
#订单ID:8201
server:
  port: 8001

注意:填写yml文件时添加中文需要查看字符编码 UTF8 否则会形成乱码
在这里插入图片描述

主程序

编写service

package cn.tedu.sp02.item.service;

import cn.tedu.sp01.pojo.Item;
import cn.tedu.sp01.service.ItemService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

@Service
@Slf4j//生成一个日志对象 lombok注解
public class ItemServiceImpl implements ItemService {

    @Override
    public List<Item> getItems(String order) {
        //创建集合向集合中添加商品
        List <Item> list =new ArrayList<>();
        list.add(new Item(1, "商品1", 1));
        list.add(new Item(2, "商品2", 4));
        list.add(new Item(3, "商品3", 2));
        list.add(new Item(4, "商品4", 6));
        list.add(new Item(5, "商品5", 1));
        return list;
    }

    @Override
    public void decreaseNumber(List<Item> items) {
        //参数名.for 快速生成循环
        for (Item item : items) {
            log.info("减少库存"+item);
        }
    }
}

创建controller

package cn.tedu.sp02.controller;

import cn.tedu.sp01.pojo.Item;
import cn.tedu.sp01.service.ItemService;
import cn.tedu.web.util.JsonResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@Slf4j
public class ItemController {

    @Autowired
    private ItemService itemService;

    @GetMapping("/{orderId}")
    private JsonResult<List<Item>> getItem(@PathVariable String orderId){
        log.info("获取商品,orderId="+orderId);
        List<Item> items = itemService.getItems(orderId);
        return JsonResult.ok().data(items);
    }

    /**
     *  @RequestBody 注解
     *  从客户端提交的“请求协议体”中,完整接收协议体数据(Json格式),转换成商品列表
     */
    @PostMapping("/decreaseNumber")
    public JsonResult<?>  decreaseNumber(@RequestBody List<Item> items){
        itemService.decreaseNumber(items);
        return JsonResult.ok().msg("减少库存成功");
    }
}
Spring Mvc 接收参数的几个注解

在这里插入图片描述

访问测试

根据orderId 来查询商品

http:lcoalhost:8001/35

减少商品库存
因为时post 请求 那么使用postman来测试

http://localhost:8001/decreaseNumber

参数:

    [
    {"id":1, "name":"abc", "number":23},
    {"id":2, "name":"def", "number":11}
    ]

在这里插入图片描述

四、user service 用户服务

  1. 新建项目
  2. 配置依赖 pom.xml
  3. 配置 application.yml
  4. 配置主程序
  5. 编写代码
新建SpringBoot起步项目

在这里插入图片描述

选择依赖项

在这里插入图片描述

pom.xml文件

需要添加 sp01-commons 项目依赖

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.9.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>cn.tedu</groupId>
    <artifactId>sp03-userservice</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>sp03-userservice</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>

        </dependency>
        <!-- 添加通用项目依赖 -->
        <dependency>
            <groupId>cn.tedu</groupId>
            <artifactId>sp01-commons</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Hoxton.SR10</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

</project>

application.yml
  1. 其中sp.user-service-users 属性为自定义属性,提供于测试的用户数据
# 向注册中心注册的服务ID
spring:
  application:
    name: user-service

server:
  port: 8101

#自定义配置属性
# 测试用的  用户数数据
# sp.user-service.users=[{id:6,username:aa,password:bb},{},{}]
sp:
  user-service:
    users: "[{'id':7,'username':'aa','password':'bb'},
             {'id':8,'username':'aa','password':'45'},
             {'id':9,'username':'aa','password':'56'}]"
主程序

默认程序,不需要修改代码

package cn.tedu.sp03;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Sp03UserserviceApplication {

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

}

UserServiceImpl
package cn.tedu.sp03.user.service;

import cn.tedu.sp01.pojo.Item;
import cn.tedu.sp01.pojo.User;
import cn.tedu.sp01.service.UserService;
import cn.tedu.web.util.JsonUtil;
import com.fasterxml.jackson.core.type.TypeReference;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.List;
/**
 * 在 yml 中配置自定义数据
 * 使用@Value获取
 */
@Service
@Slf4j
public class UserServiceImpl implements UserService {

    @Value("${sp.user-service.users}")
    private String userJson;

    @Override
    public User getUser(Integer userId) {
        //根据用户Id进行查询
        //userJson进行转换 ->List<User>
        //将json 转换成List集合
        List<User> list = JsonUtil.from(userJson, new TypeReference<List<User>>() {
        });
        for (User u: list) {
            if(u.getId().equals(userId)){
                return u;
            }
        }
        //如果找不到用户,这里返回一个假的用户数据
        return new User(userId,"用户名"+userId,"密码"+userId);
    }

    @Override
    public void addScore(Integer userId, Integer score) {
        log.info("增加用户积分:userId="+userId+",score="+score);
    }
}

UserController
package cn.tedu.sp03.user.controller;


import cn.tedu.sp01.pojo.User;
import cn.tedu.sp01.service.UserService;
import cn.tedu.web.util.JsonResult;
import lombok.extern.slf4j.Slf4j;
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
@Slf4j
public class UserController {
    @Autowired
    private UserService userService;

    @GetMapping("/{userId}")
    public JsonResult<User> getUser(@PathVariable Integer userId){
        User user = userService.getUser(userId);
        return JsonResult.ok().data(user);
    }

    @GetMapping("/{userId}/score")
    public JsonResult addScore(
            @PathVariable Integer userId, Integer score) {
        userService.addScore(userId, score);
        return JsonResult.ok().msg("增加用户积分成功");
    }

}

目录格式
在这里插入图片描述

访问测试
  1. 根据用户id查询用户信息
    http://localhost:8101/7
  2. 根据userid,为用户增加积分
    http://localhost:8101/7/score?score=100

五、Order Service

  1. 新建项目
  2. 配置依赖 pom.xml
  3. 配置application.yum文件
  4. 配置主程序
  5. 编写代码
新建SpringBoot起步项目

在这里插入图片描述

添加web依赖

在这里插入图片描述

pom.xml

需要添加 sp01-commons 项目依赖

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.9.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>cn.tedu</groupId>
    <artifactId>sp04-orderservice</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>sp04-orderservice</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <!-- 添加依赖 -->
        <dependency>
            <groupId>cn.tedu</groupId>
            <artifactId>sp01-commons</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Hoxton.SR10</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

</project>

application.yml
#端口名
spring:
  application:
    name: order-service
# 设置端口号
server:
  port: 8201
主程序

代码默认

package cn.tedu.sp04;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Sp04OrderserviceApplication {

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

}

Order Service
package cn.tedu.sp04.order.service;

import cn.tedu.sp01.pojo.Order;
import cn.tedu.sp01.service.OrderService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

@Service
@Slf4j
public class OrderServiceImpl implements OrderService {

    @Override
    public Order getOrder(String orderId) {
        log.info("获取订单,orderId="+orderId);
        //TODO: 远程调用user-service获取用户信息
        //TODO: 远程调用item-service获取商品信息
        Order order = new Order();
        order.setId(orderId);
        //order.setItems();
        //order.setUser();
        return order;
    }

    @Override
    public void addOrder(Order order) {
        //TODO: 远程调用item-service减少商品库存
        //TODO: 远程调用user-service增加用户积分
        log.info("保存订单:"+order);
    }
}

OrderController
package cn.tedu.sp04.order.controller;

import cn.tedu.sp01.pojo.Item;
import cn.tedu.sp01.pojo.Order;
import cn.tedu.sp01.pojo.User;
import cn.tedu.sp01.service.OrderService;
import cn.tedu.web.util.JsonResult;
import lombok.extern.slf4j.Slf4j;
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;

import java.util.Arrays;
import java.util.UUID;

@RestController
@Slf4j
public class Controller {

    @Autowired
    private OrderService orderService;

    @GetMapping("/{orderId}")
    public JsonResult<Order> getOrder(@PathVariable String orderId){
        log.info("get order, id="+orderId);
        Order order = orderService.getOrder(orderId);
        return JsonResult.ok(order);
    }
    @GetMapping("/")
    public JsonResult<?> addOrder() {
        Order order = new Order();
        order.setId(UUID.randomUUID().toString());
        order.setItems(Arrays.asList(new Item[]{
                new Item(1, "商品1", 1),
                new Item(2, "商品2", 6),
                new Item(3, "商品3", 2),
                new Item(4, "商品4", 4),
                new Item(5, "商品5", 1)
        }));
        order.setUser(new User(8,null,null));
        orderService.addOrder(order);
        return JsonResult.ok().msg("添加订单成功");
    }


}

访问测试
  1. 根据orderId,获取订单
    http://localhost:8201/123abc
  2. 保存订单,观察控制台日志输出
    http://localhost:8201/

六.service访问测试汇总

item-service
根据orderid,查询商品
http://localhost:8001/35

减少商品库存
http://localhost:8001/decreaseNumber

使用postman,POST发送以下格式数据:
[{“id”:1, “name”:“abc”, “number”:23},{“id”:2, “name”:“def”, “number”:11}]

user-service
根据userid查询用户信息
http://localhost:8101/7

根据userid,为用户增加积分
http://localhost:8101/7/score?score=100

order-service
根据orderid,获取订单
http://localhost:8201/123abc

保存订单,观察控制台日志输出
http://localhost:8201/

七.eureka 注册与发现

在这里插入图片描述

  1. 创建eureka项目
  2. 配置依赖 pom.xml
  3. 配置 application.yml
  4. 主程序启动eureka 服务器
  5. 启动访问测试
创建eureka server 项目:sp05-eureka
创建springboot eureka

在这里插入图片描述

添加 Eureka Service 依赖

在这里插入图片描述

配置yml 文件

spring:
  application:
    name: eureka-server

server:
  port: 2001

eureka:
  instance:
    # 配置主机名,集群中使用主机名进行互相区分
    hostname: eureka1
  client:
  #只针对单台服务器
  #不注册
    register-with-eureka: false
    #不拉取
    fetch-registry: false
  server:
    #禁用自我保护模式(默认是打开的)
    enable-self-preservation: false

在这里插入图片描述

配置pom.xml文件
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.9.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>cn.tedu</groupId>
    <artifactId>sp05-eureka</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>sp05-eureka</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
        <!-- 此处和jar的版本相匹配 2.3.9 匹配 Hoxton.SR10 -->
        <spring-cloud.version>Hoxton.SR10</spring-cloud.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

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

</project>

配置启动类

添加:@EnableEurekaServer

package cn.tedu.sp05;

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

@EnableEurekaServer//通过注解触发自动配置
@SpringBootApplication
public class Sp05EurekaApplication {

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

}

修改hosts文件,添加eureka 域名映射

本地磁盘路径:C:\Windows\System32\drivers\etc\hosts

添加内容:

127.0.0.1       eureka1
127.0.0.1       eureka2
启动访问测试

http://eureka1:2001
在这里插入图片描述

八 ,添加客户端服务器

修改 item-service、user-service、order-service,把微服务注册到 eureka 服务器

  1. 依次在item-service、user-service、order-service的pom.xml文件中添加eureka依赖

使用插件 EditStarters 快捷键: Alt + Insert
在这里插入图片描述

第一段
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

第二段
<dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Hoxton.SR10</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
  1. 依次在item-service、user-service、order-service的 yml中添加配置信息
# defaultZone - 默认地点
# 如果使用云服务,可以有服务商提供不同地点的服务器
eureka:
  client:
    service-url:
    #主机地址
      defaultZone: http://eureka1:2001/eureka

之后在IDEA 中先启动服务器,之后 启动配置

测试连接:http://eureka1:2001

在这里插入图片描述

九 ,eureka 和 “服务提供者的高可用”

在这里插入图片描述

使用 item-sercvice 举例

启动参数--server.port 可以覆盖yml中的端口配置

配置启动参数

item-service-8001

  1. --server.port=8001
    在这里插入图片描述

在这里插入图片描述

item-service-8002

  1. --server.port=8002

在这里插入图片描述

在这里插入图片描述http://localhost:8001/35

分别启动两个项目进行测试

访问 eureka 查看 item-service 注册信息
在这里插入图片描述
访问两个端口进行测试
http://localhost:8001/35
http://localhost:8002/35

eureka 高可用

添加两个服务器的 配置文件

在项目模块中新建两个配置文件
在这里插入图片描述

application-eureka1.yml
# application-eureka1.yml
eureka:
  instance:
    # 配置主机名,集群中使用主机名进行互相拆分
    hostname: eureka1
  client:
    register-with-eureka: true
    fetch-registry: true
    #主机之间进行互联 1 连接 2
    service-url:
      defaultZone: http://eureka2:2002/eureka

application-eureka2.yml
# application-eureka2.yml
eureka:
  instance:
    # 配置主机名,集群中使用主机名进行互相拆分
    hostname: eureka2
  client:
    register-with-eureka: true
    fetch-registry: true
    #2 连接 1
    service-url:
      defaultZone: http://eureka1:2001/eureka
配置启动时的参数

eureka1 启动参数:

1. --spring.profiles.active=eureka1 --server.port=2001
在这里插入图片描述
eureka2 启动参数:

1. --spring.profiles.active=eureka2 --server.port=2002
在这里插入图片描述

如果在命令行运行(CMD),可以在命令行中添加参数:
java -jar xxx.jar --spring.profiles.active=eureka1 --server.port=2001

访问 eureka 服务器,查看注册信息

http:eureka1:2001/
在这里插入图片描述
http:eureka1:2002/
在这里插入图片描述

eureka客户端注册时,同时向两个服务器注册

修改以下微服务

  1. sp02-itemservice
  2. sp03-userservice
  3. sp04-orderservice
修改application.yml
eureka:
  client:
    service-url:
      defaultZone: http://eureka1:2001/eureka, http://eureka2:2002/eureka

作用:eureka服务器宕机时,可以通过其他服务器进行访问

十 、ribbon 服务消费者

在这里插入图片描述
ribbon 提供了负载均衡和重试功能,它底层是使用RestTemplate 进行 Rest api 调用

RestTemplate

RestTemplate 是SpringBoot提供的一个Rest远程调用工具

Spring提供的一个rest api 调用公聚对rest调用做出的高度封装,暴露了非常简便的方法,来做rest远程调用:

常用方法:

getForObject( url:调用的远程路径 ,要转换的类型.class,提交的参数数据 );

postForObject(url:调用的远程路径 ,协议提数据,转换的类型)

高可用之前系统结构是通过浏览器直接访问
在这里插入图片描述

后面我们通过一个Demo项目演示 Spring Cloud 进行远程的调用

在这里插入图片描述

新建 sp06-ribbon项目进行测试
  1. 新建项目
  2. pom.xml
  3. application.yml
  4. 主程序
  5. controller
  6. 启动,并访问测试
新建SpringBoot sp06-ribbon 项目
  1. 项目名、依赖、jdk版本…
    在这里插入图片描述
  2. 添加依赖
    在这里插入图片描述
pom.xml

eureka-client中包含ribbon依赖
添加 sp01-commons依赖

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.9.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>cn.tedu</groupId>
    <artifactId>sp06-ribbon</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>sp06-ribbon</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Hoxton.SR10</spring-cloud.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>cn.tedu</groupId>
            <artifactId>sp01-commons</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <!-- 添加spring retry 依赖 -->

        <dependency>
            <groupId>org.springframework.retry</groupId>
            <artifactId>spring-retry</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

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

</project>

application.yml
#06 项目知识一个功能测试项目
# 测试 ribbon.hystrix 的功能
# 两个工具类测试完成后这个项目会删除
spring:
  application:
    name: ribbon
server:
  port: 3001
eureka:
  client:
    service-url:
      defaultZone: http://eureka1:2001/eureka, http://eureka2:2002/eureka

主程序

创建 RestTemplate实例

RestTemplate 是用来调用其他微服务的工具类,封装了远程调用代码,提供了一组用于远程调用的模板方法,例如:getForObject()postForObject()等…

  1. 创建RestTemplate实例
package cn.tedu.sp06;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
public class Sp06RibbonApplication {

    public static void main(String[] args) {
        SpringApplication.run(Sp06RibbonApplication.class, args);
    }
    //创建 RestTemplate 实例 并存入 spring 容器
    @Bean
    public RestTemplate restTemplate(){
        SimpleClientHttpRequestFactory f = new SimpleClientHttpRequestFactory();
        f.setReadTimeout(1000);
        return new RestTemplate();
    }

}
RibbonController
package cn.tedu.sp06.controller;

import cn.tedu.sp01.pojo.Item;
import cn.tedu.sp01.pojo.Order;
import cn.tedu.sp01.pojo.User;
import cn.tedu.web.util.JsonResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;

import java.util.List;

@RestController
@Slf4j
public class RilbbonController {

    @Autowired
    private RestTemplate rt;

    @GetMapping("/item-service/{orderId}")
    public JsonResult<List<Item>>  decreaseNumber(@PathVariable String orderId){
        //向指定微服务地址发送get请求,并获得该服务的返回结果
        //{1} 表示占位符,用orderId填充
        return rt.getForObject("http://localhost:8001/{1}",JsonResult.class,orderId );
    }

    @PostMapping("/item-service/decreaseNumber")
    public JsonResult<?> decreaseNumber(@RequestBody List<Item> items){
        return rt.postForObject("http://localhost:8001/decreaseNumber", items, JsonResult
        .class);
    }
    

    @GetMapping("/user-service/{userId}")
    public JsonResult<User> getUser(@PathVariable Integer userId) {
        return rt.getForObject("http://localhost:8101/{1}", JsonResult.class, userId);
    }

    @GetMapping("/user-service/{userId}/score")
    public JsonResult addScore(
            @PathVariable Integer userId, Integer score) {
        return rt.getForObject("http://localhost:8101/{1}/score?score={2}", JsonResult.class, userId, score);
    }

	

    @GetMapping("/order-service/{orderId}")
    public JsonResult<Order> getOrder(@PathVariable String orderId) {
        return rt.getForObject("http://localhost:8201/{1}", JsonResult.class, orderId);
    }

    @GetMapping("/order-service")
    public JsonResult addOrder() {
        return rt.getForObject("http://localhost:8201/", JsonResult.class);
    }

}

启动服务,并访问测试

在这里插入图片描述

  1. http://localhost:3001/item-service/35
  2. http://localhost:3001/item-service/decreaseNumber
    使用postman,POST请求发送以下格式数据:
    [{"id":1, "name":"abc", "number":23},{"id":2, "name":"def", "number":11}]
  3. http://localhost:3001/user-service/7
  4. http://localhost:3001/user-service/7/score?score=100
  5. http://localhost:3001/order-service/123abc
  6. http://localhost:3001/order-service/
十一、ribbon 父在均衡和重试
Ribbon

在这里插入图片描述

Spring Cloud 提供的工具。
Ribbon在RestTemplate的基础上进行了功能增强,添加了负载均衡重试的功能。

负载均衡

负载均衡:

  1. 对RestTemplate进行增强,在实例化RestTemplate的位置增加注解@LadBalanced注解
  2. 调用的地址改成服务id service-id
检查pom中是否添加起步依赖

eureka 依赖中已经包含了 ribbon

<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
RestTemplate 设置 @LoadBalanced

@LoadBalanced负载均衡注解,会对 RestTemplate 实例进行封装,创建动态代理对象,并切入(AOP)负载均衡代码,把请求分发到集群中的服务器

启动类

package cn.tedu.sp06;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
public class Sp06RibbonApplication {

    public static void main(String[] args) {
        SpringApplication.run(Sp06RibbonApplication.class, args);
    }
    //创建 RestTemplate 实例 并存入 spring 容器
    @LoadBalanced
    @Bean
    public RestTemplate restTemplate(){
        SimpleClientHttpRequestFactory f = new SimpleClientHttpRequestFactory();
        f.setReadTimeout(1000);
        return new RestTemplate();
    }

}

访问路径设置为服务id
package cn.tedu.sp06.controller;

import cn.tedu.sp01.pojo.Item;
import cn.tedu.sp01.pojo.Order;
import cn.tedu.sp01.pojo.User;
import cn.tedu.web.util.JsonResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;

import java.util.List;

@RestController
@Slf4j
public class RilbbonController {
    @Autowired
    private RestTemplate rt;

    @GetMapping("/item-service/{orderId}")
    public JsonResult<List<Item>> getItems(@PathVariable String orderId) {
        //这里服务器路径用 service-id 代替,ribbon 会向服务的多台集群服务器分发请求
        return rt.getForObject("http://item-service/{1}", JsonResult.class, orderId);
    }

    @PostMapping("/item-service/decreaseNumber")
    public JsonResult decreaseNumber(@RequestBody List<Item> items) {
        return rt.postForObject("http://item-service/decreaseNumber", items, JsonResult.class);
    }

	

    @GetMapping("/user-service/{userId}")
    public JsonResult<User> getUser(@PathVariable Integer userId) {
        return rt.getForObject("http://user-service/{1}", JsonResult.class, userId);
    }

    @GetMapping("/user-service/{userId}/score")
    public JsonResult addScore(
            @PathVariable Integer userId, Integer score) {
        return rt.getForObject("http://user-service/{1}/score?score={2}", JsonResult.class, userId, score);
    }
	
	

    @GetMapping("/order-service/{orderId}")
    public JsonResult<Order> getOrder(@PathVariable String orderId) {
        return rt.getForObject("http://order-service/{1}", JsonResult.class, orderId);
    }

    @GetMapping("/order-service")
    public JsonResult addOrder() {
        return rt.getForObject("http://order-service/", JsonResult.class);
    }
}

访问itemservice-ItemController进行测试
  1. 在ItemController 添加获取当前端口号的功能
  2. GET方法返回值处程序运行成功后将端口号进行即时的返回
package cn.tedu.sp02.controller;

import cn.tedu.sp01.pojo.Item;
import cn.tedu.sp01.service.ItemService;
import cn.tedu.web.util.JsonResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.Random;

@RestController
@Slf4j
public class ItemController {

    @Autowired
    private ItemService itemService;

    //获取访问 端口号
    @Value("${server.port}")
    private int port;

    @GetMapping("/{orderId}")
    private JsonResult<List<Item>> getItem(@PathVariable String orderId) throws InterruptedException {
        log.info("获取商品,orderId="+orderId);
        List<Item> items = itemService.getItems(orderId);
        //输出程序运行结果,参数数据,端口号。
        return JsonResult.ok().data(items).msg("port="+port);
    }

    /**
     *  @RequestBody 注解
     *  从客户端提交的“请求协议体”中,完整接收协议体数据(Json格式),转换成商品列表
     */
    @PostMapping("/decreaseNumber")
    public JsonResult<?>  decreaseNumber(@RequestBody List<Item> items){
        itemService.decreaseNumber(items);
        return JsonResult.ok().msg("减少库存成功");
    }
}
测试

**URL:**http://localhost:3001/item-service/66

在这里插入图片描述
在这里插入图片描述

ribbon重试

在这里插入图片描述

pom中添加 spring-retry依赖

依赖

<dependency>
	<groupId>org.springframework.retry</groupId>
	<artifactId>spring-retry</artifactId>
</dependency>
application.yml 配置
spring:
  application:
    name: ribbon
    
server:
  port: 3001
  
eureka:
  client:    
    service-url:
      defaultZone: http://eureka1:2001/eureka, http://eureka2:2002/eureka
      
ribbon:
  MaxAutoRetriesNextServer: 2
  MaxAutoRetries: 1
  OkToRetryOnAllOperations: true

OkToRetryOnAllOperations: true 默认只对GET请求重试,当是指true时,对POST等所有类型请求都重试。
MaxAutoRetries: 1当前实例重试次数,尝试失败会更换下一个实例。
MaxAutoRetriesNextServer: 2 更换实例次数

主程序设置RestTemplate 的请求工厂的超时属性
package cn.tedu.sp06;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
public class Sp06RibbonApplication {

    public static void main(String[] args) {
        SpringApplication.run(Sp06RibbonApplication.class, args);
    }
    //创建 RestTemplate 实例 并存入 spring 容器
    @LoadBalanced
    @Bean
    public RestTemplate restTemplate(){
        //主程序设置超时
        SimpleClientHttpRequestFactory f = new SimpleClientHttpRequestFactory();
        f.setReadTimeout(1000);
        return new RestTemplate();
    }

}

item-service 的 ItemController 添加延迟代码,以便测试 ribbon 的重试机制

ItemController

package cn.tedu.sp02.controller;

import cn.tedu.sp01.pojo.Item;
import cn.tedu.sp01.service.ItemService;
import cn.tedu.web.util.JsonResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.Random;

@RestController
@Slf4j
public class ItemController {

    @Autowired
    private ItemService itemService;

    //获取访问 端口号
    @Value("${server.port}")
    private int port;

    @GetMapping("/{orderId}")
    private JsonResult<List<Item>> getItem(@PathVariable String orderId) throws InterruptedException {
        log.info("获取商品,orderId="+orderId);

        //随机延迟
        if (Math.random() < 0.9) { //90% 概率执行延迟
            //随机延迟时常 0.5
            int i = new Random().nextInt(5000);
            log.info("延迟时间:"+i);
            Thread.sleep(i);
        }


        List<Item> items = itemService.getItems(orderId);
        return JsonResult.ok().data(items).msg("port="+port);
    }

    /**
     *  @RequestBody 注解
     *  从客户端提交的“请求协议体”中,完整接收协议体数据(Json格式),转换成商品列表
     */
    @PostMapping("/decreaseNumber")
    public JsonResult<?>  decreaseNumber(@RequestBody List<Item> items){
        itemService.decreaseNumber(items);
        return JsonResult.ok().msg("减少库存成功");
    }
}
测试 ribbon 的重试机制

通过 ribbon 访问 item-service,当超时,ribbon 会重试请求集群中其他服务器
http://localhost:3001/item-service/35
在这里插入图片描述

在这里插入图片描述
ribbon的重试机制,在 feign 和 zuul 中进一步进行了封装,后续可以使用feign或zuul的重试机制

十二、Hystrix 断路器

Hystrix 系统容错工具
快速失败-避免后台服务阻塞,故障向前传播,引起雪崩效应
限流
在这里插入图片描述
微服务宕机时,ribbon无法发送请求
– 关闭 user-service 和 order-service

降级:

调用后台服务失败(异常、服务崩溃、超时)时,执行当前服务中的一段代码,来向客户端发回响应结果

添加hystrix起步依赖

<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>

启动类上添加注解@EnableCircuitBreaker 表示启用hystrix断路器

package cn.tedu.sp06;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

@EnableCircuitBreaker
@SpringBootApplication
public class Sp06RibbonApplication {

    public static void main(String[] args) {
        SpringApplication.run(Sp06RibbonApplication.class, args);
    }
    //创建 RestTemplate 实例 并存入 spring 容器
    @LoadBalanced
    @Bean
    public RestTemplate restTemplate(){
        //主程序设置超时
        SimpleClientHttpRequestFactory f = new SimpleClientHttpRequestFactory();
        f.setReadTimeout(1000);
        return new RestTemplate();
    }

}

Cntroller中添加降级代码

添加注解: @HystrixCommand(fallbackMethod = “指定降级的方法名”)

package cn.tedu.sp06.controller;

import cn.tedu.sp01.pojo.Item;
import cn.tedu.web.util.JsonResult;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;

import java.util.List;

@RestController
@Slf4j
public class RilbbonController {

    @Autowired
    private RestTemplate rt;

    @HystrixCommand(fallbackMethod = "getItemsFB")
    @GetMapping("/item-service/{orderId}")
    public JsonResult<List<Item>>  getItems(@PathVariable String orderId){
        //向指定微服务地址发送get请求,并获得该服务的返回结果
        //{1} 表示占位符,用orderId填充
        return rt.getForObject("http://item-service/{1}",JsonResult.class,orderId );
    }
    /*----------------------------------------------------*/

    public JsonResult<List<Item>>  getItemsFB( String orderId){
        // 上面的方法中调用远程服务失败,之后降级,执行这个代码,向客户端返回响应数据。
        return JsonResult.err().msg("调用商品服务失败");
    }

    @HystrixCommand(fallbackMethod = "decreaseNumberFB")
    @PostMapping("/item-service/decreaseNumber")
    public JsonResult<?> decreaseNumber(@RequestBody List<Item> items){
        return rt.postForObject("http://item-service/decreaseNumber", items, JsonResult
        .class);
    }
    /*----------------------------------------------------*/
    
    public JsonResult<?> decreaseNumberFB( List<Item> items){
        return JsonResult.err().msg("调用商品服务失败");
    }

}

系统默认配置超时时间是1秒

hystrix超时设置:
  1. hystrix 默认超时时间是一秒 可以在yml文件中使用hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds进行设置
  2. hystrix等待超时后,会执行降级代码,快速向客户端返回降级结果,默认超时时间是1000毫秒。
  3. hystrix超时时间设置,应该和 ribbon 最大超时时间配合设置,否则 ribbon 重试无效
测试:
  1. 在ItemControlelr中设置随机延迟
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值