springcloud脚手架搭建

一.搭建空项目以及eureka服务

  1. New EmptyProject–>New Module–>Spring Cloud Discovery(选择Eureka Server)
  2. pom.xml中添加阿里云镜像
<repositories>
    <repository>
        <id>central</id>
        <name>aliyun maven</name>
        <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
        <layout>default</layout>
        <!-- 是否开启发布版构件下载 -->
        <releases>
            <enabled>true</enabled>
        </releases>
        <!-- 是否开启快照版构件下载 -->
        <snapshots>
            <enabled>false</enabled>
        </snapshots>
    </repository>
</repositories>
  1. 修改配置文件
server:
  port: 8761

eureka:
  instance:
    hostname: localhost
  client:
    register-with-eureka: false
    fetch-registry: false
    service-url:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
  1. 访问:http://localhost:8761/

二.搭建商品服务

  1. New Module–>Web(选择Spring Web)、Spring Cloud Discovery(选择Eureka Discovery Client)
  2. 新建一个controller,代码如下
@RestController
@RequestMapping("/api/v1/product")
public class ProductController {

    @Autowired
    private ProductService productService;

    @RequestMapping("list")
    public Object list(){
        return productService.listProduct();
    }

    @RequestMapping("find")
    public Object list(@RequestParam("id") int id){
        return productService.findByid(id);
    }
}
  1. 新建service接口
public interface ProductService {

    List<Product> listProduct();

    Product findByid(int id);
}
  1. 新建service实现类
@Service
public class ProductServiceImpl implements ProductService {

    private static Map<Integer,Product> daoMap = new HashMap<>();

    static {
        Product p1 = new Product(1, "iphone1", 111, 11);
        Product p2 = new Product(2, "iphone2", 222, 12);
        Product p3 = new Product(3, "iphone3", 333, 13);
        Product p4 = new Product(4, "iphone4", 444, 14);
        Product p5 = new Product(5, "iphone5", 555, 15);
        Product p6 = new Product(6, "iphone6", 666, 16);
        Product p7 = new Product(7, "iphone7", 777, 17);

        daoMap.put(p1.getId(),p1);
        daoMap.put(p2.getId(),p2);
        daoMap.put(p3.getId(),p3);
        daoMap.put(p4.getId(),p4);
        daoMap.put(p5.getId(),p5);
        daoMap.put(p6.getId(),p6);
        daoMap.put(p7.getId(),p7);


    }

    @Override
    public List<Product> listProduct() {
        Collection<Product> collection = daoMap.values();
        List<Product> list = new ArrayList<>(collection);
        return list;
    }

    @Override
    public Product findByid(int id) {
        return daoMap.get(id);
    }
}
  1. 修改配置
server:
  port: 8771

spring:
  application:
    name: product-service

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/
a. 注册中心报错:EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY'RE NOT. RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEING EXPIRED JUST TO BE SAFE

b. 原因:这是eureka自我保护的一个警告

c. 解决:可以在配置文件中配置
server:
  port: 8771

spring:
  application:
    name: product_service

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/
  
#关闭自我保护    
  server:
    enable-self-preservation: false
  1. 访问:
    http://localhost:8771/api/v1/product/list
    http://localhost:8771/api/v1/product/find?id=1

  2. 为什么只加配置文件,product服务就可以注册到eureka服务上,只要类路径下有spring-cloud-starter-netflix-client依赖,服务就会自动注册到eureka服务上

三.搭建订单服务

  1. 服务间调用方式
    a. RPC:远程过程调用 ,像本地服务(方法)一样调用服务器的服务,客户端和服务器之间建立TCP连接,可以一次建立一个,也可以多个调用复用一次链接,数据包小
    b. Rest(Http):发起http请求,数据包大。使用HttpClient、URLConnection
  2. New Module–>Web(选择Spring Web)、Spring Cloud Discovery(选择Eureka Discovery Client)、Cloud Routing(选择Ribbon)
  3. Ribbon类似httpClient、URLConnection
  4. 创建controller
