spring cloud 的使用
spring cloud
从springboot的分布式微服务搭建完成后来说,互相调用服务并在复杂服务中完成负载均衡等,也就是需要升级一个层面:spring cloud
eureka的使用
eureka服务端配置
新建service-registry模块,并在pom.xml中引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
新建springboot启动类并添加@EnableEurekaServer,启动eureka服务
@SpringBootApplication
@EnableEurekaServer
public class ServiceRegistryApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceRegistryApplication.class, args);
}
}
新建配置文件application.yml并加入以下配置
spring:
profile: development
application:
name: service-registry
#运行的端口号
server:
port: 8761
#注册中心配置
eureka:
instance:
hostname: localhost
port: ${server.port}
client:
registerWithEureka: false
fetchRegistry: false
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
server:
waitTimeInMsWhenSyncEmpty: 0
eureka客户端:
所有需要向eureka注册的服务都需要配置eureka服务器信息,具体版本可能有差别,根据各自的springboot进行版本修改
引入依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>1.4.3.RELEASE</version>
</dependency>
启动项添加eureka客户端注解@EnableDiscoveryClient
@SpringBootApplication
@EnableDiscoveryClient
public class DisasterWebApplication {
private static final Logger log = LoggerFactory.getLogger(DisasterWebApplication.class);
public static void main(String[] args) {
SpringApplication.run(DisasterWebApplication.class, args);
}
}
application.yml添加eureka服务器配置:
# 注册中心配置
eureka:
instance:
hostname: localhost
port: 8761
prefer-ip-address: true #在控制台中显示ip
client:
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:${eureka.instance.port}/eureka/
ribbon的使用介绍
微服务的restTemplate方式调用,注入 restTemplate,在 restTemplate.getForObject()使用有三种方式
1.ip+端口 http://localhost:8080
2.通过EurekaClient
@Autowired
private EurekaClient eurekaClient ;
方法类调用获取服务ip与端口
InstanceInfo instance = eurekaClient.getNextServerFromEureka("服务名",false);
String URL = instance .getHomePageUrl();
3.使用ribbon后,应用会根据服务名自动完成负载均衡,所以直接将服务名写入即可
/**
* 用户操作模块
* Created by lrw on 2018/11/20.
*/
@Controller
@RequestMapping("/home")
public class HomeController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/test")
public Result test(){
Result result = restTemplate.getForObject("http://API-SERVICE/task/findAllTask",Result.class);
return result;
}
}
ribbon的配置
pom引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
<version>1.4.3.RELEASE</version>
</dependency>
启动类添加注解@EnableDiscoveryClient(@LoadBalanced)
@SpringBootApplication
@EnableDiscoveryClient
@RibbonClient(value = "API-SERVICE")//启用并对API-SERVICE进行负载均衡
public class DisasterWebApplication {
private static final Logger log = LoggerFactory.getLogger(DisasterWebApplication.class);
@Bean
@LoadBalanced
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(DisasterWebApplication.class, args);
}
}
方法调用时通过服务名直接使用http://API-SERVICE/
/**
* 用户操作模块
* Created by lrw on 2018/11/20.
*/
@Controller
@RequestMapping("/home")
public class HomeController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/test")
public Result test(){
Result result = restTemplate.getForObject("http://API-SERVICE/task/findAllTask",Result.class);
return result;
}
}
Feign的使用
feign是对ribbon相似的一个封装,更快的使用负载均衡的调用API服务。
配置feign
添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>1.4.4.RELEASE</version>
</dependency>
主程序添加注解 @EnableFeignClients
@SpringBootApplication
@EnableDiscoveryClient
@RibbonClient(value = "API-SERVICE")//启用并对API-SERVICE进行负载均衡
@EnableFeignClients
public class DisasterWebApplication {
private static final Logger log = LoggerFactory.getLogger(DisasterWebApplication.class);
@Bean
@LoadBalanced
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(DisasterWebApplication.class, args);
}
}
添加feign包并创建接口UserFeignService
该接口相当于API的方法,这里是一个映射,然后可以在Controller中注入并调用方法,代替原本的restTemplate的代用方式。
@FeignClient("API-SERVICE")
public interface UserFeignService{
//这里的处理参数和注解与Controller是一样的,并且要注意接口名不能类似Feign的名称
@RequestMapping(value = "/user/findAllUser",method = RequestMethod.GET)
Result findAllUser(@RequestParam("searchTerm")String searchTerm,@RequestParam("page") String page,@RequestParam("limit") String limit);
}
//这里参数必须要用注解说明@RequestParam("searchTerm"),其他情况报错请百度
改造Controller
/**
* 用户操作模块
* Created by lrw on 2018/11/20.
*/
@Controller
@RequestMapping("/home")
public class HomeController {
@Autowired
UserFeignService userFeignService;
@ResponseBody
@GetMapping(value = "getUserInfo2")
public Result getUserInfo2(HttpServletRequest req) {
String searchTerm = req.getParameter("key[searchTerm]");
String strPage = req.getParameter("page"); // 页数
String strLimit = req.getParameter("limit"); // 每页显示
return userFeignService.findAllUser(searchTerm,strPage,strLimit);
}
}
注意:如果传入复杂对象参数,则自动变为POST请求方式去请求API服务,因为GET请求方式是无法传入例如User对象的。
自定义配置
添加FeignConfig在config包
@Configuration
public class FeignConfig {
@Bean
public Contract feignContract(){
return new feign.Contract.Default();
}
}
添加configuration在@Feign注解指定配置文件
@FeignClient(value = "API-SERVICE",configuration = FeignConfig.class)
public interface UserFeignService {
@RequestMapping(value = "/user/findAllUser",method = RequestMethod.GET)
Result findAllUser(@RequestParam("searchTerm")String searchTerm,@RequestParam("page") String page,@RequestParam("limit") String limit);
}
然后在这个地方不需要用springMvc的方式 @RequestMapping(value = “/user/findAllUser”,method = RequestMethod.GET),并且需要用feign包的@Param
@FeignClient(value = "API-SERVICE",configuration = FeignConfig.class)//这里也可以不用value,也可以用URL的方式定义请求服务
public interface UserFeignService {
//@RequestMapping(value = "/user/findAllUser",method = RequestMethod.GET)
//第一个是请求方式+空格+请求地址?参数={参数}
@RequestLine("GET /user/findAllUser?searchTerm={searchTerm}&page={page}&limit={limit}")
Result findAllUser(@Param("searchTerm") String searchTerm, @Param("page") String page, @Param("limit") String limit);
}
配置访问的API的用户名,密码
@Configuration
public class FeignConfig2 {
@Bean
public BasicAuthRequestInterceptor basicAuthRequestInterceptor(){
return new BasicAuthRequestInterceptor("admin","123456");
}
}
这个配置文件需要引用的时候再去配置在 @FeignClient(value = “API-SERVICE”,configuration = FeignConfig2.class),就能生效
Feign日志输出配置
application.xml中添加后,在写配置类方法
@Configuration
public class FeignConfig2 {
@Bean
public BasicAuthRequestInterceptor basicAuthRequestInterceptor(){
return new BasicAuthRequestInterceptor("admin","123456");
}
@Bean
Logger.level feignLoggerLevel(){
return Logger.Level.FULL;
}
}
调用出错,进入指定方法(这里是客户端的调用失败,比如提供者节点下线了)
首先引入Hystrix包,添加配置:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>1.4.4.RELEASE</version>
</dependency>
修改配置文件:
feign:
hystrix:
enable: true
实现UserFeignService接口类UserFeignServiceImpl :
这个类实现后,这里的方法将是错误后要执行的对应方法
@Component
public class UserFeignServiceImpl implements UserFeignService {
@Override
public Result findAllUser(String searchTerm, String page, String limit) {
return null;
}
}
添加注解配置fallback = UserFeignServiceImpl.class在接口上
@FeignClient(value = "API-SERVICE", configuration = FeignConfig.class,fallback = UserFeignServiceImpl.class)
public interface UserFeignService {
//第一个是请求方式+空格+请求地址
@RequestLine("GET /user/findAllUser?searchTerm={searchTerm}&page={page}&limit={limit}")
Result findAllUser(@Param("searchTerm") String searchTerm, @Param("page") String page, @Param("limit") String limit);
}
再添加上@EnableCircuitBreaker再主程序中,多次出现错误请求,将不再尝试执行API接口,直接进入执行错误方法。
注意:Hystrix还有一个factory工程可以创建出来这些错误代理类,但是原理其实和上面的@component一样的。以下是这个工厂代码:
Hystrix的使用
功能介绍 :在某个服务由于外界访问压力或者本身问题导致的错误,影响系统的效率,这时候需要对这个节点做熔断机制,所以引入Hystrix。
配置Hystrix
添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>1.4.4.RELEASE</version>
</dependency>
添加主程序注解 @EnableCircuitBreaker
@SpringBootApplication
@EnableDiscoveryClient
@ServletComponentScan
@EnableCircuitBreaker
public class ApiServiceApplication {
private static final Logger log = LoggerFactory.getLogger(ApiServiceApplication.class);
public static void main(String[] args) {
SpringApplication.run(ApiServiceApplication.class, args);
}
}
应用
用在Controller的注解@HystrixCommand(fallbackMethod = “方法名”),还可以通过ip:port/health(hystix.stream)访问当前健康状态,当方法多次出现失败,则直接执行错误方法,但是在方法又可以正常访问了,就会再次关闭熔断,恢复正常。
@ApiOperation(value = "分页查找用户", notes = "根据用户名查找用户信息")
@RequestMapping(value = "findAllUser", method = RequestMethod.GET)
@HystrixCommand(fallbackMethod = "failToFindAllUser")
public Result findAllUser(@RequestParam(required = false) @ApiParam(value = "查询条件") String searchTerm,
@RequestParam(required = false) @ApiParam(value = "页数") String page,
@RequestParam(required = false) @ApiParam(value = "条数") String limit) {
return userService.findAll(searchTerm,page,limit);
}
//异常或者失败后调用该方法
public Result failToFindAllUser(String searchTerm,String page, String limit){
return new Result(false, ErrorCode.ErrorNum_1, ErrorCode.ErrorStr_1);
}
注意:由于在这两个方法执行的时候,线程是不一样的,所以变量的传递和取值会出现问题,当出现问题可以用下面的注解,使其在用一个线程中执行:
@HystrixCommand(fallbackMethod = "failToFindAllUser",commandProperties = {
@HystrixProperty(name = "execution.isolation.strategy",value="SEMAPHORE")
})
###Hystrix设置超时时间,在xml中添加以下配置
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=3000//超时时间(秒)
Hystrix 的DashBorad功能
这个是一个web应用,用于测试和监控服务状态
首先创建hystrix-dashboard的模块
添加pom依赖:
<parent>
<groupId>com.shusi</groupId>
<artifactId>crawler-sys</artifactId>
<version>0.0.1-RELEASE</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>hystrix-dashboard</artifactId>
<version>0.0.1-RELEASE</version>
<name>hystrix-dashboard</name>
<description>hystrix-dashboard</description>
<dependencies>
<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-dashboard</artifactId>
<version>1.4.4.RELEASE</version>
</dependency>
</dependencies>
添加启动类:
@SpringBootApplication
@EnableHystrixDashboard
public class HystrixDashboardApplication {
public static void main(String[] args) {
SpringApplication.run(HystrixDashboardApplication.class, args);
}
}
配置application.xml:
server:
port: 10020
启动并访问localhost:10020/hystrix进入页面:
这里需要注意的是Api需要被调用的时候,才有会数据,不然一直处于加载状态,访问后,监控到页面如下:
注意这里的hytrix只能监测一个服务,当一个服务出现集群,就需要应用turbine来实现这个功能,因为turbine是通过Eureka上的服务名来检测集群的状态的。
接下来说下配置turbine
turbine 查看集群的健康状态
创建turbine的模块,然后继续下面的配置
添加依赖:
主程序添加注解
添加application.yml配置:
然后通过turbine的访问地址,放入dashboard仪表盘查看,如果监听多个服务集群,需要修改配置中的服务名
并且直接访问turbine地址,不要添加服务名:例如:localhost:8080/turbine.stream,放入dashborad查看
如果通过context-path的话需要配置如下,不然turbine是用ip+port检测的:
或者通过manage:port可以给Eureka设置服务的对应代理ip+端口名,用来免服务名路径的访问:localhost:8081