SpringCloud Consul Finchley架构测试

整体图

image.png

1. 搭建Consul Server Cluster

REF sites:

https://github.com/hashicorp/consul
https://www.consul.io/downloads.html

过程略(ACL)
[root@hadoopnode3 ~]# consul members -http-addr=10.0.0.8:8500
Node     Address            Status  Type    Build  Protocol  DC   Segment
node104  10.0.0.104:8301  alive   server  1.4.4  2         dc1  
node19   10.0.0.19:8301     alive   server  1.4.4  2         dc1  
node9    10.0.0.9:8301     alive   server  1.4.4  2         dc1  
node8   10.0.0.8:8301     alive   client  1.4.4  2         dc1  

2. 搭建基础maven parent projects in STS/eclipse

image.png


    4.0.0

    com.sc
    spring-boot-cloud-consul
    1.0-SNAPSHOT
    pom
    ${project.artifactId}

    
        org.springframework.boot
        spring-boot-starter-parent
        2.0.4.RELEASE
        
    

    
        consul-gateway       
        consul-auth-service
        consul-service-a
        consul-service-b
        consul-common
  

    
        UTF-8
        1.8
        1.0.0
        spring-boot-cloud-consul
        Finchley.SR1
        2.0.4
        
    

    
        
            org.springframework.boot
            spring-boot-starter-actuator
        
        
            org.springframework.boot
            spring-boot-starter-test
            test
        
    

    
        
            
            
                org.springframework.cloud
                spring-cloud-dependencies
                ${spring.cloud.version}
                pom
                import
            
            
            
        
    

    
        
            
                
                    org.springframework.boot
                    spring-boot-maven-plugin
                    
                        
                            
                                repackage
                            
                        
                    
                
                
                    com.spotify
                    docker-maven-plugin
                    ${docker.plugin.version}
                    
                        
                            package
                            
                                build
                            
                        
                    
                    
                        ${docker.image.prefix}/${project.artifactId}
                        ${project.basedir}/
                        
                            
                                /
                                ${project.build.directory}
                                ${project.build.finalName}.jar
                            
                        
                    
                
            
        
    

3. OAuth2认证项目 - consul-auth-service


│   Dockerfile
│   pom.xml

├───src
│   ├───main
│   │   ├───java
│   │   │   └───com
│   │   │       └───sc
│   │   │           └───auth
│   │   │               │   AuthApplication.java
│   │   │               │
│   │   │               ├───config
│   │   │               │       OAuthConfiguration.java
│   │   │               │       ResourceServerConfiguration.java
│   │   │               │       WebSecurityConfigurer.java
│   │   │               │
│   │   │               ├───controller
│   │   │               │       UserController.java
│   │   │               │
│   │   │               ├───entity
│   │   │               │       Authority.java
│   │   │               │       OAuthUser.java
│   │   │               │       User.java
│   │   │               │
│   │   │               ├───jpa
│   │   │               │       AuthorityJPA.java
│   │   │               │       UserJPA.java
│   │   │               │
│   │   │               └───service
│   │   │                       AuthUserDetailsService.java
│   │   │
│   │   └───resources
│   │           application.yml
│   │           bootstrap.yml
│   │           oauth2.sql
主要是提供oauth2 server side 认证. oauth2 信息包括用户表创建都在mysql, 参考oauth2.sql
参考配置:
        //data init script - oauth_client_details
        /**
        clients.jdbc(dataSource).passwordEncoder(passwordEncoder)
            .withClient("client").secret("secret")
                .authorizedGrantTypes("password", "refresh_token").scopes("read", "write")
                .accessTokenValiditySeconds(3600) // 1 hour
                .refreshTokenValiditySeconds(2592000) // 30 days
                .and()
                .withClient("service-a").secret("password")
                .authorizedGrantTypes("client_credentials", "refresh_token").scopes("server")
                .and()
                .withClient("service-b").secret("password")
                .authorizedGrantTypes("client_credentials", "refresh_token").scopes("server")
                .and()
                .withClient("client_code") // client_id
                .secret("secret_code") // client_secret
                .authorizedGrantTypes("authorization_code") // 该client允许的授权类型
                .scopes("app"); // 允许的授权范围 ;
        */

4. SpringCloud Gateway 项目 - consul-gateway

用户限流, 负载平衡, 功能转向等, 主要是配置
image.png
Spring Cloud Consul Config- support dynamic updates
image.png
server:
  port: 8900
 
