目录
1.为什么要用注册中心
1、微服务数量众多,要进行远程调用就需要知道服务端的ip地址和端口,注册中心帮助我们管理这些服务的ip和端口。
2、微服务会实时上报自己的状态,注册中心统一管理这些微服务的状态,将存在问题的服务踢出服务列表,客户端获取到可用的服务进行调用。
2.Eureka简介
Spring Cloud Eureka 是对Netflix公司的Eureka的二次封装,它实现了服务治理的功能,Spring Cloud Eureka提供服务端与客户端,服务端即是Eureka服务注册中心,客户端完成微服务向Eureka服务的注册与发现。服务端和客户端均采用Java语言编写。下图显示了Eureka Server与Eureka Client的关系:
1、Eureka Server是服务端,负责管理各各微服务结点的信息和状态。
2、在微服务上部署Eureka Client程序,远程访问Eureka Server将自己注册在Eureka Server。
3、微服务需要调用另一个微服务时从Eureka Server中获取服务调用地址,进行远程调用。
3.Ribbon介绍
Ribbon是Netflix公司开源的一个负载均衡的项目,它是一个基于HTTP、TCP的客户端负载均衡器。
1、什么是负载均衡?
负载均衡是微服务架构中必须使用的技术,通过负载均衡来实现系统的高可用、集群扩容等功能。负载均衡可通过硬件设备及软件来实现,硬件比如:F5、Array等,软件比如:LVS、Nginx等。
如下图是负载均衡的架构图:
用户请求先到达负载均衡器(也相当于一个服务),负载均衡器根据负载均衡算法将请求转发到微服务。负载均衡算法有:轮训、随机、加权轮训、加权随机、地址哈希等方法,负载均衡器维护一份服务列表,根据负载均衡算法将请求转发到相应的微服务上,所以负载均衡可以为微服务集群分担请求,降低系统的压力。
2、什么是客户端负载均衡?
上图是服务端负载均衡,客户端负载均衡与服务端负载均衡的区别在于客户端要维护一份服务列表,Ribbon从Eureka Server获取服务列表,Ribbon根据负载均衡算法直接请求到具体的微服务,中间省去了负载均衡服务。
如下图是Ribbon负载均衡的流程图:
1、在消费微服务中使用Ribbon实现负载均衡,Ribbon先从EurekaServer中获取服务列表。
2、Ribbon根据负载均衡的算法去调用微服务。
3.实例后端
3.1 创建父子工程
3.2 总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>
<groupId>com.wo</groupId>
<artifactId>springCloud_parent</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>eureka</module>
<module>book</module>
<module>user</module>
<module>pojo</module>
</modules>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.3.RELEASE</version>
<relativePath/>
</parent>
<dependencyManagement>
<dependencies>
<!-- springcloud的pom依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Finchley.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.22</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.wo</groupId>
<artifactId>book</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
</dependencyManagement>
</project>
3.2 Book
1.pom文件
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>com.wo</groupId>
<artifactId>pojo</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.1.1</version>
</dependency>
</dependencies>
2.yml文件
server:
port: 8081
spring:
application:
name: book
datasource:
driver-class-name: com.mysql.jdbc.Driver
username: root
password: 123456
url: jdbc:mysql://localhost:3306/qf?useUnicode=true&characterEncoding=utf8&useSSL=false
mybatis:
mapper-locations: classpath:mapper/*Mapper.xml
eureka:
client:
service-url:
defaultZone: http://localhost:8888/eureka
instance:
#标注当前工程注册时,使用ip地址的方式
prefer-ip-address: true
hostname: ${spring.cloud.client.ip-address}
instance-id: http://${spring.cloud.client.ip-address}:${server.port}
3.启动类
@SpringBootApplication
//标注当前工程是eureka的客户端
@EnableDiscoveryClient
public class BookSpringBootApplication {
public static void main(String[] args) {
SpringApplication.run(BookSpringBootApplication.class);
}
}
4.dao及mapper
@Mapper
public interface BookDao {
//查询所有
public List<BookPojo> findAll();
public BookPojo findById(int id);
public void delete(int id);
public void update(BookPojo bookPojo);
public void insert(BookPojo bookPojo);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.wo.dao.BookDao">
<select id="findAll" resultType="pojo.BookPojo">
SELECT * from book
</select>
<select id="findById" parameterType="int" resultType="pojo.BookPojo">
select * from book where id=#{id}
</select>
<delete id="delete" parameterType="int">
delete from book where id=#{id}
</delete>
<update id="update" parameterType="pojo.BookPojo">
UPDATE book SET name=#{name},time=#{time},price=#{price} where id=#{id};
</update>
<insert id="insert" parameterType="pojo.BookPojo">
insert into book VALUES (null,#{name},#{time},#{price})
</insert>
</mapper>
5.service及serviceimpls
public interface BookService {
public List<BookPojo> findAll();
public void delete(int id);
public BookPojo findById(int id);
public void update(BookPojo bookPojo);
public void insert(BookPojo bookPojo);
}
@Service
public class BookServiceImpl implements BookService{
@Autowired
BookDao bookDao;
@Override
public List<BookPojo> findAll() {
return bookDao.findAll();
}
@Override
public void delete(int id) {
bookDao.delete(id);
}
@Override
public BookPojo findById(int id) {
return bookDao.findById(id);
}
@Override
public void update(BookPojo bookPojo) {
bookDao.update(bookPojo);
}
@Override
public void insert(BookPojo bookPojo) {
bookDao.insert(bookPojo);
}
}
6.controller
@RestController
@RequestMapping("/book")
public class BookController {
@Autowired
BookService bookService;
@RequestMapping("/findAll")
public List<BookPojo> findAll(){
return bookService.findAll();
}
@RequestMapping("/findById")
public BookPojo findById(@RequestBody Map map){
Integer id = (Integer) map.get("id");
return bookService.findById(id);
}
@RequestMapping("/delete")
public void delete(@RequestBody Map map){
Integer id = (Integer) map.get("id");
bookService.delete(id);
}
@RequestMapping("/update")
public void update(@RequestBody BookPojo bookPojo){
bookService.update(bookPojo);
}
@RequestMapping("/insert")
public void insert(@RequestBody BookPojo bookPojo){
bookService.insert(bookPojo);
}
}
3.3 eureka
1.pom文件
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<!-- eureka服务端依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
2.yml配置
server:
port: 8888
eureka:
client:
#标注当前工程是否注册到eureka
register-with-eureka: false
#标注当前工程是否从eureka获取注册信息
fetch-registry: false
server:
#关闭自我保护机制
enable-self-preservation: false
spring:
application:
name: eureka
3.启动类
@SpringBootApplication
//标注当前工程是eureka的服务端
@EnableEurekaServer
public class EurekaSpringBootApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaSpringBootApplication.class);
}
}
3.4 pojo
1.pom文件
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
2.BookPojo
@Data
public class BookPojo {
private Integer id;
private String name;
private String time;
private Double price;
}
3.5 user
1.pom文件
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- eureka 客户端的依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- ribbon负载均衡的依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
<dependency>
<groupId>com.wo</groupId>
<artifactId>pojo</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
2.yml配置文件
server:
port: 8083
spring:
application:
name: user
eureka:
client:
service-url:
defaultZone: http://localhost:8888/eureka
instance:
#标注当前工程注册时,使用ip地址的方式
prefer-ip-address: true
hostname: ${spring.cloud.client.ip-address}
instance-id: http://${spring.cloud.client.ip-address}:${server.port}
3.启动类
@SpringBootApplication
//标注当前工程是eureka的客户端
@EnableEurekaClient
public class UserSpringBootApplication {
public static void main(String[] args) {
SpringApplication.run(UserSpringBootApplication.class);
}
// Springboot结合httpclient
@Bean
@LoadBalanced//开启负载均衡
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
4.controller
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
RestTemplate restTemplate;
@Autowired
EurekaClient eurekaClient;
// @RequestMapping("/findAll")
// public List<BookPojo> findAll(){
//从eureka上通过spring.application.name 来获取所调用服务的ip地址
InstanceInfo book = eurekaClient.getNextServerFromEureka("book", false);
//获取到所调用的服务的ip:port
String homePageUrl = book.getHomePageUrl();
System.out.println(homePageUrl);
//远程调用查询所有
//第一种方法:ip和端口号写死
//String forObject = restTemplate.getForObject("http://localhost:8081/book/findAll", String.class);
//第二种方法:从eureka根据name名称获取ip和端口号 比如:http://10.12.155.110:8081/
String forObject = restTemplate.getForObject(homePageUrl+"book/findAll", String.class);
//
//第三种方法:定义所要调用服务的instanid,根据name值获得到
String instanId = "book";
String forObject = restTemplate.getForObject("http://"+instanId+"/book/findAll", String.class);
//
// String instanId = "book";
// List forObject = restTemplate.getForObject("http://" + instanId + "/book/findAll", List.class);
// return forObject;
// }
String instanId = "book";
HttpHeaders headers = new HttpHeaders();
@RequestMapping("/findAll")
public List<BookPojo> findAll(){
return restTemplate.getForObject("http://" + instanId + "/book/findAll", List.class);
}
@RequestMapping("/findById")
public BookPojo findById(@RequestBody Map map){
headers.add("Accept", "application/json");
HttpEntity requestEntity = new HttpEntity(map, headers);
ResponseEntity<BookPojo> bookPojoResponseEntity = restTemplate.postForEntity("http://" + instanId + "/book/findById", requestEntity, BookPojo.class);
return bookPojoResponseEntity.getBody();
}
@RequestMapping("/delete")
public void delete(@RequestBody Map map){
headers.add("Accept", "application/json");
HttpEntity requestEntity = new HttpEntity(map, headers);
restTemplate.postForEntity("http://" + instanId + "/book/delete", requestEntity,null);
}
@RequestMapping("/update")
public void update(@RequestBody BookPojo bookPojo){
headers.add("Accept", "application/json");
HttpEntity requestEntity = new HttpEntity(bookPojo, headers);
restTemplate.postForEntity("http://" + instanId + "/book/update", requestEntity,null);
}
@RequestMapping("/insert")
public void insert(@RequestBody BookPojo bookPojo){
headers.add("Accept", "application/json");
HttpEntity requestEntity = new HttpEntity(bookPojo, headers);
restTemplate.postForEntity("http://" + instanId + "/book/insert", requestEntity,null);
}
}
3.6.后端测试运行
user远程调用book
4.实例前端vue
4.1 跨域
proxyTable: {
'/api': {
target: 'http://localhost:8083/', // 设置你调用的接口域名和端口号
changeOrigin: true, // 跨域
pathRewrite: {
'^/api': '/'
}
}
},
4.2 index.vue
<template>
<div>
<center>
<input type="button" @click="insert()" value="增加">
<table border="1" >
<tr>
<th>ID</th>
<th>书名</th>
<th>出版日期</th>
<th>价格</th>
<th>操作</th>
</tr>
<tr v-for="(book,index) in booklist">
<td>{{book.id}}</td>
<td>{{book.name}}</td>
<td>{{book.time}}</td>
<td>{{book.price}}</td>
<td>
<input type="button" @click="update(book.id)" value="修改">
<input type="button" @click="del(book.id)" value="删除">
</td>
</tr>
</table>
</center>
</div>
</template>
<script>
import axios from 'axios';
export default {
data () {
return {
booklist:[]
}
},
methods:{
findAll:function () {
axios.get("api/user/findAll").then(res=>{
this.booklist = res.data;
})
},
update:function (id) {
this.$router.push({name:"update",params:{id:id}})
},
insert:function () {
this.$router.push("/update")
},
del:function (id) {
axios.post("api/user/delete",{id:id}).then(res=>{
this.findAll();
})
}
},
mounted(){
this.findAll();
}
}
</script>
4.3 update.vue
<template>
<div>
<form>
<input v-model="book.id" type="hidden">
书名:<input v-model="book.name" type="text"><br>
出版日期:<input v-model="book.time" type="text"><br>
价格:<input v-model="book.price" type="text"><br>
<input type="button" @click="sub()" value="新增">
<input type="button" @click="sub1()" value="修改">
</form>
</div>
</template>
<script>
import axios from 'axios';
export default {
data () {
return {
book:{}
}
},
methods:{
findById:function (id) {
//使用id去后台进行查询
axios.post("/api/user/findById",{id:id}).then(res=>{
console.log(res.data)
this.book=res.data;
})
},
sub:function () {
axios.post("/api/user/insert",this.book).then(res=>{
this.$router.push("/")
})
},
sub1:function () {
axios.post("/api/user/update",this.book).then(res=>{
this.$router.push("/")
})
}
},
mounted(){
var id = this.$route.params.id
if (id!=undefined){
this.findById(id)
}
}
}
</script>
4.4前端测试
访问的是后端usercontroller,后端user在访问book,一切正常