docker pull consul
docker run -d --name consul -p 8500:8500 consul
-- 此处需要用sh执行 因为容器是 alpine 的
docker exec -it 镜像id sh
org.springframework.cloud
spring-cloud-starter-consul-discovery
2.1.2.RELEASE
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-actuator
server:
port: 9081
spring:
application:
name: consul-provider
cloud:
consul:
host: localhost
port: 8500
discovery:
service-name: ${spring.application.name}
package com.zbj.consul.provider;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
/**
* ConsulProviderApplication
*
* @author weigang
* @create 2019-08-10
**/
@SpringBootApplication
@EnableDiscoveryClient
public class ConsulProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ConsulProviderApplication.class, args);
}
}
package com.zbj.consul.provider;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
/**
* ConsulProviderController
*
* @author weigang
* @create 2019-08-10
**/
@RestController
@RequestMapping("/consul/provider")
public class ConsulProviderController {
@Value("${server.port}")
private Integer port;
@RequestMapping(value = "/hello", method = {RequestMethod.GET})
public String hello(String world) {
return "hello " + world + " " + port;
}
}
org.springframework.cloud
spring-cloud-starter-consul-discovery
2.1.2.RELEASE
org.springframework.boot
spring-boot-starter-web
org.springframework.cloud
spring-cloud-starter-openfeign
2.1.2.RELEASE
server:
port: 9083
spring:
application:
name: consul-consuler
cloud:
consul:
host: localhost
port: 8500
discovery:
service-name: ${spring.application.name}
enabled: true
#设置不需要注册到 consul 中
register: false
# feign启用hystrix,才能熔断、降级
feign:
hystrix:
enabled: true
package com.zbj.consul.consumer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
/**
* ConsulConsumerApplication
*
* @author weigang
* @create 2019-08-10
**/
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class ConsulConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsulConsumerApplication.class, args);
}
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
package com.zbj.consul.consumer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
/**
* ConsulConsumerController
*
* @author weigang
* @create 2019-08-10
**/
@RestController
@RequestMapping("/consul/consumer")
public class ConsulConsumerController {
@Autowired
private HelloService helloService;
@Autowired
private DiscoveryClient discoveryClient;
@Autowired
private LoadBalancerClient loadBalancerClient;
@Autowired
private RestTemplate restTemplate;
@GetMapping("/hello")
public String hello(@RequestParam(defaultValue = "world", required = false) String world) {
return helloService.hello(world);
}
@GetMapping("/hello2")
public String hello2(@RequestParam(defaultValue = "world", required = false) String world) {
return restTemplate.getForObject("http://consul-provider/consul/provider/hello?world=" + world, String.class);
}
/**
* 获取所有服务
*
* @return
*/
@GetMapping("/services")
public Object services() {
return discoveryClient.getInstances("consul-provider");
}
/**
* 从所有服务中选择一个服务(轮询)
*/
@GetMapping("/discover")
public Object discover() {
// 不能调用 choose() 返回null 原因-> 提供者pom.xml需要依赖actuator
return loadBalancerClient.choose("consul-provider").getUri().toString();
}
}
package com.zbj.consul.consumer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* HelloService
*
* @author weigang
* @create 2019-08-10
**/
@Component
public class HelloService {
@Autowired
private ConsulFeignClient consulFeignClient;
public String hello(String world) {
return consulFeignClient.sayHelloFromClientConsul(world);
}
}
package com.zbj.consul.consumer;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
/**
* ConsulFeignClient
*
* @author weigang
* @create 2019-08-10
**/
@FeignClient(value = "consul-provider", fallback = FeignClientFallBack.class)
public interface ConsulFeignClient {
/**
* 调用提供者服务
* 方法没有@RequestParam注解 则抛出异常 feign.FeignException$MethodNotAllowed: status 405 reading ConsulFeignClient#sayHelloFromClientConsul(String)
*
* @param world
* @return
*/
@RequestMapping(value = "/consul/provider/hello", method = {RequestMethod.GET})
String sayHelloFromClientConsul(@RequestParam String world);
}
package com.zbj.consul.consumer;
import org.springframework.stereotype.Component;
/**
* FeignClientFallBack
*
* @author weigang
* @create 2019-08-10
**/
@Component
public class FeignClientFallBack implements ConsulFeignClient {
@Override
public String sayHelloFromClientConsul(String world) {
return "hello error";
}
}
ribbon.eureka.enabled=false
-- 但此处没有使用eureka
# 这个BaseRemote是加了@FeignClient注解的类
# 服务提供者的地址,不是服务注册中心的地址
BaseRemote.ribbon.listOfServers=http://localhost:8086
* 异常解析: 直说出现Load balancer does not have available server for client这个错误的情况比较多,意思是负载均衡找不到那个服务,后面跟着服务名,配置错了,某一个名字写错了都可能触发,只要让他找不到就报这个错。上述解决办法适合一部分情况,手动配置多个服务以及负载均衡策略等
* 原因分析: 无意间进入下图位置,依次进入
* 最终看到下图链接中有访问 /actuator/health: 404 ,则加入actuator依赖即可
此处基于consul-provider改造 pom.xml增加依赖
org.springframework.cloud
spring-cloud-starter-consul-config
2.1.2.RELEASE
org.springframework.boot
spring-boot-configuration-processor
true
spring:
profiles:
active: test
spring:
cloud:
consul:
host: localhost
port: 8500
discovery:
service-name: ${spring.application.name}
# 关于spring.cloud.consul.config的配置项描述如下:
#
# enabled 设置config是否启用,默认为true
# format 设置配置的值的格式,可以yaml和properties
# prefix 设置配的基本目录,比如config
# defaultContext 设置默认的配置,此处为应用名
# profileSeparator profiles配置分隔符,默认为‘,’
# date-key为应用配置的key名字,值为整个应用配置的字符串
config:
enabled: true
format: YAML
prefix: config
profile-separator: ":"
data-key: data
default-context: ${spring.application.name}
application:
name: consul-provider
# 换行不能按tab键 启动报错
# 最后复制项有个空格(bar:后有一个空格)
foo:
bar: 123123
package com.zbj.consul.provider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* FooBarController
*
* @author weigang
* @create 2019-08-10
**/
@RestController
// Spring Cloud Comsul Config默认就支持动态刷新,只需要在需要动态刷新的类上加上@RefreshScope注解即可
@RefreshScope
public class FooBarController {
@Value("${foo.bar}")
String fooBar;
@Autowired
private Foo foo;
@GetMapping("/foo")
public String foo() {
return fooBar;
}
@GetMapping("/foo1")
public String foo1() {
return foo.getBar();
}
public String getFooBar() {
return fooBar;
}
public void setFooBar(String fooBar) {
this.fooBar = fooBar;
}
}