可以参考:https://blog.csdn.net/zajiayouzai/article/details/80612729
项目的构建:
1.父工程:pom
<?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"> <modelVersion>4.0.0</modelVersion>
<groupId>com.itmuch.cloud</groupId> <artifactId>microservice-spring-cloud</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>pom</packaging>
<modules> <module>microservice-provider-user</module> <module>microservice-discovery-eureka</module> </modules>
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties>
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.4.1.RELEASE</version> </parent> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Camden.SR1</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
|
2.子工程:Eureka:discovery:
Eureka:
2.1服务端:
依赖:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka-server</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> |
配置:(yml)
security: #需要导入spring-boot-starter-security basic: enabled: true user: name: user password: password123 #为Euraka server 添加一个认证,当访问localhost:8761会要求验证 server: port: 8761 //Euraeka 默认端口 eureka: client: register-with-eureka: false //目前只是单机,所以false,表示不把自己当客户端使用 fetch-registry: false //目前只是单机,所以false service-url: defaultZone: http://user:password123@localhost:8761/eureka |
通过:http://user:password123@localhost:8761/eureka 可以查看注册情况
使用:
@SpringBootApplication @EnableEurekaServer public class EurekaApplication { public static void main(String[] args) { SpringApplication.run(EurekaApplication.class, args); } } |
2.2服务客户端(注册):
依赖:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> |
配置:
eureka: client: healthcheck: enabled: true #心跳检测 serviceUrl: defaultZone: http://user:password123@localhost:8761/eureka instance: prefer-ip-address: true #默认注册后会用机器名:此处让它显示ip instance-id: #自定义显示${spring.application.name}:${spring.cloud.client.ipAddress}:${spring.application.instance_id:${server.port}}
|
使用:
@SpringBootApplication @EnableEurekaClient public class MicroserviceSimpleConsumerMovieApplication { @Bean public RestTemplate restTemplate() { return new RestTemplate(); } public static void main(String[] args) { SpringApplication.run(MicroserviceSimpleConsumerMovieApplication.class, args); } } |
2.3健康检查heath checks:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> |
配置yml:
eureka: client: healthcheck: enabled: true |
2.4.使用EurekaClient
@Autowired private UserRepository userRepository;
@Autowired private EurekaClient eurekaClient;
@Autowired private DiscoveryClient discoveryClient;
@GetMapping("/eureka-instance") public String serviceUrl() { InstanceInfo instance = this.eurekaClient.getNextServerFromEureka("MICROSERVICE-PROVIDER-USER", false); return instance.getHomePageUrl(); }
@GetMapping("/instance-info") public ServiceInstance showInfo() { ServiceInstance localServiceInstance = this.discoveryClient.getLocalServiceInstance(); return localServiceInstance; } |
2.5使用RestTemplate
配置:
@SpringBootApplication @EnableEurekaClient public class ConsumerMovieRibbonApplication {
@Bean //注入一个RestTemplate public RestTemplate restTemplate() { return new RestTemplate(); }
public static void main(String[] args) { SpringApplication.run(ConsumerMovieRibbonApplication.class, args); } } |
使用:
@RestController public class MovieController { @Autowired private RestTemplate restTemplate; @Autowired private LoadBalancerClient loadBalancerClient;
@GetMapping("/movie/{id}") public User findById(@PathVariable Long id) { return this.restTemplate.getForObject("http://localhost:8900/simple/ " + id, User.class); } } |
2.6 Robbon(客户端负载均衡)
依赖:(Euraka server中包含;可以不导入)
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix</artifactId> <version>2.0.1.RELEASE</version> </dependency> |
配置:(在RestTemplate上加注解@LoadBalanced)
@SpringBootApplication @EnableEurekaClient public class ConsumerRibbonApplication {
@Bean @LoadBalanced public RestTemplate restTemplate() { return new RestTemplate(); }
public static void main(String[] args) { SpringApplication.run(ConsumerRibbonApplication.class, args); } } |
使用:
public class MovieController { @Autowired private RestTemplate restTemplate; @Autowired private LoadBalancerClient loadBalancerClient;
@GetMapping("/movie/{id}") public User findById(@PathVariable Long id) { //使用spring.application.name 及使用Vip virtual ip return this.restTemplate.getForObject("http://microservice-provider-user/simple/" + id, User.class); } |
2.7 Robbon 的其他均衡方式,与自定义
(暂时先不管)
2.8 Feign
依赖:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-feign</artifactId> </dependency> |
使用:1.在主类上加上@EnableFeignClients
@SpringBootApplication @EnableDiscoveryClient @EnableFeignClients public class FeignApplication {
public static void main(String[] args) { SpringApplication.run(FeignApplication.class, args); }
} |
2. 定义服务接口类
使用@FeignClient("User-service-0")注解来绑定该接口对应biz-service-0服务
提示:1.必须使用@RequestMapping 不支持:@GgetMapping ,@PostMapping
2. rest 路径参数名字,必须指定,如:/user/{id} public User getuserinfo(@Pathvaluele(“id”) String id);
@FeignClient("User-service-0") //参数为对应的服务 server.application.name public interface UserClient { @RequestMapping(method = RequestMethod.GET, value ="/getuser") public User getuserinfo(); @RequestMapping(method = RequestMethod.GET, value = "/getuser") public String getuserinfostr(); @RequestMapping(method = RequestMethod.GET, value = "/info") public String info(); } |
3.调用接口:
在web层中调用上面定义的UserController,具体如下
@RestController public class UserController {
@Autowired UserClient userClient;
@RequestMapping(value = "/getuserinfo", method = RequestMethod.GET) public User getuserinfo() { return userClient.getuserinfo(); }
} |
2.9 Feign的自定义配置https://blog.csdn.net/shunhua19881987/article/details/75491971
(1)自定义配置:
如果Eureka添加了安全验证,则需要配置上面的用户名、密码.
(2)在feignClient类中修改@FeignClient注解,在注解中添加新定义的Feign配置configuration的值:
注意:服务提供者的接口参数必须写在请求路径中,否则请求无效
不使用自定义的feignClint配置:
(4)Feign日志打印配置
在configuration文件中添加feign日志配置:
在application配置文件中添加feign日志配置:
logging: level: com.itmuch.cloud.feign.UserFeignClient: DEBUG //需要学全类名 |
(5)FeignClient第一次请求报TimeOut问题解决方案:
参考1:
延长hystix的连接超时时间,默认时间是1秒 在application配置文件中添加如下配置: hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds:5000 禁用hystix的超时时间 在application配置文件中添加如下配置: hystrix.command.default.execution.timeout.enabled: false 禁用hystix 在application配置文件添加如下配置信息: feign.hystrix.enabled: false 参考网址: 超时的issue: https://github.com/spring-cloud/spring-cloud-netflix/issues/768 超时的解决方案: https://stackoverflow.com/questions/27375557/hystrix-command-fails-with-timed-out-and-no-fallback-available
|
或者参考2(application.yml)
# 解决第一次请求报超时异常的方案: # hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 5000 # 或者: # hystrix.command.default.execution.timeout.enabled: false # 或者: feign.hystrix.enabled: false ## 索性禁用feign的hystrix
# 超时的issue:https://github.com/spring-cloud/spring-cloud-netflix/issues/768 # 超时的解决方案: http://stackoverflow.com/questions/27375557/hystrix-command-fails-with-timed-out-and-no-fallback-available # hystrix配置: https://github.com/Netflix/Hystrix/wiki/Configuration#execution.isolation.thread.timeoutInMilliseconds |
3.0 Hystrix (断路器)
依赖:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> </dependency> |
使用:在主类上加: @EnableCircuitBreaker
@SpringBootApplication @EnableEurekaClient @EnableCircuitBreaker public class ConsumerMovieRibbonApplication {
@Bean @LoadBalanced public RestTemplate restTemplate() { return new RestTemplate(); } public static void main(String[] args) { SpringApplication.run(ConsumerMovieRibbonApplication.class, args); } } |
简单使用:
@RestController public class MovieController { @Autowired private RestTemplate restTemplate;
@GetMapping("/movie/{id}") @HystrixCommand(fallbackMethod = "findByIdFallback") //指定(备胎)方法名 public User findById(@PathVariable Long id) { return this.restTemplate.getForObject("http://microservice-provider-user/simple/" + id, User.class); } /** 如果上面方法短路,则会默认是用下面这个方法,返回固定值(备胎)*/ public User findByIdFallback(Long id) { User user = new User(); user.setId(0L); return user; } }
|
增强:参考:https://blog.csdn.net/qq_24504315/article/details/79120450
提示:配置了,commandProperties 后, @HystrixCommand 和HystrixCommand作用的方法是一个线程,否则@HystrixCommand 是一个隔离的线程
@RestController @SessionScope //正常情况不配置,如果运行时报找不到上下文时,就配置 public class OrderController { @Autowired private RestTemplate restTemplate; /** * SEMAPHORE 信号量隔离只是限制了总的并发数,服务使用主线程进行同步调用,即没有线程池。因此,如果只是想限制某个服务的总并发调用量或者调用的服务不涉及远程调用的话,可以使用轻量级的信号量来实现。 * @param id * @return */ @GetMapping("/order/{id}") @HystrixCommand(fallbackMethod="findByIdFallback",commandProperties={@HystrixProperty(name="execution.isolation.strategy", value="SEMAPHORE")})//信号量隔离 public User findById(@PathVariable Long id) { // http://localhost:7900/user/ // VIP virtual IP(http://localhost:7900/user/表示虚拟ip) // HAProxy Heartbeat return this.restTemplate.getForObject("http://spring-cloud-user/user/" + id, User.class);//直接写服务提供者的spring.application.name,这样方便于动态IP } /** * 若是出现调用findById方法时出现错误或超时时,则调用此方法 * @param id * @return */ public User findByIdFallback(Long id){ User user = new User(); user.setId(0L); return user; } } |
Feign支持hystrix
在接口@FeignClient中指定fallback
@FeignClient(name = "microservice-provider-user", fallback = HystrixClientFallback.class) public interface UserFeignClient { @RequestMapping(value = "/simple/{id}", method = RequestMethod.GET) public User findById(@PathVariable("id") Long id); } |
HystrixClientFallback.class:
@Component public class HystrixClientFallback implements UserFeignClient { @Override public User findById(Long id) { User user = new User(); user.setId(0L); return user;
|