@RestController
@RequestMapping("api/v1/order")
public class OrderController {

    @Autowired
    private OrderService orderService;

    @RequestMapping("save")
    public Object save(@RequestParam("user_id")int userId, @RequestParam("product_id")int productId){
        return orderService.save(userId, productId);
    }
}
  1. 创建service接口
public interface OrderService {
    ProductOrder save(int userId, int productId);
}
  1. 创建service实现类
@Service
public class OrderServiceImpl implements OrderService {

    @Autowired
    private RestTemplate restTemplate;

    @Override
    public ProductOrder save(int userId, int productId) {

        Object obj = restTemplate.getForObject("http://PRODUCT-SERVICE/api/v1/product/find?id=" + productId, Object.class);

        System.out.println(obj);

        ProductOrder productOrder = new ProductOrder();
        productOrder.setCreateTime(new Date());
        productOrder.setUserId(userId);
        productOrder.setTradeNo(UUID.randomUUID().toString());

        return productOrder;
    }
}
  1. 增加配置
server:
  port: 8781

spring:
  application:
    name: order-service

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/
  1. 启动类增加
@SpringBootApplication
public class OrderServiceApplication {

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

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

}
  1. 请求订单服务:http://127.0.0.1:8781/api/v1/order/save?product_id=3&user_id=4
  2. ribbon实现原理:调用方向注册中心拉取可调用列表,然后ribbon根据策略选取调用服务
  3. 自定义负载均衡策略:
    a. 通过设置配置文件:
server:
  port: 8781

spring:
  application:
    name: order-service

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/

#自定义负载均衡策略
product-service:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
  1. 访问:http://127.0.0.1:8781/api/v1/order/save?product_id=3&user_id=4

四.feigin改造订单服务

  1. 新增feign依赖
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
  1. 在订单服务service中新加一个接口,接口上新加注解以及添加需要调用服务的方法,请求url和入参需要和🔐调用服务方法一致
@FeignClient(name = "product-service")
public interface ProductClient {

    @RequestMapping("/api/v1/product/find")
    String findById(@RequestParam("id") int id);
}
  1. 然后对这个接口进行代码调用
@Service
public class OrderServiceImpl implements OrderService {

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private ProductClient productClient;

    @Override
    public ProductOrder save(int userId, int productId) {

        //Object obj = restTemplate.getForObject("http://PRODUCT-SERVICE/api/v1/product/find?id=" + productId, Object.class);

        String response = productClient.findById(productId);
        JsonNode jsonNode = JsonUtils.str2JsonNode(response);


        System.out.println(jsonNode);

        ProductOrder productOrder = new ProductOrder();
        productOrder.setCreateTime(new Date());
        productOrder.setUserId(userId);
        productOrder.setTradeNo(UUID.randomUUID().toString());

        return productOrder;
    }
}
  1. 访问:http://127.0.0.1:8781/api/v1/order/save?product_id=3&user_id=4

四.ribbon和feigin源码解读

  1. spring启动时候会对注解进行扫描,扫到@EnableFeignClients,开启一个feigin客户端,会扫描@FeignClient类,并对类进行实例化,放到IOC容器中,然后拦截请求,然后通过动态代理生成RestTemplate

五.服务降级熔断

概念相关
  1. 概念介绍

    a. 熔断:防止整个系统故障,包含子系统和 下游服务
    b. 降级:抛弃非核心 的接口和数据。熔断一般是下游 服务故障导致,服务降级一般是从整体系统负荷考虑,由调用方控制
    
  2. 参考文档:
    a. https://github.com/Netflix/Hystrix
    b. https://github.com/Netflix/Hystrix/wiki

  3. hystrix超时策略配置类:HystrixCommandProperties

  4. 配置参考文档:https://github.com/Netflix/Hystrix/wiki/Configuration

  5. 隔离策略:
    a. THREAD(线程池隔离,默认)
    b. SEMAPHORE(信号量)

  6. 隔离策略修改代码,增加commandProperties参数