spring:        
    #限流redis server 
  redis:
    host: 10.0.0.119
    port: 6379
    database: 0
    
  cloud:
    gateway:
      discovery:
        locator:
          # 是否用service name 自动发现
          enabled: false
          lowerCaseServiceId: true
                
      default-filters:
      #全局配置    
      # 熔断降级配置
      - name: Hystrix
        args:
          name : default
          fallbackUri: 'forward:/defaultfallback'    
          
      #RequestRateLimiter can not be global until 2.1.0
      #https://github.com/spring-cloud/spring-cloud-gateway/pull/720   
      
      routes:
      - id: auth-service
        uri: lb://consul-auth-service
        predicates:
        - Path=/uaa/**
        filters:
        - StripPrefix=1
      
      
      - id: service-a
        uri: lb://consul-service-a
        predicates:
        - Path=/svca/**
        filters:
        - StripPrefix=1
        # redis限流 , filter名称必须是RequestRateLimiter
        - name: RequestRateLimiter
          args:
                           # 使用SpEL名称引用Bean,与上面新建的RateLimiterConfig类中的bean的name相同
            key-resolver: '#{@urlPathKeyResolver}'
                            # 每秒最大访问次数
            redis-rate-limiter.replenishRate: 4
                            # 令牌桶最大容量
            redis-rate-limiter.burstCapacity: 4 
          
      - id: svcb
        uri: lb://consul-service-b
        predicates:
        - Path=/svcb/**
        filters:
        - StripPrefix=1
        - name: Hystrix
          args:
            name: fallback
            fallbackUri: 'forward:/fallback'        

      - id: rewritepath_route
        uri: https://blog.csdn.net
        predicates:
        - Path=/csdn/**
        filters:
        - RewritePath=/csdn/(?.*), /$\{segment}    
        
      - id: limit_route
        uri: http://www.baidu.com
        predicates:
        - Path=/baidu/**
        - After=2019-04-10T08:00:00.001-07:00[America/Denver]
        filters:
        - name: RequestRateLimiter
          args:
            key-resolver: '#{@remoteAddrKeyResolver}'
            redis-rate-limiter.replenishRate: 1
            redis-rate-limiter.burstCapacity: 1       



  sleuth:
    sampler:
      percentage: 1
      probability: 1.0 # 将采样比例设置为 1.0,也就是全部都需要。默认是 0.1
    integration:
      enabled: false
    scheduled:
      skip-pattern: "^org.*HystrixStreamTask$"
    web:
      client:
        enabled: true #web开启sleuth功能
      
  zipkin:
    base-url: http://${ZIPKIN-SERVER:zipkin-server}:9411      

#totally customized vars
#not solved, https://github.com/spring-cloud/spring-cloud-security/issues/61
authserver:
    #hostname: auth-service
    hostname: AUTH-SERVICE
    port: 8001
    contextPath: 

#https://docs.spring.io/spring-security-oauth2-boot/docs/current-SNAPSHOT/reference/htmlsingle/
security:
  oauth2:
    resource:
      user-info-uri: http://${authserver.hostname}:${authserver.port}${authserver.contextPath}/current
      #prefer-token-info: true # Use the token info, can be set to false to use the user info.

#HystrixDashboard
management:
  endpoints:
    web:
      exposure:
        #include: hystrix.stream, info, health
        include: "*"
      cors:
        allowed-origins: "*"
        allowed-methods: "*"
      #base-path: actuator //by default

turbine:
  app-config: consul-service-a,consul-service-b
  aggregator:
    clusterConfig: default
  clusterNameExpression: new String("default")
  combine-host: true
  instanceUrlSuffix:
    default: actuator/hystrix.stream  

hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 3000 #默认:(方法上记得要加上@HystrixCommand,否则无效)
#          strategy: SEMAPHORE
feign:
  hystrix:
    enabled: true 
              
ribbon:
  ReadTimeout: 5000
  ConnectTimeout: 5000
  #https://github.com/spring-cloud/spring-cloud-security/issues/61
  http:
    client:
      enabled: true  
      

    
logging:
  level:
    root: INFO
    org.springframework: INFO   

5. 具体子项目实现demo - consul-service-a&consul-service-b

重点是测试Feigh client and Ribbon client
FeignClient需要配置Oauth2 client信息传递
package com.sc.svca.client;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
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;



@FeignClient(name = "consul-service-b", fallback = FeignServiceBClient.ServiceBClientFallback.class)
public interface FeignServiceBClient {

    @GetMapping(value = "/test")
    String test();
    
    @RequestMapping(value = "/hi",method = RequestMethod.GET)
    String sayHiFromClientOne(@RequestParam(value = "name") String name);
    
    //String getSessionId(@RequestHeader("X-Auth-Token") String token);

    @Component
    class ServiceBClientFallback implements FeignServiceBClient {

        private static final Logger LOGGER = LoggerFactory.getLogger(ServiceBClientFallback.class);

        @Override
        public String test() {
            LOGGER.info("now flow into callback method...");
            return "SERVICE B FAILED! - FALLING BACK";
        }
        
        @Override
        public String sayHiFromClientOne(String name) {
            return "SERVICE sayHiFromClientOne  FAILED! - FALLING BACK - with parameter - name:" + name;
            //SERVICE sayHiFromClientOne  FAILED! - FALLING BACK - with parameter - name:abc
            
        }
    }
}
Ribbon Client也需要公用consul-common模块的oauth2 client配置
package com.sc.svca.service;

import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.sc.cmn.config.RestTemplateClient;

 

/**
   RestTemplate(Oauth2 supported)+Ribbon 
 **/
