①导包
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
②使用RestTemplate进行微服务内部调用以及使用ribbon
注意:微服务之间的通信方式有RPC(阿里巴巴的dubbo就是基于rpc通信框架实现的),Http(springcloud是基于http实现的)。
启动类配置RestTemplate对象
@SpringBootApplication
public class OrderApp_1030
{
//******** ribbon注解 *********
@LoadBalanced //使用ribbon的负载均衡
@Bean //发送请求调用其他微服务
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
public static void main( String[] args )
{
SpringApplication.run(OrderApp_1030.class);
}
③controller调用其他微服务
@RestController
public class OrderController {
//通信对象
@Autowired
private RestTemplate restTemplate;
@GetMapping("/order/{id}")
public User getOrder(@PathVariable("id") Long id){
/*
* 参数1:其他微服务地址
* 参数2:最后得到的类型
*/
return restTemplate.getForObject("http://user-server/user/"+id,User.class );
}
}
注意:超时配置,使用Ribbon进行服务通信时为了防止网络波动造成服务调用超时,我们可以针对Ribbon配置超时时间以及重试机制
ribbon:
ReadTimeout: 3000 #读取超时时间
ConnectTimeout: 3000 #链接超时时间
MaxAutoRetries: 1 #重试机制:同一台实例最大重试次数
MaxAutoRetriesNextServer: 1 #重试负载均衡其他的实例最大重试次数
OkToRetryOnAllOperations: false #是否所有操作都重试,因为针对post请求如果没做幂等处理可能会造成数据多次添加/修改
我们在启动服务使用Ribbon发起服务调用的时候往往会出现找不到目标服务的情况,这是因为Ribbon在进行客户端负载均衡的时候并不是启动时就创建好的,而是在实际请求的时候才会去创建,所以往往我们在发起第一次调用的时候会出现超时导致服务调用失败,我们可以通过设置Ribbon的饥饿加载来改善此情况,即在服务启动时就把Ribbon相关内容创建好。
ribbon:
eager-load:
enabled: true #开启饥饿加载
clients: user-server #针对于哪些服务需要饥饿加载
注意:feign底层还是使用的ribbon进行通信
①导包
<!-- feign客户端依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
②主配置类开始feign
@SpringBootApplication
@EnableFeignClients //开启feign
public class PayApp_1040
{
public static void main( String[] args )
{
SpringApplication.run(PayApp_1040.class);
}
}
③写接口对应要微服务的controller层
//fallbackFactory:熔断工厂类
@FeignClient(value = "user-server") //对应要被调用的微服务的服务名
public interface PayFeign {
//对应的方法,使用的时候就根据上面的无服务的服务名加上这里的方法路径,找到对应的方法
//建议直接拷贝需要调用的controller方法来改(不需要方法体)
@GetMapping("/user/{id}")
User getUser(@PathVariable("id") Long id);
}
④调用其他的微服务
//直接注入feign接口使用对应的方法,他就会根据算法自动分配到对应的微服务
@Autowired
private PayFeign payFeign;
@GetMapping("/pay/{id}")
public User pay(@PathVariable("id") Long id){
return payFeign.getUser(id);
}
如果在服务调用时出现了 “feign.RetryableException : Read timed out…”错误日志,说明Ribbon处理超时 ,我们可以配置Ribbon的超时时间:
ribbon:
ConnectTimeout: 3000
ReadTimeout: 6000
如果服务调用出现“com.netflix.hystrix.exception.HystrixRuntimeException:… timed - out and no fallback available” 错误日志,是因为Hystrix超时,默认Feign集成了Hystrix,但是高版本是关闭了Hystrix,我们可以配置Hystrix超时时间:
feign:
hystrix:
enabled: true #开启熔断支持
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 6000 #hystrix超时时间
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
@SpringBootApplication
@EnableCircuitBreaker //开启Hystrix熔断 ********
public class OrderApp_1030
{
@LoadBalanced //使用ribbon的负载均衡
@Bean //发送请求调用其他微服务的请求
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
public static void main( String[] args )
{
SpringApplication.run(OrderApp_1030.class);
}
}
@RestController
public class OrderController {
@Autowired
private RestTemplate restTemplate;
//方法熔断(只要调用微服务出错了就找到这里面的托底方法)
@HystrixCommand(fallbackMethod = "fallbackMethod")
@GetMapping("/order/{id}")
public User getOrder(@PathVariable("id") Long id){
return restTemplate.getForObject("http://user-server/user/"+id,User.class );
}
//托底数据(格式要和上面的方法一样)
public User fallbackMethod(@PathVariable("id") Long id){
//返回托底数据
return new User(-1L ,"无此用户",0);
}
}
feign不用导入hystrix的包
注意:最终调用的的时候会根据这个接口实现负载均衡,如果出错会走托底数据
//fallbackFactory:熔断工厂类 (里面的类就是对于的托底数据类)
@FeignClient(value = "user-server",fallbackFactory = PayFeignFallbackFactory.class)
public interface PayFeign {
@GetMapping("/user/{id}")
User getUser(@PathVariable("id") Long id);
}
/**
* 熔断降价类
*
* 接口里面的泛型对应的就是负载均衡的接口
*/
@Component
public class PayFeignFallbackFactory implements FallbackFactory<PayFeign> {
//降级方法
@Override
public PayFeign create(Throwable throwable) {
return new PayFeign() {
@Override
public User getUser(Long id) {
throwable.printStackTrace();
return new User(-1L,"不可用",0);
}
};
}
}
1、导包
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
2、启动类开始zuul
@SpringBootApplication
@EnableEurekaClient //注册中心客户端
@EnableZuulProxy //开启zuul
public class ZuulApp_1060
{
public static void main( String[] args )
{
SpringApplication.run(ZuulApp_1060.class);
}
}
3、yml配置
#注册到EurekaServer
eureka:
client:
serviceUrl:
defaultZone: http://localhost/eureka/
instance:
prefer-ip-address: true #ip注册
instance-id: zuul-server:1060 #实例ID
spring:
application:
name: zuul-server #客户端名称
server:
port: 1060 #端口
#最终浏览器范围访问:localhost:1060/servers/pay/访问的微服务接口/参数
zuul:
prefix: "/servers" #统一访问前缀
ignoredServices: "*" #禁用掉使用浏览器通过服务名的方式访问服务
routes:
pay-server: "/pay/**" #指定pay-server这个服务使用 /pay路径来访问 - 别名
order-server: "/order/**" #指定order-server这个服务使用 /order路径来访问
类似于以前的单体项目的filter拦截器
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
/**
ZuulFilter:这个类实现了IZuulFilter接口(里面包含了需要需要放行的方法,和被拦截后的方法),
然后他又加了拦截的类型(前置拦截,每次访问的时候都会拦截),和拦截器执行顺序
*/
@Component
public class LoginFilter extends ZuulFilter{
//拦截器类型(前置拦截器)
@Override
public String filterType() {
return "pre";
}
//拦截器执行顺序,越小执行就越快
@Override
public int filterOrder() {
return 0;
}
//定义是否放行
@Override
public boolean shouldFilter() {
//1.获取上下文对象
RequestContext currentContext = RequestContext.getCurrentContext();
//2.根据上下文对象拿到请求对象
HttpServletRequest request = currentContext.getRequest();
String uri = request.getRequestURI();
//3.判断是否是需要放行的路径
List<String> list = Arrays.asList("/login", "/register");
//如果当前路径是不拦截路径,那么就将它放行
if(list.contains(uri)){
//返回false就不拦截
return false;
}
//返回true就是进行拦截,直接下面的拦截方法
return true;
}
//处理拦截方法
@Override
public Object run() throws ZuulException {
//1.获取当前请求的token,判断是否是登录状态
RequestContext currentContext = RequestContext.getCurrentContext();
//请求对象
HttpServletRequest request = currentContext.getRequest();
//响应对象
HttpServletResponse response = currentContext.getResponse();
response.setContentType("application/json; charset=utf-8");
String uToken = request.getHeader("u-token");
//2.如果是空的,难么就响应数据到前台,前台根据这个数据,跳转到登录界面
if(StringUtils.isBlank(uToken)){
try {
//2.1 响应数据为json格式
response.getWriter().print("{\"success\":false,\"msg\":\"NoUser\"}");
//2.2 设置响应状态
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
} catch (IOException e) {
e.printStackTrace();
}
//3.阻止代码继续执行(直接返回false和true是没用的,必须要根据请求上下文对象设置false阻止代码继续执行)
RequestContext.getCurrentContext().setSendZuulResponse(false);
}
//4.这里的返回结果没有任何意义,不用管(证明是登录了的,放行)
return null;
}
}
1、导包
<!-- 配置中心服务端 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
2、启动类
@SpringBootApplication
@EnableEurekaClient
@EnableConfigServer //开启配置中心
public class ConfigApp_1070
{
public static void main( String[] args )
{
SpringApplication.run(ConfigApp_1070.class);
}
}
3、yml配置
#注册到EurekaServer
eureka:
client:
serviceUrl:
defaultZone: http://localhost/eureka/
instance:
prefer-ip-address: true #ip注册
instance-id: config-server:1070 #实例ID
spring:
application:
name: config-server #客户端名称
cloud:
config:
server:
git:
uri: 码云的仓库路径
username: 账号 #账号密码写真实的快一些我觉得,不使用也能访问有点慢
password: 密码
server:
port: 1070
1、导包(config客户端的包)
<!-- 配置中心 客户端-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-client</artifactId>
</dependency>
2、yml配置
注意:要使用config去拉取代码,需要将yml变成bootstrap.yml(bootstrap.yml执行优先级要高于application.yml),加载的时候需要先去码云拉代码,所以要使用优先级高的
#配置中心的地址
spring:
cloud:
config:
uri: http://localhost:1070 #zuul要从配置中心拉取文件
#你要拉取具体的哪个配置文件
name: application-pay #配置文件名字 dev是环境
profile: dev #环境 组成完整的文件名:application-pay-dev.yml
label: master #主分支