前言
springcloud搭建记录
一、建立父项目
1、新建mavem quicklystart项目,为父项目。
2、删除src目录,把项目iml文件放到.idea下。
修改.idea下的workspace.xml,添加以下代码,以便于使用services,运行多服务:
<component name="RunDashboard">
<option name="configurationTypes">
<set>
<option value="SpringBootApplicationConfigurationType" />
</set>
</option>
</component>
3、pom引入依赖(自name之下,全部替换):
<!-- 父项目的打包方式 -->
<packaging>pom</packaging>
<!-- 使用springboot2.0.3版本 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.3.RELEASE</version>
<relativePath/>
</parent>
<!-- 使用springcloud的finchley版本 -->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Finchley.RELEASE</spring-cloud.version>
</properties>
<dependencies>
<!-- 测试类,包含了junit -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- 工具类 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>4.3.1</version>
</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>
二、创建子项目:注册服务中心eureka
1、new一个module,使用maven,不用任何模板。
2、引入pom依赖:
<dependencies>
<!-- 构建服务注册中心 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
3、创建启动类(注意是properties)
@SpringBootApplication //表示它是一个启动类
@EnableEurekaServer //表示它是注册中的 服务器
public class EurekaServer {
public static void main(String[] args) {
//判断端口号是否被占用
int port=8761; //这个是注册中心 服务器的默认端口。
if(!NetUtil.isUsableLocalPort(port)){
System.err.printf("注册中心服务器端口%d已被占用,无法启动",port);//打印端口不可用信息
System.exit(1); //退出控制台
}
//使用springapplicationbuilder启动应用(并设置启动的端口号)
new SpringApplicationBuilder(EurekaServer.class).properties("server.port="+port).run(args);
}
}
4、编写application.yml配置文件
#配置这个微服务的名称
spring:
application:
name: eureka-server
#配置eureka server注册中心服务的相关信息
#hostname(主机名称),register-with-eureka(不注册到服务器,因为它本身就是服务器),
#fetch-registry(是否获取注册信息,为服务器不注册),
#service-url(自己作为服务器公布出来的地址,后续的微服务注册到服务中心,就是要用这个地址)
eureka:
instance:
hostname: localhost
client:
register-with-eureka: false
fetch-registry: false
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
三、创建子项目:第三方数据(看需求,数据在第三方或数据库)
1、pom引入依赖
<dependencies>
<!--spring mvc。web服务-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- eureka-client 。用于注册为微服务-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
2、启动类
@SpringBootApplication
@EnableEurekaClient //注册为微服务
public class ThirdPartData {
public static void main(String[] args) {
int eurekaServerPort=8761;
int port=8090;
//判断eureka是否已经启动
if(NetUtil.isUsableLocalPort(eurekaServerPort)){
System.err.printf("端口%d未启用,判断是eureka-server服务未启动,本服务无法使用,故退出%n",eurekaServerPort);
System.exit(1);
}
//看启动是否带了参数,带参则使用此参数(端口号),使得在启动时,使用不同参数就可以了。
if(args!=null && args.length!=0){
for (String arg : args) {
if(arg.startsWith("port=")) {
String strPort= StrUtil.subAfter(arg, "port=", true);
if(NumberUtil.isNumber(strPort)) {
port = Convert.toInt(strPort);
}
}
}
}
//判断启动端口是否可用
if(!NetUtil.isUsableLocalPort(port)) {
System.err.printf("端口%d被占用了,无法启动%n", port );
System.exit(1);
}
//使用springapplicationbuilder启动应用
new SpringApplicationBuilder(ThirdPartData.class).properties("server.port=" + port).run(args);
}
}
3、pom
spring:
application:
name: third-part-data
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
四、创建子项目:数据采集和存储
1、RestTemple数据采集
(1)引入依赖
<!-- springboot web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- eureka-client -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
(2)service中获取数据
public List<Index> fetch_data_from_third(){
List<Map> maps_data=restTemplate.getForObject("http://127.0.0.1:8090/datas/codes.json",List.class);
return transfromMapToIndex(maps_data);
}
public List<Index> transfromMapToIndex(List<Map> thirdMap){
List<Index> lists=new ArrayList<>();
for(Map map:thirdMap){
Index index=new Index();
String code=map.get("code").toString();
String name=map.get("name").toString();
index.setCode(code);
index.setName(name);
lists.add(index);
}
return lists;
}
(3)启动类注入RestTemple的bean
/注入resttemple
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
(4)配置文件
spring:
application:
name: index-collection-store-service
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
2、断路器:当采集数据为空,或采集出错。
(1)引入pom依赖
<!-- 断路器,调用的微服务,不可用时的处理 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
(2)启动类开启断路器注解
@EnableHystrix //启用断路器
public class CollectionWithStoreData {
(3)service采集数据的方法使用熔断注解,编写熔断方法(返回值和参数须一致)
@HystrixCommand(fallbackMethod = "third_part_not_connected")
public List<Index> fetch_data_from_third(){
List<Map> maps_data=restTemplate.getForObject("http://127.0.0.1:8090/datas/codes.json",List.class);
return transfromMapToIndex(maps_data);
}
public List<Index> thrid_part_no_connect(){
System.out.println("第三方数据未连接,断路器启用");
Index index=new Index("0000","无效代码");
return CollectionUtil.toList(index);
}
3、数据缓存:redis查询使用
(1)下载redis,并启动服务端。下载redis desk manager启动服务端,用于查看数据。
(2)pom引入依赖
<!-- redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
(3)启动类使用启动缓存注解
@EnableCaching //开启缓存,使用redis
public class CollectionWithStoreData {
public static void main(String[] args) {
(4)services使用缓存注解,将数据保存到redis。
@Service
@CacheConfig(cacheNames = "indexes") //存储到redis的缓存名称是indexes
public class IndexService {
//保存到redis,方法返回的所有数据都会被保存
@Cacheable(key="'all_codes'") //保存到redis使用的key值,是all_codes
public List<Index> fetch_data_from_third(){
}
(5)编写redis的配置类
/*
redis的配置类
*/
@Configuration
@EnableRedisRepositories
public class RedisConfig {
private Duration timeToLive = Duration.ZERO;
public void setTimeToLive(Duration timeToLive) {
this.timeToLive = timeToLive;
}
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
RedisSerializer<String> redisSerializer = new StringRedisSerializer();
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
//解决查询缓存转换异常的问题
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.PUBLIC_ONLY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
// 配置序列化(解决乱码的问题)
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(timeToLive)
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
.disableCachingNullValues();
RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
.cacheDefaults(config)
.build();
return cacheManager;
}
}
4、redis中数据的刷新
(1)编写工具类,获得一个service的对象(一个在使用中,去再获取一个)
/*
工具类,用于获取Service
*/
@Component
public class SpringContextUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext;
private SpringContextUtil() {
System.out.println("SpringContextUtil()");
}
public static <T> T getBean(Class<T> clazz) {
return applicationContext.getBean(clazz);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("applicationContext:"+applicationContext);
SpringContextUtil.applicationContext = applicationContext;
}
}
(2)数据刷新:思路,先清空,再去保存
/*
redis数据保存说明:
fresh刷新方法:先从另一个微服务获得对应地址参数code的数据,
——》然后将code和得到的所有数据 保存到一个map对象中
——》如果redis中有这个map对象,删除
——》使用store方法将这个map对象更新到redis中
*/
@Service
@CacheConfig(cacheNames = "index_datas") //在redis中的分组
public class IndexDataService {
@Autowired
RestTemplate restTemplate;
Map<String,List<IndexData>> allIndexDataList=new HashMap<>();//存放所有的指数数据,使用
//三方获取指数数据
public List<IndexData> fetch_third_indexData(String code){
List<Map> mapList=restTemplate.getForObject("http://127.0.0.1:8090/datas/"+code+".json",List.class);
/*JSONArray jsonArray=new JSONArray();//转换的中间量
jsonArray.addAll(mapList);
List<IndexData> list=jsonArray.toJavaList(IndexData.class);*/
return transformMapToT(mapList);
}
//数据转换
public List<IndexData> transformMapToT(List<Map> maps){
List<IndexData> indexDataList=new ArrayList<>();
for(Map map:maps){
IndexData indexData=new IndexData();
String date=map.get("date").toString();
float closePoint= Convert.toFloat(map.get("closePoint"));
indexData.setDate(date);
indexData.setClosePoint(closePoint);
indexDataList.add(indexData);
}
return indexDataList;
}
//保存数据。对应key。更新
@CachePut(key = "'indexdata-code-'+ #p0")//前缀+参数
public List<IndexData> store(String code){
return allIndexDataList.get(code);//将code对应的所有数据保存redis
}
//获取数据
@Cacheable(key = "'indexdata-code-'+ #p0")
public List<IndexData> get(String code){
return CollUtil.toList(); //从缓存中拿到数据
}
//清空redis分组index_datas中对应键值对的数据缓存数据
@CacheEvict(key = "'indexdata-code-'+ #p0")
public void remove(String code){
}
//刷新数据
@HystrixCommand(fallbackMethod = "third_part_no_connect")
public List<IndexData> fresh(String code){
//通过地址拿到对应指数的数据
List<IndexData> indexDataCode=fetch_third_indexData(code);//拿到对应code的数据
allIndexDataList.put(code,indexDataCode);//存入一个code对应的所有数据
System.out.println("code"+code);
System.out.println("数据长度:"+allIndexDataList.get(code).size());
IndexDataService indexDataService= SpringContextUtil.getBean(IndexDataService.class);
indexDataService.remove(code);
return indexDataService.store(code);
}
//熔断方法需要与被注解方法,有一致类型的参数和返回值
public List<IndexData> third_part_no_connect(String code){
System.out.println("third_part_no_connect");
IndexData index= new IndexData();
index.setClosePoint(0);
index.setDate("n/a");
return CollectionUtil.toList(index);
}
}
5、定时器:定时去刷新redis中的数据(作用:定时任务)
(1)引入pom依赖
<!-- 定时器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
(2)编写任务类,指明=定时执行方法
public class DataSynJob extends QuartzJobBean {
@Autowired
IndexDataService indexDataService;
@Autowired
IndexService indexService;
@Override
protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
System.out.println("定时启动:"+ DateUtil.now());
List<Index> indexList=indexService.fresh();//刷新指数数据
for(Index index:indexList){
indexDataService.fresh(index.getCode());
}
System.out.println("定时器结束:"+DateUtil.now());
}
}
(3)编写定时器配置,指明启动的任务类和时间
/*
定时器配置
*/
@Configuration
public class QuartzConfig {
//时间。类常量
private static final int interval=8;
//注入bean
@Bean
public JobDetail weatherDataSyncJobDetail() {
return JobBuilder.newJob(DataSynJob.class).withIdentity("dataSynJob")
.storeDurably().build();
}
@Bean
public Trigger weatherDataSyncTrigger() {
SimpleScheduleBuilder schedBuilder = SimpleScheduleBuilder.simpleSchedule()
.withIntervalInHours(interval).repeatForever();
//.withIntervalInMinutes(interval).repeatForever(); //使用时间间隔--分钟,小时
return TriggerBuilder.newTrigger().forJob(weatherDataSyncJobDetail())
.withIdentity("indexDataSyncTrigger").withSchedule(schedBuilder).build();
}
}
五、创建子项目:指数代码获取集群
1、引入pom
<dependencies>
<!-- springboot web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- eureka-client -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
</dependencies>
2、配置yml
spring:
application:
name: index-codes-service
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
3、配置类(获取当前端口号、redis配置类)
(1)获取当前端口
@Component
public class IpConfiguration implements ApplicationListener<WebServerInitializedEvent> {
private int serverPort;
@Override
public void onApplicationEvent(WebServerInitializedEvent event) {
this.serverPort = event.getWebServer().getPort();
}
public int getPort() {
return this.serverPort;
}
}
(2)redis配置类
@Configuration
@ConfigurationProperties(prefix = "spring.cache.redis")
public class RedisCacheConfig {
private Duration timeToLive = Duration.ZERO;
public void setTimeToLive(Duration timeToLive) {
this.timeToLive = timeToLive;
}
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
RedisSerializer<String> redisSerializer = new StringRedisSerializer();
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
//解决查询缓存转换异常的问题
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.PUBLIC_ONLY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
// 配置序列化(解决乱码的问题)
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(timeToLive)
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
.disableCachingNullValues();
RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
.cacheDefaults(config)
.build();
return cacheManager;
}
}
4、实体类、service、controller
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Index implements Serializable {
String code;
String name;
}
@Service
@CacheConfig(cacheNames = "indexes")
public class IndexService {
List<Index> indexList;
//从redis中去获取数据
@Cacheable(key = "'all_codes'")
public List<Index> get(){
Index index=new Index();
index.setName("无效指数");
index.setCode("0/0");
return CollUtil.toList(index);
}
}
@RestController
@CrossOrigin //允许跨域
public class IndexController {
@Autowired
IndexService indexService;
@Autowired
IpConfig ipConfig;
@GetMapping("/codes")
public List<Index> get(){
System.out.println("当前的端口号:"+ipConfig.getPort());
return indexService.get();
}
}
六、创建子项目:指数数据获取集群(同五)
七、创建网关服务zuul
1、引入pom
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
2、配置文件配置
zuul:
routes:
api-a:
path: /api-codes/**
serviceId: INDEX-CODES-SERVICE
3、启动类开启端口
@SpringBootApplication
@EnableZuulProxy
@EnableEurekaClient
@EnableDiscoveryClient
public class IndexZuulServiceApplication {
// http://127.0.0.1:8031/api-codes/codes
public static void main(String[] args) {
八、模拟回测服务模块 (为回测视图提供数据)
1、引入pom(使用feign从指数数据服务获取数据)
<dependencies>
<!-- springboot web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- eureka-client -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- feign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- 断路器 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
</dependencies>
2、配置文件开启feign
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
spring:
application:
name: trend-trading-backtest-service
feign.hystrix.enabled: true
3、创建指数实体类(略),编写feign客户端端口(从指数服务获取数据)和实现类(熔断)
@FeignClient(value = "INDEX-DATA-SERVICE",fallback = IndexDataClientFeignHystrix.class)
public interface IndexDataClient {
@GetMapping("/data/{code}")
public List<IndexData> getIndexData(@PathVariable("code") String code);
}
@Component
public class IndexDataClientFeignHystrix implements IndexDataClient {
@Override
public List<IndexData> getIndexData(String code) {
IndexData indexData = new IndexData();
indexData.setClosePoint(0);
indexData.setDate("0000-00-00");
return CollectionUtil.toList(indexData);
}
4、service调用客户端获取数据,controller调用service
5、启动类开启feign,调用服务(发现客户端)
@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
@EnableFeignClients
public class TrendTradingBackTestServiceApplication {
6、将这个服务通过网关去映射
api-b:
path: /api-backtest/**
serviceId: TREND-TRADING-BACKTEST-SERVICE
九、模拟回测视图(vue、charj、thymeleaf)—略
十、周边服务(与业务功能并无关系)
1、服务链路追踪
(1)父项目piom下添加依赖
<!--zipkin-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
(2)每个微服务aplication配置添加zipkin的地址
spring:
zipkin:
base-url: http://localhost:941
(3)每个微服务的启动类增加simpler,表示一直取样。
@Bean
public Sampler defaultSampler() {
return Sampler.ALWAYS_SAMPLE;
}
(4)cmd启动链路服务器
java -jar zipkin-server-2.10.1-exec.jar
注:后面步骤配置了消息总线rabbitmq后,启动命令,要带上参数,才能看到配置客户端服务的链路
java -jar zipkin-server-2.10.1-exec.jar --zipkin.collector.rabbitmq.addresses=localhost
(5)启动每一个微服务,访问:http://localhost:9411/zipkin/dependency/,查看微服务调用。
2、配置服务器
(1)新建模块(配置服务器模块)
(2)引入依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
</dependencies>
(3)启动类开启配置服务
@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
@EnableConfigServer
public class ConfigServer {
}
(4)在github上新建和编写配置文件
注意:这里编写的配置文件的文件名前缀必须于[步骤3]配置客户端的spring-application-name一致,否则报错。
(5)配置文件配置git地址和所属目录
spring:
application:
name: index-config-server
cloud:
config:
label: master
server:
git:
#git地址
uri: https://github.com/zhuyanxue/sudyconfig.git
#文件所属目录
search-paths: qinyan
#git使用main,而不是master
default-label: main
(6)访问地址(key值和文件后缀dev):http://localhost:8060/version/dev
3、配置客户端(与配置服务器合用)
(1)在要使用git上配置的微服务添加pom依赖
<!--config-client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
(2)resource目录下添加bootstrap.yml(先于appication.yml启动,通常是不变的系统信息)配置文件。
注意:
spring:
application:
#这里的名称与github上的文件名前缀要一致对应。github文件名只能是:trend-trading-backtest-view-dev.properties
name: trend-trading-backtest-view
bootstrap.yml
spring:
cloud:
config:
label: main
profile: dev
discovery:
enabled: true
#配置服务器的eureka名称
serviceId: index-config-server
#从application移到这里做配置
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
(3)类中使用@Value("${version}")获取配置信息,进行一些操作。
@Value("${version}")
private String version;
4、消息总线bus (实时刷新:不需要重启微服务,只要git上信息改变,页面上的信息也就会改变)
(1)安装rabbitmq,启动rabiitmq。
cmd命令:
net start RabbitMQ
访问:http://127.0.0.1:15672/,即可看到管理界面。
(2)配置客户端引入依赖
<!-- 用于访问路径:/actuator/bus-refresh -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- 支持mq -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
(3)bootstrap新增bus配置和rabbitmq配置
spring:
cloud:
config:
label: main
profile: dev
discovery:
enabled: true
#配置服务器的eureka名称
serviceId: index-config-server
#消息总线bus配置,
bus:
enabled: true
trace:
enabled: true
#rabbitmq配置
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
#从application移到这里做配置
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
(4)application新增management配置,这样才能访问 /actuator/bus-refresh
#跨域,访问 /actuator/bus-refresh,
management:
endpoints:
web:
exposure:
include: "*"
cors:
allowed-origins: "*"
allowed-methods: "*"
(5)使用配置文件的类(如控制类,使用注解允许刷新)
@Controller
@RefreshScope
public class ViewController {
@Value("${version}")
private String version;
(6)新增一个工具类,使用post方式访问:http://localhost:8041/actuator/bus-refresh(端口号与配置客户端端口号一致)。(不用会有405错误,get访问不到)
//这个工具类,实际部署时,是放在本地运行,修改了git之后,就可以运行此工具类,刷新配置
public class FreshConfig {
public static void main(String[] args) {
HashMap<String,String> headers =new HashMap<>();
headers.put("Content-Type", "application/json; charset=utf-8");
System.out.println("因为要去git获取,还要刷新config-server, 会比较卡,所以一般会要好几秒才能完成,请耐心等待");
String result = HttpUtil.createPost("http://localhost:8041/actuator/bus-refresh").addHeaders(headers).execute().body();
System.out.println("result:"+result);
System.out.println("refresh 完成");
}
}
5、断路器(RestTemple和feign两种方式实现)
6、断路器监控(监控数据什么时候可用,什么时候不可用。配合使用断路器的微服务使用)
(1)新建断路器监控模块
(2)pom引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
(3)启动类开启监控注解
@SpringBootApplication
@EnableHystrixDashboard
public class IndexHystrixDashboardApplication {
public static void main(String[] args) {
int port = 8070;
(4)配置文件,配置服务名
spring:
application:
name: hystrix-dashboard
(5)使用断路器的微服务(例如:从数据服务获取数据的视图服务),启动类开启注解,将信息共享给监控中心。
@EnableFeignClients
//共享信息给监控中心
@EnableCircuitBreaker
public class ProductViewServiceFeignApplication {
(6)测试类(测试监控中心)【实际项目,发布时,可用去掉。这里只是为了查看效果,不断进行数据访问】
public class AccessViewService {
public static void main(String[] args) {
while(true) {
ThreadUtil.sleep(1000);
try {
String html= HttpUtil.get("http://127.0.0.1:8012/products");
System.out.println("html length:" + html.length());
}
catch(Exception e) {
System.err.println(e.getMessage());
}
}
}
}
(7)启动项目
访问http://localhost:8020/hystrix——》输入地址http://localhost:8012/actuator/hystrix.stream
——》monitor stream ,就可查看监控信息。
7、断路器聚合监控(断路器监控只是对一个微服务实例;当微服务成为集群时,需要使用断路器聚合监控将多个实例聚合到turbine中)
(1)创建聚合监控模块
(2)引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-turbine</artifactId>
</dependency>
(3)启动类是turbine注解
@SpringBootApplication
@EnableTurbine
public class ProductServiceTurbineApplication {
(4)配置文件application.yml配置要监控的微服务(会发生熔断的微服务集群)
spring:
application.name: turbine
turbine:
aggregator:
clusterConfig: default
appConfig: product-view-service-feign ### 配置Eureka中的serviceId列表,表明监控哪些服务
clusterNameExpression: new String("default")
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
(5)编写测试类访问
public class AccessViewService {
public static void main(String[] args) {
while(true) {
ThreadUtil.sleep(1000);
access(8012);
access(8013);
}
}
public static void access(int port) {
try {
String html= HttpUtil.get(String.format("http://127.0.0.1:%d/products",port));
System.out.printf("%d 地址的视图服务访问成功,返回大小是 %d%n" ,port, html.length());
}
catch(Exception e) {
System.err.printf("%d 地址的视图服务无法访问%n",port);
}
}
}
(6)启动项目:访问http://localhost:8070/hystrix,输入参数http://localhost:8080/turbine.stream,即可查看聚合监控效果。
End、结尾
1、端口号总结:
(1)微服务
eureka-server: 8761
product-data-service: 8001,8002,8003
product-view-service-ribbon: 8010
product-view-service-feign: 8012, 8013, 8014
hystrix-dashboard: 8020
turbine: 8021
config-server: 8030
zuul: 8040
(2)第三方
zipkin:9411
rabbitMQ: 5672
2、项目导图
,