
✅作者简介:2022年博客新星 第八。热爱国学的Java后端开发者,修心和技术同步精进。
🍎个人主页:Java Fans的博客
🍊个人信条:不迁怒,不贰过。小知识,大智慧。
💞当前专栏:SpringCloud 微服务学习专栏
✨特色专栏:国学周更-心性养成之路
🥭本文内容:SpringCloud 微服务系列——【服务间的通信方式、OpenFeign、Hystrix组件使用】
文章目录

在整个微服务架构中,服务间的服务改如何调用,有哪些调用方式?

在springcloud中服务间调用方式主要是使用 http restful方式进行服务间调用。
spring框架提供的RestTemplate类可用于在应用中调用rest服务,它简化了与http服务的通信方式,统一了RESTful的标准,封装了http链接, 我们只需要传入url及返回值类型即可。相较于之前常用的HttpClient,RestTemplate是一种更优雅的调用RESTful服务的方式。
创建用户服务的springboot工程
引入依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 引入consul客户端依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
<!-- 引入健康检查依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
编写application.yml文件
server:
port: 8888 #配置服务端口
spring:
application:
name: USERS #配置服务名
cloud:
consul:
port: 8500 #注册中心端口
host: localhost #注册中心地址
编写启动类,添加注解
@SpringBootApplication
@EnableDiscoveryClient
public class UsersApplication {
public static void main(String[] args) {
SpringApplication.run(UsersApplication.class, args);
}
}
创建订单服务的springboot工程
引入依赖(参照用户服务)
编写application.yml文件
server:
port: 8889 #配置服务端口
spring:
application:
name: ORDERS #配置服务名
cloud:
consul:
port: 8500 #注册中心端口
host: localhost #注册中心地址
编写启动类,添加注解
@SpringBootApplication
@EnableDiscoveryClient
public class OrdersApplication {
public static void main(String[] args) {
SpringApplication.run(OrdersApplication.class, args);
}
}
编写用户服务和订单服务的控制层,并实现调用
@Controller
public class UserController {
private Logger logger = LoggerFactory.getLogger(getClass());
@GetMapping("/user")
@ResponseBody
public String invokeOrder(){
logger.info("用户服务被调用!");
// 创建发送http请求的 RestTemplate 对象
RestTemplate restTemplate = new RestTemplate();
String rest = restTemplate.getForObject("http://localhost:8889/order", String.class);
logger.info("订单服务调用成功,返回结果"+rest);
return " Invoke UsersService ok "+rest;
}
}
@Controller
public class OrderController {
private Logger logger = LoggerFactory.getLogger(getClass());
@GetMapping("/order")
@ResponseBody
public String OrderTest(){
logger.info("订单服务被调用!");
return " invoke OrderService ok";
}
}
问题:
1.以上的通信方式已经将访问的地址写成固定的值,无法实现负载的均衡
2.提供接口的服务发生改变,不利于服务的维护
解决问题:负载均衡
1.自定义负载均衡
//实现调用订单接口的随机策略 实现负载均衡
public String getHost(){
ArrayList<String> hosts = new ArrayList<>();
hosts.add("localhost:8889");
hosts.add("localhost:9090");
return hosts.get(new Random().nextInt(hosts.size()));
}
问题:
引入Ribbon的依赖
<!--引入ribbon依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
编写application.yml
server:
port: 8082
spring:
application:
name: USERSCLIENT #服务名称
cloud:
consul:
host: localhost # 服务注册的地址
discovery:
service-name: USERSCLIENT # 服务注册的名字
port: 8500
改写用户服务和订单服务的控制层,并实现调用
@Controller
public class UsersController {
@Autowired
private DiscoveryClient discoveryClient; //直接注入
@Autowired
private LoadBalancerClient loadBalancerClient; //直接注入
@Autowired
private RestTemplate restTemplate; //配置后注入
@GetMapping("/user")
@ResponseBody
public String invokeOrders(){
RestTemplate restTemplate = new RestTemplate();
String result = restTemplate.getForObject("http://localhost:8091/order", String.class);
System.out.println(result);
return "users service ok";
}
@GetMapping("/user2")
@ResponseBody
public String invokeOrders2(){
List<ServiceInstance> ordersclient = discoveryClient.getInstances("ORDERSCLIENT");
ServiceInstance serviceInstance = ordersclient.get(new Random().nextInt(ordersclient.size()));
URI uri = serviceInstance.getUri();
System.out.println("uri = " + uri);
RestTemplate restTemplate = new RestTemplate();
String result = restTemplate.getForObject(uri+"/order", String.class);
System.out.println(result);
return "users service ok";
}
@GetMapping("/user3")
@ResponseBody
public String invokeOrders3(){
ServiceInstance ordersclient = loadBalancerClient.choose("ORDERSCLIENT");
URI uri = ordersclient.getUri();
System.out.println("uri = " + uri);
return "users service ok";
}
@GetMapping("/user4")
@ResponseBody
public String invokeOrders4(){
String result = restTemplate.getForObject("http://ORDERSCLIENT/order", String.class);
return "users service ok";
}
}
使用Ribbon注解实现负载均衡前需要使用配置类配置RestTemplate
@Configuration
public class BeansConfig {
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
Ribbon的负载均衡策略
1.ribbon负载均衡算法
RoundRobinRule 轮循策略 按顺序循环选择 Server
RandomRule 随机策略 随机选择 Server
AvailabilityFilteringRule 可用过滤策略
`会先过滤由于多次访问故障而处于断路器跳闸状态的服务,还有并发的连接数量超过阈值的服务,然后对剩余的服务列表按照轮询策略进行访问
WeightedResponseTimeRule 响应时间加权策略
根据平均响应的时间计算所有服务的权重,响应时间越快服务权重越大被选中的概率越高,刚启动时如果统计信息不足,则使用RoundRobinRule策略,等统计信息足够会切换到
RetryRule 重试策略
先按照RoundRobinRule的策略获取服务,如果获取失败则在制定时间内进行重试,获取可用的服务。
BestAviableRule 最低并发策略
会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yfY52EEv-1682171435733)(springcloud.assets/image-20200713162940968.png)]
设置负载均衡策略(服务调用端)
#设置负载均衡策略
ORDERSCLIENT: # 服务的名称
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
思考: 使用RestTemplate+ribbon已经可以完成对端的调用,为什么还要使用feign?
String restTemplateForObject = restTemplate.getForObject("http://服务名/url?参数" + name, String.class);
存在问题:
官网:https://cloud.spring.io/spring-cloud-openfeign/reference/html/
Feign是一个声明式的伪Http客户端,它使得服务间的通信变得更简单。使用Feign,只需要创建一个接口并添加注解。同时可以完成数据的自动转换,它具有可插拔的注解特性(可以使用springmvc的注解),可使用Feign 注解和JAX-RS注解。Feign支持可插拔的编码器和解码器。Feign默认集成了Ribbon,默认实现了负载均衡的效果并且springcloud为feign添加了springmvc注解的支持
1.创建商品服务和商品类目服务(注册Consul注册中心)
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients //OpenFeign注解
public class ProductsApplication {
public static void main(String[] args) {
SpringApplication.run(ProductsApplication.class, args);
}
}
@SpringBootApplication
@EnableDiscoveryClient
public class CategoryApplication {
public static void main(String[] args) {
SpringApplication.run(CategoryApplication.class, args);
}
}
2.添加OpenFeign依赖(消费者端)
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
3.添加启动类注解
@EnableFeignClients
4.在消费端编写OpenFeign客户端接口
@FeignClient("CATEGORY")
public interface CategoryClient {
@GetMapping("/category") //请求方式 和请求的路径和服务提供者保持一致
String test01();
}
5.消费者调用
@Controller
public class ProductsController {
@Autowired
private CategoryClient categoryClient; //注入接口
@RequestMapping("/product")
@ResponseBody
public String testProduct(){
String s = categoryClient.test01();//调用接口中的方法
return "Products Ok ::"+s;
}
}
服务间参数传递和响应处理
1.参数的传递(零散)
1.普通传参
//客户端接口方法
@GetMapping("/cat2")
String test02(@RequestParam("id") Integer id,@RequestParam("name")String name);
2.Restful传参
//客户端接口方法
@GetMapping("/cat2/{id}/{name}")
String test02(@PathVariable("id") Integer id, @PathVariab le("name") String name);
2.参数的传递(对象)
//客户端接口方法
@PostMapping("/p4")
String test04(@RequestBody User user);
// 服务的提供方 controller
@PostMapping("/p4")
@ResponseBody
public String test04(@RequestBody User user){
logger.info(user.toString());
return "Products ok";
}
3.参数的传递(数组)
//数组
@GetMapping("/p5")
String test05(@RequestParam("ids")String[] ids);
//服务的提供方
@GetMapping("/p5")
@ResponseBody
public String test05(String[] ids){
for (String id : ids) {
System.out.println(id);
}
return "test05 ok";
}
服务间响应处理
@GetMapping("/cat6")
@ResponseBody //响应单个数据
public Product test05(Integer id){
Product product = productsClient.test06(id);
return product;
}
@GetMapping("/cat7")
@ResponseBody //响应集合
public List<Product> test06(Integer id){
return productsClient.test07(id);
}
服务调用超时处理(超出1秒调用超时)

修改超时时间
feign:
client:
config:
PRODUCTS:
connectTimeout: 5000 #连接超时时间
readTimeout: 5000 #读取超时时间
#全局超时时间控制
feign:
client:
config:
default:
connectTimeout:: 5000 #连接超时时间
readTimeout: 5000 #读取超时时间
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0cWDswwR-1682171435735)(springcloud.assets/image-20200715123359665.png)]
In a distributed environment, inevitably some of the many service dependencies will fail. Hystrix is a library that helps you control the interactions between these distributed services by adding latency tolerance and fault tolerance logic. Hystrix does this by isolating points of access between the services, stopping cascading failures across them, and providing fallback options, all of which improve your system’s overall resiliency. --[摘自官方]
官网:https://github.com/Netflix/Hystrix
作用
名词解析:
1.服务雪崩
在微服务之间进行服务调用是由于某一个服务故障,导致级联服务故障的现象,称为雪崩效应。雪崩效应描述的是提供方不可用,导致消费方不可用并将不可用逐渐放大的过程

如果此时,Service A的流量波动很大,流量经常会突然性增加!那么在这种情况下,就算Service A能扛得住请求,Service B和Service C未必能扛得住这突发的请求。此时,如果Service C因为抗不住请求,变得不可用。那么Service B的请求也会阻塞,慢慢耗尽Service B的线程资源,Service B就会变得不可用。紧接着,Service A也会不可用,这一过程如下图所示

2.服务熔断
“熔断器”本身是一种开关装置,当某个服务单元发生故障之后,通过断路器(hystrix)的故障监控,某个异常条件被触发,直接熔断整个服务。向调用方法返回一个符合预期的、可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方法无法处理的异常,就保证了服务调用方的线程不会被长时间占用,避免故障在分布式系统中蔓延,乃至雪崩。如果目标服务情况好转则恢复调用。服务熔断是解决服务雪崩的重要手段

3.服务降级
服务压力剧增的时候根据当前的业务情况及流量对一些服务和页面有策略的降级,以此缓解服务器的压力,以保证核心任务的进行。同时保证部分甚至大部分任务客户能得到正确的响应。也就是当前的请求处理不了了或者出错了,给一个默认的返回。

降级和熔断总结
1.共同点
2.异同点
3.总结
服务熔断的实现
1.创建一个单独的springboot工程,演示Hystrix
2.引入consul注册中心的相关依赖
3.引入Hystrix依赖
<!--引入hystrix-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
4.编写启动类 添加注解
@SpringBootApplication
@EnableDiscoveryClient
@EnableCircuitBreaker //开启hystrix熔断服务
public class HysTrixApplication {
public static void main(String[] args) {
SpringApplication.run(HysTrixApplication.class, args);
}
}
5.编写控制器 添加注解
@Controller
public class HystrixController {
@RequestMapping("/h1")
@ResponseBody
@HystrixCommand(fallbackMethod = "Test01fallbackMethod")
public String test01(Integer id){
if(id<0){
throw new RuntimeException("无效id!");
}
return "hystrix ok";
}
@ResponseBody
public String Test01fallbackMethod(Integer id){
return "商品过于火爆,服务被熔断,请稍后再试!"+id;
}
}
5.总结
6.断路器打开条件
1、 当满足一定的阀值的时候(默认10秒内超过20个请求失败)
2、 当失败率达到一定的时候(默认10秒内超过50%的请求失败)
3、 到达以上阀值,断路器将会开启
4、 当开启的时候,所有请求都不会进行转发
5、 一段时间之后(默认是5秒),这个时候断路器是半开状态,会让其中一个请求进行转发。如果成功,断路器会关闭,若失败,继续开启。重复4和5。

默认的服务FallBack处理方法
服务降级的实现
服务降级: 站在系统整体负荷角度 实现: 关闭系统中某些边缘服务 保证系统核心服务运行
Emps 核心服务 Depts 边缘服务
1.客户端openfeign + hystrix实现服务降级实现
2.开启openfeign支持服务降级
feign.hystrix.enabled=true #开启openfeign支持降级
3.在openfeign客户端中加如Hystrix
@FeignClient(value = "HYSTRIXCLIENT",fallback = HysClientFallback.class)
public interface HysClient {
@GetMapping("/h2")
String test02(@RequestParam Integer id);
}
4.开发fallback处理类
@Component
public class HysClientFallback implements HysClient{
@Override
public String test02(Integer id) {
return "服务器繁忙,请稍后再试!"+id;
}
}
码文不易,本篇文章就介绍到这里,如果想要学习更多Java系列知识,点击关注博主,博主带你零基础学习Java知识。与此同时,对于日常生活有困扰的朋友,欢迎阅读我的第四栏目:《国学周更—心性养成之路》,学习技术的同时,我们也注重了心性的养成。

我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div
我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看rubyzip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d
类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc
很好奇,就使用rubyonrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提
假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于
我正在尝试使用ruby和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我
关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。
我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t
我想安装一个带有一些身份验证的私有(private)Rubygem服务器。我希望能够使用公共(public)Ubuntu服务器托管内部gem。我读到了http://docs.rubygems.org/read/chapter/18.但是那个没有身份验证-如我所见。然后我读到了https://github.com/cwninja/geminabox.但是当我使用基本身份验证(他们在他们的Wiki中有)时,它会提示从我的服务器获取源。所以。如何制作带有身份验证的私有(private)Rubygem服务器?这是不可能的吗?谢谢。编辑:Geminabox问题。我尝试“捆绑”以安装新的gem..
我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h