API网关 | 从0开始构建SpringCloud微服务(12)

照例附上项目github链接

本项目实现的是将一个简单的天气预报系统一步一步改造成一个SpringCloud微服务系统的过程。本章主要讲解API网关

项目存在的问题

在目前的项目中我们构建了许多的API微服务,当第三方服务想要调用我们的API微服务的时候是通过微服务的名称进行调用的,没有一个统一的入口。

使用API网关的意义

  • 集合多个API
  • 统一API入口

API网关就是为了统一服务的入口,可以方便地实现对平台的众多服务接口进行管控,对访问服务的身份进行验证,防止数据的篡改等。

我们的一个微服务可能需要依赖多个API微服务,API网关可以在中间做一个api的聚集。

那么我们的微服务只需要统一地经过API网关就可以了(只需要依赖于API网关就可以了),不用关心各种API微服务的细节,需要调用的API微服务由API网关来进行转发。

使用API网关的利与弊

API网关的利

  • 避免将内部信息泄露给外部
  • 为微服务添加额外的安全层
  • 支持混合通信协议
  • 降低构建微服务的复杂性
  • 微服务模拟与虚拟化

API网关的弊

  • 在架构上需要额外考虑更多编排与管理
  • 路由逻辑配置要进行统一的管理
  • 可能引发单点故障

API网关的实现

Zuul:SpringCloud提供的API网关的组件

Zuul是Netflix开源的微服务网关,他可以和Eureka,Ribbon,Hystrix等组件配合使用。Zuul组件的核心是一系列的过滤器,这些过滤器可以完成以下功能:

  1. 身份认证和安全: 识别每一个资源的验证要求,并拒绝那些不符的请求
  2. 审查与监控:
  3. 动态路由:动态将请求路由到不同后端集群
  4. 压力测试:逐渐增加指向集群的流量,以了解性能
  5. 负载分配:为每一种负载类型分配对应容量,并弃用超出限定值的请求
  6. 静态响应处理:边缘位置进行响应,避免转发到内部集群
  7. 多区域弹性:跨域AWS Region进行请求路由,旨在实现ELB(ElasticLoad Balancing)使用多样化

Spring Cloud对Zuul进行了整合和增强。目前,Zuul使用的默认是Apache的HTTP Client,也可以使用Rest Client,可以设置ribbon.restclient.enabled=true。

集成Zuul

在原来项目的基础上,创建一个msa-weather-eureka-client-zuul作为API网关。

添加依赖

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

添加注解

添加@EnableZuulProxy启用Zuul的代理功能。

@SpringBootApplication
@EnableDiscoveryClient
@EnableZuulProxy
public class Application {

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

添加配置

这里配置的是设置路由的url。

当有人访问 /city/ 路径的时候,就对访问这个路径的请求做一个转发,转发到msa-weather-city-eureka服务中去,同理,当有人访问 /data/ 路径的时候,API网关也会将这个请求转发到msa-weather-data-eureka服务中去。

zuul:
  routes:
    city: 
      path: /city/**
      service-id: msa-weather-city-eureka
    data: 
      path: /data/**
      service-id: msa-weather-data-eureka

微服务依赖API网关

原来天气预报微服务依赖了城市数据API微服务,以及天气数据API微服务,这里我们对其进行修改让其依赖API网关,让API网关处理天气预报微服务其他两个微服务的调用。

首先删去原来调用其他两个API微服务的Feign客户端——CityClient以及WeatherDataClient,创建一个新的Feign客户端——DataClient。

package com.demo.service;

import java.util.List;

import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

import com.demo.vo.City;
import com.demo.vo.WeatherResponse;

@FeignClient("msa-weather-eureka-client-zuul")
public interface DataClient {

    //获取城市列表
    @RequestMapping(value="/city/cities",method=RequestMethod.GET)
    List<City> listCity()throws Exception;
    
    //通过城市Id查询对应城市的天气数据
    @RequestMapping(value="/data/weather/cityId",method=RequestMethod.GET)
    WeatherResponse getDataByCityId(@RequestParam("cityId")String cityId);
}

在service中使用该客户端对API网关进行调用,API网关根据我们请求的路径去调用相应的微服务。

@Service
public class WeatherReportServiceImpl implements WeatherReportService{
    //@Autowired 
    //private WeatherDataClient weatherDataClient;
    
    @Autowired
    private DataClient dataClient;
    
    //根据城市Id获取天气预报的数据
    @Override
    public Weather getDataByCityId(String cityId) {
        //由天气数据API微服务来提供根据城市Id查询对应城市的天气的功能
        WeatherResponse resp=dataClient.getDataByCityId(cityId);
        Weather data=resp.getData();
        return data;
    }
}

在controller也是同理。

//传入城市Id得到对应城市的天气数据
    @GetMapping("/cityId/{cityId}")
    public Weather getReportByCityId(@PathVariable("cityId")String cityId,Model model)throws Exception{
        //获取城市Id列表
        //由城市数据API微服务来提供数据
        List<City>cityList=null;
        
        try {
            cityList=dataClient.listCity();
        }catch (Exception e) {
            logger.error("Exception!",e);
        }
        
        Weather weather=weatherReportService.getDataByCityId(cityId);
        
        return weather;
    }

测试结果

API网关 | 从0开始构建SpringCloud微服务(12)

API网关 | 从0开始构建SpringCloud微服务(12)

相关推荐