赞
踩
springcloud看上去很难,很恐怖,但是我一遍学下来,感觉还是蛮容易的,面向配置编程,这一篇主要是讲配置,细节可能比较少。首先我们的微服务应该有以下部分:
简单说一下,服务注册中心就是我们的一个类,可以有很多方法,但是我们的方法只能被我们同一个项目中的其他类调用,或者说我电脑上写了一个类,有很多方法,但是我没办法让另一台电脑调用这个方法,解决方案就是将这个类注册进一个注册中心,然后其他的(电脑)通过网络连接从注册中心中就可以去访问我们的方法。
这么做的好处是我们的业务拆分了,我们把那些不同的业务直接拆分,如果需要调用别的业务方法,去注册中心去拿,而且我们可以每一个业务都准备好几份客户端,这样爆掉几也不会怎么样,高可用。而且我们还可以在项目运营的时候进行版本升级,什么意思呢?比如说我们开了100个支付服务,但是我们又要引入新的支付方式,原来的单体项目只能项目下线,然后重新部署。但这里我们可以先关闭10台机器,然后进行版本升级,再部署上线,然后慢慢的把这100台都更新到新版本,这个过程可以靠定时任务来做,这就是业务拆分的好处。
简单的说就是去服务注册中心去调用其他的服务,一般而言我们的springcloud中任何的组件都是一个服务,可以理解为是一个独立的项目。
我们的业务拆分了,除了可以每个业务部署多个做高可用以外,还可以做一些异常处理。平时单体项目如果出现异常是一件很恐怖的事情,因为我们可能是出现了BUG了,或者机房着火了,反正线上运营出现问题就很难受,导致我们的项目如果做的很大就会出现各种事故。
服务降级是指我们A服务调用B业务的方法,但是B服务很久没响应,报错了,机房着火了,服务器炸了之类的。我们A服务可以自己准备一个兜底的方法,比如访问一个资源找不到,如果把404交给客户,客户可能以为自己网断了,我们可以给用户一个友好的提示界面:机房着火,请稍后再试,这样客户就知道不是自己的问题了。
这个不好解释,简单的说就是我们的JavaEE不是有三大组件Filter,Servlet,Listener吗?我们的项目可以理解为Servlet,基本都是Spring框架架起整个业务。我们的网关是一种Filter,这么好理解吧。我们的网关就是看门的,可以做很多事情,负载均衡,限流,权限等等,这个和Nginx很像。
这个得看项目才能体现到,简单的说,我们的每一个服务就是一个项目,有配置文件和springboot主启动类这些东西,然后我们的服务都要连接数据库吧,但是我们一般都是把数据库连接的账号密码都写在这些配置文件,有2个问题:我们很多服务都是连接同一套数据库,账号密码都是一样的我们要改得大规模去改;这么做不安全,一般而言数据库的数据应该有人负责和管理,密码知道的人少一点比较好也好排查商业卧底。
服务配置中心大概是这样的:运维人员先把我们的一些公用的配置都放在git上,然后添加一个配置中心服务,去读取git上的配置,如果git进行更改,比如运维人员要换数据库密码了,这时候我们的配置中心可以动态的获取最新的数据。然后我们每一个普通的服务都去配置中心拿数据,并且在配置中心更新时,应该可以让各个服务都相应的更改配置
比如git上放一个yml文件,里面有如下内容:
jdbc:
password: 123456
这时候运维人员密码改成123,然后配置中心刷新一手,然后告诉各个服务,哥们,我换密码了。卧槽,各个服务一一去读取新的密码。
其实微服务的东西很多,比如链路跟踪,日志跟踪,分布式锁,分布式事务很多的东西需要学,这一篇主要是讲一下springcloud-Netflix的各个组件,然后分享一个简单的小demo。
简单介绍一下项目
cloud-common:抽取公共的一些类,比如POJO类
cloud-config:配置中心
cloud-consumer:模拟一个消费者,会调用order的方法
cloud-order:模拟一个生产者,用于被别人调用,当然我们也可以直接调用
cloud-gateway:网关
cloud-eureka:注册中心
cloud-config-client:模拟一个普通的服务,其配置信息都是从配置中心拉取
先写一个生产者,生产者提供了两个简单的方法,我写个主启动类,启动服务后直接去调用这两个方法,不会出问题。
OrderController控制器
模拟一个很ok的业务方法,和一个用时比较久的方法,模拟一些超时的检测。
@RestController public class OrderController { @GetMapping("ok") public ResultData ok(){ return new ResultData(200,"理论上应该成功的正常方法_成功",null); } @GetMapping("timeout_success") public ResultData timeoutSuccess(){ try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } return new ResultData(200,"理论上应该成功的超时测试方法_成功",null); } }
现在我们想把这个服务注册进注册中心,这样我们的其他服务就可以去访问,这里我们选用Eureka注册中心。我们的这些服务都可以理解为一个客户端,都要注册进注册中心,就像我们的小公司都是在某个大厦里面租了几层,我们在一楼大厅就可以在企业的花名册看到我们的公司名。所以我们需要导入一个jar包,表示>spring-cloud-starter-netflix-eureka-client
表示这是一个客户端,然后我们进行一些客户端的配置。
<dependencies> <!-- eureka注册中心服务端 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <!-- 通用Api --> <dependency> <groupId>com.kaikeba</groupId> <artifactId>cloud-common</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
application.yml
我们的服务占用8001端口
服务名是:cloud-order-service,服务名可以重复的,表示同一个服务
instance-id: order-8000这个则不能重复,用于区分。如果不写,eureka会把端口加上服务名作为我们的
register-with-eureka: true和fetch-registry: true表示是否注册进注册中心,肯定啊。
defaultZone: http://eureka7001.com:7001/eureka 表示我们要连接的eureka服务器的地址,这里我们的eureka还没写好呢。
server: port: 8001 spring: application: name: cloud-order-service eureka: instance: instance-id: order-8000 prefer-ip-address: true client: register-with-eureka: true fetch-registry: true service-url: defaultZone: http://eureka7001.com:7001/eureka
同时我们导入了Eureka的配置,还需要再主启动类上去让他生效,使用@EnableEurekaClient
@SpringBootApplication
@EnableEurekaClient
public class OrderMainApplication {
public static void main(String[] args) {
SpringApplication.run(OrderMainApplication.class, args);
}
}
Eureka服务端端
和客户端的区别就是jar包后面跟着的是server,表示这个是一个Eureka的服务端。
<dependencies> <!-- eureka注册中心服务端 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
注意一下,hostname这个属性,表示服务器的名字,不能重复
register-with-eureka: false,fetch-registry: false表示我自己是注册中心,我自己是个服务,但是我们需要给别人看吗,我们这个服务根本没有任何业务逻辑,这里选择false,我们没必要吧自己也注册了吧。
server: port: 7001 spring: application: name: cloud-eureka-service eureka: instance: hostname: eureka7001.com client: register-with-eureka: false fetch-registry: false service-url: defaultZone: http://eureka7001.com:7001/eureka
主启动类,@EnableEurekaServer表示开启eureka服务注册中心。
@EnableEurekaServer
@SpringBootApplication
public class EurekaMainApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaMainApplication.class, args);
}
}
当然还有一步,那就是改host文件,具体可以看一下网上的帖子。
windows:C:\Windows\System32\drivers\etc\hosts 这个文件,然后加如下内容,我们这里加一个7001就行了
#springcloud
127.0.0.1 eureka7001.com
然后cmd输入以下命令
ipconfig/flushdns
出现一下内容表示我们刷新了我们的hosts文件的修改。
然后我们启动一下我们的服务,先启动注册中心再启动order。
先1后2,不然报错说找不到Eureka配置中心,访问localhost:7001可以看到如下内容:
我们可以看到我们的CLOUD-ORDER-SERVICE服务已经进入了注册中心,右边order-8000是我们配置的instance-id名。1表示我们现在只有一个服务,以后可以搞集群。
我们访问8001,可以成功访问的。
我们的Eureka是注册中心只是注册,我们要访问注册中心需要一个桥梁,我们要调用哪个服务的方法,怎么调用,要传递什么参数,这里我们需要添加一个组件,openFeign,这里我们不需要再去写一个发现中心,我们需要调用其他业务的方法时,只需要把我们这个服务添加一些注解支持,就可以去访问注册中心的内容
现在我们添加一个消费者,表示要调用别的服务。
pom.xml
<!-- 发现中心 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <!-- eureka注册中心服务端 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <!-- 通用Api --> <dependency> <groupId>com.kaikeba</groupId> <artifactId>cloud-common</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency>
application.xml
server: port: 81 spring: application: name: cloud-consumer-service eureka: instance: instance-id: consumer-81 prefer-ip-address: true client: register-with-eureka: true fetch-registry: true service-url: defaultZone: http://eureka7001.com:7001/eureka
这里我们要写一个接口,然后接口方法上加上RequestMapping的注解,然后我们的spring会为我们生成一个代理类,该类的方法的方法体就是我们order的方法体。简单的说,我们要调用一个服务,我们先写一个接口,当我们调用该接口方法时,会去注册中心找一个服务,然后把请求参数都交给他,最后这个服务会把结果返回给我们的的接口方法,我们只是一个调用者,没有属于自己的方法体。
@FeignClient(value = “CLOUD-ORDER-SERVICE”)表示我们该接口要调用哪个服务,然后该接口类的方法又是调用该服务的那些方法,最后使用@Component注解将接口交给spring,spring会为我们生产一个代理对象。
@Component
@FeignClient(value = "CLOUD-ORDER-SERVICE")
public interface ConsumerService {
@GetMapping("ok")
ResultData success();
@GetMapping("timeout_success")
ResultData timeoutSuccess();
}
Controller
这是我们的控制器,注入了consumerService,然后我们的自己写一套访问方式,这里的url请求可以自行修改。consumerService调用的是ok
,这里我们调用success
,没关系。
@RestController
public class ConsumerController {
@Resource
ConsumerService consumerService;
@GetMapping("success")
public ResultData ok(){
return consumerService.success();
}
@GetMapping("timeout_success")
public ResultData timeoutSuccess(){
return consumerService.timeoutSuccess();
}
}
主启动类
消费者本身也是一个服务,也把它注册进注册中心。我们又加入了openFeign服务发现,所以要是用**@EnableDiscoveryClient** 和**@EnableFeignClients这两个注解,一个标识开启服务发现,另一个标识开启Feign客户端的支持,我们的服务发现中心有很多,@EnableDiscoveryClient**是必须的,另一个看你用什么发现中心就用哪个。
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
@EnableEurekaClient
public class ConsumerMainApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerMainApplication.class, args);
}
}
我们运行主启动类,这里我们还是要先把eureka的服务启动再启动consumer和order。
我们看到两个服务都注册进eureka,当然注册没什么技术含量,关键是调用是否生效。
成功调用!其实我这里放了一个小彩蛋,那就是那个超时方法会报错,但是我们在8001去调用是却没什么问题,这里就是一些服务熔断的一些东西了,下一篇再讲哈。
我们调用原来的提供者的方法,也是可以的。
学会以上内容,我们的微服务就算入门了,我们现在可以进行一些简单的业务拆分,比如:
这是我之前写的快递e栈的项目,但是我们的电脑端和微信端很多东西都重复了,调用同一套POJO类,同一套的Controller,这里我们可以写一个provider-server,表示我这个服务专门提供方法的,其他的服务你们尽管来掉就完事了!我的e_expres-order-pc和e_expres-order-wx都去注册中心去拿e_express-provider-server
的方法,这就是简单的服务注册中心的应用了。我们以后的项目都是A调B,B调C,服务拆分不但好分工,而且BUG排查也非常的快。
注册中心是为了解决单体应用而生的技术,而网关,配置中心,服务熔断等都是为了解决微服务的问题而生的技术,所以学起来会觉得枯燥,这些内容放下一篇。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。