@RestController
@RequestMapping("api/v1/order")
public class OrderController {

    @Autowired
    private OrderService orderService;
    @Autowired
    private StringRedisTemplate redisTemplate;

    @RequestMapping("save")
    @HystrixCommand(fallbackMethod = "saveOrderFail",commandProperties = {@HystrixProperty(name = "execution.isolation.strategy", value = "SEMAPHORE")})
    public Object save(@RequestParam("user_id")int userId, @RequestParam("product_id")int productId, HttpServletRequest request){
        Map<String, Object> msg = new HashMap<>();
        msg.put("code",0);
        msg.put("data",orderService.save(userId, productId));
        return msg;
    }

    public Object saveOrderFail(int userId, int productId, HttpServletRequest request){

        new Thread(()->{
            //监控报警
            String saveOrderKey = "save-order";
            String sendValue = redisTemplate.opsForValue().get(saveOrderKey);
            String ip = request.getRemoteAddr();

            if (StringUtils.isNullOrEmpty(sendValue)){
                System.out.println("紧急短信下发,ip地址是:"+ip);
                redisTemplate.opsForValue().set(saveOrderKey,"save-order-fail",20, TimeUnit.SECONDS);
            }else {
                System.out.println("已经发送过短信,20s内不重复发");
            }
        }).start();


        Map<String, Object> msg = new HashMap<>();
        msg.put("code",-1);
        msg.put("msg","清稍后再试");
        return msg;
    }

}
  1. 其他配置:
    c. execution.isolation.thread.timeoutInMilliseconds,默认1000毫秒
    b. execution.timeout.enabled,是否开启超时限制
    e. execution.isolation.semaphore.maxConcurrentRequests隔离策略为信号量的时候,如果达到最大并发数,后续请求会被拒绝,默认是10
Ribbon消费使用hystrix代码相关
  1. 添加依赖
<dependency>
    <groupId>org.springframework.cloud</groupId>
     <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
 </dependency>
  1. 启动类增加注解:@EnableCircuitBreaker,如果注解太多可以用@SpringCloudApplication代替
  2. api方法上增加@HystrixCommand(fallbackMethod = “saveOrderFail”)
  3. 编写fallback方法实现,方法签名(saveOrderFail)和api方法签名一致
@RestController
@RequestMapping("api/v1/order")
public class OrderController {

    @Autowired
    private OrderService orderService;

    @RequestMapping("save")
    @HystrixCommand(fallbackMethod = "saveOrderFail")
    public Object save(@RequestParam("user_id")int userId, @RequestParam("product_id")int productId){
        Map<String, Object> msg = new HashMap<>();
        msg.put("code",0);
        msg.put("data",orderService.save(userId, productId));
        return msg;
    }

    public Object saveOrderFail(int userId, int productId){
        Map<String, Object> msg = new HashMap<>();
        msg.put("code",-1);
        msg.put("msg","清稍后再试");
        return msg;
    }
    
}
Feign结合Hystrix断路器
  1. 创建FeignClient接口类,实现feign接口,为fallback参数指定fallback方法
@FeignClient(name = "product-service", fallback = ProductClientFallback.class)
public interface ProductClient {

    @RequestMapping("/api/v1/product/find")
    String findById(@RequestParam("id") int id);
}
  1. 创建接口类,实现ProductClient接口,
@Component
public class ProductClientFallback implements ProductClient {
    @Override
    public String findById(int id) {
        System.out.println();
        return "feign中断路器已开启";
    }
}
  1. 在配置文件中开启断路器
server:
  port: 8781

spring:
  application:
    name: order-service

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/

#自定义负载均衡策略
product-service:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

