项目:
E:\weatherProject\micro-weather-basic-client 和
E:\weatherProject\micro-weather-eureka-client 一个项目 客户端
实现一个服务注册和发现
micro-weather-eureka-server 和 micro-weather-basic-server 是同一个项目 服务端
服务注册和发现
实现服务的注册与发现:helloworld的服务端实现
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-serverartifactId>
<version>2.1.0.RELEASEversion>
dependency>
dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>Greenwichversion>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
@EnableEurekaServer
@SpringBootApplication
@EnableEurekaServer
public class MicroWeatherBasic2Application {
public static void main(String[] args) {
SpringApplication.run(MicroWeatherBasic2Application.class, args);
}
}
server.port:8761
eureka.instance.hostname:localhost
#是否注册eureka
eureka.client.registerWithEureka:false
#是否启用获取服务注册信息
eureka.client.fetchRegistry:false
#注册和查询都需要依赖该地址,多个以逗号分隔。
eureka.client.serverUrl.defaultZone:http://${eureka.instance.hostname}:${server.port}/eureka/
#留存的服务实例低于多少比例进入保护模式,保护模式将不再注销已经停止心跳的服务来兼容片区故障。
eureka.server.renewal-percent-threshold=0.5
#是否开启保护模式
eureka.server.enable-self-preservation=false
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
<version>2.1.0.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
spring.application.name:micro-weather-eureka-client
eureka.client.service-url.defaultZone:http://localhost:8761/eureka/
@EnableDiscoveryClient
package mystudy.microweatherbasic2;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class MicroWeatherBasic2Application {
public static void main(String[] args) {
SpringApplication.run(MicroWeatherBasic2Application.class, args);
}
}
在命令行中运行各微服务jar包(目的是让个服务占用不同的端口)
2.切换到jar包所在目录执行(获得jar包方式见笔记maven):
java -jar xxx.jar --server.port=8081
micro-weather-eureka-client-feign(此项目演示如何调用其他已有的服务)
msa-weather-city-eureka
micro-weather-eureka-server
消费者模式之一的模式框架,集成了HttpClient和Ribbon
Spring Cloud Starter Netflix Eureka Client Finchley.M2
Spring Cloud Starter OpenFeign Finchley.M2
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-openfeignartifactId>
<version>2.1.0.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
<version>2.1.0.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-openfeignartifactId>
<version>2.1.0.RELEASEversion>
dependency>
package mystudy.microweatherbasic2;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableDiscoveryClient//eureka注册客户端
@EnableFeignClients//Feign,调用服务端
public class MicroWeatherBasic2Application {
public static void main(String[] args) {
SpringApplication.run(MicroWeatherBasic2Application.class, args);
}
}
CityClient.java
package mystudy.microweatherbasic2.weatherDataService;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
@FeignClient("mas-weather-city-eureka")//指定要调用的服务的名称
public interface CityClient {
@GetMapping("/cities")
String listCity();
}
package mystudy.microweatherbasic2.controller;
import mystudy.microweatherbasic2.weatherDataService.CityClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
//Controller类
@RestController
public class CityController {
@Autowired
private CityClient cityClient;
@GetMapping("/cities")
public String listCity(){
//通过Feign客户端来查找城市相关信息
String body = cityClient.listCity();
return body;
}
}
配置文件application.properties
spring.application.name:micro-weather-eureka-client-feign
eureka.client.service-url.defaultZone:http://localhost:8761/eureka/
feign.client.config.feignName.connect-timeout=5000
feign.client.config.geignName.read-timeout=5000
完成后将三个项目全部开启,注意端口需要设置不同,然后在浏览器中访问:
http://localhost:8080/cities
功能
认证、压力测试、金丝雀测试、动态路由、负载削减、安全、静态响应处理、主动/主动交换管理
开发环境:
Spring Cloud Starter Netflix Zuul Finchley.M2
项目:micro-weather-eureka-zuul
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
<version>2.1.0.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-zuulartifactId>
<version>2.1.0.RELEASEversion>
dependency>
@EnableZuulProxy
package mystudy.microweatherbasic2;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
@SpringBootApplication
@EnableDiscoveryClient
@EnableZuulProxy
public class MicroWeatherBasic2Application {
public static void main(String[] args) {
SpringApplication.run(MicroWeatherBasic2Application.class, args);
}
}
spring.application.name:micro-weather-eureka-client-zuul
eureka.client.service-url.defaultZone:http://localhost:8761/eureka/
zuul.routes.hi.path:/hi/**
zuul.routes.hi.serviceId:msa-weather-city-eureka
#'hi'是自定义的路径名字
集中化配置文件,从外部配置微服务的配置文件
Config Server (默认使用git)和Config Client
项目:
micro-weather-config-client
micro-weather-config-server
Spring Cloud Config Server Finchley.M2
org-springframework.cloud:spring-cloud-config-server
org-springframework.cloud:spring-cloud-config-client
@EnableConfigServer
package mystudy.microweatherbasic2;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.config.server.EnableConfigServer;
@SpringBootApplication
@EnableDiscoveryClient
@EnableConfigServer
public class MicroWeatherBasic2Application {
public static void main(String[] args) {
SpringApplication.run(MicroWeatherBasic2Application.class, args);
}
}
spring.application.name:micro-weather-config-server
server.port=8888
eureka.client.service-url.defaultZone:http://localhost:8761/eureka/
spring.cloud.config.server.git.uri=https://github.com/handingzhang/spring-cloud-micro-weather-development
spring.cloud.config.server.git.search-paths=config-repo
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
<version>2.1.0.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-config-serverartifactId>
<version>2.1.0.RELEASEversion>
dependency>
http://localhost:8888/auther/dev
package mystudy.microweatherbasic2;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class MicroWeatherBasic2Application {
public static void main(String[] args) {
SpringApplication.run(MicroWeatherBasic2Application.class, args);
}
}
spring.application.name:micro-weather-config-client
eureka.client.service-url.defaultZone:http://localhost:8761/eureka/
#下面是配置代码运行的环境,开发环境
spring.cloud.config.profile=dev
spring.cloud.config.uri=http://localhost:8888/
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
<version>2.1.0.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-config-clientartifactId>
<version>2.1.0.RELEASEversion>
dependency>
项目:micro-weather-config-client
spring-cloud-micro-weather-development/config-repo/micro-weather-config-client-dev.properties
auther=zhanghanding
<dependency>
<groupId>org.junitgroupId>
<artifactId>junit-gradleartifactId>
<version>5.0.0-ALPHAversion>
dependency>
package mystudy.microweatherbasic2;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class MicroWeatherBasic2ApplicationTests {
@Value("${auther}")
private String auther;
@Test
public void contextLoads() {
assert auther.equals("zhanghanding");
}
}
micro-weather-eureka-server
micro-weather-config-server
micro-weather-config-client
运行三个项目,client项目不报错则,配置中心的配置文件正确
断路器、断路器模式
异常处理、日志功能、测试失败的操作、手动复位、并发、加速断路、重试失败请求
目的一致(都是保护系统)、表现类似(返回固定的信息)、粒度一致
触发条件不同:熔断是某个服务无法响应之后产生的,降级是整个系统的性能无法满足负载时产生的。
管理目标的层次不同:熔断是框架级的,每个微服务都需要熔断机制,无层次之分;降级是有层次的,一般从外围服务开始。
micro-weather-eureka-client-feign-hystrix
hystrix官网:
https://github.com/Netflix/Hystrix/wiki
hystrix配置:
https://github.com/Netflix/Hystrix/wiki/Configuration
Spring Cloud Starter Netflix Hystrix Finchley.M2
依赖:
org.springframework.cloud:spring-cloud-starter-netflix-hystrix
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-hystrixartifactId>
<version>2.1.0.RELEASEversion>
dependency>
@EnableCircuitBreaker
package mystudy.microweatherbasic2;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableDiscoveryClient//eureka注册客户端
@EnableFeignClients//Feign,调用服务端
@EnableCircuitBreaker//hystrix 断路器
public class MicroWeatherBasic2Application {
public static void main(String[] args) {
SpringApplication.run(MicroWeatherBasic2Application.class, args);
}
}
重点
@GetMapping("/cities")
@HystrixCommand(fallbackMethod = "defaultCities")
public List<City> listCity(){
//通过Feign客户端来查找城市相关信息
List<City> cityList = cityClient.listCity();
return cityList;
}
public String defaultCities(){
//..
}
package mystudy.microweatherbasic2.controller;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import mystudy.microweatherbasic2.City;
import mystudy.microweatherbasic2.weatherDataService.CityClient;
import mystudy.microweatherbasic2.weatherDataService.GetWeatherDataService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
//Controller类
@RestController
public class CityController {
@Autowired
private CityClient cityClient;
@Autowired
private GetWeatherDataService getWeatherDataService;
@GetMapping("/cities")
@HystrixCommand(fallbackMethod = "defaultCities")
public List<City> listCity(){
//通过Feign客户端来查找城市相关信息
List<City> cityList = cityClient.listCity();
return cityList;
}
public List<City> defaultCities(){
City city = new City();
city.setCityId("0000000");
city.setCityName("null");
city.setCityPingyin("null");
city.setProvinceCenter("null");
List<City> cityList = new ArrayList<City>();
cityList.add(0,city);
return cityList;
}
}
基本和前面的项目一样
spring.application.name:micro-weather-eureka-client-feign-hystrix
eureka.client.service-url.defaultZone:http://localhost:8761/eureka/
feign.client.config.feignName.connect-timeout=5000
feign.client.config.geignName.read-timeout=5000
运行:
micro-weather-eureka-server
msa-weather-city-eureka
micro-weather-eureka-client-feign-hystrix
正常访问http://localhost:8080/cities后,关掉msa-weather-city-eureka服务
再次访问http://localhost:8080/cities,显示默认的city信息。
微服务中使用@FeignClient处可以多加一个声明fallback = CityClientFallback.class, 而简化上面那样的设置fallback方法
依赖:与上面一样,在原来的依赖里多加一项
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-hystrixartifactId>
<version>2.1.0.RELEASEversion>
dependency>
@EnableCircuitBreaker
package mystudy.microweatherbasic;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
@EnableCircuitBreaker
public class MicroWeatherBasicApplication {
public static void main(String[] args) {
SpringApplication.run(MicroWeatherBasicApplication.class, args);
}
}
增加
#此配置项使调用feign的类能调用fallback类,即让feign能使用断路器功能
feign.hystrix.enabled=true
spring.application.name:msa-weather-report-eureka-feign-hystrix
eureka.client.service-url.defaultZone:http://localhost:8761/eureka/
feign.client.config.feignName.connect-timeout=5000
feign.client.config.geignName.read-timeout=5000
#此配置项使调用feign的类能调用fallback类,即使feign能使用断路器功能
feign.hystrix.enabled=true
加入下面的注释,使之能被自动注入
@Component
增加
@Component
@FeignClient(name="msa-weather-city-eureka",fallback = CityClientFallback.class)
package mystudy.microweatherbasic.weatherDataService;
import mystudy.microweatherbasic.city.City;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import java.util.List;
@Component
@FeignClient(name="msa-weather-city-eureka",fallback = CityClientFallback.class)
public interface CityClient {
@GetMapping("/cities")
List<City> getCityList() throws Exception;
}
增加点
@Component
@FeignClient(name="msa-weather-data-eureka",fallback = WeatherDataClientFallback.class)
package mystudy.microweatherbasic.weatherDataService;
import mystudy.microweatherbasic.weather.Weather;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@Component
@FeignClient(name="msa-weather-data-eureka",fallback = WeatherDataClientFallback.class)
public interface WeatherDataClient {
@GetMapping("/weather/cityID/{cityID}")
Weather getWeatherById(@PathVariable("cityID") String cityId);
}
package mystudy.microweatherbasic.weatherDataService;
//CityClient接口的fallback类
import mystudy.microweatherbasic.city.City;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
@Component//是spring的bean,所以要加此注释
public class CityClientFallback implements CityClient {
@Override
public List<City> getCityList() throws Exception {
City city = new City();
city.setCityId("0000000");
city.setCityName("null");
city.setCityPingyin("null");
city.setProvinceCenter("null");
List<City> cityList = new ArrayList<City>();
cityList.add(0,city);
return cityList;
}
}
package mystudy.microweatherbasic.weatherDataService;
import mystudy.microweatherbasic.weather.Data;
import mystudy.microweatherbasic.weather.Forecast;
import mystudy.microweatherbasic.weather.Weather;
import mystudy.microweatherbasic.weather.Yesterday;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
@Component
public class WeatherDataClientFallback implements WeatherDataClient{
@Override
public Weather getWeatherById(String cityId) {
Weather weather = new Weather();
Data data = new Data();
Forecast forecast = new Forecast();
forecast.setDate("null");
forecast.setFengli("null");
forecast.setFengxiang("null");
forecast.setHigh("null");
forecast.setLow("null");
forecast.setType("null");
Yesterday yesterday = new Yesterday();
yesterday.setDate("null");
yesterday.setFl("null");
yesterday.setFx("null");
yesterday.setHigh("null");
yesterday.setLow("null");
yesterday.setType("null");
List<Forecast> forecastList =new ArrayList<>();
forecastList.add(0,forecast);
data.setCity("null");
data.setForecast(forecastList);
data.setGanmao("null");
data.setWendu("null");
data.setYesterday(yesterday);
weather.setData(data);
return weather;
}
}
依赖改成:
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-hystrixartifactId>
<version>1.4.6.RELEASEversion>
dependency>
配置:
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=1
hystrix.threadpool.default.coreSize=1
hystrix.threadpool.default.maxQueueSize=1
hystrix.threadpool.default.maximumSize=1
hystrix.command.default.circuitBreaker.errorThresholdPercentage=1
hystrix.command.default.circuitBreaker.sleepWindowInMilliseconds=100000
添加注解:
package com.mooc.house.api.dao;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Repository;
import com.mooc.house.api.common.RestResponse;
import com.mooc.house.api.config.GenericRest;
import com.mooc.house.api.model.Agency;
import com.mooc.house.api.model.ListResponse;
import com.mooc.house.api.model.User;
import com.mooc.house.api.utils.Rests;
import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
@Repository
//定制化的配置,让spring识别出这个类是一个Hystrix 的command类
@DefaultProperties(groupKey="userDao",
commandProperties={@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="2000")},
threadPoolProperties={@HystrixProperty(name="coreSize",value="10")
,@HystrixProperty(name="maxQueueSize",value="1000")},
threadPoolKey="userDao"
)
public class UserDao {
@Autowired
private GenericRest rest;
@Value("${user.service.name}")
private String userServiceName;
...
//回调方法
public User getUserByTokenFb(String token){
return new User();
}
/**
* 调用鉴权服务
* @param token
* @return
*/
@HystrixCommand(fallbackMethod="getUserByTokenFb")//声明回调哪个方法
public User getUserByToken(String token) {
String url = "http://" + userServiceName + "/user/get?token=" + token;
ResponseEntity<RestResponse<User>> responseEntity = rest.get(url, new ParameterizedTypeReference<RestResponse<User>>() {});
RestResponse<User> response = responseEntity.getBody();
if (response == null || response.getCode() != 0) {
return null;
}
return response.getResult();
}
@HystrixCommand(fallback="getUserByTokenFb",ignoreException=IOException.class)//可以忽略这种异常
public User getAgentById(Long id) {
return Rests.exc(() ->{
String url = Rests.toUrl(userServiceName, "/agency/agentDetail?id=" +id);
ResponseEntity<RestResponse<User>> responseEntity =
rest.get(url, new ParameterizedTypeReference<RestResponse<User>>() {});
return responseEntity.getBody();
}).getResult();
}
...
public String getEmail(String key) {
return Rests.exc(() -> {
String url = Rests.toUrl(userServiceName, "/user/getKeyEmail?key=" + key);
ResponseEntity<RestResponse<String>> responseEntity =
rest.get(url,new ParameterizedTypeReference<RestResponse<String>>() {});
return responseEntity.getBody();
}).getResult();
}
}
新建一个springboot项目
主类上添加:
@EnableHystrixDashboard
依赖:
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-hystrixartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-hystrix-dashboardartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
浏览器输入:localhost:9097/hystrix/moniter
增加判断if,report为空则返回固定的信息,但为了不出现空指针异常,熔断机制默认后端返回的report总是有值的
<html xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet"
href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/css/bootstrap.min.css"
integrity="sha384-PsH8R72JQ3SOdhVi3uxftmaW6Vc51MKb0q5P2rRUpPvrszuE4W1povHYgTpBfshb"
crossorigin="anonymous">
<title>天气预报title>
head>
<body>
<div class="container">
<div class="row">
<h3 th:text="${reportModel.title}">waylauh3>
<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 th:if="${reportModel.report} !=null">
<div class="row">
<p>
空气质量指数:<span>无此项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>
天气类型p>
最高温度p>
最低温度p>
风向p>
div>
div>
div>
div>
<div th:if="${reportModel.report} ==null">
<div>
天气数据暂时无法访问到
div>
div>
div>
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"
integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN"
crossorigin="anonymous">script>
<script
src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.3/umd/popper.min.js"
integrity="sha384-vFJXuSJphROIrBnz7yo7oB41mKfc8JzQZiCq4NCceLEaO4IHwicKwpJf9c9IpFgh"
crossorigin="anonymous">script>
<script
src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/js/bootstrap.min.js"
integrity="sha384-alpBpkh1PFOepccYVYDB4do5UnbKysX5WZXm3XxPqe5iKTfUKjNkCk9SaVuEZflJ"
crossorigin="anonymous">script>
<script type="text/javascript" th:src="@{/js/report.js}">script>
body>
html>
http://localhost:8080/report/cityID/101280903
运行整个天气系统,用msa-weather-report-eureka-feign-hystrix代替msa-weather-report-eureka-feign在idea中运行,成功后,依次手动关掉city和weather两个msa服务,就可以看出效果