@Service
public class RibbonUserServiceClient extends RestTemplateClient {

    //@Autowired
    //RestTemplate restTemplate;

    @HystrixCommand(fallbackMethod = "userIdNotFound")
    public String getCurrUserId(HttpServletRequest request) {

        String userId = getRestObject("consul-auth-service", "/getCurrUserId", getHeaders(request), null, String.class);
 
        return userId;
    }

    public String userIdNotFound(HttpServletRequest request) {
        return "can not find any userId logged in!";
    } 
}
consul-service-a sample
package com.sc.svca;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.cloud.netflix.turbine.EnableTurbine;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableOAuth2Client;

import com.netflix.hystrix.contrib.metrics.eventstream.HystrixMetricsStreamServlet;
import com.sc.cmn.config.FeignHystrixConcurrencyStrategy;

@EnableTurbine //Notes: 开启turbine,@EnableTurbine注解包含了@EnableDiscoveryClient注解
//@EnableTurbineStream # 不可共存
@EnableDiscoveryClient 
@EnableFeignClients
@SpringBootApplication
@EnableCircuitBreaker //Note: 如果不加,Dashboard无法接收到来自Feign内部断路器的监控数据
@EnableHystrix //Note: 开启断路器
//@EnableHystrixDashboard //Hystrix仪表板, server UI part, now using standalone version
@EnableOAuth2Client
@ComponentScan({"com.sc.cmn","com.sc.svca"})
public class ServiceAApplication {
    
     /**
      * http://localhost:8764/turbine.stream
        * 访问地址 http://localhost:8762/actuator/hystrix.stream
        * @param args
     */

    public static void main(String[] args) {
        SpringApplication.run(ServiceAApplication.class, args);
    }
    
 
    @Bean
    public FeignHystrixConcurrencyStrategy feignHystrixConcurrencyStrategy() {
        return new FeignHystrixConcurrencyStrategy();
    } 
    
    @Bean
    public ServletRegistrationBean getServlet(){
        HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
        ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
        registrationBean.setLoadOnStartup(1);
        registrationBean.addUrlMappings("/actuator/hystrix.stream");
        registrationBean.setName("HystrixMetricsStreamServlet");
        return registrationBean;
    }
    

}
Controller测试部分sample
package com.sc.svca.controller;

import java.security.Principal;

import javax.servlet.http.HttpServletRequest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.context.config.annotation.RefreshScope;
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 org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import com.sc.cmn.util.ResponseModel;
import com.sc.svca.client.FeignServiceBClient;
import com.sc.svca.service.RibbonServiceBClient;
import com.sc.svca.service.RibbonUserServiceClient;

/**
 * curl -X POST -vu client:secret http://gateway-server:8900/uaa/oauth/token -H "Accept: application/json" -d "password=password&username=admin&grant_type=password&scope=read%20write"
 * 
 *
 */
//Spring Cloud Consul Config support dynamic change, add @RefreshScope
@RefreshScope
@RestController
public class ServiceAController {

    private static final Logger log = LoggerFactory.getLogger(ServiceAController.class);

    //from application.yml config
    @Value("${msg:unknown}")
    private String msg;
    