feign:
  hystrix:
    enabled: true
  1. 报错相关:
    a. 报错:springboot使用Fegin,创建服务接口,在controller里面注入Feign接口对象,结果启动报错
    b.原因:注解 @EnableFeignClients 与 @ComponentScan 有冲突,两种注解都会搜索注入指定目录中的 bean 。@EnableFeignClients 引入了 FeignClientsRegistrar 类,实现了 Spring 的bean 资源的加载。FeignClientsRegistrar中registerFeignClients方法获取了@EnableFeignClients注解中的basepackage 属性值,并进行注入。如果两种注解都使用时,其中@EnableFeignClients会覆盖 @ComponentScan 中指定的目录,从而恢复到默认目录。
    c. 解决:在注解@EnableFeignClients指定clients,例如:@EnableFeignClients(clients = ProductClient.class)
Hystrix断路器整合redis预警
  1. 加入redis依赖
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
  1. controller引入StringRedisTemplate
@RestController
@RequestMapping("api/v1/order")
public class OrderController {

    @Autowired
    private OrderService orderService;
    @Autowired
    private StringRedisTemplate redisTemplate;

    @RequestMapping("save")
    @HystrixCommand(fallbackMethod = "saveOrderFail")
    public Object save(@RequestParam("user_id")int userId, @RequestParam("product_id")int productId, HttpServletRequest request){
        Map<String, Object> msg = new HashMap<>();
        msg.put("code",0);
        msg.put("data",orderService.save(userId, productId));
        return msg;
    }

    public Object saveOrderFail(int userId, int productId, HttpServletRequest request){

        new Thread(()->{
            //监控报警
            String saveOrderKey = "save-order";
            String sendValue = redisTemplate.opsForValue().get(saveOrderKey);
            String ip = request.getRemoteAddr();

            if (StringUtils.isNullOrEmpty(sendValue)){
                System.out.println("紧急短信下发,ip地址是:"+ip);
                redisTemplate.opsForValue().set(saveOrderKey,"save-order-fail",20, TimeUnit.SECONDS);
            }else {
                System.out.println("已经发送过短信,20s内不重复发");
            }
        }).start();

        Map<String, Object> msg = new HashMap<>();
        msg.put("code",-1);
        msg.put("msg","清稍后再试");
        return msg;
    }
}

Hystrix仪表盘使用

参考博客:https://baijiahao.baidu.com/s?id=1623004854011062838&wfr=spider&for=pc

  1. 新建hystrix_dashboard模块
  2. 添加依赖
<!-- hystrix监控web依赖 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
  1. 启动类增加注解@EnableHystrixDashboard
  2. 配置文件新增endpoint(目的是springboot2.0之后不会开启全部的监控元数据信息)
server.port=8791
spring.application.name=hystrix-dashboard
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/

# actuator监控
management.endpoints.web.exposure.include=*
  1. 访问:http://localhost:8781/hystrix
  2. Hystrix Dashboard支持三种监控方式
    a. 通过URL:turbine-hostname:port/actuator/turbine.stream开启,实现对默认集群的监控
    b. 通过URL:turbine-hostname:port/actuator/turbine.stream?cluster=[clusterName]开启,实现对clusterName集群的监控
    c. 通过URL/hystrix-app:port/actuator/hystrix.stream开启,实现对具体某个服务实例的监控
  3. Delay参数:控制服务器上轮询监控信息的延迟时间,默认2000毫秒
6.监控具体服务
  1. 对于Ribbon工程,在Dashboard输入: http://localhost:8781/actuator/hystrix.stream
    如果显示:Unable to connect to Command Metric Stream
    是因为:配置文件中没配置actuator,因为监控路径默认不开放
  2. 对于Feign工程,Feign自己集成了断路器,需要在启动类加@EnableCircuitBreaker注解
2.Hystrix仪表盘参数
参数含义
Host请求速率
Circuit阀值
  1. 仪表盘数据通过sse server-send-event推送到前端
7.Turbine项目聚合监控
  1. 原理:通过将将自己注册到注册中心,发现同一个注册中心上的hystrix服务,然后聚合数据。再通过暴露自己的端点,在仪表盘上进行展示
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值