查询天气预报系统之---API网关的意义(十二-1)

API网关旨在用一套且统一的API入口点,来组合一个或多个内部API。

API网关定位为应用系统服务接口的网关,区别于网络技术的网关,但是原理是一样的。API网关统一服务入口,可方便实现对平台众多服务接口进行管控,如对访问服务的身份认证、防报文重放与数据篡改、功能调用的业务鉴权、以及响应数据的脱敏、流量与并发控制,甚至基于API调用的计量或计费等。

API网关使用场景:

1.黑白名单:实现通过IP地址控制禁止访问网关功能

2.日志:实现访问日志的记录,可以用于分析访问、处理性能指标、同时将分析结果支持其它模块功能应用。

3.协议适配:实现通信协议校验、适配转换功能。

4.身份认证:负责网关访问身份认证验证。

5.计流限流:实现微服务访问流量计算,基于流量计算分析进行限流,可以定义多种限流规则。

6.路由:是API网关很核心的模块功能,此模块实现根据请求锁定目标微服务,并将请求进行转发。

 

API网关带来的好处以及弊端

好处:

1,避免将内部信息泄露给外部

在数据安全方面,API网关能够将外部公共API与内部微服务API区分开来,使各项微服务在添加或变更时,能有明确的安全边界。这样,微服务架构就能随时间推移而始终通过重组来保证系统安全,且不会对外部绑定客户造成影响。另外,其还能为全部微服务提供单一入口点,从而避免外部客户进行服务发现版本控制信息查看。

 

2,为微服务添加额外的安全层

API网关能提供一套额外的保护层,足以应对SQL注入、XML解析攻击及拒绝服务(DOS)攻击等常见威胁因素,从而实现额外的保护层效果。系统的权限控制也可以在这一层来实施。

 

3,支持混合通信协议

面向外部的API,由于考虑到平台和语言的无关性,往往向外提供基于HTTP或REST的API。但内部微服务往往会采用不同的通信协议。此类协议包括ProtoBuff、AMQP或其它集成有SOAP、JSON-RPC或XML-RPC的系统。API网关可跨越这些协议,提供一个外部统一的、基于REST的API,并允许各团队以此为基础选择最适合内部架构的协议方案。

 

4,降低构建微服务的复杂性

微服务架构系统往往拥有比单个架构更多的管理复杂度如API令牌验证、访问控制及速率限制等。每一项功能的实施,都会给相关实现服务带来影响,进而会延长微服务的开发时间。API网关能够从代码层面隔离这些功能项,使开发人员在构建单个微服务时,能够专注于实际的核心业务。

 

5,微服务模拟与虚拟化

通过将微服务内部的API与外部的API加以区分,大家可以模拟或虚拟化自己的服务,从而满足设计要求或配合集成测试。

 

弊端:

 

1,由于额外API网关的加入,会使整个开发在架构上考虑更多的编排与管理工作。

2,在开发过程中,对路由逻辑配置要进行统一的管理,从而能够确保以合理的路由方式对接外部API与专用微服务。

3,除非架构本身充分适应高可用性与规模化要求,,否则API网关往往会成为一项限制性因素,甚至引发单点故障。

 

 

springCloud ZuuI

 

ZuuI的基本功能如下:

1.验证与安全保障:识别面向各类资源的验证要求并拒绝那些与要求不符的请求。

2.审查与监控:在边缘位置追踪有意义的数据及统计结果,从而为用户带来准确的生产状态结论。

3.动态路由:以动态方式根据需要将请求路由至不同后端集群处。

4.压力测试:逐渐增加指向集群的负载流量,从而计算性能水平。

5.负载分配:为每一种负载类型分配对应容量,并弃用超出限定值的请求。

6.静态响应处理:在边缘位置直接建立部分响应,从而避免其流入内部集群。

 

ZuuI 处理每个请求的方式是针对每个请求使用一个线程来处理。通常情况下,为了提高性能,所有请求会被放到处理队列中,从线程池中选取空闲线程来处理该请求。

 

之前天气预报微服务 最初依赖于天气数据API微服务及城市数据API微服务,现在把这两个API微服务合并到API网关中,由API网关负责请求的转发,这样最新的天气预报微服务只需要依赖API网关就可以了。

新建项目:springBoot-report-feign-gateway

项目结构:

 

pom文件:

<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.csq.study</groupId>
  <artifactId>springBoot-report-feign-gateway</artifactId>
  <version>0.0.1-SNAPSHOT</version>
<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.5.14.RELEASE</version>
	</parent>
	
	<properties>
		<java.version>1.8</java.version>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<spring.cloud.version>Edgware.SR5</spring.cloud.version>
	    <quartz.version>2.0.0.RELEASE</quartz.version>
	</properties>
	
	<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>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-test -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-thymeleaf -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
			</dependency>
			  <!-- Eureka服务-->
		<dependency>
		    <groupId>org.springframework.cloud</groupId>
		    <artifactId>spring-cloud-starter-eureka</artifactId>		
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-config</artifactId>		
		</dependency>
		<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-openfeign -->
      	<dependency>
   		 <groupId>org.springframework.cloud</groupId>
   		 <artifactId>spring-cloud-starter-openfeign</artifactId>
		</dependency>
		</dependencies>
</project>

 

controller层:

package com.csq.study.springboot.controller;

import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;

import com.csq.study.springboot.service.DataClient;
import com.csq.study.springboot.service.WeatherReportService;
import com.csq.study.springboot.vo.City;
/**
 * 天气预报API
 * @Description: TODO 
 * @ClassName: WeatherReportController
 * @author chengshengqing  2019年6月6日 下午3:37:51
 * @see TODO
 */