    //from consul config
    @Value("${my.consul.val}")
    String myConsulVal;

    //@Autowired
    //EurekaDiscoveryClient discoveryClient;
    //replace eureka with consul
    @Autowired
    private DiscoveryClient discoveryClient;
    
    @Autowired
    private FeignServiceBClient serviceBClient;

    @Autowired
    private RibbonUserServiceClient userService;

    @GetMapping(value = "/")
    public String printServiceA() {

        log.info(discoveryClient.getInstances("consul-service-a").toString());      

        ServiceInstance serviceInstance = discoveryClient.getInstances("consul-service-a").get(0);

        return serviceInstance.getServiceId() + " (" + serviceInstance.getHost() + ":" + serviceInstance.getPort() + ")"
                + "===>name:" + msg + "
"; //consul-service-a (10.0.0.63:8901)===>name:test_string
} @Value("${server.port}") String port; @RequestMapping("/hi2") public String home(@RequestParam(value = "name", defaultValue = "defName") String name) { return "hi " + name + " ,i am from port:" + port + ", get consul config my.consul.val=" + myConsulVal; /** * dynamic config change on Consul * [root@hadoopnode4 sts]# curl -i -H "Authorization: Bearer 8cb5bd5b-86c7-4f06-991c-ccc358060bfc" http://10.0.0.8:8900/svca/hi2 hi defName ,i am from port:8901, get consul config my.consul.val=myVal [updated on Consul console...] [root@hadoopnode4 sts]# curl -i -H "Authorization: Bearer 8cb5bd5b-86c7-4f06-991c-ccc358060bfc" http://10.0.0.8:8900/svca/hi2 hi defName ,i am from port:8901, get consul config my.consul.val=myVal_new * */ } /** * way 1 - feign fallback * * the name parameter must be provided when calling * * [root@hadoopnode4 ~]# curl -i -H "Authorization: Bearer * a8914a80-c6c0-46ae-9828-2bb7c8cb521a" http://10.0.0.63:8080/hi?name=abc * * @param name * @return */ @GetMapping(value = "/hi") public String sayHi(@RequestParam String name) { log.info("calling into svca-hi with name======>" + name); return serviceBClient.sayHiFromClientOne(name); } /** * * [root@hadoopnode4 ~]# curl -i -H "Authorization: Bearer * 1c76f4f5-cf95-428c-ae96-c45715c8f7ae" http://10.0.0.63:8901/test * {"status":200,"msg":"æˆ�功","data":{},"perm":null} * * @return */ @GetMapping(value = "/test") public String test() { return serviceBClient.test(); } @Autowired RibbonServiceBClient serviceBService; /** * way 2 - ribbon fallback * * [root@hadoopnode4 ~]# curl -X POST -i -H "Authorization: Bearer * 1c76f4f5-cf95-428c-ae96-c45715c8f7ae" http://10.0.0.63:8901/hitest -d * 'name=zhou&b=2&c=3' * * {"status":200,"msg":"æˆ�功","data":{"b":"2","c":"3","name":"zhou"}}[root@hadoopnode4 * ~]# * * @return */ @RequestMapping(value = "/hitest", method = RequestMethod.POST) public ResponseModel hiTest(HttpServletRequest request) { // request.getParameter("name") //hib?name=abc return serviceBService.hiService(request); } @RequestMapping(value = "/hib") public String hiB(HttpServletRequest request) { return serviceBService.hi(request); } @RequestMapping(value = "/getCurrUserId") public String getCurrUserId(HttpServletRequest request) { return userService.getCurrUserId(request); } @GetMapping(path = "/current") public Principal getCurrentAccount(Principal principal) { return principal; } @Autowired private LoadBalancerClient loadBalancer; /** * 获取所有服务 */ @RequestMapping("/services") public Object services() { // 获取对应服务名称的所有实例信息 return discoveryClient.getInstances("consul-service-b"); //[{"serviceId":"consul-a","host":"10.0.0.63","port":8201,"secure":false,"metadata":{"secure":"false"},"uri":"http://10.0.0.63:8201","scheme":null}] } @Autowired RestTemplate restTemplate; /** * Demo for invalid call, without token passed * error":"invalid_token * * @return */ @RequestMapping("/callB") public String call() { ServiceInstance serviceInstance = loadBalancer.choose("consul-service-b"); log.info("serviceInstance.getUri:" + serviceInstance.getUri()); log.info("serviceInstance.getServiceId:" + serviceInstance.getServiceId()); String callServiceResult = restTemplate.getForObject(serviceInstance.getUri().toString() + "/hi", String.class); log.info("callServiceResult:" + callServiceResult); return callServiceResult; } }
基础pom配置


    4.0.0

    
        com.sc
        spring-boot-cloud-consul
        1.0-SNAPSHOT
    

    consul-service-a
    1.0-SNAPSHOT
    jar
    ${project.artifactId}

    

        
        
            com.sc
            consul-common
            1.0-SNAPSHOT
        

        
            org.springframework.boot
            spring-boot-starter-web
        
        
        
            org.springframework.cloud
            spring-cloud-starter-consul-discovery
        
        
            org.springframework.cloud
            spring-cloud-starter-consul-config
        
        
        
            org.springframework.cloud
            spring-cloud-starter-security
        
        
            org.springframework.cloud
            spring-cloud-starter-openfeign
         

        
        
            org.springframework.cloud
            spring-cloud-starter-netflix-hystrix
        
        
         
        
        
            org.springframework.cloud
            spring-cloud-starter-netflix-ribbon
        
        
        
        
            org.springframework.boot
            spring-boot-starter-actuator
        
        

        
        
            org.springframework.cloud
            spring-cloud-starter-sleuth
        
        
            org.springframework.cloud
            spring-cloud-starter-zipkin
        

        
        
            org.springframework.cloud
            spring-cloud-starter-netflix-turbine
            
                
                    org.springframework.cloud
                    
                        spring-cloud-starter-netflix-eureka-client
                    
                
            
        

        
            org.springframework.boot
            spring-boot-starter-test
            test
        
    



    
        ${project.artifactId}
        
            
                org.springframework.boot
                spring-boot-maven-plugin
            
            
        
    


连接注册consul部分yml配置
 
# document: https://docs.spring.io/spring-security-oauth2-boot/docs/current-SNAPSHOT/reference/htmlsingle/

security:
  oauth2:
    client:
      clientId: service-a  #clientId from OAUTH2 definition in db
      clientSecret: ${SVCA_CLIENT_SECRET_OAUTH2:password} 
      accessTokenUri: http://${authserver.hostname}:${authserver.port}${authserver.contextPath}/oauth/token
      grant-type: client_credentials,password
      scope: server

spring:  
  profiles:
    active: consul-dev  #consul 配置key profile
  application:
    name: consul-service-a
  cloud:
    consul:
      host: hadoopnode3
      port: 8500
      discovery:
        serviceName: consul-service-a
        instance-id: ${spring.application.name}:${spring.cloud.client.ip-address}:${server.port} #实例ID
        health-check-path: /actuator/health                                                      #健康检查
        health-check-interval: 10s
        prefer-ip-address: true
        #ip-address: 10.0.0.63
        #register-health-check: true
        #health-check-critical-timeout: 2m
      token: ${SPRING_CLOUD_CONSUL_TOKEN:245d0a09-7139-bbea-aadc-ff170a0562b1}
      config:
        enabled: true #设置config是否启用,默认为true
        format: yaml   #设置配置的值的格式,可以yaml和properties        
        prefix: config     #目录,比如config
        profile-separator: ':'    #配置分隔符,默认为‘,’
        data-key: data  #为应用配置的key名字,值为整个应用配置的字符串
        
        #defaultContext 设置默认的配置,被所有的应用读取,本例子没用的

6. Zipkin Server

java -jar zipkin-server-2.12.9-exec.jar --STORAGE_TYPE=mysql --MYSQL_DB=zipkin --MYSQL_USER=root --MYSQL_PASS=mypass --MYSQL_HOST=10.0.0.8 --MYSQL_TCP_PORT=3306

https://zipkin.io/pages/architecture.html?from=singlemessage&isappinstalled=0

看到 每个call的时间及轨迹
image.png

7. Hystrix Dashboard

感受实时统计

https://github.com/kennedyoliveira/standalone-hystrix-dashboard

image.png
[root@hadoopnode4 sts]# java -jar standalone-hystrix-dashboard-1.5.6-all.jar
http://hadoopnode4:7979/hystrix-dashboard/

7. 结束语

https://github.com/dong099/spring-cloud-enterprise-consul

你可能感兴趣的:(SpringCloud Consul Finchley架构测试)