springcloud注册服务原理(Spring Cloud使用Consul作为注册中心示例)
最近在极客时间上面学习丁雪丰老师的《玩转 Spring 全家桶》 ,学到了服务注册中心这块 ,动手实践了一下 ,老师的视频录制是3年前 ,现在也有了些变化 ,自己也动手解决了一下 ,只有自己写一写才理解的更加细致一些 ,不然光看一遍视频 ,有点走马观花 。 现将主要过程记录如下
准备工作
首先在本地docker上面安装Consul
docker pull consul docker run --name my_consul -d -p 8500:8500 -p 8600:8600/udp consul在8500端口上面启动了consul , 在浏览器上输入http://localhost:8500/ 就可以看到打开如下图的一个界面 。
编写两个服务
我们这里简单的做两个服务 ,一个bookshop-service , 它提供读写表的服务;另外一个是book-customer-service , 它调用bookshop-service在控制台输出读取到的数据 。
父POM文件定义 <properties> <java.version>19</java.version> <spring-cloud.version>2022.0.0</spring-cloud.version> <spring-boot.version>3.0.0</spring-boot.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>${spring-boot.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>在dependencyManagement中引入spring-boot-dependencies和spring-cloud-dependencies
编写bookshop-service服务
首先在POM中引入需要用到的包
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-consul-discovery</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency> </dependencies>spring-cloud-starter-consul-discovery就是用来和consul通信的包
spring-boot-starter-data-jpa 和 h2是我们访问数据库的包,我们使用内存数据库h2
定义一个model @Entity @Table(name = "T_BOOK") @Data @Builder @NoArgsConstructor @AllArgsConstructor public class Book implements Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; private String author; @Column(updatable = false) @CreationTimestamp private Date createTime; @UpdateTimestamp private Date updateTime; }在resouces下面放入schema.sql用来建表和插入数据
drop table t_book if exists; create table t_book ( id bigint auto_increment, create_time timestamp, update_time timestamp, name varchar(255), author varchar(200), primary key (id) ); insert into t_book (name, author, create_time, update_time) values (python, zhangsan, now(), now()); insert into t_book (name, author, create_time, update_time) values (hadoop, lisi, now(), now()); insert into t_book (name, author, create_time, update_time) values (java, wangwu, now(), now());定义一个JpaRepository
@RepositoryRestResource(collectionResourceRel = "rest-books", path = "rest-books") public interface BookRepository extends JpaRepository<Book, Long> { List<Book> findByName(@Param("name") String name); }我还加了一个hateoas的注解 ,可以对外提供restful服务 ,
定义一个controller提供服务
@RestController @RequestMapping("/book") @Slf4j public class BookController { @Autowired private BookService bookService; @GetMapping(path = "/getAll", params = "!name") public List<Book> getAll() { return bookService.getAllBook(); } @GetMapping("/{id}") public Optional<Book> getById(@PathVariable Long id) { Optional<Book> book = bookService.getBook(id); return book; } @GetMapping(path = "/", params = "name") public Optional<Book> getByName(@RequestParam String name) { return bookService.getBook(name); } }BookService就是调用BookRepository,这里代码省略
在application.properties中加入如下配置 spring.application.name=bookshop-service server.port=0 spring.cloud.consul.host=localhost spring.cloud.consul.port=8500 spring.cloud.consul.discovery.prefer-ip-address=true spring.jpa.hibernate.ddl-auto=none spring.jpa.properties.hibernate.show_sql=true spring.jpa.properties.hibernate.format_sql=true management.endpoints.web.exposure.include=* management.endpoint.health.show-details=always management.health.redis.enabled=false到这里这个服务就编写好了 ,我们启动它
我们可以看到这个服务就已经上来了 。编写book-customer-service
它是一个控制台程序 ,我们为了演示它 ,也把它弄成了一个service
首先依然是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-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-rest</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-consul-discovery</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.7</version> </dependency> </dependencies>定义一个ApplicationRunner来取数据和打印数据
@Component @Slf4j public class BookCustomerRunner implements ApplicationRunner { @Autowired private RestTemplate restTemplate; @Autowired private DiscoveryClient discoveryClient; @Override public void run(ApplicationArguments args) throws Exception { showServiceInstances(); readBooks(); queryBook(1L); } private void showServiceInstances() { log.info("DiscoveryClient: {}", discoveryClient.getClass().getName()); discoveryClient.getInstances("bookshop-service").forEach(s -> { log.info("Host: {}, Port: {}", s.getHost(), s.getPort()); }); } private void readBooks() { ParameterizedTypeReference<List<Book>> ptr = new ParameterizedTypeReference<List<Book>>() {}; ResponseEntity<List<Book>> list = restTemplate .exchange("http://bookshop-service/book/getAll", HttpMethod.GET, null, ptr); list.getBody().forEach(c -> log.info("Book: {}", c)); } private void queryBook(Long id){ Book book = restTemplate .getForObject("http://bookshop-service/book/{id}", Book.class, id); log.info("Book: {}", book); } }这里我们可以看到我们使用了“http://bookshop-service/book/getAll ” , 它就是我们注册到consul的服务 ,我们在浏览器输入是拿不到数据的 ,这里spring帮我们做了转换 ,我们的showServiceInstances ,就是打印出真实的host和port 。
在这个示例中我们的输出如下
它打印出来对应的Host和Post ,然后打印出来了我们从数据库里读出来的几条数据 。
我们来看一下现在consul是怎么显示的 , 可以看到book-customer-service也已经上来了 。
最后
代码我上传到了https://github.com/dengkun39/redisdemo
在学习的时候我还使用了zookeeper来作为服务注册中心 , 代码几乎不需要动 ,只是需要把spring-cloud-starter-consul-discovery 换成spring-cloud-starter-zookeeper-discovery,然后配置文件中加上spring.cloud.zookeeper.connect-string=localhost:2181 。 使用zookeeper遇到一段时间后再访问就出现 ”I/O error on GET request for "http://bookshop-service/book/getAll": Permission denied: no further information“ 应该是有些设置没有弄到位 , 刚开始学习 ,没有办法细究是怎么回事 。服务注册中心学完了,如果需要对外提供服务应该怎么弄呢?总不能像我这样明明是控制台也把它弄成一个服务吧 。 这个问题我暂时不知道答案 ,留在这里 ,后面学习了再来回答它 。
创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!