注册中心
由于微服务多为集群部署,在这种情况下,微服务之间要调用彼此的接口,如果使用url或者ip地址的形式调用会带来很多麻烦,例如无法确定要连的主机是否可用。在这种情形下,便需要一个系统对所有的微服务进行统一的管理,实时的确定各个微服务所部署的主机的可用状态,这个系统就是注册中心。
注册中心主要的作用就是统一管理各个微服务,微服务向注册中心注册自己的信息,然后定时的和注册中心通信,以便注册中心区分可用和不可用的主机;而要调用服务的程序则向注册中心申请相应服务,注册中心返回给程序请求服务可用的主机ip以及端口号,这样实现服务的调用,常用的注册中心有eureka,zookeeper等,这里介绍了eureka的使用。
eureka是springcloud下的一个注册中心,通过它可以实现微服务的统一管理;
eureka的部署:
首先父工程的pom文件:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <!--springboot依赖,微服务工程使用springboot构建--> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.1.RELEASE</version> </parent> <groupId>cn.ly</groupId> <artifactId>ly_cloud_test</artifactId> <packaging>pom</packaging> <version>1.0-SNAPSHOT</version> <modules> <module>ly_eureka</module> <module>ly_feignribbon_test_server01</module> <module>ly_fergnribbon_test_server02</module> </modules> <properties> <okhttp.version>3.9.1</okhttp.version> <feign-okhttp.version>8.18.0</feign-okhttp.version> </properties> <!--依赖管理,对子工程进行相应的依赖版本管理--> <dependencyManagement> <dependencies> <!--springcloud依赖--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Finchley.SR1</version> <type>pom</type> <scope>import</scope> </dependency> <!--远程调用--> <dependency> <groupId>com.squareup.okhttp3</groupId> <artifactId>okhttp</artifactId> <version>${okhttp.version}</version> </dependency> <dependency> <groupId>com.squareup.okhttp3</groupId> <artifactId>okhttp</artifactId> <version>${okhttp.version}</version> </dependency> <dependency> <groupId>com.netflix.feign</groupId> <artifactId>feign-okhttp</artifactId> <version>${feign-okhttp.version}</version> </dependency> </dependencies> </dependencyManagement> </project>
然后建立eureka子工程ly_eureka,
引入依赖:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency>
配置文件application.yml:
server:
port: ${PORT:50101} #端口号
spring:
application:
name: ly-govern-center #服务名
eureka:
client:
register-with-eureka: false #是否向注册中心注册服务
fetch-registry: false #是否向注册中心发现服务
serviceUrl:
defaultZone: http://localhost:50101/eureka/ #注册中心地址,配置多服务器注册中心时需要将多个ip用‘,’分隔
server:
enable-self-preservation: false #自我保护设置,当微服务不再报告状态时,也不会立即将其删除,在开发阶段建议设置false
eviction-interval-timer-in-ms: 60000 #服务注册表清理间隔
instance:
hostname: ${EUREKA_DOMAIN:eureka01}
springboot的主函数:
package cn.ly; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; @SpringBootApplication @EnableEurekaServer // 声明这是一个eureka的服务器 public class GovernCenterApplication { public static void main(String[] args) { SpringApplication.run(GovernCenterApplication.class, args); } }
直接启动即可完成eureka注册中心的部署,启动后,在浏览器中输入:localhost:50101看到eureka页面即表明部署成功;
作为一个服务,其本身也是可以集群部署的,只要在配置文件defaultzone中将所有的要部署的ip写上去就行了,中间用逗号隔开;
向eureka中注册服务
服务使用springboot来搭建,创建子工程server01,引入依赖:
<!--向eureka注册服务依赖--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
在主函数上添加注解:
@EnableDiscoveryClient //表明该工程是eureka的客户端,会向eureka注册服务并从中发现服务
提供一个简单的controller:
package cn.ly.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; @Controller public class HelloController { int i = 0; @ResponseBody @RequestMapping("/hello") public String hello() { i++; System.out.println("被调用..."+i); return "hello"; } }
配置文件application.yml
server:
port: ${PORT:31001}
spring:
application:
name: ly-server01 #服务名,在下面的配置中作为实例名注册到eureka中,其他程序调用服务时需要向eureka提供要调用的实例名,即这个名称
eureka:
client:
fetch-registry: true #发现服务
register-with-eureka: true #注册服务
service-url:
defaultZone: ${EUREKA_server:http://localhost:50101/eureka/} #eureka的地址
instance:
prefer-ip-address: true #将自己的IP注册到eureka
ip-address: ${IP_ADDRESS:127.0.0.1} #注册的ip地址
instance-id: ${spring.application.name}:${server.port} #注册的实例名称
启动该服务,再次访问localhost:50101,即可看到:
如此,服务便注册成功了。
通过ribbon调用服务
ribbon是一个通过客户端实现负载均衡的系统,它通过服务名向eureka请求可用的服务url,然后通过负载均衡的算法选择一个可用的服务主机进行访问,获得结果;
建立另一个微服务子工程server02,和上述服务一样,需要做成eureka客户端,除此之外,还需要引入额外的依赖:
<dependency> <groupId>com.squareup.okhttp3</groupId> <artifactId>okhttp</artifactId>
</dependency>
另外,需要配置bean:
@Bean @LoadBalanced // 这个注解是实现负载均衡的,如果不写的话,就只能直接通过url访问其他服务 public RestTemplate restTemplate() { return new RestTemplate(new OkHttp3ClientHttpRequestFactory()); }
配置文件同上面的一致;
另外将上server01工程开上两份,开启时通过设置jvm的参数-DPORT 来设置不同的端口,否则端口会冲突;
通过restTemplate调用hello接口:
@Autowired RestTemplate restTemplate; @Test public void run() { for (int i = 0; i < 10; i++) {
// 直接通过服务名调用接口 ResponseEntity<String> forEntity = restTemplate.getForEntity("http://ly-server01/hello", String.class); String body = forEntity.getBody(); System.out.println(body); } }
上面调用了hello接口10次,对应于服务端,两个注册服务的服务分别被调用5次,这是因为默认的负载均衡的方式是轮询,也就是依次调用可用的主机;
通过feign调用
feign客户端调用远程http接口,可以实现向调用本地接口方法那样调用远程服务,依旧是server02工程调用server01工程的服务;
在server02中添加依赖:
<!--feign调用--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <dependency> <groupId>com.netflix.feign</groupId> <artifactId>feign-okhttp</artifactId> </dependency>
在主函数上添加注解:@EnableFeignClients
在该工程下创建一个feign接口如下:
package cn.ly.client; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.RequestMapping; @FeignClient("ly-server01") // feign客户端注解,value是要调用的服务名,就是server01工程的服务名 public interface HelloClient {
// 注意一点,如果方法返回的是一个复杂对象类型,那么这个对象类型必须有无参构造器,否则会报错 @RequestMapping("/hello") public String hello();// 这个方法和要调用的服务的接口方法书写要一致,上面的url注意要书写正确且完整,如果被调用的接口的controller类上也有url,那么在这里需要将其补全 }
然后在测试方法中将这个接口注入进去后,即可直接使用方法:
@Autowired HelloClient helloClient; @Test public void run2() { for (int i = 0; i < 10; i++) { String hello = helloClient.hello(); System.out.println(hello); } }
上面依旧是调用了10次,分别在两个开启的server01工程中执行5次,轮询的负载均衡;