zoukankan      html  css  js  c++  java
  • spring cloud微服务

    Eureka  注册服务中心

      作为 SpringCloud 项目,里面是有很多的微服务的。 为了管理这些微服务,SpringCloud 提供了  Eureka 注册中心。 

    1.   导入pom.xml
      <?xml version="1.0" encoding="UTF-8"?>
       
      <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">
          <parent>
              <artifactId>trendParentProject</artifactId>
              <groupId>cn.how2j.trend</groupId>
              <version>0.0.1-SNAPSHOT</version>
          </parent>
          <modelVersion>4.0.0</modelVersion>
       
          <artifactId>eureka-server</artifactId>
       
          <dependencies>
              <dependency>
                  <groupId>org.springframework.cloud</groupId>
                  <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
              </dependency>
          </dependencies>
       
      </project>
      pom.xml
    2. EurekaServerApplication
      添加@EnableEurekaServer 表示它是个注册中心服务器
    3. 端口判断功能
      //8761 这个端口是默认的,就不要修改了,后面的子项目,都会访问这个端口。
              int port = 8761;
              if(!NetUtil.isUsableLocalPort(port)) {
                  System.err.printf("端口%d被占用了,无法启动%n", port );
                  System.exit(1);
              }
              new SpringApplicationBuilder(EurekaServerApplication.class).properties("server.port=" + port).run(args);
      端口判断
    4. application.yml
      eureka:
        instance:
          #hostname: localhost 表示主机名称
          hostname: localhost
        client:
          #registerWithEureka:false. 表示是否注册到服务器。 因为它本身就是服务器,所以就无需把自己注册到服务器了。
          registerWithEureka: false
          #fetchRegistry: false. 表示是否获取服务器的注册信息,和上面同理,这里也设置为 false。
          fetchRegistry: false
          serviceUrl:
            #defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
            #自己作为服务器,公布出来的地址。 比如后续某个微服务要把自己注册到 eureka server, 那么就要使用这个地址: http://localhost:8761/eureka/
            defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
      
      spring:
        application:
          #name: eurka-server 表示这个微服务本身的名称是 eureka-server
          name: eureka-server
    5. ThirdPartIndexDataApplication中@EnableEurekaClient表示注册为微服务
      <!-- 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>pom.xml
              </dependency>

      eureka:
        client:
          serviceUrl:
      #这段表示注册额中心的地址是 http://localhost:8761/eureka/
      #与 eureka server 里的application.yml 中的 defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ 遥相呼应。
            defaultZone: http://localhost:8761/eureka/
      spring:
        application:
      #表示到注册中心里,本应用的名称就是 third-part-index-data-project 
          name: third-part-index-data-project

    工具类RestTemplate

    @Service
    public class IndexService {
        private List<Index> indexes;
        @Autowired RestTemplate restTemplate;
     
        public List<Index> fetch_indexes_from_third_part(){
            List<Map> temp= restTemplate.getForObject("http://127.0.0.1:8090/indexes/codes.json",List.class);
            return map2Index(temp);
        }
        private List<Index> map2Index(List<Map> temp) {
            List<Index> indexes = new ArrayList<>();
            for (Map map : temp) {
                String code = map.get("code").toString();
                String name = map.get("name").toString();
                Index index= new Index();
                index.setCode(code);
                index.setName(name);
                indexes.add(index);
            }
            return indexes;
        }
    }

    启动类另外声明了@Bean 这样IndexService才能够使用 RestTemplate

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

    Hystrix 断路器

      pom.xml

    <!-- 断路器 -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
            </dependency>
     

      

    1. IndexGatherStoreApplication  添加 @EnableHystrix 来启动断路器
    2. 在方法上增加:@HystrixCommand(fallbackMethod = "third_part_not_connected")
      这表示如果下面的方法获取失败了,就自动调用 third_part_not_connected 方法 并返回。
      @HystrixCommand(fallbackMethod = "third_part_not_connected")
          public List<Index> fetch_indexes_from_third_part(){
              List<Map> temp= restTemplate.getForObject("http://127.0.0.1:8090/indexes/codes.json",List.class);
              return map2Index(temp);
          }
       
          public List<Index> third_part_not_connected(){
              System.out.println("third_part_not_connected()");
              Index index= new Index();
              index.setCode("000000");
              index.setName("无效指数代码");
              return CollectionUtil.toList(index);
          }

     Redis

    1. pom.xml
      <!-- redis -->
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-data-redis</artifactId>
              </dependency>
    2. 增加 @EnableCaching 表示启动缓存  增加 redisPort, 用于判断 redis 服务器是否启动
      @EnableCaching
      public class IndexGatherStoreApplication {
              int redisPort = 6379;
              if(NetUtil.isUsableLocalPort(redisPort)) {
                  System.err.printf("检查到端口%d 未启用,判断 redis 服务器没有启动,本服务无法使用,故退出%n", redisPort );
                  System.exit(1);
              }
      1. IndexService  增加 @CacheConfig(cacheNames="indexes")  表示缓存的名称是 indexes. 如图所示,保存到 redis 就会以 indexes 命名

        在fetch_indexes_from_third_part 方法上增加: @Cacheable(key="'all_codes'") 表示保存到 redis 用的 key 就会使 all_codes.
        @CacheConfig(cacheNames="indexes")
        public class IndexService {
        @Cacheable(key="'all_codes'")
            public List<Index> fetch_indexes_from_third_part(){

          @CacheEvict(key="'indexData-code-'+ #p0")
            public void remove(String code){
                 
            }
         
            @CachePut(key="'indexData-code-'+ #p0")
            public List<IndexData> store(String code){
                return indexDatas.get(code);
            }
         
            @Cacheable(key="'indexData-code-'+ #p0")
            public List<IndexData> get(String code){
                return CollUtil.toList();
            }
    3. 新增 RedisCacheConfig 配置类
      import java.time.Duration;
       
      import org.springframework.boot.context.properties.ConfigurationProperties;
      import org.springframework.cache.CacheManager;
      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;
      import org.springframework.data.redis.cache.RedisCacheConfiguration;
      import org.springframework.data.redis.cache.RedisCacheManager;
      import org.springframework.data.redis.connection.RedisConnectionFactory;
      import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
      import org.springframework.data.redis.serializer.RedisSerializationContext;
      import org.springframework.data.redis.serializer.RedisSerializer;
      import org.springframework.data.redis.serializer.StringRedisSerializer;
       
      import com.fasterxml.jackson.annotation.JsonAutoDetect;
      import com.fasterxml.jackson.annotation.PropertyAccessor;
      import com.fasterxml.jackson.databind.ObjectMapper;
       
      @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;
          }
      }

     quartz 定时器 

    1.   pom.xml
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-quartz</artifactId>
              </dependency>    

    2. job 任务类
      import java.util.List;
       
      import cn.hutool.core.date.DateUtil;
      import org.quartz.JobExecutionContext;
      import org.quartz.JobExecutionException;
      import org.slf4j.Logger;
      import org.slf4j.LoggerFactory;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.scheduling.quartz.QuartzJobBean;
       
      import cn.how2j.trend.pojo.Index;
      import cn.how2j.trend.service.IndexDataService;
      import cn.how2j.trend.service.IndexService;
       
      public class IndexDataSyncJob extends QuartzJobBean {
           
          @Autowired
          private IndexService indexService;
       
          @Autowired
          private IndexDataService indexDataService;
           
          @Override
          protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
              System.out.println("定时启动:" + DateUtil.now());
              List<Index> indexes = indexService.fresh();
              for (Index index : indexes) {
                   indexDataService.fresh(index.getCode());
              }
              System.out.println("定时结束:" + DateUtil.now());
       
          }
       
      }
    3. 定时器配置类
      import org.quartz.JobBuilder;
      import org.quartz.JobDetail;
      import org.quartz.SimpleScheduleBuilder;
      import org.quartz.Trigger;
      import org.quartz.TriggerBuilder;
      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;
       
      import cn.how2j.trend.job.IndexDataSyncJob;
       
      @Configuration
      public class QuartzConfiguration {
       
          private static final int interval = 1;
       
          @Bean
          public JobDetail weatherDataSyncJobDetail() {
              return JobBuilder.newJob(IndexDataSyncJob.class).withIdentity("indexDataSyncJob")
              .storeDurably().build();
          }
           
          @Bean
          public Trigger weatherDataSyncTrigger() {
              SimpleScheduleBuilder schedBuilder = SimpleScheduleBuilder.simpleSchedule()
                      .withIntervalInMinutes(interval).repeatForever();
               
              return TriggerBuilder.newTrigger().forJob(weatherDataSyncJobDetail())
                      .withIdentity("indexDataSyncTrigger").withSchedule(schedBuilder).build();
          }
      }

    @CrossOrigin 允许跨域

        向@RequestMapping注解处理程序方法添加一个@CrossOrigin注解,以便启用CORS(默认情况下,@CrossOrigin允许在@RequestMapping注解中指定的所有源和HTTP方法)   

    @GetMapping("/codes")
        @CrossOrigin
        public List<Index> codes() throws Exception {
            System.out.println("current instance's port is "+ ipConfiguration.getPort());
            return indexService.get();
        }

        出于安全原因,浏览器限制从脚本内发起的跨源HTTP请求。 例如,XMLHttpRequest和Fetch API遵循同源策略。 这意味着使用这些API的Web应用程序只能从加载应用程序的同一个域请求HTTP资源,除非使用CORS头文件。

      跨域的体现,在于它的域名不同或者端口不同.

    zuul 网关

    1.   pom.xml
              <dependency>
                  <groupId>org.springframework.cloud</groupId>
                  <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
              </dependency>

    2. application.yml
      eureka:
        client:
          serviceUrl:
            defaultZone: http://localhost:8761/eureka/
      spring:
        application:
          name: index-zuul-service
      zuul:
        routes:
          api-a:
            path: /api-codes/**
      #所有的访问 /api-codes/ 的请求,都会自动转到 INDEX-CODES-SERVICE 去。 而 INDEX-CODES-SERVICE 有3个,就会在这3个之间来回切换
            serviceId: INDEX-CODES-SERVICE
      api-b:
      path: /api-backtest/**
      serviceId: TREND-TRADING-BACKTEST-SERVICE
      api-c:
      path:/api-view/**
      serviceId:TREND-TRADING-BACKTEST-VIEW
    3. Application  @EnableZuulProxy 就表示这是个网关
      import cn.hutool.core.util.NetUtil;
      import org.springframework.boot.autoconfigure.SpringBootApplication;
      import org.springframework.boot.builder.SpringApplicationBuilder;
      import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
      import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
      import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
        
      @SpringBootApplication
      @EnableZuulProxy
      @EnableEurekaClient
      @EnableDiscoveryClient
      public class IndexZuulServiceApplication {
      //  http://127.0.0.1:8031/api-codes/codes
          public static void main(String[] args) {
              int port = 8031;
              if(!NetUtil.isUsableLocalPort(port)) {
                  System.err.printf("端口%d被占用了,无法启动%n", port );
                  System.exit(1);
              }
              new SpringApplicationBuilder(IndexZuulServiceApplication.class).properties("server.port=" + port).run(args);
        
          }
       
      }


    feign 方式访问

      采用feign进行服务之间的调用,可以简化调用流程,真正感觉到是在同一个项目中调用另一个类的方法的欢快感。 

    1.   pom.xml
      <!-- feign -->
              <dependency>
                  <groupId>org.springframework.cloud</groupId>
                  <artifactId>spring-cloud-starter-openfeign</artifactId>
              </dependency>
    2. application.yml
      eureka:
        client:
          serviceUrl:
            defaultZone: http://localhost:8761/eureka/
      spring:
        application:
          name:  trend-trading-backtest-service
      
      #用于开启 feign 模式的断路器
      feign.hystrix.enabled: true
    3. 使用feign 模式从 INDEX-DATA-SERVICE 微服务获取数据
      import java.util.List;
       
      import org.springframework.cloud.openfeign.FeignClient;
      import org.springframework.web.bind.annotation.GetMapping;
      import org.springframework.web.bind.annotation.PathVariable;
       
      import cn.how2j.trend.pojo.IndexData;
      
       //这句话表示访问不了的时候,就去找 IndexDataClientFeignHystrix 要数据了
      @FeignClient(value = "INDEX-DATA-SERVICE",fallback = IndexDataClientFeignHystrix.class)
      public interface IndexDataClient {
          @GetMapping("/data/{code}")
          public List<IndexData> getIndexData(@PathVariable("code") String code);
      }
    4. 熔断发生的时候,此方法就会被调用
      import java.util.List;
       
      import org.springframework.stereotype.Component;
       
      import cn.how2j.trend.pojo.IndexData;
      import cn.hutool.core.collection.CollectionUtil;
       
      @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);
          }
       
      }
    5. Application
      @EnableFeignClients 注解用于启动 Feign 方式
      @EnableFeignClients
      public class TrendTradingBackTestServiceApplication {
       
          public static void main(String[] args) {

    SpringCloud 对 thymeleaf 的支持

    1.   pom.xml
      <!--thymeleaf-->
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-thymeleaf</artifactId>
              </dependency>
    2. application.yml
      eureka:
        client:
          serviceUrl:
            defaultZone: http://localhost:8761/eureka/
      spring:
        application:
          name: trend-trading-backtest-view
        thymeleaf:
          mode: LEGACYHTML5
          encoding: UTF-8
          content-type: text/html
          cache: false
  • 相关阅读:
    日记2014/06/25
    Cocos2dx 3.1.1 学习笔记整理(4):事件监听与Action的初步使用
    Cocos2dx 3.1.1 学习笔记整理(3):逐帧动画
    Cocos2dx 3.1.1 学习笔记整理(2):创建场景与载入图片
    Cocos2dx 3.1.1 学习笔记整理(1) 新建项目
    5-20
    5-19
    5-18
    5-17
    5-16
  • 原文地址:https://www.cnblogs.com/ideaAI/p/13818028.html
Copyright © 2011-2022 走看看