国内如何做国外网站的兼职项目,建筑模板是干嘛用的,黑龙江做网站找谁,网站服务器配置参考指南Eureka 注册中心#xff0c;服务的自动注册、发现、状态监控
Ribbon 负载均衡#xff0c;Eureka中已经集成了负载均衡组件
Hystrix 熔断器#xff0c;用于隔离访问远程服务、第三方库#xff0c;防止出现级联失败。
Feign 远程调用#xff0c;将Rest的请求进行隐藏服务的自动注册、发现、状态监控
Ribbon 负载均衡Eureka中已经集成了负载均衡组件
Hystrix 熔断器用于隔离访问远程服务、第三方库防止出现级联失败。
Feign 远程调用将Rest的请求进行隐藏已经集成了Ribbon和Hystix的依赖和自动配置
Zuul 网关由网关来实现 鉴权、动态路由等等操作。Zuul就是我们服务的统一入口
nginx 高可用
1.Hystix
1.1.简介
Hystix即熔断器。
主页https://github.com/Netflix/Hystrix/ Hystix是Netflix开源的一个延迟和容错库用于隔离访问远程服务、第三方库防止出现级联失败。 1.2.熔断器的工作机制 正常工作的情况下客户端请求调用服务API接口 当有服务出现异常时直接进行失败回滚服务降级处理 当服务繁忙时如果服务出现异常不是粗暴的直接报错而是返回一个友好的提示虽然拒绝了用户的访问但是会返回一个结果。
这就好比去买鱼平常超市买鱼会额外赠送杀鱼的服务。等到逢年过节超时繁忙时可能就不提供杀鱼服务了这就是服务的降级。
系统特别繁忙时一些次要服务暂时中断优先保证主要服务的畅通一切资源优先让给主要服务来使用在双十一、618时京东天猫都会采用这样的策略。
1.3.动手实践
1.3.1.引入依赖
首先在user-consumer中引入Hystix依赖
dependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-netflix-hystrix/artifactId
/dependency1.3.2.开启熔断
1.3.2.改造消费者
我们改造user-consumer添加一个用来访问的user服务的DAO并且声明一个失败时的回滚处理函数
Component
public class UserDao {Autowiredprivate RestTemplate restTemplate;private static final Logger logger LoggerFactory.getLogger(UserDao.class);HystrixCommand(fallbackMethod queryUserByIdFallback)public User queryUserById(Long id){long begin System.currentTimeMillis();String url http://user-service/user/ id;User user this.restTemplate.getForObject(url, User.class);long end System.currentTimeMillis();// 记录访问用时logger.info(访问用时{}, end - begin);return user;}public User queryUserByIdFallback(Long id){User user new User();user.setId(id);user.setName(用户信息查询出现异常);return user;}
}HystrixCommand(fallbackMethodqueryUserByIdFallback)声明一个失败回滚处理函数queryUserByIdFallback当queryUserById执行超时默认是1000毫秒就会执行fallback函数返回错误提示。为了方便查看熔断的触发时机我们记录请求访问时间。
在原来的业务逻辑中调用这个DAO
Service
public class UserService {Autowiredprivate UserDao userDao;public ListUser queryUserByIds(ListLong ids) {ListUser users new ArrayList();ids.forEach(id - {// 我们测试多次查询users.add(this.userDao.queryUserById(id));});return users;}
}1.3.3.改造服务提供者
改造服务提供者随机休眠一段时间以触发熔断
Service
public class UserService {Autowiredprivate UserMapper userMapper;public User queryById(Long id) throws InterruptedException {// 为了演示超时现象我们在这里然线程休眠,时间随机 0~2000毫秒Thread.sleep(new Random().nextInt(2000));return this.userMapper.selectByPrimaryKey(id);}
}
1.3.4.启动测试
然后运行并查看日志
id为9、10、11的访问时间分别是 id为12的访问时间
因此只有12是正常访问其它都会触发熔断我们来查看结果 1.3.5.优化
虽然熔断实现了但是我们的重试机制似乎没有生效是这样吗
其实这里是因为我们的Ribbon超时时间设置的是1000ms: 而Hystix的超时时间默认也是1000ms因此重试机制没有被触发而是先触发了熔断。
所以Ribbon的超时时间一定要小于Hystix的超时时间。
我们可以通过hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds来设置Hystrix超时时间。
hystrix:command:default:execution:isolation:thread:timeoutInMillisecond: 6000 # 设置hystrix的超时时间为6000ms2.Feign
在前面的学习中我们使用了Ribbon的负载均衡功能大大简化了远程调用时的代码
String baseUrl http://user-service/user/;
User user this.restTemplate.getForObject(baseUrl id, User.class)如果就学到这里你可能以后需要编写类似的大量重复代码格式基本相同无非参数不一样。有没有更优雅的方式来对这些代码再次优化呢
这就是我们接下来要学的Feign的功能了。
2.1.简介
有道词典的英文解释 为什么叫伪装
Feign可以把Rest的请求进行隐藏伪装成类似SpringMVC的Controller一样。你不用再自己拼接url拼接参数等等操作一切都交给Feign去做。
项目主页https://github.com/OpenFeign/feign 2.2.快速入门
2.2.1.导入依赖
dependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-openfeign/artifactId
/dependency2.2.2.Feign的客户端
FeignClient(user-service)
public interface UserFeignClient {GetMapping(/user/{id})User queryUserById(PathVariable(id) Long id);
}首先这是一个接口Feign会通过动态代理帮我们生成实现类。这点跟mybatis的mapper很像FeignClient声明这是一个Feign客户端类似Mapper注解。同时通过value属性指定服务名称接口中的定义方法完全采用SpringMVC的注解Feign会根据注解帮我们生成URL并访问获取结果
改造原来的调用逻辑不再调用UserDao
Service
public class UserService {Autowiredprivate UserFeignClient userFeignClient;public ListUser queryUserByIds(ListLong ids) {ListUser users new ArrayList();ids.forEach(id - {// 我们测试多次查询users.add(this.userFeignClient.queryUserById(id));});return users;}
}2.2.3.开启Feign功能
我们在启动类上添加注解开启Feign功能
SpringBootApplication
EnableDiscoveryClient
EnableHystrix
EnableFeignClients // 开启Feign功能
public class UserConsumerDemoApplication {public static void main(String[] args) {SpringApplication.run(UserConsumerDemoApplication.class, args);}
}你会发现RestTemplate的注册被我删除了。Feign中已经自动集成了Ribbon负载均衡因此我们不需要自己定义RestTemplate了
2.2.4.启动测试
访问接口 正常获取到了结果。
2.3.负载均衡
Feign中本身已经集成了Ribbon依赖和自动配置 因此我们不需要额外引入依赖也不需要再注册RestTemplate对象。
另外我们可以像上节课中讲的那样去配置Ribbon可以通过ribbon.xx来进行全局配置。也可以通过服务名.ribbon.xx来对指定服务配置
user-service:ribbon:ConnectTimeout: 250 # 连接超时时间(ms)ReadTimeout: 1000 # 通信超时时间(ms)OkToRetryOnAllOperations: true # 是否对所有操作重试MaxAutoRetriesNextServer: 1 # 同一服务不同实例的重试次数MaxAutoRetries: 1 # 同一实例的重试次数2.4.Hystix支持
Feign默认也有对Hystix的集成 只不过默认情况下是关闭的。我们需要通过下面的参数来开启
feign:hystrix:enabled: true # 开启Feign的熔断功能但是Feign中的Fallback配置不像Ribbon中那样简单了。
1首先我们要定义一个类实现刚才编写的UserFeignClient作为fallback的处理类
Component
public class UserFeignClientFallback implements UserFeignClient {Overridepublic User queryUserById(Long id) {User user new User();user.setId(id);user.setName(用户查询出现异常);return user;}
}
2然后在UserFeignClient中指定刚才编写的实现类
FeignClient(value user-service, fallback UserFeignClientFallback.class)
public interface UserFeignClient {GetMapping(/user/{id})User queryUserById(PathVariable(id) Long id);
}
3重启测试
我们关闭user-service服务然后在页面访问 2.5.请求压缩(了解)
Spring Cloud Feign 支持对请求和响应进行GZIP压缩以减少通信过程中的性能损耗。通过下面的参数即可开启请求与响应的压缩功能
feign:compression:request:enabled: true # 开启请求压缩response:enabled: true # 开启响应压缩同时我们也可以对请求的数据类型以及触发压缩的大小下限进行设置
feign:compression:request:enabled: true # 开启请求压缩mime-types: text/html,application/xml,application/json # 设置压缩的数据类型min-request-size: 2048 # 设置触发压缩的大小下限注上面的数据类型、压缩大小下限均为默认值。
2.6.日志级别(了解)
前面讲过通过logging.level.xxdebug来设置日志级别。然而这个对Fegin客户端而言不会产生效果。因为FeignClient注解修改的客户端在被代理时都会创建一个新的Fegin.Logger实例。我们需要额外指定这个日志的级别才可以。
1设置com.leyou包下的日志级别都为debug
logging:level:com.leyou: debug2编写配置类定义日志级别
Configuration
public class FeignConfig {BeanLogger.Level feignLoggerLevel(){return Logger.Level.FULL;}
}这里指定的Level级别是FULLFeign支持4种级别 NONE不记录任何日志信息这是默认值。BASIC仅记录请求的方法URL以及响应状态码和执行时间HEADERS在BASIC的基础上额外记录了请求和响应的头信息FULL记录所有请求和响应的明细包括头信息、请求体、元数据。
3在FeignClient中指定配置类
FeignClient(value user-service, fallback UserFeignClientFallback.class, configuration FeignConfig.class)
public interface UserFeignClient {GetMapping(/user/{id})User queryUserById(PathVariable(id) Long id);
}4重启项目即可看到每次访问的日志 3.Zuul网关
通过前面的学习使用Spring Cloud实现微服务的架构基本成型大致是这样的 我们使用Spring Cloud Netflix中的Eureka实现了服务注册中心以及服务注册与发现而服务间通过Ribbon或Feign实现服务的消费以及均衡负载通过Spring Cloud Config实现了应用多环境的外部化配置以及版本管理。为了使得服务集群更为健壮使用Hystrix的融断机制来避免在微服务架构中个别服务出现异常时引起的故障蔓延。
在该架构中我们的服务集群包含内部服务Service A和Service B他们都会注册与订阅服务至Eureka Server而Open Service是一个对外的服务通过均衡负载公开至服务调用方。我们把焦点聚集在对外服务这块直接暴露我们的服务地址这样的实现是否合理或者是否有更好的实现方式呢
先来说说这样架构需要做的一些事儿以及存在的不足
首先破坏了服务无状态特点。
为了保证对外服务的安全性我们需要实现对服务访问的权限控制而开放服务的权限控制机制将会贯穿并污染整个开放服务的业务逻辑这会带来的最直接问题是破坏了服务集群中REST API无状态的特点。
从具体开发和测试的角度来说在工作中除了要考虑实际的业务逻辑之外还需要额外考虑对接口访问的控制处理。
其次无法直接复用既有接口。
当我们需要对一个即有的集群内访问接口实现外部服务访问时我们不得不通过在原有接口上增加校验逻辑或增加一个代理调用来实现权限控制无法直接复用原有的接口
面对类似上面的问题我们要如何解决呢答案是服务网关
为了解决上面这些问题我们需要将权限控制这样的东西从我们的服务单元中抽离出去而最适合这些逻辑的地方就是处于对外访问最前端的地方我们需要一个更强大一些的均衡负载器的 服务网关。
服务网关是微服务架构中一个不可或缺的部分。通过服务网关统一向外系统提供REST API的过程中除了具备服务路由、均衡负载功能之外它还具备了权限控制等功能。Spring Cloud Netflix中的Zuul就担任了这样的一个角色为微服务架构提供了前门保护的作用同时将权限控制这些较重的非业务逻辑内容迁移到服务路由层面使得服务集群主体能够具备更高的可复用性和可测试性。
3.1.简介
官网https://github.com/Netflix/zuul Zuul维基百科
电影《捉鬼敢死队》中的怪兽Zuul在纽约引发了巨大骚乱。
事实上在微服务架构中Zuul就是守门的大Boss一夫当关万夫莫开 3.2.Zuul加入后的架构 不管是来自于客户端PC或移动端的请求还是服务内部调用。一切对服务的请求都会经过Zuul这个网关然后再由网关来实现 鉴权、动态路由等等操作。Zuul就是我们服务的统一入口。
3.3.快速入门
3.3.1.新建工程
填写基本信息 添加Zuul依赖 3.3.2.编写启动类
通过EnableZuulProxy注解开启Zuul的功能
SpringBootApplication
EnableZuulProxy // 开启Zuul的网关功能
public class ZuulDemoApplication {public static void main(String[] args) {SpringApplication.run(ZuulDemoApplication.class, args);}
}3.3.3.编写配置
server:port: 10010 #服务端口
spring: application: name: api-gateway #指定服务名3.3.4.编写路由规则
我们需要用Zuul来代理user-service服务先看一下控制面板中的服务状态 ip为127.0.0.1端口为8081
映射规则
zuul:routes:user-service: # 这里是路由id随意写path: /user-service/** # 这里是映射路径url: http://127.0.0.1:8081 # 映射路径对应的实际url地址我们将符合path 规则的一切请求都代理到 url参数指定的地址
本例中我们将 /user-service/**开头的请求代理到http://127.0.0.1:8081
3.3.5.启动测试
访问的路径中需要加上配置规则的映射路径我们访问http://127.0.0.1:8081/user-service/user/10 3.4.面向服务的路由
在刚才的路由规则中我们把路径对应的服务地址写死了如果同一服务有多个实例的话这样做显然就不合理了。
我们应该根据服务的名称去Eureka注册中心查找 服务对应的所有实例列表然后进行动态路由才对
3.4.1.添加Eureka客户端依赖
dependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-netflix-eureka-client/artifactId
/dependency3.4.2.开启Eureka客户端发现功能
SpringBootApplication
EnableZuulProxy // 开启Zuul的网关功能
EnableDiscoveryClient
public class ZuulDemoApplication {public static void main(String[] args) {SpringApplication.run(ZuulDemoApplication.class, args);}
}3.4.3.添加Eureka配置获取服务信息
eureka:client:registry-fetch-interval-seconds: 5 # 获取服务列表的周期5sservice-url:defaultZone: http://127.0.0.1:10086/eurekainstance:prefer-ip-address: trueip-address: 127.0.0.13.4.4.修改映射配置通过服务名称获取
因为已经有了Eureka客户端我们可以从Eureka获取服务的地址信息因此映射时无需指定IP地址而是通过服务名称来访问而且Zuul已经集成了Ribbon的负载均衡功能。
zuul:routes:user-service: # 这里是路由id随意写path: /user-service/** # 这里是映射路径serviceId: user-service # 指定服务名称3.4.5.启动测试
再次启动这次Zuul进行代理时会利用Ribbon进行负载均衡访问 日志中可以看到使用了负载均衡器 3.5.简化的路由配置
在刚才的配置中我们的规则是这样的
zuul.routes.route.path/xxx/** 来指定映射路径。route是自定义的路由名zuul.routes.route.serviceId/user-service来指定服务名。
而大多数情况下我们的route路由名称往往和 服务名会写成一样的。因此Zuul就提供了一种简化的配置语法zuul.routes.serviceIdpath
比方说上面我们关于user-service的配置可以简化为一条
zuul:routes:user-service: /user-service/** # 这里是映射路径省去了对服务名称的配置。
3.6.默认的路由规则
在使用Zuul的过程中上面讲述的规则已经大大的简化了配置项。但是当服务较多时配置也是比较繁琐的。因此Zuul就指定了默认的路由规则 也就是说刚才的映射规则我们完全不配置也是OK的不信就试试看。
默认情况下一切服务的映射路径就是服务名本身。 例如服务名为user-service则默认的映射路径就是/user-service/**
3.7.路由前缀
配置示例
zuul:prefix: /api # 添加路由前缀routes:user-service: # 这里是路由id随意写path: /user-service/** # 这里是映射路径service-id: user-service # 指定服务名称我们通过zuul.prefix/api来指定了路由的前缀这样在发起请求时路径就要以/api开头。
路径/api/user-service/user/1将会被代理到/user-service/user/1
3.8.过滤器
Zuul作为网关的其中一个重要功能就是实现请求的鉴权。而这个动作我们往往是通过Zuul提供的过滤器来实现的。
3.8.1.ZuulFilter
ZuulFilter是过滤器的顶级父类。在这里我们看一下其中定义的4个最重要的方法
public abstract ZuulFilter implements IZuulFilter{abstract public String filterType();abstract public int filterOrder();boolean shouldFilter();// 来自IZuulFilterObject run() throws ZuulException;// IZuulFilter
}shouldFilter返回一个Boolean值判断该过滤器是否需要执行。返回true执行返回false不执行。run过滤器的具体业务逻辑。filterType返回字符串代表过滤器的类型。包含以下4种
pre请求在被路由之前执行
routing在路由请求时调用
post在routing和errror过滤器之后调用
error处理请求时发生错误调用
filterOrder通过返回的int值来定义过滤器的执行顺序数字越小优先级越高。
3.8.2.过滤器执行生命周期
这张是Zuul官网提供的请求生命周期图清晰的表现了一个请求在各个过滤器的执行顺序。 正常流程 请求到达首先会经过pre类型过滤器而后到达routing类型进行路由请求就到达真正的服务提供者执行请求返回结果后会到达post过滤器。而后返回响应。异常流程
整个过程中pre或者routing过滤器出现异常都会直接进入error过滤器再error处理完毕后会将请求交给POST过滤器最后返回给用户。
如果是error过滤器自己出现异常最终也会进入POST过滤器而后返回。
如果是POST过滤器出现异常会跳转到error过滤器但是与pre和routing不同的时请求不会再到达POST过滤器了。
所有内置过滤器列表 3.8.3.使用场景
场景非常多
请求鉴权一般放在pre类型如果发现没有访问权限直接就拦截了异常处理一般会在error类型和post类型过滤器中结合来处理。服务调用时长统计pre和post结合使用。
3.9.自定义过滤器
接下来我们来自定义一个过滤器模拟一个登录的校验。基本逻辑如果请求中有access-token参数则认为请求有效放行。
3.9.1.定义过滤器类
Component
public class LoginFilter extends ZuulFilter{Overridepublic String filterType() {// 登录校验肯定是在前置拦截return pre;}Overridepublic int filterOrder() {// 顺序设置为1return 1;}Overridepublic boolean shouldFilter() {// 返回true代表过滤器生效。return true;}Overridepublic Object run() throws ZuulException {// 登录校验逻辑。// 1获取Zuul提供的请求上下文对象RequestContext ctx RequestContext.getCurrentContext();// 2) 从上下文中获取request对象HttpServletRequest req ctx.getRequest();// 3) 从请求中获取tokenString token req.getParameter(access-token);// 4) 判断if(token null || .equals(token.trim())){// 没有token登录校验失败拦截ctx.setSendZuulResponse(false);// 返回401状态码。也可以考虑重定向到登录页。ctx.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());}// 校验通过可以考虑把用户信息放入上下文继续向后执行return null;}
}
3.9.2.测试
没有token参数时访问失败 添加token参数后 3.10.负载均衡和熔断
Zuul中默认就已经集成了Ribbon负载均衡和Hystix熔断机制。但是所有的超时策略都是走的默认值比如熔断超时时间只有1S很容易就触发了。因此建议我们手动进行配置
zuul:retryable: true
ribbon:ConnectTimeout: 250 # 连接超时时间(ms)ReadTimeout: 2000 # 通信超时时间(ms)OkToRetryOnAllOperations: true # 是否对所有操作重试MaxAutoRetriesNextServer: 2 # 同一服务不同实例的重试次数MaxAutoRetries: 1 # 同一实例的重试次数
hystrix:command:default:execution:isolation:thread:timeoutInMillisecond: 6000 # 熔断超时时长6000ms