@RestController
@RequestMapping("/report")
public class WeatherReportController  {
	    //在应用中添加日志
		private final static Logger logger=LoggerFactory.getLogger(WeatherReportController.class);

		@Autowired
		private WeatherReportService weatherReportService;
		
		@Autowired
		private DataClient dataClient;

		@RequestMapping(value="/cityId/{cityId}", method=RequestMethod.GET)
		//@PathVariable:标识从路径中获取参数
		public ModelAndView getReportByCityId(@PathVariable("cityId") String cityId,Model model) throws Exception {

			List<City> cityList=null;

			try {
				//TODO 改为由城市数据API微服务来提供数据
				cityList= dataClient.listCity();

			} catch (Exception e) {
				logger.error("Exception!",e);
			}

			model.addAttribute("title", "天气预报");
			model.addAttribute("cityId", cityId);
			model.addAttribute("cityList", cityList);
			model.addAttribute("report", weatherReportService.getDataByCityId(cityId));

			return new ModelAndView("report","reportModel",model);
		}

		
}

 

service层接口以及实现类:

DataClient 接口:

package com.csq.study.springboot.service;

import java.util.List;

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

import com.csq.study.springboot.vo.City;
import com.csq.study.springboot.vo.WeatherResponse;

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

	//获取城市列表
	@RequestMapping(value="/city/cities", method=RequestMethod.GET)
	List<City>listCity()throws Exception;
	
	//根据城市id查询天气
	@RequestMapping(value="/data/weather/cityId/{cityId}", method=RequestMethod.GET)
	WeatherResponse getDataByCityId(@PathVariable("cityId") String cityId);
}

WeatherReportService 接口: 

package com.csq.study.springboot.service;

import com.csq.study.springboot.vo.Weather;

public interface  WeatherReportService {

	Weather getDataByCityId(String cityId);
	
}

WeatherReportServiceImpl实现类

package com.csq.study.springboot.service;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Service;

import com.csq.study.springboot.vo.Weather;
import com.csq.study.springboot.vo.WeatherResponse;
/**
 * 天气预报服务
 * @Description: TODO 
 * @ClassName: WeatherReportServiceImpl
 * @author chengshengqing  2019年6月6日 下午3:36:13
 * @see TODO
 */
@Service
public class WeatherReportServiceImpl implements WeatherReportService {

	
	@Autowired
	private DataClient dataClient;
	
	@Override
	public Weather getDataByCityId(String cityId) {
		//由天气数据API微服务来提供数据
		WeatherResponse resp=dataClient.getDataByCityId(cityId);
		Weather data=resp.getData();
		return data;
	}

}

 

vo类分别为:city、Forecast、Weather、WeatherResponse、Yesterday

启动类:

package com.csq.study.springboot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.feign.EnableFeignClients;
import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication
@EnableScheduling
@EnableDiscoveryClient
@EnableFeignClients
public class Aplication {
	public static void main(String[] args) {
		
		SpringApplication.run(Aplication.class, args);
	}

}

 

js文件:

/**
 * report页面下拉框事件
 */

$(function(){

    $("#selectCityId").change(function () {
        var cityId=$("#selectCityId").val();
        var url='/report/cityId/'+cityId;
        window.location.href=url;
    })

});

report.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8"/>
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"/>

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous"/>

    <title>天气预报</title>
</head>
<body>
    <div class="container">
        <div class="row">
            <h3 th:text="${reportModel.title}">天气</h3>
            <select class="custom-select" id="selectCityId">
                <option th:each="city : ${reportModel.cityList}"
                        th:value="${city.cityId}" th:text="${city.cityName}"
                        th:selected="${city.cityId eq reportModel.cityId}"></option>
            </select>
        </div>

        <div class="row">
            <h1 class="text-success" th:text="${reportModel.report.city}">城市名称</h1>
        </div>

        <div class="row">
            <p>
                空气质量指数:<span th:text="${reportModel.report.aqi}"></span>
            </p>
        </div>

        <div class="row">
            <p>
                当前温度:<span th:text="${reportModel.report.wendu}"></span>
            </p>
        </div>

        <div class="row">
            <p>
                温馨提示:<span th:text="${reportModel.report.ganmao}"></span>
            </p>
        </div>

        <div class="row">
            <div class="card border-info" th:each="forecast : ${reportModel.report.forecast}">
                <div class="card-body text-info">
                    <p class="card-text" th:text="${forecast.date}">日期</p>
                    <p class="card-text" th:text="${forecast.type}">天气类型</p>
                    <p class="card-text" th:text="${forecast.high}">最高温度</p>
                    <p class="card-text" th:text="${forecast.low}">最低温度</p>
                    <p class="card-text" th:text="${forecast.fengxiang}">风向</p>
                </div>
            </div>
        </div>
    </div>

    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
    <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script>

    <!-- Optional JavaScript -->
    <script type="text/javascript" th:src="@{/js/report.js}"></script>
</body>
</html>

配置文件:

#端口号
server.port=8089

#热部署静态文件
spring.thymeleaf.cache=false

#应用名称
spring.application.name=springBoot-report-feign-gateway

#注册服务器的URL
eureka.client.service-url.defaultZone=http://localhost:8762/eureka/

#请求服务时的超时时间
feign.client.config.feignName.connectTimeout=5000

#读数据时的超时时间
feign.client.config.feignName.readTimeout=5000

#hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 5000

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值